Merge "Update New Project Template Dependencies" am: f908080572 am: f4369128a0 am: 0562b43a09 am: 22b7ea6e04 am: fbab2ed4ed
am: b3d01579a0
* commit 'b3d01579a09d632ecbd090c533687256e520d74f':
diff --git a/.idea/dictionaries/adt.xml b/.idea/dictionaries/adt.xml
index 6a5aa48..ecf4830 100644
--- a/.idea/dictionaries/adt.xml
+++ b/.idea/dictionaries/adt.xml
@@ -4,6 +4,7 @@
<w>&amp</w>
<w>aapt</w>
<w>aarrggbb</w>
+ <w>abis</w>
<w>accessors</w>
<w>actionbar</w>
<w>adb's</w>
@@ -288,6 +289,7 @@
<w>versioncode</w>
<w>vert</w>
<w>vmsig</w>
+ <w>waitable</w>
<w>wakelock</w>
<w>wakelocks</w>
<w>wakeup</w>
@@ -314,4 +316,4 @@
<w>zipalign</w>
</words>
</dictionary>
-</component>
\ No newline at end of file
+</component>
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 0cf047b..51d9659 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
<component name="InspectionProjectProfileManager">
<profile version="1.0" is_locked="false">
<option name="myName" value="Project Default" />
@@ -106,6 +107,7 @@
<inspection_tool class="DollarSignInName" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EqualsHashCodeCalledOnUrl" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="FileEqualsUsage" enabled="false" level="WARNING" enabled_by_default="false" />
+ <inspection_tool class="GroovyAssignabilityCheck" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="GtkPreferredJComboBoxRenderer" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlExtraClosingTag" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="HtmlUnknownAttribute" enabled="false" level="WARNING" enabled_by_default="false">
diff --git a/.idea/libraries/JUnit4.xml b/.idea/libraries/JUnit4.xml
index 4a45849..463c02b 100644
--- a/.idea/libraries/JUnit4.xml
+++ b/.idea/libraries/JUnit4.xml
@@ -2,10 +2,12 @@
<library name="JUnit4">
<CLASSES>
<root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/junit/junit/4.12/junit-4.12.jar!/" />
+ <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/junit/junit/4.12/junit-4.12-sources.jar!/" />
+ <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-sources.jar!/" />
</SOURCES>
</library>
</component>
\ No newline at end of file
diff --git a/.idea/libraries/builder_model.xml b/.idea/libraries/builder_model.xml
index 10e5fa0..24e07b5 100644
--- a/.idea/libraries/builder_model.xml
+++ b/.idea/libraries/builder_model.xml
@@ -1,11 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
<component name="libraryTable">
<library name="builder-model">
<CLASSES>
- <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/builder-model/builder-model-1.1.0-rc1.jar!/" />
+ <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/offline-m2/com/android/tools/build/builder-model/1.3.0/builder-model-1.3.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
- <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/builder-model/builder-model-1.1.0-rc1-sources.jar!/" />
+ <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/offline-m2/com/android/tools/build/builder-model/1.3.0/builder-model-1.3.0-sources.jar!/" />
</SOURCES>
</library>
</component>
\ No newline at end of file
diff --git a/.idea/libraries/ecj.xml b/.idea/libraries/ecj.xml
index 2cb447d..d92fa51 100644
--- a/.idea/libraries/ecj.xml
+++ b/.idea/libraries/ecj.xml
@@ -1,11 +1,11 @@
<component name="libraryTable">
<library name="ecj">
<CLASSES>
- <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4/ecj-4.4.jar!/" />
+ <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4.2/ecj-4.4.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
- <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4/ecj-4.4-sources.jar!/" />
+ <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4.2/ecj-4.4.2-sources.jar!/" />
</SOURCES>
</library>
</component>
\ No newline at end of file
diff --git a/.idea/libraries/jack_api_0_1.xml b/.idea/libraries/jack_api_0_1.xml
deleted file mode 100644
index c8406b4..0000000
--- a/.idea/libraries/jack_api_0_1.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<component name="libraryTable">
- <library name="jack-api-0.1">
- <CLASSES>
- <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/com/android/tools/jack/jack-api/0.1/jack-api-0.1.jar!/" />
- </CLASSES>
- <JAVADOC />
- <SOURCES />
- </library>
-</component>
\ No newline at end of file
diff --git a/.idea/libraries/jsr_305.xml b/.idea/libraries/jsr_305.xml
new file mode 100644
index 0000000..788b3ab
--- /dev/null
+++ b/.idea/libraries/jsr_305.xml
@@ -0,0 +1,11 @@
+<component name="libraryTable">
+ <library name="jsr-305">
+ <CLASSES>
+ <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar!/" />
+ </SOURCES>
+ </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/truth.xml b/.idea/libraries/truth.xml
index a97570c..97137f6 100644
--- a/.idea/libraries/truth.xml
+++ b/.idea/libraries/truth.xml
@@ -1,11 +1,11 @@
<component name="libraryTable">
<library name="truth">
<CLASSES>
- <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/com/google/truth/truth/0.24/truth-0.24.jar!/" />
+ <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/com/google/truth/truth/0.26/truth-0.26.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
- <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/com/google/truth/truth/0.24/truth-0.24-sources.jar!/" />
+ <root url="jar://$PROJECT_DIR$/../../prebuilts/tools/common/m2/repository/com/google/truth/truth/0.26/truth-0.26-sources.jar!/" />
</SOURCES>
</library>
</component>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 3f3d65f..8ec0917 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -6,6 +6,19 @@
</component>
<component name="EntryPointsManager">
<entry_points version="2.0" />
+ <list size="11">
+ <item index="0" class="java.lang.String" itemvalue="org.gradle.api.tasks.TaskAction" />
+ <item index="1" class="java.lang.String" itemvalue="org.gradle.model.Defaults" />
+ <item index="2" class="java.lang.String" itemvalue="org.gradle.model.Finalize" />
+ <item index="3" class="java.lang.String" itemvalue="org.gradle.model.Model" />
+ <item index="4" class="java.lang.String" itemvalue="org.gradle.model.Mutate" />
+ <item index="5" class="java.lang.String" itemvalue="org.gradle.model.Validate" />
+ <item index="6" class="java.lang.String" itemvalue="org.gradle.platform.base.BinaryTasks" />
+ <item index="7" class="java.lang.String" itemvalue="org.gradle.platform.base.BinaryType" />
+ <item index="8" class="java.lang.String" itemvalue="org.gradle.platform.base.ComponentBinaries" />
+ <item index="9" class="java.lang.String" itemvalue="org.gradle.platform.base.ComponentType" />
+ <item index="10" class="java.lang.String" itemvalue="org.gradle.platform.base.LanguageType" />
+ </list>
</component>
<component name="FrameworkDetectionExcludesConfiguration">
<type id="android" />
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 6ffad99..5d2d335 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -15,10 +15,13 @@
<module fileurl="file://$PROJECT_DIR$/draw9patch/draw9patch.iml" filepath="$PROJECT_DIR$/draw9patch/draw9patch.iml" />
<module fileurl="file://$PROJECT_DIR$/device_validator/dvlib/dvlib.iml" filepath="$PROJECT_DIR$/device_validator/dvlib/dvlib.iml" />
<module fileurl="file://$PROJECT_DIR$/sdk-common/generate-locale-data/generate-locale-data.iml" filepath="$PROJECT_DIR$/sdk-common/generate-locale-data/generate-locale-data.iml" />
+ <module fileurl="file://$PROJECT_DIR$/build-system/google-services/google-services.iml" filepath="$PROJECT_DIR$/build-system/google-services/google-services.iml" />
<module fileurl="file://$PROJECT_DIR$/build-system/gradle/gradle.iml" filepath="$PROJECT_DIR$/build-system/gradle/gradle.iml" />
<module fileurl="file://$PROJECT_DIR$/build-system/gradle-core/gradle-core.iml" filepath="$PROJECT_DIR$/build-system/gradle-core/gradle-core.iml" />
<module fileurl="file://$PROJECT_DIR$/build-system/gradle-experimental/gradle-experimental.iml" filepath="$PROJECT_DIR$/build-system/gradle-experimental/gradle-experimental.iml" />
<module fileurl="file://$PROJECT_DIR$/build-system/integration-test/integration-test.iml" filepath="$PROJECT_DIR$/build-system/integration-test/integration-test.iml" />
+ <module fileurl="file://$PROJECT_DIR$/jack/jack-api/jack-api.iml" filepath="$PROJECT_DIR$/jack/jack-api/jack-api.iml" />
+ <module fileurl="file://$PROJECT_DIR$/jack/jill-api/jill-api.iml" filepath="$PROJECT_DIR$/jack/jill-api/jill-api.iml" />
<module fileurl="file://$PROJECT_DIR$/layoutlib-api/layoutlib-api-base.iml" filepath="$PROJECT_DIR$/layoutlib-api/layoutlib-api-base.iml" />
<module fileurl="file://$PROJECT_DIR$/lint/libs/lint-api/lint-api-base.iml" filepath="$PROJECT_DIR$/lint/libs/lint-api/lint-api-base.iml" />
<module fileurl="file://$PROJECT_DIR$/lint/libs/lint-checks/lint-checks-base.iml" filepath="$PROJECT_DIR$/lint/libs/lint-checks/lint-checks-base.iml" />
diff --git a/.idea/scopes/custom_scope.xml b/.idea/scopes/custom_scope.xml
new file mode 100644
index 0000000..1acfd41
--- /dev/null
+++ b/.idea/scopes/custom_scope.xml
@@ -0,0 +1,3 @@
+<component name="DependencyValidationManager">
+ <scope name="custom_scope" pattern="(src:*..*||test:*..*)&&!test[manifest-merger-base]:com.android.manifmerger.data..*&&!test[manifest-merger-base]:com.android.manifmerger.data2..*&&!test[lint-cli]:com.android.tools.lint.checks.data..*&&!test[sdk-common-base]:testData..*&&!test[builder]:testData..*&&!test[assetstudio-base]:com.android.assetstudiolib.testdata..*&&!src[assetstudio-base]:images..*" />
+</component>
\ No newline at end of file
diff --git a/annotations/build.gradle b/annotations/build.gradle
index 506a91f..dbfcea6 100644
--- a/annotations/build.gradle
+++ b/annotations/build.gradle
@@ -1,12 +1,3 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
apply plugin: 'sdk-java-lib'
diff --git a/annotations/src/main/java/com/android/annotations/NonNull.java b/annotations/src/main/java/com/android/annotations/NonNull.java
index 973ebb6..e306a31 100644
--- a/annotations/src/main/java/com/android/annotations/NonNull.java
+++ b/annotations/src/main/java/com/android/annotations/NonNull.java
@@ -32,7 +32,7 @@
* This is a marker annotation and it has no specific attributes.
*/
@Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.SOURCE)
@Target({METHOD,PARAMETER,LOCAL_VARIABLE,FIELD})
public @interface NonNull {
}
diff --git a/annotations/src/main/java/com/android/annotations/NonNullByDefault.java b/annotations/src/main/java/com/android/annotations/NonNullByDefault.java
index 3db891c..23903d1 100644
--- a/annotations/src/main/java/com/android/annotations/NonNullByDefault.java
+++ b/annotations/src/main/java/com/android/annotations/NonNullByDefault.java
@@ -41,7 +41,7 @@
* This is a marker annotation and it has no specific attributes.
*/
@Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.SOURCE)
@Target({PACKAGE, TYPE})
public @interface NonNullByDefault {
}
diff --git a/annotations/src/main/java/com/android/annotations/Nullable.java b/annotations/src/main/java/com/android/annotations/Nullable.java
index d9c3861..376c1f6 100755
--- a/annotations/src/main/java/com/android/annotations/Nullable.java
+++ b/annotations/src/main/java/com/android/annotations/Nullable.java
@@ -43,7 +43,7 @@
* This is a marker annotation and it has no specific attributes.
*/
@Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.SOURCE)
@Target({METHOD, PARAMETER, LOCAL_VARIABLE, FIELD})
public @interface Nullable {
}
diff --git a/annotations/src/main/java/com/android/annotations/concurrency/GuardedBy.java b/annotations/src/main/java/com/android/annotations/concurrency/GuardedBy.java
index 9489bb1..ec606b7 100644
--- a/annotations/src/main/java/com/android/annotations/concurrency/GuardedBy.java
+++ b/annotations/src/main/java/com/android/annotations/concurrency/GuardedBy.java
@@ -27,7 +27,7 @@
* with the specified lock being held.
*/
@Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface GuardedBy {
String value();
diff --git a/annotations/src/main/java/com/android/annotations/concurrency/Immutable.java b/annotations/src/main/java/com/android/annotations/concurrency/Immutable.java
index d6c9a4a..a794d68 100644
--- a/annotations/src/main/java/com/android/annotations/concurrency/Immutable.java
+++ b/annotations/src/main/java/com/android/annotations/concurrency/Immutable.java
@@ -27,7 +27,7 @@
* is immutable.
*/
@Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Immutable {
}
diff --git a/asset-studio/assetstudio-base.iml b/asset-studio/assetstudio-base.iml
index f25586b..9835f9f 100644
--- a/asset-studio/assetstudio-base.iml
+++ b/asset-studio/assetstudio-base.iml
@@ -13,6 +13,6 @@
<orderEntry type="module" module-name="common" exported="" />
<orderEntry type="module" module-name="layoutlib-api-base" exported="" />
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
+ <orderEntry type="module" module-name="sdk-common-base" />
</component>
-</module>
-
+</module>
\ No newline at end of file
diff --git a/asset-studio/assetstudio.iml b/asset-studio/assetstudio.iml
index 2076a01..c11975b 100644
--- a/asset-studio/assetstudio.iml
+++ b/asset-studio/assetstudio.iml
@@ -13,6 +13,6 @@
<orderEntry type="module" module-name="common" exported="" />
<orderEntry type="module" module-name="layoutlib-api" exported="" />
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
+ <orderEntry type="module" module-name="sdk-common" />
</component>
-</module>
-
+</module>
\ No newline at end of file
diff --git a/asset-studio/build.gradle b/asset-studio/build.gradle
index bb335ce..1e186ad 100644
--- a/asset-studio/build.gradle
+++ b/asset-studio/build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'java'
+apply plugin: 'jacoco'
apply plugin: 'sdk-java-lib'
group = 'com.android.tools'
@@ -7,6 +8,7 @@
dependencies {
compile project(':base:layoutlib-api')
+ compile project(':base:sdk-common')
testCompile 'junit:junit:4.12'
}
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/ActionBarIconGenerator.java b/asset-studio/src/main/java/com/android/assetstudiolib/ActionBarIconGenerator.java
index 55f3409..8fa0a48 100644
--- a/asset-studio/src/main/java/com/android/assetstudiolib/ActionBarIconGenerator.java
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/ActionBarIconGenerator.java
@@ -15,8 +15,9 @@
*/
package com.android.assetstudiolib;
-import com.android.assetstudiolib.Util.Effect;
-import com.android.assetstudiolib.Util.FillEffect;
+import com.android.ide.common.util.AssetUtil;
+import com.android.ide.common.util.AssetUtil.Effect;
+import com.android.ide.common.util.AssetUtil.FillEffect;
import java.awt.Color;
import java.awt.Graphics2D;
@@ -40,29 +41,25 @@
? new Rectangle(0, 0, 32, 32)
: new Rectangle(4, 4, 24, 24);
final float scaleFactor = GraphicGenerator.getMdpiScaleFactor(options.density);
- Rectangle imageRect = Util.scaleRectangle(iconSizeMdpi, scaleFactor);
- Rectangle targetRect = Util.scaleRectangle(targetRectMdpi, scaleFactor);
- BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height);
+ Rectangle imageRect = AssetUtil.scaleRectangle(iconSizeMdpi, scaleFactor);
+ Rectangle targetRect = AssetUtil.scaleRectangle(targetRectMdpi, scaleFactor);
+ BufferedImage outImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
Graphics2D g = (Graphics2D) outImage.getGraphics();
- BufferedImage tempImage = Util.newArgbBufferedImage(
- imageRect.width, imageRect.height);
- Graphics2D g2 = (Graphics2D) tempImage.getGraphics();
- Util.drawCenterInside(g2, options.sourceImage, targetRect);
+ BufferedImage tempImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
+ Graphics2D g2 = (Graphics2D)tempImage.getGraphics();
+ AssetUtil.drawCenterInside(g2, options.sourceImage, targetRect);
if (actionBarOptions.theme == Theme.CUSTOM) {
- Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
- new FillEffect(new Color(actionBarOptions.customThemeColor), 0.8),
- });
+ AssetUtil.drawEffects(g, tempImage, 0, 0, new Effect[]{
+ new FillEffect(new Color(actionBarOptions.customThemeColor), 0.8),});
} else if (actionBarOptions.theme == Theme.HOLO_LIGHT) {
- Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
- new FillEffect(new Color(0x333333), 0.6),
- });
+ AssetUtil.drawEffects(g, tempImage, 0, 0,
+ new Effect[]{new FillEffect(new Color(0x333333), 0.6),});
} else {
assert actionBarOptions.theme == Theme.HOLO_DARK;
- Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
- new FillEffect(new Color(0xFFFFFF), 0.8)
- });
+ AssetUtil.drawEffects(g, tempImage, 0, 0,
+ new Effect[]{new FillEffect(new Color(0xFFFFFF), 0.8)});
}
g.dispose();
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/GraphicGenerator.java b/asset-studio/src/main/java/com/android/assetstudiolib/GraphicGenerator.java
index 360408b..693619a 100644
--- a/asset-studio/src/main/java/com/android/assetstudiolib/GraphicGenerator.java
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/GraphicGenerator.java
@@ -137,7 +137,10 @@
* example in unselected tabs we change foo.png to foo-unselected.png
*/
protected String getIconName(Options options, String name) {
- return name + ".png"; //$NON-NLS-1$
+ if (options.density == Density.ANYDPI) {
+ return name + SdkConstants.DOT_XML;
+ }
+ return name + SdkConstants.DOT_PNG; //$NON-NLS-1$
}
/**
@@ -146,6 +149,10 @@
* notification icons we add in -v9 or -v11.
*/
protected String getIconFolder(Options options) {
+ if (options.density == Density.ANYDPI) {
+ return SdkConstants.FD_RES + '/' +
+ ResourceFolderType.DRAWABLE.getName();
+ }
StringBuilder sb = new StringBuilder(50);
sb.append(SdkConstants.FD_RES);
sb.append('/');
@@ -176,6 +183,12 @@
*/
public void generate(String category, Map<String, Map<String, BufferedImage>> categoryMap,
GraphicGeneratorContext context, Options options, String name) {
+ // Vector image only need to generate one preview image, so we by pass all the
+ // other image densities.
+ if (options.density == Density.ANYDPI) {
+ generateImageAndUpdateMap(category, categoryMap, context, options, name);
+ return;
+ }
Density[] densityValues = Density.values();
// Sort density values into ascending order
Arrays.sort(densityValues, new Comparator<Density>() {
@@ -184,7 +197,6 @@
return d1.getDpiValue() - d2.getDpiValue();
}
});
-
for (Density density : densityValues) {
if (!density.isValidValueForDevice()) {
continue;
@@ -195,19 +207,27 @@
continue;
}
options.density = density;
- BufferedImage image = generate(context, options);
- if (image != null) {
- String mapCategory = category;
- if (mapCategory == null) {
- mapCategory = options.density.getResourceValue();
- }
- Map<String, BufferedImage> imageMap = categoryMap.get(mapCategory);
- if (imageMap == null) {
- imageMap = new LinkedHashMap<String, BufferedImage>();
- categoryMap.put(mapCategory, imageMap);
- }
- imageMap.put(getIconPath(options, name), image);
+ generateImageAndUpdateMap(category, categoryMap, context, options, name);
+ }
+ }
+
+ private void generateImageAndUpdateMap(String category,
+ Map<String, Map<String, BufferedImage>> categoryMap,
+ GraphicGeneratorContext context,
+ Options options,
+ String name) {
+ BufferedImage image = generate(context, options);
+ if (image != null) {
+ String mapCategory = category;
+ if (mapCategory == null) {
+ mapCategory = options.density.getResourceValue();
}
+ Map<String, BufferedImage> imageMap = categoryMap.get(mapCategory);
+ if (imageMap == null) {
+ imageMap = new LinkedHashMap<String, BufferedImage>();
+ categoryMap.put(mapCategory, imageMap);
+ }
+ imageMap.put(getIconPath(options, name), image);
}
}
@@ -223,6 +243,9 @@
* @return a factor to multiple mdpi distances with to compute the target density
*/
public static float getMdpiScaleFactor(Density density) {
+ if (density == Density.ANYDPI) {
+ density = Density.XXXHIGH;
+ }
return density.getDpiValue() / (float) Density.MEDIUM.getDpiValue();
}
@@ -288,10 +311,9 @@
*
* @return an iterator for the available image names
*/
- public static Iterator<String> getClipartNames() {
+ public static Iterator<String> getResourcesNames(String pathPrefix, String filenameExtension) {
List<String> names = new ArrayList<String>(80);
try {
- String pathPrefix = "images/clipart/big/"; //$NON-NLS-1$
ZipFile zipFile = null;
ProtectionDomain protectionDomain = GraphicGenerator.class.getProtectionDomain();
URL url = protectionDomain.getCodeSource().getLocation();
@@ -317,7 +339,7 @@
while (enumeration.hasMoreElements()) {
ZipEntry zipEntry = enumeration.nextElement();
String name = zipEntry.getName();
- if (!name.startsWith(pathPrefix) || !name.endsWith(".png")) { //$NON-NLS-1$
+ if (!name.startsWith(pathPrefix) || !name.endsWith(filenameExtension)) { //$NON-NLS-1$
continue;
}
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/LauncherIconGenerator.java b/asset-studio/src/main/java/com/android/assetstudiolib/LauncherIconGenerator.java
index bf69c85..040be4b 100644
--- a/asset-studio/src/main/java/com/android/assetstudiolib/LauncherIconGenerator.java
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/LauncherIconGenerator.java
@@ -17,6 +17,7 @@
package com.android.assetstudiolib;
import com.android.annotations.NonNull;
+import com.android.ide.common.util.AssetUtil;
import com.android.resources.Density;
import com.android.utils.Pair;
@@ -144,26 +145,26 @@
Rectangle imageRect = IMAGE_SIZE_WEB;
if (!launcherOptions.isWebGraphic) {
- imageRect = Util.scaleRectangle(IMAGE_SIZE_MDPI,
- GraphicGenerator.getMdpiScaleFactor(launcherOptions.density));
+ imageRect = AssetUtil.scaleRectangle(IMAGE_SIZE_MDPI, GraphicGenerator
+ .getMdpiScaleFactor(launcherOptions.density));
}
Rectangle targetRect = TARGET_RECTS.get(
Pair.of(launcherOptions.shape, launcherOptions.density));
if (targetRect == null) {
// Scale up from MDPI if no density-specific target rectangle is defined.
- targetRect = Util.scaleRectangle(
- TARGET_RECTS.get(Pair.of(launcherOptions.shape, Density.MEDIUM)),
- GraphicGenerator.getMdpiScaleFactor(launcherOptions.density));
+ targetRect = AssetUtil
+ .scaleRectangle(TARGET_RECTS.get(Pair.of(launcherOptions.shape, Density.MEDIUM)),
+ GraphicGenerator.getMdpiScaleFactor(launcherOptions.density));
}
- BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height);
+ BufferedImage outImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
Graphics2D g = (Graphics2D) outImage.getGraphics();
if (backImage != null) {
g.drawImage(backImage, 0, 0, null);
}
- BufferedImage tempImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height);
+ BufferedImage tempImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
Graphics2D g2 = (Graphics2D) tempImage.getGraphics();
if (maskImage != null) {
g2.drawImage(maskImage, 0, 0, null);
@@ -173,9 +174,9 @@
}
if (launcherOptions.crop) {
- Util.drawCenterCrop(g2, launcherOptions.sourceImage, targetRect);
+ AssetUtil.drawCenterCrop(g2, launcherOptions.sourceImage, targetRect);
} else {
- Util.drawCenterInside(g2, launcherOptions.sourceImage, targetRect);
+ AssetUtil.drawCenterInside(g2, launcherOptions.sourceImage, targetRect);
}
g.drawImage(tempImage, 0, 0, null);
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/MenuIconGenerator.java b/asset-studio/src/main/java/com/android/assetstudiolib/MenuIconGenerator.java
index 33b9c34..8b8eb7e 100644
--- a/asset-studio/src/main/java/com/android/assetstudiolib/MenuIconGenerator.java
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/MenuIconGenerator.java
@@ -16,9 +16,10 @@
package com.android.assetstudiolib;
-import com.android.assetstudiolib.Util.Effect;
-import com.android.assetstudiolib.Util.FillEffect;
-import com.android.assetstudiolib.Util.ShadowEffect;
+import com.android.ide.common.util.AssetUtil;
+import com.android.ide.common.util.AssetUtil.Effect;
+import com.android.ide.common.util.AssetUtil.FillEffect;
+import com.android.ide.common.util.AssetUtil.ShadowEffect;
import java.awt.Color;
import java.awt.GradientPaint;
@@ -39,46 +40,21 @@
Rectangle imageSizeHdpi = new Rectangle(0, 0, 48, 48);
Rectangle targetRectHdpi = new Rectangle(8, 8, 32, 32);
float scaleFactor = GraphicGenerator.getMdpiScaleFactor(options.density);
- Rectangle imageRect = Util.scaleRectangle(imageSizeHdpi, scaleFactor);
- Rectangle targetRect = Util.scaleRectangle(targetRectHdpi, scaleFactor);
+ Rectangle imageRect = AssetUtil.scaleRectangle(imageSizeHdpi, scaleFactor);
+ Rectangle targetRect = AssetUtil.scaleRectangle(targetRectHdpi, scaleFactor);
- BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height);
+ BufferedImage outImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
Graphics2D g = (Graphics2D) outImage.getGraphics();
- BufferedImage tempImage = Util.newArgbBufferedImage(
- imageRect.width, imageRect.height);
+ BufferedImage tempImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
Graphics2D g2 = (Graphics2D) tempImage.getGraphics();
- Util.drawCenterInside(g2, options.sourceImage, targetRect);
+ AssetUtil.drawCenterInside(g2, options.sourceImage, targetRect);
- Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
- new FillEffect(
- new GradientPaint(
- 0, 0,
- new Color(0xa3a3a3),
- 0, imageRect.height,
- new Color(0x787878))),
- new ShadowEffect(
- 0,
- 2 * scaleFactor,
- 2 * scaleFactor,
- Color.BLACK,
- 0.2,
- true),
- new ShadowEffect(
- 0,
- 1,
- 0,
- Color.BLACK,
- 0.35,
- true),
- new ShadowEffect(
- 0,
- -1,
- 0,
- Color.WHITE,
- 0.35,
- true),
- });
+ AssetUtil.drawEffects(g, tempImage, 0, 0, new Effect[]{new FillEffect(
+ new GradientPaint(0, 0, new Color(0xa3a3a3), 0, imageRect.height, new Color(0x787878))),
+ new ShadowEffect(0, 2 * scaleFactor, 2 * scaleFactor, Color.BLACK, 0.2, true),
+ new ShadowEffect(0, 1, 0, Color.BLACK, 0.35, true),
+ new ShadowEffect(0, -1, 0, Color.WHITE, 0.35, true),});
g.dispose();
g2.dispose();
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/NotificationIconGenerator.java b/asset-studio/src/main/java/com/android/assetstudiolib/NotificationIconGenerator.java
index b84af1b..c145a39 100644
--- a/asset-studio/src/main/java/com/android/assetstudiolib/NotificationIconGenerator.java
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/NotificationIconGenerator.java
@@ -15,9 +15,10 @@
*/
package com.android.assetstudiolib;
-import com.android.assetstudiolib.Util.Effect;
-import com.android.assetstudiolib.Util.FillEffect;
-import com.android.assetstudiolib.Util.ShadowEffect;
+import com.android.ide.common.util.AssetUtil;
+import com.android.ide.common.util.AssetUtil.Effect;
+import com.android.ide.common.util.AssetUtil.FillEffect;
+import com.android.ide.common.util.AssetUtil.ShadowEffect;
import java.awt.Color;
import java.awt.GradientPaint;
@@ -52,14 +53,13 @@
}
final float scaleFactor = GraphicGenerator.getMdpiScaleFactor(options.density);
- Rectangle imageRect = Util.scaleRectangle(iconSizeMdpi, scaleFactor);
- Rectangle targetRect = Util.scaleRectangle(targetRectMdpi, scaleFactor);
+ Rectangle imageRect = AssetUtil.scaleRectangle(iconSizeMdpi, scaleFactor);
+ Rectangle targetRect = AssetUtil.scaleRectangle(targetRectMdpi, scaleFactor);
- BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height);
+ BufferedImage outImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
Graphics2D g = (Graphics2D) outImage.getGraphics();
- BufferedImage tempImage = Util.newArgbBufferedImage(
- imageRect.width, imageRect.height);
+ BufferedImage tempImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
Graphics2D g2 = (Graphics2D) tempImage.getGraphics();
if (notificationOptions.version == Version.OLDER) {
@@ -69,31 +69,18 @@
+ ".png");
g.drawImage(backImage, 0, 0, null);
BufferedImage top = options.sourceImage;
- BufferedImage filled = Util.filledImage(top, Color.WHITE);
- Util.drawCenterInside(g, filled, targetRect);
+ BufferedImage filled = AssetUtil.filledImage(top, Color.WHITE);
+ AssetUtil.drawCenterInside(g, filled, targetRect);
} else if (notificationOptions.version == Version.V11) {
- Util.drawCenterInside(g2, options.sourceImage, targetRect);
- Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
- new FillEffect(Color.WHITE),
- });
+ AssetUtil.drawCenterInside(g2, options.sourceImage, targetRect);
+ AssetUtil.drawEffects(g, tempImage, 0, 0, new Effect[]{new FillEffect(Color.WHITE),});
} else {
assert notificationOptions.version == Version.V9;
- Util.drawCenterInside(g2, options.sourceImage, targetRect);
- Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
- new FillEffect(
- new GradientPaint(
- 0, 0,
- new Color(0x919191),
- 0, imageRect.height,
- new Color(0x828282))),
- new ShadowEffect(
- 0,
- 1,
- 0,
- Color.WHITE,
- 0.10,
- true),
- });
+ AssetUtil.drawCenterInside(g2, options.sourceImage, targetRect);
+ AssetUtil.drawEffects(g, tempImage, 0, 0, new Effect[]{new FillEffect(
+ new GradientPaint(0, 0, new Color(0x919191), 0, imageRect.height,
+ new Color(0x828282))),
+ new ShadowEffect(0, 1, 0, Color.WHITE, 0.10, true),});
}
g.dispose();
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/TabIconGenerator.java b/asset-studio/src/main/java/com/android/assetstudiolib/TabIconGenerator.java
index 3d2ac30..a540f5d 100644
--- a/asset-studio/src/main/java/com/android/assetstudiolib/TabIconGenerator.java
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/TabIconGenerator.java
@@ -15,9 +15,10 @@
*/
package com.android.assetstudiolib;
-import com.android.assetstudiolib.Util.Effect;
-import com.android.assetstudiolib.Util.FillEffect;
-import com.android.assetstudiolib.Util.ShadowEffect;
+import com.android.ide.common.util.AssetUtil;
+import com.android.ide.common.util.AssetUtil.Effect;
+import com.android.ide.common.util.AssetUtil.FillEffect;
+import com.android.ide.common.util.AssetUtil.ShadowEffect;
import java.awt.Color;
import java.awt.GradientPaint;
@@ -40,96 +41,40 @@
Rectangle iconSizeMdpi = new Rectangle(0, 0, 32, 32);
Rectangle targetRectMdpi = new Rectangle(2, 2, 28, 28);
final float scaleFactor = GraphicGenerator.getMdpiScaleFactor(options.density);
- Rectangle imageRect = Util.scaleRectangle(iconSizeMdpi, scaleFactor);
- Rectangle targetRect = Util.scaleRectangle(targetRectMdpi, scaleFactor);
- BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height);
+ Rectangle imageRect = AssetUtil.scaleRectangle(iconSizeMdpi, scaleFactor);
+ Rectangle targetRect = AssetUtil.scaleRectangle(targetRectMdpi, scaleFactor);
+ BufferedImage outImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
Graphics2D g = (Graphics2D) outImage.getGraphics();
- BufferedImage tempImage = Util.newArgbBufferedImage(
- imageRect.width, imageRect.height);
+ BufferedImage tempImage = AssetUtil.newArgbBufferedImage(imageRect.width, imageRect.height);
Graphics2D g2 = (Graphics2D) tempImage.getGraphics();
- Util.drawCenterInside(g2, options.sourceImage, targetRect);
+ AssetUtil.drawCenterInside(g2, options.sourceImage, targetRect);
TabOptions tabOptions = (TabOptions) options;
if (tabOptions.selected) {
if (tabOptions.oldStyle) {
- Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
- new FillEffect(
- new GradientPaint(
- 0, 0,
- new Color(0xa3a3a3),
- 0, imageRect.height,
- new Color(0x787878))),
- new ShadowEffect(
- 0,
- 2 * scaleFactor,
- 2 * scaleFactor,
- Color.BLACK,
- 0.2,
- true),
- new ShadowEffect(
- 0,
- 1,
- 0,
- Color.BLACK,
- 0.35,
- true),
- new ShadowEffect(
- 0,
- -1,
- 0,
- Color.WHITE,
- 0.35,
- true),
- });
+ AssetUtil.drawEffects(g, tempImage, 0, 0, new Effect[]{new FillEffect(
+ new GradientPaint(0, 0, new Color(0xa3a3a3), 0, imageRect.height,
+ new Color(0x787878))),
+ new ShadowEffect(0, 2 * scaleFactor, 2 * scaleFactor, Color.BLACK, 0.2, true),
+ new ShadowEffect(0, 1, 0, Color.BLACK, 0.35, true),
+ new ShadowEffect(0, -1, 0, Color.WHITE, 0.35, true),});
} else {
- Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
- new FillEffect(Color.WHITE),
- new ShadowEffect(
- 0,
- 0,
- 3 * scaleFactor,
- Color.BLACK,
- 0.25,
- false),
- });
+ AssetUtil.drawEffects(g, tempImage, 0, 0, new Effect[]{new FillEffect(Color.WHITE),
+ new ShadowEffect(0, 0, 3 * scaleFactor, Color.BLACK, 0.25, false),});
}
} else {
// Unselected
if (tabOptions.oldStyle) {
- Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
- new FillEffect(
- new GradientPaint(
- 0, 0.25f * imageRect.height,
- new Color(0xf9f9f9),
- 0, imageRect.height,
- new Color(0xdfdfdf))),
- new ShadowEffect(
- 0,
- 2 * scaleFactor,
- 2 * scaleFactor,
- Color.BLACK,
- 0.1,
- true),
- new ShadowEffect(
- 0,
- 1,
- 0,
- Color.BLACK,
- 0.35,
- true),
- new ShadowEffect(
- 0,
- -1,
- 0,
- Color.WHITE,
- 0.35,
- true),
- });
+ AssetUtil.drawEffects(g, tempImage, 0, 0, new Effect[]{new FillEffect(
+ new GradientPaint(0, 0.25f * imageRect.height, new Color(0xf9f9f9), 0,
+ imageRect.height, new Color(0xdfdfdf))),
+ new ShadowEffect(0, 2 * scaleFactor, 2 * scaleFactor, Color.BLACK, 0.1, true),
+ new ShadowEffect(0, 1, 0, Color.BLACK, 0.35, true),
+ new ShadowEffect(0, -1, 0, Color.WHITE, 0.35, true),});
} else {
- Util.drawEffects(g, tempImage, 0, 0, new Effect[] {
- new FillEffect(new Color(0x808080)),
- });
+ AssetUtil.drawEffects(g, tempImage, 0, 0,
+ new Effect[]{new FillEffect(new Color(0x808080)),});
}
}
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/TextRenderUtil.java b/asset-studio/src/main/java/com/android/assetstudiolib/TextRenderUtil.java
index 3ec6a34..5bc1afc 100644
--- a/asset-studio/src/main/java/com/android/assetstudiolib/TextRenderUtil.java
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/TextRenderUtil.java
@@ -16,6 +16,8 @@
package com.android.assetstudiolib;
+import com.android.ide.common.util.AssetUtil;
+
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
@@ -47,7 +49,7 @@
options = new Options();
}
- BufferedImage tempImage = Util.newArgbBufferedImage(1, 1);
+ BufferedImage tempImage = AssetUtil.newArgbBufferedImage(1, 1);
if (text == null || text.isEmpty()) {
return tempImage;
}
@@ -75,8 +77,8 @@
bounds.getWidth() + 2 * delta, bounds.getHeight() + 2 * delta);
}
- BufferedImage image = Util.newArgbBufferedImage(
- Math.max(1, (int) bounds.getWidth()), Math.max(1, (int) bounds.getHeight()));
+ BufferedImage image = AssetUtil.newArgbBufferedImage(Math.max(1, (int)bounds.getWidth()),
+ Math.max(1, (int)bounds.getHeight()));
Graphics2D g = (Graphics2D) image.getGraphics();
g.setColor(new Color(options.foregroundColor, true));
g.setFont(font);
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/Util.java b/asset-studio/src/main/java/com/android/assetstudiolib/Util.java
deleted file mode 100644
index da718b8..0000000
--- a/asset-studio/src/main/java/com/android/assetstudiolib/Util.java
+++ /dev/null
@@ -1,451 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.assetstudiolib;
-
-import java.awt.AlphaComposite;
-import java.awt.Color;
-import java.awt.Composite;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Image;
-import java.awt.Paint;
-import java.awt.Rectangle;
-import java.awt.image.BufferedImage;
-import java.awt.image.BufferedImageOp;
-import java.awt.image.ConvolveOp;
-import java.awt.image.Kernel;
-import java.awt.image.Raster;
-import java.awt.image.RescaleOp;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A set of utility classes for manipulating {@link BufferedImage} objects and drawing them to
- * {@link Graphics2D} canvases.
- */
-public class Util {
- /**
- * Scales the given rectangle by the given scale factor.
- *
- * @param rect The rectangle to scale.
- * @param scaleFactor The factor to scale by.
- * @return The scaled rectangle.
- */
- public static Rectangle scaleRectangle(Rectangle rect, float scaleFactor) {
- return new Rectangle(
- Math.round(rect.x * scaleFactor),
- Math.round(rect.y * scaleFactor),
- Math.round(rect.width * scaleFactor),
- Math.round(rect.height * scaleFactor));
- }
-
- /**
- * Creates a new ARGB {@link BufferedImage} of the given width and height.
- *
- * @param width The width of the new image.
- * @param height The height of the new image.
- * @return The newly created image.
- */
- public static BufferedImage newArgbBufferedImage(int width, int height) {
- return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
- }
-
- /**
- * Smoothly scales the given {@link BufferedImage} to the given width and height using the
- * {@link Image#SCALE_SMOOTH} algorithm (generally bicubic resampling or bilinear filtering).
- *
- * @param source The source image.
- * @param width The destination width to scale to.
- * @param height The destination height to scale to.
- * @return A new, scaled image.
- */
- public static BufferedImage scaledImage(BufferedImage source, int width, int height) {
- Image scaledImage = source.getScaledInstance(width, height, Image.SCALE_SMOOTH);
- BufferedImage scaledBufImage = new BufferedImage(width, height,
- BufferedImage.TYPE_INT_ARGB);
- Graphics g = scaledBufImage.createGraphics();
- g.drawImage(scaledImage, 0, 0, null);
- g.dispose();
- return scaledBufImage;
- }
-
- /**
- * Applies a gaussian blur of the given radius to the given {@link BufferedImage} using a kernel
- * convolution.
- *
- * @param source The source image.
- * @param radius The blur radius, in pixels.
- * @return A new, blurred image, or the source image if no blur is performed.
- */
- public static BufferedImage blurredImage(BufferedImage source, double radius) {
- if (radius == 0) {
- return source;
- }
-
- final int r = (int) Math.ceil(radius);
- final int rows = r * 2 + 1;
- final float[] kernelData = new float[rows * rows];
-
- final double sigma = radius / 3;
- final double sigma22 = 2 * sigma * sigma;
- final double sqrtPiSigma22 = Math.sqrt(Math.PI * sigma22);
- final double radius2 = radius * radius;
-
- double total = 0;
- int index = 0;
- double distance2;
-
- int x, y;
- for (y = -r; y <= r; y++) {
- for (x = -r; x <= r; x++) {
- distance2 = 1.0 * x * x + 1.0 * y * y;
- if (distance2 > radius2) {
- kernelData[index] = 0;
- } else {
- kernelData[index] = (float) (Math.exp(-distance2 / sigma22) / sqrtPiSigma22);
- }
- total += kernelData[index];
- ++index;
- }
- }
-
- for (index = 0; index < kernelData.length; index++) {
- kernelData[index] /= total;
- }
-
- // We first pad the image so the kernel can operate at the edges.
- BufferedImage paddedSource = paddedImage(source, r);
- BufferedImage blurredPaddedImage = operatedImage(paddedSource, new ConvolveOp(
- new Kernel(rows, rows, kernelData), ConvolveOp.EDGE_ZERO_FILL, null));
- return blurredPaddedImage.getSubimage(r, r, source.getWidth(), source.getHeight());
- }
-
- /**
- * Inverts the alpha channel of the given {@link BufferedImage}. RGB data for the inverted area
- * are undefined, so it's generally best to fill the resulting image with a color.
- *
- * @param source The source image.
- * @return A new image with an alpha channel inverted from the original.
- */
- public static BufferedImage invertedAlphaImage(BufferedImage source) {
- final float[] scaleFactors = new float[]{1, 1, 1, -1};
- final float[] offsets = new float[]{0, 0, 0, 255};
-
- return operatedImage(source, new RescaleOp(scaleFactors, offsets, null));
- }
-
- /**
- * Applies a {@link BufferedImageOp} on the given {@link BufferedImage}.
- *
- * @param source The source image.
- * @param op The operation to perform.
- * @return A new image with the operation performed.
- */
- public static BufferedImage operatedImage(BufferedImage source, BufferedImageOp op) {
- BufferedImage newImage = newArgbBufferedImage(source.getWidth(), source.getHeight());
- Graphics2D g = (Graphics2D) newImage.getGraphics();
- g.drawImage(source, op, 0, 0);
- return newImage;
- }
-
- /**
- * Fills the given {@link BufferedImage} with a {@link Paint}, preserving its alpha channel.
- *
- * @param source The source image.
- * @param paint The paint to fill with.
- * @return A new, painted/filled image.
- */
- public static BufferedImage filledImage(BufferedImage source, Paint paint) {
- BufferedImage newImage = newArgbBufferedImage(source.getWidth(), source.getHeight());
- Graphics2D g = (Graphics2D) newImage.getGraphics();
- g.drawImage(source, 0, 0, null);
- g.setComposite(AlphaComposite.SrcAtop);
- g.setPaint(paint);
- g.fillRect(0, 0, source.getWidth(), source.getHeight());
- return newImage;
- }
-
- /**
- * Pads the given {@link BufferedImage} on all sides by the given padding amount.
- *
- * @param source The source image.
- * @param padding The amount to pad on all sides, in pixels.
- * @return A new, padded image, or the source image if no padding is performed.
- */
- public static BufferedImage paddedImage(BufferedImage source, int padding) {
- if (padding == 0) {
- return source;
- }
-
- BufferedImage newImage = newArgbBufferedImage(
- source.getWidth() + padding * 2, source.getHeight() + padding * 2);
- Graphics2D g = (Graphics2D) newImage.getGraphics();
- g.drawImage(source, padding, padding, null);
- return newImage;
- }
-
- /**
- * Trims the transparent pixels from the given {@link BufferedImage} (returns a sub-image).
- *
- * @param source The source image.
- * @return A new, trimmed image, or the source image if no trim is performed.
- */
- public static BufferedImage trimmedImage(BufferedImage source) {
- final int minAlpha = 1;
- final int srcWidth = source.getWidth();
- final int srcHeight = source.getHeight();
- Raster raster = source.getRaster();
- int l = srcWidth, t = srcHeight, r = 0, b = 0;
-
- int alpha, x, y;
- int[] pixel = new int[4];
- for (y = 0; y < srcHeight; y++) {
- for (x = 0; x < srcWidth; x++) {
- raster.getPixel(x, y, pixel);
- alpha = pixel[3];
- if (alpha >= minAlpha) {
- l = Math.min(x, l);
- t = Math.min(y, t);
- r = Math.max(x, r);
- b = Math.max(y, b);
- }
- }
- }
-
- if (l > r || t > b) {
- // No pixels, couldn't trim
- return source;
- }
-
- return source.getSubimage(l, t, r - l + 1, b - t + 1);
- }
-
- /**
- * Draws the given {@link BufferedImage} to the canvas, at the given coordinates, with the given
- * {@link Effect}s applied. Note that drawn effects may be outside the bounds of the source
- * image.
- *
- * @param g The destination canvas.
- * @param source The source image.
- * @param x The x offset at which to draw the image.
- * @param y The y offset at which to draw the image.
- * @param effects The list of effects to apply.
- */
- public static void drawEffects(Graphics2D g, BufferedImage source, int x, int y,
- Effect[] effects) {
- List<ShadowEffect> shadowEffects = new ArrayList<ShadowEffect>();
- List<FillEffect> fillEffects = new ArrayList<FillEffect>();
-
- for (Effect effect : effects) {
- if (effect instanceof ShadowEffect) {
- shadowEffects.add((ShadowEffect) effect);
- } else if (effect instanceof FillEffect) {
- fillEffects.add((FillEffect) effect);
- }
- }
-
- Composite oldComposite = g.getComposite();
- for (ShadowEffect effect : shadowEffects) {
- if (effect.inner) {
- continue;
- }
-
- // Outer shadow
- g.setComposite(AlphaComposite.getInstance(
- AlphaComposite.SRC_OVER, (float) effect.opacity));
- g.drawImage(
- filledImage(
- blurredImage(source, effect.radius),
- effect.color),
- (int) effect.xOffset, (int) effect.yOffset, null);
- }
- g.setComposite(oldComposite);
-
- // Inner shadow & fill effects.
- final Rectangle imageRect = new Rectangle(0, 0, source.getWidth(), source.getHeight());
- BufferedImage out = newArgbBufferedImage(imageRect.width, imageRect.height);
- Graphics2D g2 = (Graphics2D) out.getGraphics();
- double fillOpacity = 1.0;
-
- g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
- g2.drawImage(source, 0, 0, null);
- g2.setComposite(AlphaComposite.SrcAtop);
-
- // Gradient fill
- for (FillEffect effect : fillEffects) {
- g2.setPaint(effect.paint);
- g2.fillRect(0, 0, imageRect.width, imageRect.height);
- fillOpacity = Math.max(0, Math.min(1, effect.opacity));
- }
-
- // Inner shadows
- for (ShadowEffect effect : shadowEffects) {
- if (!effect.inner) {
- continue;
- }
-
- BufferedImage innerShadowImage = newArgbBufferedImage(
- imageRect.width, imageRect.height);
- Graphics2D g3 = (Graphics2D) innerShadowImage.getGraphics();
- g3.drawImage(source, (int) effect.xOffset, (int) effect.yOffset, null);
- g2.setComposite(AlphaComposite.getInstance(
- AlphaComposite.SRC_ATOP, (float) effect.opacity));
- g2.drawImage(
- filledImage(
- blurredImage(invertedAlphaImage(innerShadowImage), effect.radius),
- effect.color),
- 0, 0, null);
- }
-
- g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) fillOpacity));
- g.drawImage(out, x, y, null);
- g.setComposite(oldComposite);
- }
-
- /**
- * Draws the given {@link BufferedImage} to the canvas, centered, wholly contained within the
- * bounds defined by the destination rectangle, and with preserved aspect ratio.
- *
- * @param g The destination canvas.
- * @param source The source image.
- * @param dstRect The destination rectangle in the destination canvas into which to draw the
- * image.
- */
- public static void drawCenterInside(Graphics2D g, BufferedImage source, Rectangle dstRect) {
- final int srcWidth = source.getWidth();
- final int srcHeight = source.getHeight();
- if (srcWidth * 1.0 / srcHeight > dstRect.width * 1.0 / dstRect.height) {
- final int scaledWidth = Math.max(1, dstRect.width);
- final int scaledHeight = Math.max(1, dstRect.width * srcHeight / srcWidth);
- Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
- g.drawImage(scaledImage,
- dstRect.x,
- dstRect.y + (dstRect.height - scaledHeight) / 2,
- dstRect.x + dstRect.width,
- dstRect.y + (dstRect.height - scaledHeight) / 2 + scaledHeight,
- 0,
- 0,
- 0 + scaledWidth,
- 0 + scaledHeight,
- null);
- } else {
- final int scaledWidth = Math.max(1, dstRect.height * srcWidth / srcHeight);
- final int scaledHeight = Math.max(1, dstRect.height);
- Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
- g.drawImage(scaledImage,
- dstRect.x + (dstRect.width - scaledWidth) / 2,
- dstRect.y,
- dstRect.x + (dstRect.width - scaledWidth) / 2 + scaledWidth,
- dstRect.y + dstRect.height,
- 0,
- 0,
- 0 + scaledWidth,
- 0 + scaledHeight,
- null);
- }
- }
-
- /**
- * Draws the given {@link BufferedImage} to the canvas, centered and cropped to fill the
- * bounds defined by the destination rectangle, and with preserved aspect ratio.
- *
- * @param g The destination canvas.
- * @param source The source image.
- * @param dstRect The destination rectangle in the destination canvas into which to draw the
- * image.
- */
- public static void drawCenterCrop(Graphics2D g, BufferedImage source, Rectangle dstRect) {
- final int srcWidth = source.getWidth();
- final int srcHeight = source.getHeight();
- if (srcWidth * 1.0 / srcHeight > dstRect.width * 1.0 / dstRect.height) {
- final int scaledWidth = dstRect.height * srcWidth / srcHeight;
- final int scaledHeight = dstRect.height;
- Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
- g.drawImage(scaledImage,
- dstRect.x,
- dstRect.y,
- dstRect.x + dstRect.width,
- dstRect.y + dstRect.height,
- 0 + (scaledWidth - dstRect.width) / 2,
- 0,
- 0 + (scaledWidth - dstRect.width) / 2 + dstRect.width,
- 0 + dstRect.height,
- null);
- } else {
- final int scaledWidth = dstRect.width;
- final int scaledHeight = dstRect.width * srcHeight / srcWidth;
- Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
- g.drawImage(scaledImage,
- dstRect.x,
- dstRect.y,
- dstRect.x + dstRect.width,
- dstRect.y + dstRect.height,
- 0,
- 0 + (scaledHeight - dstRect.height) / 2,
- 0 + dstRect.width,
- 0 + (scaledHeight - dstRect.height) / 2 + dstRect.height,
- null);
- }
- }
-
- /**
- * An effect to apply in
- * {@link Util#drawEffects(java.awt.Graphics2D, java.awt.image.BufferedImage, int, int, Util.Effect[])}
- */
- public abstract static class Effect {
- }
-
- /**
- * An inner or outer shadow.
- */
- public static class ShadowEffect extends Effect {
- public double xOffset;
- public double yOffset;
- public double radius;
- public Color color;
- public double opacity;
- public boolean inner;
-
- public ShadowEffect(double xOffset, double yOffset, double radius, Color color,
- double opacity, boolean inner) {
- this.xOffset = xOffset;
- this.yOffset = yOffset;
- this.radius = radius;
- this.color = color;
- this.opacity = opacity;
- this.inner = inner;
- }
- }
-
- /**
- * A fill, defined by a paint.
- */
- public static class FillEffect extends Effect {
- public Paint paint;
- public double opacity;
-
- public FillEffect(Paint paint, double opacity) {
- this.paint = paint;
- this.opacity = opacity;
- }
-
- public FillEffect(Paint paint) {
- this.paint = paint;
- this.opacity = 1.0;
- }
- }
-}
diff --git a/asset-studio/src/main/java/com/android/assetstudiolib/VectorIconGenerator.java b/asset-studio/src/main/java/com/android/assetstudiolib/VectorIconGenerator.java
new file mode 100644
index 0000000..ecfd24c
--- /dev/null
+++ b/asset-studio/src/main/java/com/android/assetstudiolib/VectorIconGenerator.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.assetstudiolib;
+
+import java.awt.image.BufferedImage;
+
+/**
+ * Generate icons for the vector drawable.
+ * We need the image in original format and size, therefore, there is no extra
+ * operation on the sourceImage.
+ */
+public class VectorIconGenerator extends GraphicGenerator {
+ @Override
+ public BufferedImage generate(GraphicGeneratorContext context, Options options) {
+ return options.sourceImage;
+ }
+
+ public static class VectorIconOptions extends GraphicGenerator.Options {
+ }
+}
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_3d_rotation_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_3d_rotation_24dp.xml
new file mode 100644
index 0000000..8a08cf2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_3d_rotation_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.52,21.48C4.25,19.94 1.91,16.76 1.55,13H0.05C0.56,19.16 5.71,24 12,24l0.66,-0.03 -3.81,-3.81 -1.33,1.32zm0.89,-6.52c-0.19,0 -0.37,-0.03 -0.52,-0.08 -0.16,-0.06 -0.29,-0.13 -0.4,-0.24 -0.11,-0.1 -0.2,-0.22 -0.26,-0.37 -0.06,-0.14 -0.09,-0.3 -0.09,-0.47h-1.3c0,0.36 0.07,0.68 0.21,0.95 0.14,0.27 0.33,0.5 0.56,0.69 0.24,0.18 0.51,0.32 0.82,0.41 0.3,0.1 0.62,0.15 0.96,0.15 0.37,0 0.72,-0.05 1.03,-0.15 0.32,-0.1 0.6,-0.25 0.83,-0.44s0.42,-0.43 0.55,-0.72c0.13,-0.29 0.2,-0.61 0.2,-0.97 0,-0.19 -0.02,-0.38 -0.07,-0.56 -0.05,-0.18 -0.12,-0.35 -0.23,-0.51 -0.1,-0.16 -0.24,-0.3 -0.4,-0.43 -0.17,-0.13 -0.37,-0.23 -0.61,-0.31 0.2,-0.09 0.37,-0.2 0.52,-0.33 0.15,-0.13 0.27,-0.27 0.37,-0.42 0.1,-0.15 0.17,-0.3 0.22,-0.46 0.05,-0.16 0.07,-0.32 0.07,-0.48 0,-0.36 -0.06,-0.68 -0.18,-0.96 -0.12,-0.28 -0.29,-0.51 -0.51,-0.69 -0.2,-0.19 -0.47,-0.33 -0.77,-0.43C9.1,8.05 8.76,8 8.39,8c-0.36,0 -0.69,0.05 -1,0.16 -0.3,0.11 -0.57,0.26 -0.79,0.45 -0.21,0.19 -0.38,0.41 -0.51,0.67 -0.12,0.26 -0.18,0.54 -0.18,0.85h1.3c0,-0.17 0.03,-0.32 0.09,-0.45s0.14,-0.25 0.25,-0.34c0.11,-0.09 0.23,-0.17 0.38,-0.22 0.15,-0.05 0.3,-0.08 0.48,-0.08 0.4,0 0.7,0.1 0.89,0.31 0.19,0.2 0.29,0.49 0.29,0.86 0,0.18 -0.03,0.34 -0.08,0.49 -0.05,0.15 -0.14,0.27 -0.25,0.37 -0.11,0.1 -0.25,0.18 -0.41,0.24 -0.16,0.06 -0.36,0.09 -0.58,0.09H7.5v1.03h0.77c0.22,0 0.42,0.02 0.6,0.07s0.33,0.13 0.45,0.23c0.12,0.11 0.22,0.24 0.29,0.4 0.07,0.16 0.1,0.35 0.1,0.57 0,0.41 -0.12,0.72 -0.35,0.93 -0.23,0.23 -0.55,0.33 -0.95,0.33zm8.55,-5.92c-0.32,-0.33 -0.7,-0.59 -1.14,-0.77 -0.43,-0.18 -0.92,-0.27 -1.46,-0.27H12v8h2.3c0.55,0 1.06,-0.09 1.51,-0.27 0.45,-0.18 0.84,-0.43 1.16,-0.76 0.32,-0.33 0.57,-0.73 0.74,-1.19 0.17,-0.47 0.26,-0.99 0.26,-1.57v-0.4c0,-0.58 -0.09,-1.1 -0.26,-1.57 -0.18,-0.47 -0.43,-0.87 -0.75,-1.2zm-0.39,3.16c0,0.42 -0.05,0.79 -0.14,1.13 -0.1,0.33 -0.24,0.62 -0.43,0.85 -0.19,0.23 -0.43,0.41 -0.71,0.53 -0.29,0.12 -0.62,0.18 -0.99,0.18h-0.91V9.12h0.97c0.72,0 1.27,0.23 1.64,0.69 0.38,0.46 0.57,1.12 0.57,1.99v0.4zM12,0l-0.66,0.03 3.81,3.81 1.33,-1.33c3.27,1.55 5.61,4.72 5.96,8.48h1.5C23.44,4.84 18.29,0 12,0z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_accessibility_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_accessibility_24dp.xml
new file mode 100644
index 0000000..075be55
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_accessibility_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2zm9,7h-6v13h-2v-6h-2v6H9V9H3V7h18v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_account_balance_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_balance_24dp.xml
new file mode 100644
index 0000000..c3a8755
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_balance_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,10v7h3v-7H4zm6,0v7h3v-7h-3zM2,22h19v-3H2v3zm14,-12v7h3v-7h-3zm-4.5,-9L2,6v2h19V6l-9.5,-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_account_balance_wallet_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_balance_wallet_24dp.xml
new file mode 100644
index 0000000..6b570ff
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_balance_wallet_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,18v1c0,1.1 -0.9,2 -2,2H5c-1.11,0 -2,-0.9 -2,-2V5c0,-1.1 0.89,-2 2,-2h14c1.1,0 2,0.9 2,2v1h-9c-1.11,0 -2,0.9 -2,2v8c0,1.1 0.89,2 2,2h9zm-9,-2h10V8H12v8zm4,-2.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_account_box_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_box_24dp.xml
new file mode 100644
index 0000000..13ed54c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_box_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,5v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2H5c-1.11,0 -2,0.9 -2,2zm12,4c0,1.66 -1.34,3 -3,3s-3,-1.34 -3,-3 1.34,-3 3,-3 3,1.34 3,3zm-9,8c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1v1H6v-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_account_child_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_child_24dp.xml
new file mode 100644
index 0000000..c40ad49
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_child_24dp.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,13.49m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,2.5c1.24,0 2.25,1.01 2.25,2.25S13.24,9 12,9 9.75,7.99 9.75,6.75 10.76,4.5 12,4.5zm5,10.56v2.5c-0.45,0.41 -0.96,0.77 -1.5,1.05v-0.68c0,-0.34 -0.17,-0.65 -0.46,-0.92 -0.65,-0.62 -1.89,-1.02 -3.04,-1.02 -0.96,0 -1.96,0.28 -2.65,0.73l-0.17,0.12 -0.21,0.17c0.78,0.47 1.63,0.72 2.54,0.82l1.33,0.15c0.37,0.04 0.66,0.36 0.66,0.75 0,0.29 -0.16,0.53 -0.4,0.66 -0.28,0.15 -0.64,0.09 -0.95,0.09 -0.35,0 -0.69,-0.01 -1.03,-0.05 -0.5,-0.06 -0.99,-0.17 -1.46,-0.33 -0.49,-0.16 -0.97,-0.38 -1.42,-0.64 -0.22,-0.13 -0.44,-0.27 -0.65,-0.43l-0.31,-0.24c-0.04,-0.02 -0.28,-0.18 -0.28,-0.23v-4.28c0,-1.58 2.63,-2.78 5,-2.78s5,1.2 5,2.78v1.78z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_account_circle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_circle_24dp.xml
new file mode 100644
index 0000000..1f209f7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_account_circle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,3c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zm0,14.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_add_shopping_cart_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_add_shopping_cart_24dp.xml
new file mode 100644
index 0000000..281dd33
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_add_shopping_cart_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,9h2V6h3V4h-3V1h-2v3H8v2h3v3zm-4,9c-1.1,0 -1.99,0.9 -1.99,2S5.9,22 7,22s2,-0.9 2,-2 -0.9,-2 -2,-2zm10,0c-1.1,0 -1.99,0.9 -1.99,2s0.89,2 1.99,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm-9.83,-3.25l0.03,-0.12 0.9,-1.63h7.45c0.75,0 1.41,-0.41 1.75,-1.03l3.86,-7.01L19.42,4h-0.01l-1.1,2 -2.76,5H8.53l-0.13,-0.27L6.16,6l-0.95,-2 -0.94,-2H1v2h2l3.6,7.59 -1.35,2.45c-0.16,0.28 -0.25,0.61 -0.25,0.96 0,1.1 0.9,2 2,2h12v-2H7.42c-0.13,0 -0.25,-0.11 -0.25,-0.25z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_24dp.xml
new file mode 100644
index 0000000..7918b03
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM12.5,8H11v6l4.75,2.85 0.75,-1.23 -4,-2.37V8zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_add_24dp.xml
new file mode 100644
index 0000000..5e0d986
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7zm1,-11h-2v3H8v2h3v3h2v-3h3v-2h-3V9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_off_24dp.xml
new file mode 100644
index 0000000..b041418
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,6c3.87,0 7,3.13 7,7 0,0.84 -0.16,1.65 -0.43,2.4l1.52,1.52c0.58,-1.19 0.91,-2.51 0.91,-3.92 0,-4.97 -4.03,-9 -9,-9 -1.41,0 -2.73,0.33 -3.92,0.91L9.6,6.43C10.35,6.16 11.16,6 12,6zm10,-0.28l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM2.92,2.29L1.65,3.57 2.98,4.9l-1.11,0.93 1.42,1.42 1.11,-0.94 0.8,0.8C3.83,8.69 3,10.75 3,13c0,4.97 4.02,9 9,9 2.25,0 4.31,-0.83 5.89,-2.2l2.2,2.2 1.27,-1.27L3.89,3.27l-0.97,-0.98zm13.55,16.1C15.26,19.39 13.7,20 12,20c-3.87,0 -7,-3.13 -7,-7 0,-1.7 0.61,-3.26 1.61,-4.47l9.86,9.86zM8.02,3.28L6.6,1.86l-0.86,0.71 1.42,1.42 0.86,-0.71z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_on_24dp.xml
new file mode 100644
index 0000000..c8e3f7a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_alarm_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7zm-1.46,-5.47L8.41,12.4l-1.06,1.06 3.18,3.18 6,-6 -1.06,-1.06 -4.93,4.95z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_android_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_android_24dp.xml
new file mode 100644
index 0000000..5aaace3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_android_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5V19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5V19h1c0.55,0 1,-0.45 1,-1V8H6v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zm17,0c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zm-4.97,-5.84l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5H9V4h1v1zm5,0h-1V4h1v1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_announcement_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_announcement_24dp.xml
new file mode 100644
index 0000000..da2bb25
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_announcement_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-7,9h-2V5h2v6zm0,4h-2v-2h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_aspect_ratio_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_aspect_ratio_24dp.xml
new file mode 100644
index 0000000..a6892ce
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_aspect_ratio_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,12h-2v3h-3v2h5v-5zM7,9h3V7H5v5h2V9zm14,-6H3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16.01H3V4.99h18v14.02z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_assessment_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_assessment_24dp.xml
new file mode 100644
index 0000000..1d393b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_assessment_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM9,17H7v-7h2v7zm4,0h-2V7h2v10zm4,0h-2v-4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_24dp.xml
new file mode 100644
index 0000000..0717c5f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-7,0c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm2,14H7v-2h7v2zm3,-4H7v-2h10v2zm0,-4H7V7h10v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_ind_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_ind_24dp.xml
new file mode 100644
index 0000000..0e0db8d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_ind_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-7,0c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm0,4c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zm6,12H6v-1.4c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1V19z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_late_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_late_24dp.xml
new file mode 100644
index 0000000..cbf3474
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_late_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-6,15h-2v-2h2v2zm0,-4h-2V8h2v6zm-1,-9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_return_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_return_24dp.xml
new file mode 100644
index 0000000..f320ca4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_return_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-7,0c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm4,12h-4v3l-5,-5 5,-5v3h4v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_returned_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_returned_24dp.xml
new file mode 100644
index 0000000..094cc9d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_returned_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-7,0c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm0,15l-5,-5h3V9h4v4h3l-5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_turned_in_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_turned_in_24dp.xml
new file mode 100644
index 0000000..818d8f1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_assignment_turned_in_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-7,0c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm-2,14l-4,-4 1.41,-1.41L10,14.17l6.59,-6.59L18,9l-8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_autorenew_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_autorenew_24dp.xml
new file mode 100644
index 0000000..25da1c4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_autorenew_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,6v3l4,-4 -4,-4v3c-4.42,0 -8,3.58 -8,8 0,1.57 0.46,3.03 1.24,4.26L6.7,14.8c-0.45,-0.83 -0.7,-1.79 -0.7,-2.8 0,-3.31 2.69,-6 6,-6zm6.76,1.74L17.3,9.2c0.44,0.84 0.7,1.79 0.7,2.8 0,3.31 -2.69,6 -6,6v-3l-4,4 4,4v-3c4.42,0 8,-3.58 8,-8 0,-1.57 -0.46,-3.03 -1.24,-4.26z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_backup_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_backup_24dp.xml
new file mode 100644
index 0000000..9a79995
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_backup_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM14,13v4h-4v-4H7l5,-5 5,5h-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_book_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_book_24dp.xml
new file mode 100644
index 0000000..005d746
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_book_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,2H6c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,4h5v8l-2.5,-1.5L6,12V4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_bookmark_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_bookmark_24dp.xml
new file mode 100644
index 0000000..61d6f32
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_bookmark_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_bookmark_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_bookmark_outline_24dp.xml
new file mode 100644
index 0000000..9605ee6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_bookmark_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2zm0,15l-5,-2.18L7,18V5h10v13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_bug_report_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_bug_report_24dp.xml
new file mode 100644
index 0000000..70894fb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_bug_report_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8H4v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1H4v2h2v1c0,0.34 0.04,0.67 0.09,1H4v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3H20v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1H20V8zm-6,8h-4v-2h4v2zm0,-4h-4v-2h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_cached_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_cached_24dp.xml
new file mode 100644
index 0000000..a987172
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_cached_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,8l-4,4h3c0,3.31 -2.69,6 -6,6 -1.01,0 -1.97,-0.25 -2.8,-0.7l-1.46,1.46C8.97,19.54 10.43,20 12,20c4.42,0 8,-3.58 8,-8h3l-4,-4zM6,12c0,-3.31 2.69,-6 6,-6 1.01,0 1.97,0.25 2.8,0.7l1.46,-1.46C15.03,4.46 13.57,4 12,4c-4.42,0 -8,3.58 -8,8H1l4,4 4,-4H6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_check_circle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_check_circle_24dp.xml
new file mode 100644
index 0000000..0ad8603
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_check_circle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm-2,15l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_class_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_class_24dp.xml
new file mode 100644
index 0000000..005d746
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_class_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,2H6c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,4h5v8l-2.5,-1.5L6,12V4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_credit_card_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_credit_card_24dp.xml
new file mode 100644
index 0000000..3f58e10
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_credit_card_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,4H4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V6c0,-1.11 -0.89,-2 -2,-2zm0,14H4v-6h16v6zm0,-10H4V6h16v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_dashboard_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_dashboard_24dp.xml
new file mode 100644
index 0000000..c9c8fa6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_dashboard_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,13h8V3H3v10zm0,8h8v-6H3v6zm10,0h8V11h-8v10zm0,-18v6h8V3h-8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_delete_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_delete_24dp.xml
new file mode 100644
index 0000000..fc0a9fb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_delete_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_description_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_description_24dp.xml
new file mode 100644
index 0000000..9edc43e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_description_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14,2H6c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2V8l-6,-6zm2,16H8v-2h8v2zm0,-4H8v-2h8v2zm-3,-5V3.5L18.5,9H13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_dns_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_dns_24dp.xml
new file mode 100644
index 0000000..b72f863
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_dns_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,13H4c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1v-6c0,-0.55 -0.45,-1 -1,-1zM7,19c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM20,3H4c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1V4c0,-0.55 -0.45,-1 -1,-1zM7,9c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_done_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_done_24dp.xml
new file mode 100644
index 0000000..02e8321
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_done_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_done_all_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_done_all_24dp.xml
new file mode 100644
index 0000000..d910df1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_done_all_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zm4.24,-1.41L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_event_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_event_24dp.xml
new file mode 100644
index 0000000..e0030fd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_event_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,12h-5v5h5v-5zM16,1v2H8V1H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2h-1V1h-2zm3,18H5V8h14v11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_exit_to_app_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_exit_to_app_24dp.xml
new file mode 100644
index 0000000..4aa81b0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_exit_to_app_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_explore_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_explore_24dp.xml
new file mode 100644
index 0000000..affc1d7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_explore_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,10.9c-0.61,0 -1.1,0.49 -1.1,1.1s0.49,1.1 1.1,1.1c0.61,0 1.1,-0.49 1.1,-1.1s-0.49,-1.1 -1.1,-1.1zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm2.19,12.19L6,18l3.81,-8.19L18,6l-3.81,8.19z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_extension_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_extension_24dp.xml
new file mode 100644
index 0000000..57a615d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_extension_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.5,11H19V7c0,-1.1 -0.9,-2 -2,-2h-4V3.5C13,2.12 11.88,1 10.5,1S8,2.12 8,3.5V5H4c-1.1,0 -1.99,0.9 -1.99,2v3.8H3.5c1.49,0 2.7,1.21 2.7,2.7s-1.21,2.7 -2.7,2.7H2V20c0,1.1 0.9,2 2,2h3.8v-1.5c0,-1.49 1.21,-2.7 2.7,-2.7 1.49,0 2.7,1.21 2.7,2.7V22H17c1.1,0 2,-0.9 2,-2v-4h1.5c1.38,0 2.5,-1.12 2.5,-2.5S21.88,11 20.5,11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_face_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_face_24dp.xml
new file mode 100644
index 0000000..70e4ff1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_face_24dp.xml
@@ -0,0 +1,33 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14.69,17.1c-0.74,0.58 -1.7,0.9 -2.69,0.9s-1.95,-0.32 -2.69,-0.9c-0.22,-0.17 -0.53,-0.13 -0.7,0.09 -0.17,0.22 -0.13,0.53 0.09,0.7 0.91,0.72 2.09,1.11 3.3,1.11s2.39,-0.39 3.31,-1.1c0.22,-0.17 0.26,-0.48 0.09,-0.7 -0.17,-0.23 -0.49,-0.26 -0.71,-0.1z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8.5,12.5m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,0C5.37,0 0,5.37 0,12s5.37,12 12,12 12,-5.37 12,-12S18.63,0 12,0zm7.96,14.82c-1.09,3.74 -4.27,6.46 -8.04,6.46 -3.78,0 -6.96,-2.72 -8.04,-6.47 -1.19,-0.11 -2.13,-1.18 -2.13,-2.52 0,-1.27 0.85,-2.31 1.97,-2.5 2.09,-1.46 3.8,-3.49 4.09,-5.05v-0.01c1.35,2.63 6.3,5.19 11.83,5.06l0.3,-0.03c1.28,0 2.31,1.14 2.31,2.54 0,1.38 -1.02,2.51 -2.29,2.52z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.5,12.5m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_favorite_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_favorite_24dp.xml
new file mode 100644
index 0000000..1e38193
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_favorite_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_favorite_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_favorite_outline_24dp.xml
new file mode 100644
index 0000000..3a5916c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_favorite_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.5,3c-1.74,0 -3.41,0.81 -4.5,2.09C10.91,3.81 9.24,3 7.5,3 4.42,3 2,5.42 2,8.5c0,3.78 3.4,6.86 8.55,11.54L12,21.35l1.45,-1.32C18.6,15.36 22,12.28 22,8.5 22,5.42 19.58,3 16.5,3zm-4.4,15.55l-0.1,0.1 -0.1,-0.1C7.14,14.24 4,11.39 4,8.5 4,6.5 5.5,5 7.5,5c1.54,0 3.04,0.99 3.57,2.36h1.87C13.46,5.99 14.96,5 16.5,5c2,0 3.5,1.5 3.5,3.5 0,2.89 -3.14,5.74 -7.9,10.05z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_find_in_page_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_find_in_page_24dp.xml
new file mode 100644
index 0000000..2a9c2fe
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_find_in_page_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,19.59V8l-6,-6H6c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c0.45,0 0.85,-0.15 1.19,-0.4l-4.43,-4.43c-0.8,0.52 -1.74,0.83 -2.76,0.83 -2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5c0,1.02 -0.31,1.96 -0.83,2.75L20,19.59zM9,13c0,1.66 1.34,3 3,3s3,-1.34 3,-3 -1.34,-3 -3,-3 -3,1.34 -3,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_find_replace_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_find_replace_24dp.xml
new file mode 100644
index 0000000..f11608e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_find_replace_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,6c1.38,0 2.63,0.56 3.54,1.46L12,10h6V4l-2.05,2.05C14.68,4.78 12.93,4 11,4c-3.53,0 -6.43,2.61 -6.92,6H6.1c0.46,-2.28 2.48,-4 4.9,-4zm5.64,9.14c0.66,-0.9 1.12,-1.97 1.28,-3.14H15.9c-0.46,2.28 -2.48,4 -4.9,4 -1.38,0 -2.63,-0.56 -3.54,-1.46L10,12H4v6l2.05,-2.05C7.32,17.22 9.07,18 11,18c1.55,0 2.98,-0.51 4.14,-1.36L20,21.49 21.49,20l-4.85,-4.86z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_flip_to_back_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_flip_to_back_24dp.xml
new file mode 100644
index 0000000..0fadaf5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_flip_to_back_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,7H7v2h2V7zm0,4H7v2h2v-2zm0,-8c-1.11,0 -2,0.9 -2,2h2V3zm4,12h-2v2h2v-2zm6,-12v2h2c0,-1.1 -0.9,-2 -2,-2zm-6,0h-2v2h2V3zM9,17v-2H7c0,1.1 0.89,2 2,2zm10,-4h2v-2h-2v2zm0,-4h2V7h-2v2zm0,8c1.1,0 2,-0.9 2,-2h-2v2zM5,7H3v12c0,1.1 0.89,2 2,2h12v-2H5V7zm10,-2h2V3h-2v2zm0,12h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_flip_to_front_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_flip_to_front_24dp.xml
new file mode 100644
index 0000000..878e1ec
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_flip_to_front_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,13h2v-2H3v2zm0,4h2v-2H3v2zm2,4v-2H3c0,1.1 0.89,2 2,2zM3,9h2V7H3v2zm12,12h2v-2h-2v2zm4,-18H9c-1.11,0 -2,0.9 -2,2v10c0,1.1 0.89,2 2,2h10c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,12H9V5h10v10zm-8,6h2v-2h-2v2zm-4,0h2v-2H7v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_get_app_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_get_app_24dp.xml
new file mode 100644
index 0000000..9c90028
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_get_app_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_grade_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_grade_24dp.xml
new file mode 100644
index 0000000..e2c53b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_grade_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_group_work_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_group_work_24dp.xml
new file mode 100644
index 0000000..f7d2245
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_group_work_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM8,17.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5zM9.5,8c0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5S9.5,9.38 9.5,8zm6.5,9.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_help_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_help_24dp.xml
new file mode 100644
index 0000000..5b524e3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_help_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,17h-2v-2h2v2zm2.07,-7.75l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2H8c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_highlight_remove_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_highlight_remove_24dp.xml
new file mode 100644
index 0000000..e1302a7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_highlight_remove_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14.59,8L12,10.59 9.41,8 8,9.41 10.59,12 8,14.59 9.41,16 12,13.41 14.59,16 16,14.59 13.41,12 16,9.41 14.59,8zM12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_history_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_history_24dp.xml
new file mode 100644
index 0000000..a290938
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_history_24dp.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,3c-4.97,0 -9,4.03 -9,9H1l3.89,3.89 0.07,0.14L9,12H6c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm-1,5v5l4.28,2.54 0.72,-1.21 -3.5,-2.08V8H12z"
+ android:fillAlpha=".9"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_home_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_home_24dp.xml
new file mode 100644
index 0000000..2a8e066
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_home_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_https_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_https_24dp.xml
new file mode 100644
index 0000000..9154ab9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_https_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zm-6,9c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zm3.1,-9H8.9V6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_info_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_info_24dp.xml
new file mode 100644
index 0000000..cea2f8d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_info_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-6h2v6zm0,-8h-2V7h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_info_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_info_outline_24dp.xml
new file mode 100644
index 0000000..f500013
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_info_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,17h2v-6h-2v6zm1,-15C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2V7h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_input_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_input_24dp.xml
new file mode 100644
index 0000000..6b10c4e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_input_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,3.01H3c-1.1,0 -2,0.9 -2,2V9h2V4.99h18v14.03H3V15H1v4.01c0,1.1 0.9,1.98 2,1.98h18c1.1,0 2,-0.88 2,-1.98v-14c0,-1.11 -0.9,-2 -2,-2zM11,16l4,-4 -4,-4v3H1v2h10v3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_invert_colors_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_invert_colors_24dp.xml
new file mode 100644
index 0000000..8d98986
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_invert_colors_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.66,7.93L12,2.27 6.34,7.93c-3.12,3.12 -3.12,8.19 0,11.31C7.9,20.8 9.95,21.58 12,21.58c2.05,0 4.1,-0.78 5.66,-2.34 3.12,-3.12 3.12,-8.19 0,-11.31zM12,19.59c-1.6,0 -3.11,-0.62 -4.24,-1.76C6.62,16.69 6,15.19 6,13.59s0.62,-3.11 1.76,-4.24L12,5.1v14.49z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_label_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_label_24dp.xml
new file mode 100644
index 0000000..7958502
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_label_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.63,5.84C17.27,5.33 16.67,5 16,5L5,5.01C3.9,5.01 3,5.9 3,7v10c0,1.1 0.9,1.99 2,1.99L16,19c0.67,0 1.27,-0.33 1.63,-0.84L22,12l-4.37,-6.16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_label_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_label_outline_24dp.xml
new file mode 100644
index 0000000..06069cb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_label_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.63,5.84C17.27,5.33 16.67,5 16,5L5,5.01C3.9,5.01 3,5.9 3,7v10c0,1.1 0.9,1.99 2,1.99L16,19c0.67,0 1.27,-0.33 1.63,-0.84L22,12l-4.37,-6.16zM16,17H5V7h11l3.55,5L16,17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_language_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_language_24dp.xml
new file mode 100644
index 0000000..6e17188
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_language_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zm6.93,6h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2 0,0.68 0.06,1.34 0.14,2H4.26zm0.82,2h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zm2.95,-8H5.08c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14H9.66c-0.09,-0.66 -0.16,-1.32 -0.16,-2 0,-0.68 0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2 0,0.68 -0.07,1.34 -0.16,2zm0.25,5.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2 0,-0.68 -0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_launch_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_launch_24dp.xml
new file mode 100644
index 0000000..c8b33be
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_launch_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_list_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_list_24dp.xml
new file mode 100644
index 0000000..689c602
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_list_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,13h2v-2H3v2zm0,4h2v-2H3v2zm0,-8h2V7H3v2zm4,4h14v-2H7v2zm0,4h14v-2H7v2zM7,7v2h14V7H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_24dp.xml
new file mode 100644
index 0000000..9154ab9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zm-6,9c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zm3.1,-9H8.9V6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_open_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_open_24dp.xml
new file mode 100644
index 0000000..bcd6b79
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_open_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zm6,-9h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h1.9c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zm0,12H6V10h12v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_outline_24dp.xml
new file mode 100644
index 0000000..10e2fcf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_lock_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zm-6,-5.1c1.71,0 3.1,1.39 3.1,3.1v2H9V6h-0.1c0,-1.71 1.39,-3.1 3.1,-3.1zM18,20H6V10h12v10zm-6,-3c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_loyalty_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_loyalty_24dp.xml
new file mode 100644
index 0000000..9a92666
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_loyalty_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21.41,11.58l-9,-9C12.05,2.22 11.55,2 11,2H4c-1.1,0 -2,0.9 -2,2v7c0,0.55 0.22,1.05 0.59,1.42l9,9c0.36,0.36 0.86,0.58 1.41,0.58 0.55,0 1.05,-0.22 1.41,-0.59l7,-7c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-0.55 -0.23,-1.06 -0.59,-1.42zM5.5,7C4.67,7 4,6.33 4,5.5S4.67,4 5.5,4 7,4.67 7,5.5 6.33,7 5.5,7zm11.77,8.27L13,19.54l-4.27,-4.27C8.28,14.81 8,14.19 8,13.5c0,-1.38 1.12,-2.5 2.5,-2.5 0.69,0 1.32,0.28 1.77,0.74l0.73,0.72 0.73,-0.73c0.45,-0.45 1.08,-0.73 1.77,-0.73 1.38,0 2.5,1.12 2.5,2.5 0,0.69 -0.28,1.32 -0.73,1.77z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_markunread_mailbox_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_markunread_mailbox_24dp.xml
new file mode 100644
index 0000000..b947d8f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_markunread_mailbox_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,6H10v6H8V4h6V0H6v6H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_note_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_note_add_24dp.xml
new file mode 100644
index 0000000..20a348e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_note_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14,2H6c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2V8l-6,-6zm2,14h-3v3h-2v-3H8v-2h3v-3h2v3h3v2zm-3,-7V3.5L18.5,9H13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_open_in_browser_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_open_in_browser_24dp.xml
new file mode 100644
index 0000000..26ca0e3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_open_in_browser_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,4H5c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h4v-2H5V8h14v10h-4v2h4c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.89,-2 -2,-2zm-7,6l-4,4h3v6h2v-6h3l-4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_open_in_new_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_open_in_new_24dp.xml
new file mode 100644
index 0000000..c8b33be
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_open_in_new_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_open_with_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_open_with_24dp.xml
new file mode 100644
index 0000000..9b115d9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_open_with_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,9h4V6h3l-5,-5 -5,5h3v3zm-1,1H6V7l-5,5 5,5v-3h3v-4zm14,2l-5,-5v3h-3v4h3v3l5,-5zm-9,3h-4v3H7l5,5 5,-5h-3v-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_pageview_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_pageview_24dp.xml
new file mode 100644
index 0000000..7eadee8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_pageview_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,8c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zm8,-5H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-1.41,16l-3.83,-3.83c-0.8,0.52 -1.74,0.83 -2.76,0.83 -2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5c0,1.02 -0.31,1.96 -0.83,2.75L19,17.59 17.59,19z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_payment_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_payment_24dp.xml
new file mode 100644
index 0000000..3f58e10
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_payment_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,4H4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V6c0,-1.11 -0.89,-2 -2,-2zm0,14H4v-6h16v6zm0,-10H4V6h16v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_camera_mic_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_camera_mic_24dp.xml
new file mode 100644
index 0000000..fc8a6cb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_camera_mic_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,5h-3.17L15,3H9L7.17,5H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h7v-2.09c-2.83,-0.48 -5,-2.94 -5,-5.91h2c0,2.21 1.79,4 4,4s4,-1.79 4,-4h2c0,2.97 -2.17,5.43 -5,5.91V21h7c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2zm-6,8c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2V9c0,-1.1 0.9,-2 2,-2s2,0.9 2,2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_contact_cal_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_contact_cal_24dp.xml
new file mode 100644
index 0000000..10dfe40
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_contact_cal_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3h-1V1h-2v2H8V1H6v2H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-7,3c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zm6,12H6v-1c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1v1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_data_setting_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_data_setting_24dp.xml
new file mode 100644
index 0000000..f48b14b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_data_setting_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.99,11.5c0.34,0 0.67,0.03 1,0.07L20,0 0,20h11.56c-0.04,-0.33 -0.07,-0.66 -0.07,-1 0,-4.14 3.36,-7.5 7.5,-7.5zm3.71,7.99c0.02,-0.16 0.04,-0.32 0.04,-0.49 0,-0.17 -0.01,-0.33 -0.04,-0.49l1.06,-0.83c0.09,-0.08 0.12,-0.21 0.06,-0.32l-1,-1.73c-0.06,-0.11 -0.19,-0.15 -0.31,-0.11l-1.24,0.5c-0.26,-0.2 -0.54,-0.37 -0.85,-0.49l-0.19,-1.32c-0.01,-0.12 -0.12,-0.21 -0.24,-0.21h-2c-0.12,0 -0.23,0.09 -0.25,0.21l-0.19,1.32c-0.3,0.13 -0.59,0.29 -0.85,0.49l-1.24,-0.5c-0.11,-0.04 -0.24,0 -0.31,0.11l-1,1.73c-0.06,0.11 -0.04,0.24 0.06,0.32l1.06,0.83c-0.02,0.16 -0.03,0.32 -0.03,0.49 0,0.17 0.01,0.33 0.03,0.49l-1.06,0.83c-0.09,0.08 -0.12,0.21 -0.06,0.32l1,1.73c0.06,0.11 0.19,0.15 0.31,0.11l1.24,-0.5c0.26,0.2 0.54,0.37 0.85,0.49l0.19,1.32c0.02,0.12 0.12,0.21 0.25,0.21h2c0.12,0 0.23,-0.09 0.25,-0.21l0.19,-1.32c0.3,-0.13 0.59,-0.29 0.84,-0.49l1.25,0.5c0.11,0.04 0.24,0 0.31,-0.11l1,-1.73c0.06,-0.11 0.03,-0.24 -0.06,-0.32l-1.07,-0.83zm-3.71,1.01c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_device_info_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_device_info_24dp.xml
new file mode 100644
index 0000000..54536e4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_device_info_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,7h-2v2h2V7zm0,4h-2v6h2v-6zm4,-9.99L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_identity_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_identity_24dp.xml
new file mode 100644
index 0000000..a34bd73
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_identity_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,5.9c1.16,0 2.1,0.94 2.1,2.1s-0.94,2.1 -2.1,2.1S9.9,9.16 9.9,8s0.94,-2.1 2.1,-2.1m0,9c2.97,0 6.1,1.46 6.1,2.1v1.1H5.9V17c0,-0.64 3.13,-2.1 6.1,-2.1M12,4C9.79,4 8,5.79 8,8s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm0,9c-2.67,0 -8,1.34 -8,4v3h16v-3c0,-2.66 -5.33,-4 -8,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_media_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_media_24dp.xml
new file mode 100644
index 0000000..e225808
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_media_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2,6H0v5h0.01L0,20c0,1.1 0.9,2 2,2h18v-2H2V6zm20,-2h-8l-2,-2H6c-1.1,0 -1.99,0.9 -1.99,2L4,16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zM7,15l4.5,-6 3.5,4.51 2.5,-3.01L21,15H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_phone_msg_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_phone_msg_24dp.xml
new file mode 100644
index 0000000..842b965
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_phone_msg_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,15.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.58l2.2,-2.21c0.28,-0.27 0.36,-0.66 0.25,-1.01C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1zM12,3v10l3,-3h6V3h-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_scan_wifi_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_scan_wifi_24dp.xml
new file mode 100644
index 0000000..6a82875
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_perm_scan_wifi_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,3C6.95,3 3.15,4.85 0,7.23L12,22 24,7.25C20.85,4.87 17.05,3 12,3zm1,13h-2v-6h2v6zm-2,-8V6h2v2h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_picture_in_picture_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_picture_in_picture_24dp.xml
new file mode 100644
index 0000000..9b5d81e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_picture_in_picture_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,7h-8v6h8V7zm2,-4H3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,1.98 2,1.98h18c1.1,0 2,-0.88 2,-1.98V5c0,-1.1 -0.9,-2 -2,-2zm0,16.01H3V4.98h18v14.03z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_polymer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_polymer_24dp.xml
new file mode 100644
index 0000000..bee03f9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_polymer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,4h-4L7.11,16.63 4.5,12 9,4H5L0.5,12 5,20h4l7.89,-12.63L19.5,12 15,20h4l4.5,-8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_print_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_print_24dp.xml
new file mode 100644
index 0000000..9bfb690
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_print_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,8H5c-1.66,0 -3,1.34 -3,3v6h4v4h12v-4h4v-6c0,-1.66 -1.34,-3 -3,-3zm-3,11H8v-5h8v5zm3,-7c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zm-1,-9H6v4h12V3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_query_builder_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_query_builder_24dp.xml
new file mode 100644
index 0000000..cbbd47e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_query_builder_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_question_answer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_question_answer_24dp.xml
new file mode 100644
index 0000000..5e7d985
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_question_answer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,6h-2v9H6v2c0,0.55 0.45,1 1,1h11l4,4V7c0,-0.55 -0.45,-1 -1,-1zm-4,6V3c0,-0.55 -0.45,-1 -1,-1H3c-0.55,0 -1,0.45 -1,1v14l4,-4h10c0.55,0 1,-0.45 1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_receipt_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_receipt_24dp.xml
new file mode 100644
index 0000000..5a02c79
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_receipt_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,17H6v-2h12v2zm0,-4H6v-2h12v2zm0,-4H6V7h12v2zM3,22l1.5,-1.5L6,22l1.5,-1.5L9,22l1.5,-1.5L12,22l1.5,-1.5L15,22l1.5,-1.5L18,22l1.5,-1.5L21,22V2l-1.5,1.5L18,2l-1.5,1.5L15,2l-1.5,1.5L12,2l-1.5,1.5L9,2 7.5,3.5 6,2 4.5,3.5 3,2v20z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_redeem_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_redeem_24dp.xml
new file mode 100644
index 0000000..7f78847
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_redeem_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,6h-2.18c0.11,-0.31 0.18,-0.65 0.18,-1 0,-1.66 -1.34,-3 -3,-3 -1.05,0 -1.96,0.54 -2.5,1.35l-0.5,0.67 -0.5,-0.68C10.96,2.54 10.05,2 9,2 7.34,2 6,3.34 6,5c0,0.35 0.07,0.69 0.18,1H4c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8c0,-1.11 -0.89,-2 -2,-2zm-5,-2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM9,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm11,15H4v-2h16v2zm0,-5H4V8h5.08L7,10.83 8.62,12 11,8.76l1,-1.36 1,1.36L15.38,12 17,10.83 14.92,8H20v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_reorder_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_reorder_24dp.xml
new file mode 100644
index 0000000..78be3e9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_reorder_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,15h18v-2H3v2zm0,4h18v-2H3v2zm0,-8h18V9H3v2zm0,-6v2h18V5H3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_report_problem_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_report_problem_24dp.xml
new file mode 100644
index 0000000..70ad19a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_report_problem_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M1,21h22L12,2 1,21zm12,-3h-2v-2h2v2zm0,-4h-2v-4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_restore_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_restore_24dp.xml
new file mode 100644
index 0000000..9963c56
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_restore_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,3c-4.97,0 -9,4.03 -9,9H1l3.89,3.89 0.07,0.14L9,12H6c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm-1,5v5l4.28,2.54 0.72,-1.21 -3.5,-2.08V8H12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_room_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_room_24dp.xml
new file mode 100644
index 0000000..04b8734
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_room_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zm0,9.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_schedule_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_schedule_24dp.xml
new file mode 100644
index 0000000..7bf67c6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_schedule_24dp.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"
+ android:fillAlpha=".9"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_search_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_search_24dp.xml
new file mode 100644
index 0000000..ed83d8e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_search_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zm-6,0C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_24dp.xml
new file mode 100644
index 0000000..e92e5b4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_applications_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_applications_24dp.xml
new file mode 100644
index 0000000..19e3442
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_applications_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm7,-7H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2V5c0,-1.1 -0.89,-2 -2,-2zm-1.75,9c0,0.23 -0.02,0.46 -0.05,0.68l1.48,1.16c0.13,0.11 0.17,0.3 0.08,0.45l-1.4,2.42c-0.09,0.15 -0.27,0.21 -0.43,0.15l-1.74,-0.7c-0.36,0.28 -0.76,0.51 -1.18,0.69l-0.26,1.85c-0.03,0.17 -0.18,0.3 -0.35,0.3h-2.8c-0.17,0 -0.32,-0.13 -0.35,-0.29l-0.26,-1.85c-0.43,-0.18 -0.82,-0.41 -1.18,-0.69l-1.74,0.7c-0.16,0.06 -0.34,0 -0.43,-0.15l-1.4,-2.42c-0.09,-0.15 -0.05,-0.34 0.08,-0.45l1.48,-1.16c-0.03,-0.23 -0.05,-0.46 -0.05,-0.69 0,-0.23 0.02,-0.46 0.05,-0.68l-1.48,-1.16c-0.13,-0.11 -0.17,-0.3 -0.08,-0.45l1.4,-2.42c0.09,-0.15 0.27,-0.21 0.43,-0.15l1.74,0.7c0.36,-0.28 0.76,-0.51 1.18,-0.69l0.26,-1.85c0.03,-0.17 0.18,-0.3 0.35,-0.3h2.8c0.17,0 0.32,0.13 0.35,0.29l0.26,1.85c0.43,0.18 0.82,0.41 1.18,0.69l1.74,-0.7c0.16,-0.06 0.34,0 0.43,0.15l1.4,2.42c0.09,0.15 0.05,0.34 -0.08,0.45l-1.48,1.16c0.03,0.23 0.05,0.46 0.05,0.69z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_backup_restore_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_backup_restore_24dp.xml
new file mode 100644
index 0000000..874ab3b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_backup_restore_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14,12c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2 0.9,2 2,2 2,-0.9 2,-2zm-2,-9c-4.97,0 -9,4.03 -9,9H0l4,4 4,-4H5c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.51,0 -2.91,-0.49 -4.06,-1.3l-1.42,1.44C8.04,20.3 9.94,21 12,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_bluetooth_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_bluetooth_24dp.xml
new file mode 100644
index 0000000..23ad97e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_bluetooth_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,24h2v-2h-2v2zm-4,0h2v-2H7v2zm8,0h2v-2h-2v2zm2.71,-18.29L12,0h-1v7.59L6.41,3 5,4.41 10.59,10 5,15.59 6.41,17 11,12.41V20h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,3.83l1.88,1.88L13,7.59V3.83zm1.88,10.46L13,16.17v-3.76l1.88,1.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_cell_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_cell_24dp.xml
new file mode 100644
index 0000000..89fdea7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_cell_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,24h2v-2H7v2zm4,0h2v-2h-2v2zm4,0h2v-2h-2v2zM16,0.01L8,0C6.9,0 6,0.9 6,2v16c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V2c0,-1.1 -0.9,-1.99 -2,-1.99zM16,16H8V4h8v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_display_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_display_24dp.xml
new file mode 100644
index 0000000..65bd27e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_display_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,3H3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16.01H3V4.99h18v14.02zM8,16h2.5l1.5,1.5 1.5,-1.5H16v-2.5l1.5,-1.5 -1.5,-1.5V8h-2.5L12,6.5 10.5,8H8v2.5L6.5,12 8,13.5V16zm4,-7c1.66,0 3,1.34 3,3s-1.34,3 -3,3V9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_ethernet_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_ethernet_24dp.xml
new file mode 100644
index 0000000..57be67f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_ethernet_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.77,6.76L6.23,5.48 0.82,12l5.41,6.52 1.54,-1.28L3.42,12l4.35,-5.24zM7,13h2v-2H7v2zm10,-2h-2v2h2v-2zm-6,2h2v-2h-2v2zm6.77,-7.52l-1.54,1.28L20.58,12l-4.35,5.24 1.54,1.28L23.18,12l-5.41,-6.52z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_antenna_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_antenna_24dp.xml
new file mode 100644
index 0000000..d73b70d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_antenna_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,5c-3.87,0 -7,3.13 -7,7h2c0,-2.76 2.24,-5 5,-5s5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zm1,9.29c0.88,-0.39 1.5,-1.26 1.5,-2.29 0,-1.38 -1.12,-2.5 -2.5,-2.5S9.5,10.62 9.5,12c0,1.02 0.62,1.9 1.5,2.29v3.3L7.59,21 9,22.41l3,-3 3,3L16.41,21 13,17.59v-3.3zM12,1C5.93,1 1,5.93 1,12h2c0,-4.97 4.03,-9 9,-9s9,4.03 9,9h2c0,-6.07 -4.93,-11 -11,-11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_component_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_component_24dp.xml
new file mode 100644
index 0000000..bf8dcc5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_component_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5,2c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4H1v6h6V6H5V2zm4,14c0,1.3 0.84,2.4 2,2.82V23h2v-4.18c1.16,-0.41 2,-1.51 2,-2.82v-2H9v2zm-8,0c0,1.3 0.84,2.4 2,2.82V23h2v-4.18C6.16,18.4 7,17.3 7,16v-2H1v2zM21,6V2c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4h-2v6h6V6h-2zm-8,-4c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4H9v6h6V6h-2V2zm4,14c0,1.3 0.84,2.4 2,2.82V23h2v-4.18c1.16,-0.41 2,-1.51 2,-2.82v-2h-6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_composite_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_composite_24dp.xml
new file mode 100644
index 0000000..bf8dcc5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_composite_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5,2c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4H1v6h6V6H5V2zm4,14c0,1.3 0.84,2.4 2,2.82V23h2v-4.18c1.16,-0.41 2,-1.51 2,-2.82v-2H9v2zm-8,0c0,1.3 0.84,2.4 2,2.82V23h2v-4.18C6.16,18.4 7,17.3 7,16v-2H1v2zM21,6V2c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4h-2v6h6V6h-2zm-8,-4c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4H9v6h6V6h-2V2zm4,14c0,1.3 0.84,2.4 2,2.82V23h2v-4.18c1.16,-0.41 2,-1.51 2,-2.82v-2h-6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_hdmi_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_hdmi_24dp.xml
new file mode 100644
index 0000000..a6c8cfb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_hdmi_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,7V4c0,-1.1 -0.9,-2 -2,-2H8c-1.1,0 -2,0.9 -2,2v3H5v6l3,6v3h8v-3l3,-6V7h-1zM8,4h8v3h-2V5h-1v2h-2V5h-1v2H8V4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_svideo_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_svideo_24dp.xml
new file mode 100644
index 0000000..a062b66
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_input_svideo_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8,11.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S5,10.67 5,11.5 5.67,13 6.5,13 8,12.33 8,11.5zm7,-5c0,-0.83 -0.67,-1.5 -1.5,-1.5h-3C9.67,5 9,5.67 9,6.5S9.67,8 10.5,8h3c0.83,0 1.5,-0.67 1.5,-1.5zM8.5,15c-0.83,0 -1.5,0.67 -1.5,1.5S7.67,18 8.5,18s1.5,-0.67 1.5,-1.5S9.33,15 8.5,15zM12,1C5.93,1 1,5.93 1,12s4.93,11 11,11 11,-4.93 11,-11S18.07,1 12,1zm0,20c-4.96,0 -9,-4.04 -9,-9s4.04,-9 9,-9 9,4.04 9,9 -4.04,9 -9,9zm5.5,-11c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zm-2,5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_overscan_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_overscan_24dp.xml
new file mode 100644
index 0000000..2e0b0dc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_overscan_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.01,5.5L10,8h4l-1.99,-2.5zM18,10v4l2.5,-1.99L18,10zM6,10l-2.5,2.01L6,14v-4zm8,6h-4l2.01,2.5L14,16zm7,-13H3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16.01H3V4.99h18v14.02z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_phone_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_phone_24dp.xml
new file mode 100644
index 0000000..eb7881b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_phone_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,9h-2v2h2V9zm4,0h-2v2h2V9zm3,6.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.58l2.2,-2.21c0.28,-0.27 0.36,-0.66 0.25,-1.01C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1zM19,9v2h2V9h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_power_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_power_24dp.xml
new file mode 100644
index 0000000..8d64e7f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_power_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,24h2v-2H7v2zm4,0h2v-2h-2v2zm2,-22h-2v10h2V2zm3.56,2.44l-1.45,1.45C16.84,6.94 18,8.83 18,11c0,3.31 -2.69,6 -6,6s-6,-2.69 -6,-6c0,-2.17 1.16,-4.06 2.88,-5.12L7.44,4.44C5.36,5.88 4,8.28 4,11c0,4.42 3.58,8 8,8s8,-3.58 8,-8c0,-2.72 -1.36,-5.12 -3.44,-6.56zM15,24h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_remote_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_remote_24dp.xml
new file mode 100644
index 0000000..ed5c7cc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_remote_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,9H9c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1V10c0,-0.55 -0.45,-1 -1,-1zm-3,6c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM7.05,6.05l1.41,1.41C9.37,6.56 10.62,6 12,6s2.63,0.56 3.54,1.46l1.41,-1.41C15.68,4.78 13.93,4 12,4s-3.68,0.78 -4.95,2.05zM12,0C8.96,0 6.21,1.23 4.22,3.22l1.41,1.41C7.26,3.01 9.51,2 12,2s4.74,1.01 6.36,2.64l1.41,-1.41C17.79,1.23 15.04,0 12,0z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_voice_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_voice_24dp.xml
new file mode 100644
index 0000000..a3857ec
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_settings_voice_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,24h2v-2H7v2zm5,-11c1.66,0 2.99,-1.34 2.99,-3L15,4c0,-1.66 -1.34,-3 -3,-3S9,2.34 9,4v6c0,1.66 1.34,3 3,3zm-1,11h2v-2h-2v2zm4,0h2v-2h-2v2zm4,-14h-1.7c0,3 -2.54,5.1 -5.3,5.1S6.7,13 6.7,10H5c0,3.41 2.72,6.23 6,6.72V20h2v-3.28c3.28,-0.49 6,-3.31 6,-6.72z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_shop_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_shop_24dp.xml
new file mode 100644
index 0000000..97e4e62
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_shop_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,6V4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2H2v13c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V6h-6zm-6,-2h4v2h-4V4zM9,18V9l7.5,4L9,18z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_shop_two_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_shop_two_24dp.xml
new file mode 100644
index 0000000..8634fb3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_shop_two_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,9H1v11c0,1.11 0.89,2 2,2h14c1.11,0 2,-0.89 2,-2H3V9zm15,-4V3c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2H5v11c0,1.11 0.89,2 2,2h14c1.11,0 2,-0.89 2,-2V5h-5zm-6,-2h4v2h-4V3zm0,12V8l5.5,3 -5.5,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_shopping_basket_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_shopping_basket_24dp.xml
new file mode 100644
index 0000000..2207e4f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_shopping_basket_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.21,9l-4.38,-6.56c-0.19,-0.28 -0.51,-0.42 -0.83,-0.42 -0.32,0 -0.64,0.14 -0.83,0.43L6.79,9H2c-0.55,0 -1,0.45 -1,1 0,0.09 0.01,0.18 0.04,0.27l2.54,9.27c0.23,0.84 1,1.46 1.92,1.46h13c0.92,0 1.69,-0.62 1.93,-1.46l2.54,-9.27L23,10c0,-0.55 -0.45,-1 -1,-1h-4.79zM9,9l3,-4.4L15,9H9zm3,8c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_shopping_cart_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_shopping_cart_24dp.xml
new file mode 100644
index 0000000..f28015e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_shopping_cart_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,18c-1.1,0 -1.99,0.9 -1.99,2S5.9,22 7,22s2,-0.9 2,-2 -0.9,-2 -2,-2zM1,2v2h2l3.6,7.59 -1.35,2.45c-0.16,0.28 -0.25,0.61 -0.25,0.96 0,1.1 0.9,2 2,2h12v-2H7.42c-0.14,0 -0.25,-0.11 -0.25,-0.25l0.03,-0.12 0.9,-1.63h7.45c0.75,0 1.41,-0.41 1.75,-1.03l3.58,-6.49c0.08,-0.14 0.12,-0.31 0.12,-0.48 0,-0.55 -0.45,-1 -1,-1H5.21l-0.94,-2H1zm16,16c-1.1,0 -1.99,0.9 -1.99,2s0.89,2 1.99,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_speaker_notes_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_speaker_notes_24dp.xml
new file mode 100644
index 0000000..de7f2bc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_speaker_notes_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM8,14H6v-2h2v2zm0,-3H6V9h2v2zm0,-3H6V6h2v2zm7,6h-5v-2h5v2zm3,-3h-8V9h8v2zm0,-3h-8V6h8v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_spellcheck_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_spellcheck_24dp.xml
new file mode 100644
index 0000000..a61fef1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_spellcheck_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.45,16h2.09L9.43,3H7.57L2.46,16h2.09l1.12,-3h5.64l1.14,3zm-6.02,-5L8.5,5.48 10.57,11H6.43zm15.16,0.59l-8.09,8.09L9.83,16l-1.41,1.41 5.09,5.09L23,13l-1.41,-1.41z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_star_rate_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_star_rate_24dp.xml
new file mode 100644
index 0000000..44811e6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_star_rate_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,14.3l3.71,2.7 -1.42,-4.36L18,10h-4.55L12,5.5 10.55,10H6l3.71,2.64L8.29,17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_stars_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_stars_24dp.xml
new file mode 100644
index 0000000..94f4a56
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_stars_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zm4.24,16L12,15.45 7.77,18l1.12,-4.81 -3.73,-3.23 4.92,-0.42L12,5l1.92,4.53 4.92,0.42 -3.73,3.23L16.23,18z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_store_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_store_24dp.xml
new file mode 100644
index 0000000..cef78e8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_store_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,4H4v2h16V4zm1,10v-2l-1,-5H4l-1,5v2h1v6h10v-6h4v6h2v-6h1zm-9,4H6v-4h6v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_subject_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_subject_24dp.xml
new file mode 100644
index 0000000..0049c9c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_subject_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14,17H4v2h10v-2zm6,-8H4v2h16V9zM4,15h16v-2H4v2zM4,5v2h16V5H4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_supervisor_account_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_supervisor_account_24dp.xml
new file mode 100644
index 0000000..e775937
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_supervisor_account_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.5,12c1.38,0 2.49,-1.12 2.49,-2.5S17.88,7 16.5,7C15.12,7 14,8.12 14,9.5s1.12,2.5 2.5,2.5zM9,11c1.66,0 2.99,-1.34 2.99,-3S10.66,5 9,5C7.34,5 6,6.34 6,8s1.34,3 3,3zm7.5,3c-1.83,0 -5.5,0.92 -5.5,2.75V19h11v-2.25c0,-1.83 -3.67,-2.75 -5.5,-2.75zM9,13c-2.33,0 -7,1.17 -7,3.5V19h7v-2.25c0,-0.85 0.33,-2.34 2.37,-3.47C10.5,13.1 9.66,13 9,13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_horiz_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_horiz_24dp.xml
new file mode 100644
index 0000000..de4cb80
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_horiz_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_vert_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_vert_24dp.xml
new file mode 100644
index 0000000..d7374f4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_vert_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,17.01V10h-2v7.01h-3L15,21l4,-3.99h-3zM9,3L5,6.99h3V14h2V6.99h3L9,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_vert_circle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_vert_circle_24dp.xml
new file mode 100644
index 0000000..89dca6e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_swap_vert_circle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM6.5,9L10,5.5 13.5,9H11v4H9V9H6.5zm11,6L14,18.5 10.5,15H13v-4h2v4h2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_system_update_tv_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_system_update_tv_24dp.xml
new file mode 100644
index 0000000..191c10a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_system_update_tv_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,16.5l4,-4h-3v-9h-2v9H8l4,4zm9,-13h-6v1.99h6v14.03H3V5.49h6V3.5H3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2v-14c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_tab_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_tab_24dp.xml
new file mode 100644
index 0000000..77cbd77
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_tab_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,3H3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H3V5h10v4h8v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_tab_unselected_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_tab_unselected_24dp.xml
new file mode 100644
index 0000000..e7a1cc9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_tab_unselected_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M1,9h2V7H1v2zm0,4h2v-2H1v2zm0,-8h2V3c-1.1,0 -2,0.9 -2,2zm8,16h2v-2H9v2zm-8,-4h2v-2H1v2zm2,4v-2H1c0,1.1 0.9,2 2,2zM21,3h-8v6h10V5c0,-1.1 -0.9,-2 -2,-2zm0,14h2v-2h-2v2zM9,5h2V3H9v2zM5,21h2v-2H5v2zM5,5h2V3H5v2zm16,16c1.1,0 2,-0.9 2,-2h-2v2zm0,-8h2v-2h-2v2zm-8,8h2v-2h-2v2zm4,0h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_theaters_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_theaters_24dp.xml
new file mode 100644
index 0000000..7c662e7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_theaters_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,3v2h-2V3H8v2H6V3H4v18h2v-2h2v2h8v-2h2v2h2V3h-2zM8,17H6v-2h2v2zm0,-4H6v-2h2v2zm0,-4H6V7h2v2zm10,8h-2v-2h2v2zm0,-4h-2v-2h2v2zm0,-4h-2V7h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_thumb_down_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_thumb_down_24dp.xml
new file mode 100644
index 0000000..85a2639
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_thumb_down_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,3H6c-0.83,0 -1.54,0.5 -1.84,1.22l-3.02,7.05c-0.09,0.23 -0.14,0.47 -0.14,0.73v1.91l0.01,0.01L1,14c0,1.1 0.9,2 2,2h6.31l-0.95,4.57 -0.03,0.32c0,0.41 0.17,0.79 0.44,1.06L9.83,23l6.59,-6.59c0.36,-0.36 0.58,-0.86 0.58,-1.41V5c0,-1.1 -0.9,-2 -2,-2zm4,0v12h4V3h-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_thumb_up_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_thumb_up_24dp.xml
new file mode 100644
index 0000000..05d5fd3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_thumb_up_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M1,21h4V9H1v12zm22,-11c0,-1.1 -0.9,-2 -2,-2h-6.31l0.95,-4.57 0.03,-0.32c0,-0.41 -0.17,-0.79 -0.44,-1.06L14.17,1 7.59,7.59C7.22,7.95 7,8.45 7,9v10c0,1.1 0.9,2 2,2h9c0.83,0 1.54,-0.5 1.84,-1.22l3.02,-7.05c0.09,-0.23 0.14,-0.47 0.14,-0.73v-1.91l-0.01,-0.01L23,10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_thumbs_up_down_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_thumbs_up_down_24dp.xml
new file mode 100644
index 0000000..9b574e6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_thumbs_up_down_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,6c0,-0.55 -0.45,-1 -1,-1H5.82l0.66,-3.18 0.02,-0.23c0,-0.31 -0.13,-0.59 -0.33,-0.8L5.38,0 0.44,4.94C0.17,5.21 0,5.59 0,6v6.5c0,0.83 0.67,1.5 1.5,1.5h6.75c0.62,0 1.15,-0.38 1.38,-0.91l2.26,-5.29c0.07,-0.17 0.11,-0.36 0.11,-0.55V6zm10.5,4h-6.75c-0.62,0 -1.15,0.38 -1.38,0.91l-2.26,5.29c-0.07,0.17 -0.11,0.36 -0.11,0.55V18c0,0.55 0.45,1 1,1h5.18l-0.66,3.18 -0.02,0.24c0,0.31 0.13,0.59 0.33,0.8l0.79,0.78 4.94,-4.94c0.27,-0.27 0.44,-0.65 0.44,-1.06v-6.5c0,-0.83 -0.67,-1.5 -1.5,-1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_toc_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_toc_24dp.xml
new file mode 100644
index 0000000..c9f8b1b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_toc_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,9h14V7H3v2zm0,4h14v-2H3v2zm0,4h14v-2H3v2zm16,0h2v-2h-2v2zm0,-10v2h2V7h-2zm0,6h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_today_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_today_24dp.xml
new file mode 100644
index 0000000..ddf162e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_today_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3h-1V1h-2v2H8V1H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V8h14v11zM7,10h5v5H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_track_changes_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_track_changes_24dp.xml
new file mode 100644
index 0000000..d402da3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_track_changes_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M19.07,4.93l-1.41,1.41C19.1,7.79 20,9.79 20,12c0,4.42 -3.58,8 -8,8s-8,-3.58 -8,-8c0,-4.08 3.05,-7.44 7,-7.93v2.02C8.16,6.57 6,9.03 6,12c0,3.31 2.69,6 6,6s6,-2.69 6,-6c0,-1.66 -0.67,-3.16 -1.76,-4.24l-1.41,1.41C15.55,9.9 16,10.9 16,12c0,2.21 -1.79,4 -4,4s-4,-1.79 -4,-4c0,-1.86 1.28,-3.41 3,-3.86v2.14c-0.6,0.35 -1,0.98 -1,1.72 0,1.1 0.9,2 2,2s2,-0.9 2,-2c0,-0.74 -0.4,-1.38 -1,-1.72V2h-1C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10c0,-2.76 -1.12,-5.26 -2.93,-7.07z"
+ android:fillColor="#231F20"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_translate_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_translate_24dp.xml
new file mode 100644
index 0000000..592b094
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_translate_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.87,15.07l-2.54,-2.51 0.03,-0.03c1.74,-1.94 2.98,-4.17 3.71,-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5,7.92 10.44,9.75 9,11.35 8.07,10.32 7.3,9.19 6.69,8h-2c0.73,1.63 1.73,3.17 2.98,4.56l-5.09,5.02L4,19l5,-5 3.11,3.11 0.76,-2.04zM18.5,10h-2L12,22h2l1.12,-3h4.75L21,22h2l-4.5,-12zm-2.62,7l1.62,-4.33L19.12,17h-3.24z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_down_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_down_24dp.xml
new file mode 100644
index 0000000..3a6ba95
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_down_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,18l2.29,-2.29 -4.88,-4.88 -4,4L2,7.41 3.41,6l6,6 4,-4 6.3,6.29L22,12v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_neutral_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_neutral_24dp.xml
new file mode 100644
index 0000000..f5b62c6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_neutral_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,12l-4,-4v3H3v2h15v3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_up_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_up_24dp.xml
new file mode 100644
index 0000000..cd5e654
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_trending_up_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,6l2.29,2.29 -4.88,4.88 -4,-4L2,16.59 3.41,18l6,-6 4,4 6.3,-6.29L22,12V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_turned_in_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_turned_in_24dp.xml
new file mode 100644
index 0000000..61d6f32
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_turned_in_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_turned_in_not_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_turned_in_not_24dp.xml
new file mode 100644
index 0000000..9605ee6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_turned_in_not_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2zm0,15l-5,-2.18L7,18V5h10v13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_verified_user_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_verified_user_24dp.xml
new file mode 100644
index 0000000..0893b4d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_verified_user_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12V5l-9,-4zm-2,16l-4,-4 1.41,-1.41L10,14.17l6.59,-6.59L18,9l-8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_agenda_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_agenda_24dp.xml
new file mode 100644
index 0000000..3f40bc6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_agenda_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,13H3c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h17c0.55,0 1,-0.45 1,-1v-6c0,-0.55 -0.45,-1 -1,-1zm0,-10H3c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h17c0.55,0 1,-0.45 1,-1V4c0,-0.55 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_array_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_array_24dp.xml
new file mode 100644
index 0000000..c90c3f9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_array_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,18h3V5H4v13zM18,5v13h3V5h-3zM8,18h9V5H8v13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_carousel_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_carousel_24dp.xml
new file mode 100644
index 0000000..4df06be
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_carousel_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,19h10V4H7v15zm-5,-2h4V6H2v11zM18,6v11h4V6h-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_column_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_column_24dp.xml
new file mode 100644
index 0000000..b9dc907
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_column_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,18h5V5h-5v13zm-6,0h5V5H4v13zM16,5v13h5V5h-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_day_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_day_24dp.xml
new file mode 100644
index 0000000..0734594
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_day_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2,21h19v-3H2v3zM20,8H3c-0.55,0 -1,0.45 -1,1v6c0,0.55 0.45,1 1,1h17c0.55,0 1,-0.45 1,-1V9c0,-0.55 -0.45,-1 -1,-1zM2,3v3h19V3H2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_headline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_headline_24dp.xml
new file mode 100644
index 0000000..512155c9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_headline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,15h17v-2H4v2zm0,4h17v-2H4v2zm0,-8h17V9H4v2zm0,-6v2h17V5H4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_list_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_list_24dp.xml
new file mode 100644
index 0000000..089b28e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_list_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,14h4v-4H4v4zm0,5h4v-4H4v4zM4,9h4V5H4v4zm5,5h12v-4H9v4zm0,5h12v-4H9v4zM9,5v4h12V5H9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_module_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_module_24dp.xml
new file mode 100644
index 0000000..701708c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_module_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,11h5V5H4v6zm0,7h5v-6H4v6zm6,0h5v-6h-5v6zm6,0h5v-6h-5v6zm-6,-7h5V5h-5v6zm6,-6v6h5V5h-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_quilt_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_quilt_24dp.xml
new file mode 100644
index 0000000..e2f1072
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_quilt_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,18h5v-6h-5v6zm-6,0h5V5H4v13zm12,0h5v-6h-5v6zM10,5v6h11V5H10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_stream_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_stream_24dp.xml
new file mode 100644
index 0000000..5b8c90f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_stream_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,18h17v-6H4v6zM4,5v6h17V5H4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_view_week_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_week_24dp.xml
new file mode 100644
index 0000000..9c0d632
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_view_week_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,5H3c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h3c0.55,0 1,-0.45 1,-1V6c0,-0.55 -0.45,-1 -1,-1zm14,0h-3c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h3c0.55,0 1,-0.45 1,-1V6c0,-0.55 -0.45,-1 -1,-1zm-7,0h-3c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h3c0.55,0 1,-0.45 1,-1V6c0,-0.55 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_visibility_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_visibility_24dp.xml
new file mode 100644
index 0000000..46a571c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_visibility_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zm0,-8c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_visibility_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_visibility_off_24dp.xml
new file mode 100644
index 0000000..209bdec
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_visibility_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zm4.31,-0.78l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_giftcard_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_giftcard_24dp.xml
new file mode 100644
index 0000000..7f78847
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_giftcard_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,6h-2.18c0.11,-0.31 0.18,-0.65 0.18,-1 0,-1.66 -1.34,-3 -3,-3 -1.05,0 -1.96,0.54 -2.5,1.35l-0.5,0.67 -0.5,-0.68C10.96,2.54 10.05,2 9,2 7.34,2 6,3.34 6,5c0,0.35 0.07,0.69 0.18,1H4c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8c0,-1.11 -0.89,-2 -2,-2zm-5,-2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM9,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm11,15H4v-2h16v2zm0,-5H4V8h5.08L7,10.83 8.62,12 11,8.76l1,-1.36 1,1.36L15.38,12 17,10.83 14.92,8H20v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_membership_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_membership_24dp.xml
new file mode 100644
index 0000000..00af65d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_membership_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.11,0 -2,0.89 -2,2v11c0,1.11 0.89,2 2,2h4v5l4,-2 4,2v-5h4c1.11,0 2,-0.89 2,-2V4c0,-1.11 -0.89,-2 -2,-2zm0,13H4v-2h16v2zm0,-5H4V4h16v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_travel_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_travel_24dp.xml
new file mode 100644
index 0000000..7ee959d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_wallet_travel_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,6h-3V4c0,-1.11 -0.89,-2 -2,-2H9c-1.11,0 -2,0.89 -2,2v2H4c-1.11,0 -2,0.89 -2,2v11c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8c0,-1.11 -0.89,-2 -2,-2zM9,4h6v2H9V4zm11,15H4v-2h16v2zm0,-5H4V8h3v2h2V8h6v2h2V8h3v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/action/ic_work_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/action/ic_work_24dp.xml
new file mode 100644
index 0000000..363b68b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/action/ic_work_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,6h-4V4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2H4c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8c0,-1.11 -0.89,-2 -2,-2zm-6,0h-4V4h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/alert/ic_error_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/alert/ic_error_24dp.xml
new file mode 100644
index 0000000..ef1a8f4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/alert/ic_error_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-2h2v2zm0,-4h-2V7h2v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/alert/ic_warning_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/alert/ic_warning_24dp.xml
new file mode 100644
index 0000000..70ad19a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/alert/ic_warning_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M1,21h22L12,2 1,21zm12,-3h-2v-2h2v2zm0,-4h-2v-4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_album_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_album_24dp.xml
new file mode 100644
index 0000000..8b3ecb9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_album_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,14.5c-2.49,0 -4.5,-2.01 -4.5,-4.5S9.51,7.5 12,7.5s4.5,2.01 4.5,4.5 -2.01,4.5 -4.5,4.5zm0,-5.5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_av_timer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_av_timer_24dp.xml
new file mode 100644
index 0000000..f954282
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_av_timer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,17c0,0.55 0.45,1 1,1s1,-0.45 1,-1 -0.45,-1 -1,-1 -1,0.45 -1,1zm0,-14v4h2V5.08c3.39,0.49 6,3.39 6,6.92 0,3.87 -3.13,7 -7,7s-7,-3.13 -7,-7c0,-1.68 0.59,-3.22 1.58,-4.42L12,13l1.41,-1.41 -6.8,-6.8v0.02C4.42,6.45 3,9.05 3,12c0,4.97 4.02,9 9,9 4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9h-1zm7,9c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1 0.45,1 1,1 1,-0.45 1,-1zM6,12c0,0.55 0.45,1 1,1s1,-0.45 1,-1 -0.45,-1 -1,-1 -1,0.45 -1,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_closed_caption_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_closed_caption_24dp.xml
new file mode 100644
index 0000000..1cc3ea4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_closed_caption_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,4H5c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm-8,7H9.5v-0.5h-2v3h2V13H11v1c0,0.55 -0.45,1 -1,1H7c-0.55,0 -1,-0.45 -1,-1v-4c0,-0.55 0.45,-1 1,-1h3c0.55,0 1,0.45 1,1v1zm7,0h-1.5v-0.5h-2v3h2V13H18v1c0,0.55 -0.45,1 -1,1h-3c-0.55,0 -1,-0.45 -1,-1v-4c0,-0.55 0.45,-1 1,-1h3c0.55,0 1,0.45 1,1v1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_equalizer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_equalizer_24dp.xml
new file mode 100644
index 0000000..d94c27d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_equalizer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,20h4V4h-4v16zm-6,0h4v-8H4v8zM16,9v11h4V9h-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_explicit_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_explicit_24dp.xml
new file mode 100644
index 0000000..7dcc5af
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_explicit_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-4,6h-4v2h4v2h-4v2h4v2H9V7h6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_fast_forward_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_fast_forward_24dp.xml
new file mode 100644
index 0000000..248556a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_fast_forward_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,18l8.5,-6L4,6v12zm9,-12v12l8.5,-6L13,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_fast_rewind_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_fast_rewind_24dp.xml
new file mode 100644
index 0000000..bd09f10
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_fast_rewind_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,18V6l-8.5,6 8.5,6zm0.5,-6l8.5,6V6l-8.5,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_games_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_games_24dp.xml
new file mode 100644
index 0000000..8891427
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_games_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,7.5V2H9v5.5l3,3 3,-3zM7.5,9H2v6h5.5l3,-3 -3,-3zM9,16.5V22h6v-5.5l-3,-3 -3,3zM16.5,9l-3,3 3,3H22V9h-5.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_hearing_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_hearing_24dp.xml
new file mode 100644
index 0000000..f696b00
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_hearing_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,20c-0.29,0 -0.56,-0.06 -0.76,-0.15 -0.71,-0.37 -1.21,-0.88 -1.71,-2.38 -0.51,-1.56 -1.47,-2.29 -2.39,-3 -0.79,-0.61 -1.61,-1.24 -2.32,-2.53C9.29,10.98 9,9.93 9,9c0,-2.8 2.2,-5 5,-5s5,2.2 5,5h2c0,-3.93 -3.07,-7 -7,-7S7,5.07 7,9c0,1.26 0.38,2.65 1.07,3.9 0.91,1.65 1.98,2.48 2.85,3.15 0.81,0.62 1.39,1.07 1.71,2.05 0.6,1.82 1.37,2.84 2.73,3.55 0.51,0.23 1.07,0.35 1.64,0.35 2.21,0 4,-1.79 4,-4h-2c0,1.1 -0.9,2 -2,2zM7.64,2.64L6.22,1.22C4.23,3.21 3,5.96 3,9s1.23,5.79 3.22,7.78l1.41,-1.41C6.01,13.74 5,11.49 5,9s1.01,-4.74 2.64,-6.36zM11.5,9c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5 -1.12,-2.5 -2.5,-2.5 -2.5,1.12 -2.5,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_high_quality_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_high_quality_24dp.xml
new file mode 100644
index 0000000..31c633d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_high_quality_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,4H5c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm-8,11H9.5v-2h-2v2H6V9h1.5v2.5h2V9H11v6zm7,-1c0,0.55 -0.45,1 -1,1h-0.75v1.5h-1.5V15H14c-0.55,0 -1,-0.45 -1,-1v-4c0,-0.55 0.45,-1 1,-1h3c0.55,0 1,0.45 1,1v4zm-3.5,-0.5h2v-3h-2v3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_loop_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_loop_24dp.xml
new file mode 100644
index 0000000..5ad1561
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_loop_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,4V1L8,5l4,4V6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zm0,14c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_24dp.xml
new file mode 100644
index 0000000..e2e95d5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zm5.3,-3c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11H5c0,3.41 2.72,6.23 6,6.72V21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_none_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_none_24dp.xml
new file mode 100644
index 0000000..ce4d384
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_none_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zm-1.2,-9.1c0,-0.66 0.54,-1.2 1.2,-1.2 0.66,0 1.2,0.54 1.2,1.2l-0.01,6.2c0,0.66 -0.53,1.2 -1.19,1.2 -0.66,0 -1.2,-0.54 -1.2,-1.2V4.9zm6.5,6.1c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11H5c0,3.41 2.72,6.23 6,6.72V21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_off_24dp.xml
new file mode 100644
index 0000000..73bf970
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_mic_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,11h-1.7c0,0.74 -0.16,1.43 -0.43,2.05l1.23,1.23c0.56,-0.98 0.9,-2.09 0.9,-3.28zm-4.02,0.17c0,-0.06 0.02,-0.11 0.02,-0.17V5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v0.18l5.98,5.99zM4.27,3L3,4.27l6.01,6.01V11c0,1.66 1.33,3 2.99,3 0.22,0 0.44,-0.03 0.65,-0.08l1.66,1.66c-0.71,0.33 -1.5,0.52 -2.31,0.52 -2.76,0 -5.3,-2.1 -5.3,-5.1H5c0,3.41 2.72,6.23 6,6.72V21h2v-3.28c0.91,-0.13 1.77,-0.45 2.54,-0.9L19.73,21 21,19.73 4.27,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_movie_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_movie_24dp.xml
new file mode 100644
index 0000000..6d4c52e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_movie_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,4l2,4h-3l-2,-4h-2l2,4h-3l-2,-4H8l2,4H7L5,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4h-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_add_24dp.xml
new file mode 100644
index 0000000..78eaca1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zm16,-4H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-1,9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_books_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_books_24dp.xml
new file mode 100644
index 0000000..aa446b2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_books_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zm16,-4H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-1,9H9V9h10v2zm-4,4H9v-2h6v2zm4,-8H9V5h10v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_music_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_music_24dp.xml
new file mode 100644
index 0000000..0465e98
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_my_library_music_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-2,5h-3v5.5c0,1.38 -1.12,2.5 -2.5,2.5S10,13.88 10,12.5s1.12,-2.5 2.5,-2.5c0.57,0 1.08,0.19 1.5,0.51V5h4v2zM4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_new_releases_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_new_releases_24dp.xml
new file mode 100644
index 0000000..8fbe033
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_new_releases_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M23,12l-2.44,-2.78 0.34,-3.68 -3.61,-0.82 -1.89,-3.18L12,3 8.6,1.54 6.71,4.72l-3.61,0.81 0.34,3.68L1,12l2.44,2.78 -0.34,3.69 3.61,0.82 1.89,3.18L12,21l3.4,1.46 1.89,-3.18 3.61,-0.82 -0.34,-3.68L23,12zm-10,5h-2v-2h2v2zm0,-4h-2V7h2v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_not_interested_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_not_interested_24dp.xml
new file mode 100644
index 0000000..90eae2a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_not_interested_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8 0,-1.85 0.63,-3.55 1.69,-4.9L16.9,18.31C15.55,19.37 13.85,20 12,20zm6.31,-3.1L7.1,5.69C8.45,4.63 10.15,4 12,4c4.42,0 8,3.58 8,8 0,1.85 -0.63,3.55 -1.69,4.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_24dp.xml
new file mode 100644
index 0000000..c843731
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,19h4V5H6v14zm8,-14v14h4V5h-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_circle_fill_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_circle_fill_24dp.xml
new file mode 100644
index 0000000..a91240e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_circle_fill_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm-1,14H9V8h2v8zm4,0h-2V8h2v8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_circle_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_circle_outline_24dp.xml
new file mode 100644
index 0000000..f9232e0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_pause_circle_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,16h2V8H9v8zm3,-14C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zm1,-4h2V8h-2v8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_play_arrow_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_arrow_24dp.xml
new file mode 100644
index 0000000..f9f849e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_arrow_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8,5v14l11,-7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_play_circle_fill_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_circle_fill_24dp.xml
new file mode 100644
index 0000000..9c6d722
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_circle_fill_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm-2,14.5v-9l6,4.5 -6,4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_play_circle_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_circle_outline_24dp.xml
new file mode 100644
index 0000000..65f6993
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_circle_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,16.5l6,-4.5 -6,-4.5v9zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_play_shopping_bag_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_shopping_bag_24dp.xml
new file mode 100644
index 0000000..97e4e62
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_play_shopping_bag_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,6V4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2H2v13c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V6h-6zm-6,-2h4v2h-4V4zM9,18V9l7.5,4L9,18z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_playlist_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_playlist_add_24dp.xml
new file mode 100644
index 0000000..a2ddbbb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_playlist_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14,10H2v2h12v-2zm0,-4H2v2h12V6zm4,8v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zM2,16h8v-2H2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_queue_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_queue_24dp.xml
new file mode 100644
index 0000000..78eaca1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_queue_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zm16,-4H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-1,9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_queue_music_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_queue_music_24dp.xml
new file mode 100644
index 0000000..7169362
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_queue_music_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,6H3v2h12V6zm0,4H3v2h12v-2zM3,16h8v-2H3v2zM17,6v8.18c-0.31,-0.11 -0.65,-0.18 -1,-0.18 -1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3V8h3V6h-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_radio_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_radio_24dp.xml
new file mode 100644
index 0000000..ac67acc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_radio_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3.24,6.15C2.51,6.43 2,7.17 2,8v12c0,1.1 0.89,2 2,2h16c1.11,0 2,-0.9 2,-2V8c0,-1.11 -0.89,-2 -2,-2H8.3l8.26,-3.34L15.88,1 3.24,6.15zM7,20c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zm13,-8h-2v-2h-2v2H4V8h16v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_recent_actors_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_recent_actors_24dp.xml
new file mode 100644
index 0000000..3d93f4c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_recent_actors_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,5v14h2V5h-2zm-4,14h2V5h-2v14zM14,5H2c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1V6c0,-0.55 -0.45,-1 -1,-1zM8,7.75c1.24,0 2.25,1.01 2.25,2.25S9.24,12.25 8,12.25 5.75,11.24 5.75,10 6.76,7.75 8,7.75zM12.5,17h-9v-0.75c0,-1.5 3,-2.25 4.5,-2.25s4.5,0.75 4.5,2.25V17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_repeat_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_repeat_24dp.xml
new file mode 100644
index 0000000..0362e27
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_repeat_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,7h10v3l4,-4 -4,-4v3H5v6h2V7zm10,10H7v-3l-4,4 4,4v-3h12v-6h-2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_repeat_one_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_repeat_one_24dp.xml
new file mode 100644
index 0000000..bf42eaf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_repeat_one_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,7h10v3l4,-4 -4,-4v3H5v6h2V7zm10,10H7v-3l-4,4 4,4v-3h12v-6h-2v4zm-4,-2V9h-1l-2,1v1h1.5v4H13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_replay_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_replay_24dp.xml
new file mode 100644
index 0000000..176e43a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_replay_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,5V1L7,6l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6 -6,-2.69 -6,-6H4c0,4.42 3.58,8 8,8s8,-3.58 8,-8 -3.58,-8 -8,-8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_shuffle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_shuffle_24dp.xml
new file mode 100644
index 0000000..9940b5b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_shuffle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10.59,9.17L5.41,4 4,5.41l5.17,5.17 1.42,-1.41zM14.5,4l2.04,2.04L4,18.59 5.41,20 17.96,7.46 20,9.5V4h-5.5zm0.33,9.41l-1.41,1.41 3.13,3.13L14.5,20H20v-5.5l-2.04,2.04 -3.13,-3.13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_skip_next_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_skip_next_24dp.xml
new file mode 100644
index 0000000..6ccd524
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_skip_next_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_skip_previous_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_skip_previous_24dp.xml
new file mode 100644
index 0000000..9b3d6f8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_skip_previous_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,6h2v12H6zm3.5,6l8.5,6V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_snooze_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_snooze_24dp.xml
new file mode 100644
index 0000000..09fa95b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_snooze_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7zm-3,-9h3.63L9,15.2V17h6v-2h-3.63L15,10.8V9H9v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_stop_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_stop_24dp.xml
new file mode 100644
index 0000000..0bd2ca4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_stop_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,6h12v12H6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_subtitles_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_subtitles_24dp.xml
new file mode 100644
index 0000000..45cdb1e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_subtitles_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zM4,12h4v2H4v-2zm10,6H4v-2h10v2zm6,0h-4v-2h4v2zm0,-4H10v-2h10v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_surround_sound_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_surround_sound_24dp.xml
new file mode 100644
index 0000000..bb19563
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_surround_sound_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zM7.76,16.24l-1.41,1.41C4.78,16.1 4,14.05 4,12c0,-2.05 0.78,-4.1 2.34,-5.66l1.41,1.41C6.59,8.93 6,10.46 6,12s0.59,3.07 1.76,4.24zM12,16c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4zm5.66,1.66l-1.41,-1.41C17.41,15.07 18,13.54 18,12s-0.59,-3.07 -1.76,-4.24l1.41,-1.41C19.22,7.9 20,9.95 20,12c0,2.05 -0.78,4.1 -2.34,5.66zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_video_collection_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_video_collection_24dp.xml
new file mode 100644
index 0000000..681053d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_video_collection_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zm16,-4H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-8,12.5v-9l6,4.5 -6,4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_videocam_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_videocam_24dp.xml
new file mode 100644
index 0000000..a51b841
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_videocam_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_videocam_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_videocam_off_24dp.xml
new file mode 100644
index 0000000..b6dc40c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_videocam_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,6.5l-4,4V7c0,-0.55 -0.45,-1 -1,-1H9.82L21,17.18V6.5zM3.27,2L2,3.27 4.73,6H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.21,0 0.39,-0.08 0.54,-0.18L19.73,21 21,19.73 3.27,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_down_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_down_24dp.xml
new file mode 100644
index 0000000..847964d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_down_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM5,9v6h4l5,5V4L9,9H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_mute_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_mute_24dp.xml
new file mode 100644
index 0000000..6b12444
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_mute_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,9v6h4l5,5V4l-5,5H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_off_24dp.xml
new file mode 100644
index 0000000..634a7c7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zm2.5,0c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9H3v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18V4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_up_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_up_24dp.xml
new file mode 100644
index 0000000..c8c4271
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_volume_up_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,9v6h4l5,5V4L7,9H3zm13.5,3c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/av/ic_web_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/av/ic_web_24dp.xml
new file mode 100644
index 0000000..621df45
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/av/ic_web_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm-5,14H4v-4h11v4zm0,-5H4V9h11v4zm5,5h-4V9h4v9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_business_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_business_24dp.xml
new file mode 100644
index 0000000..62bc39f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_business_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,7V3H2v18h20V7H12zM6,19H4v-2h2v2zm0,-4H4v-2h2v2zm0,-4H4V9h2v2zm0,-4H4V5h2v2zm4,12H8v-2h2v2zm0,-4H8v-2h2v2zm0,-4H8V9h2v2zm0,-4H8V5h2v2zm10,12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2,-8h-2v2h2v-2zm0,4h-2v2h2v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_24dp.xml
new file mode 100644
index 0000000..3a3ba47
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_end_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_end_24dp.xml
new file mode 100644
index 0000000..0f3a0ec
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_end_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,9c-1.6,0 -3.15,0.25 -4.6,0.72v3.1c0,0.39 -0.23,0.74 -0.56,0.9 -0.98,0.49 -1.87,1.12 -2.66,1.85 -0.18,0.18 -0.43,0.28 -0.7,0.28 -0.28,0 -0.53,-0.11 -0.71,-0.29L0.29,13.08c-0.18,-0.17 -0.29,-0.42 -0.29,-0.7 0,-0.28 0.11,-0.53 0.29,-0.71C3.34,8.78 7.46,7 12,7s8.66,1.78 11.71,4.67c0.18,0.18 0.29,0.43 0.29,0.71 0,0.28 -0.11,0.53 -0.29,0.71l-2.48,2.48c-0.18,0.18 -0.43,0.29 -0.71,0.29 -0.27,0 -0.52,-0.11 -0.7,-0.28 -0.79,-0.74 -1.69,-1.36 -2.67,-1.85 -0.33,-0.16 -0.56,-0.5 -0.56,-0.9v-3.1C15.15,9.25 13.6,9 12,9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_made_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_made_24dp.xml
new file mode 100644
index 0000000..1048f49
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_made_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,5v2h6.59L4,18.59 5.41,20 17,8.41V15h2V5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_merge_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_merge_24dp.xml
new file mode 100644
index 0000000..4eee325
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_merge_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,20.41L18.41,19 15,15.59 13.59,17 17,20.41zM7.5,8H11v5.59L5.59,19 7,20.41l6,-6V8h3.5L12,3.5 7.5,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_missed_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_missed_24dp.xml
new file mode 100644
index 0000000..a462e9f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_missed_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.59,7L12,14.59 6.41,9H11V7H3v8h2v-4.59l7,7 9,-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_received_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_received_24dp.xml
new file mode 100644
index 0000000..ac65fd2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_received_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,5.41L18.59,4 7,15.59V9H5v10h10v-2H8.41z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_split_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_split_24dp.xml
new file mode 100644
index 0000000..c34b117
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_call_split_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14,4l2.29,2.29 -2.88,2.88 1.42,1.42 2.88,-2.88L20,10V4zm-4,0H4v6l2.29,-2.29 4.71,4.7V20h2v-8.41l-5.29,-5.3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_chat_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_chat_24dp.xml
new file mode 100644
index 0000000..c28e3cf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_chat_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,9h12v2H6V9zm8,5H6v-2h8v2zm4,-6H6V6h12v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_clear_all_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_clear_all_24dp.xml
new file mode 100644
index 0000000..c0829b4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_clear_all_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5,13h14v-2H5v2zm-2,4h14v-2H3v2zM7,7v2h14V7H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_comment_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_comment_24dp.xml
new file mode 100644
index 0000000..e73d2c8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_comment_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21.99,4c0,-1.1 -0.89,-2 -1.99,-2H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4 -0.01,-18zM18,14H6v-2h12v2zm0,-3H6V9h12v2zm0,-3H6V6h12v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_contacts_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_contacts_24dp.xml
new file mode 100644
index 0000000..8a2b3e8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_contacts_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,0H4v2h16V0zM4,24h16v-2H4v2zM20,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm-8,2.75c1.24,0 2.25,1.01 2.25,2.25s-1.01,2.25 -2.25,2.25S9.75,10.24 9.75,9 10.76,6.75 12,6.75zM17,17H7v-1.5c0,-1.67 3.33,-2.5 5,-2.5s5,0.83 5,2.5V17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_dialer_sip_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_dialer_sip_24dp.xml
new file mode 100644
index 0000000..787f93a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_dialer_sip_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,3h-1v5h1V3zm-2,2h-2V4h2V3h-3v3h2v1h-2v1h3V5zm3,-2v5h1V6h2V3h-3zm2,2h-1V4h1v1zm0,10.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.01,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.59l2.2,-2.21c0.27,-0.26 0.35,-0.65 0.24,-1C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_dialpad_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_dialpad_24dp.xml
new file mode 100644
index 0000000..faa4d8b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_dialpad_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,19c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM6,1c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm0,6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm0,6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm12,-8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zm-6,8c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm6,0c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm0,-6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm-6,0c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm0,-6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_dnd_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_dnd_on_24dp.xml
new file mode 100644
index 0000000..90eae2a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_dnd_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8 0,-1.85 0.63,-3.55 1.69,-4.9L16.9,18.31C15.55,19.37 13.85,20 12,20zm6.31,-3.1L7.1,5.69C8.45,4.63 10.15,4 12,4c4.42,0 8,3.58 8,8 0,1.85 -0.63,3.55 -1.69,4.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_email_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_email_24dp.xml
new file mode 100644
index 0000000..bf2ac7d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_email_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,4l-8,5 -8,-5V6l8,5 8,-5v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_forum_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_forum_24dp.xml
new file mode 100644
index 0000000..5e7d985
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_forum_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,6h-2v9H6v2c0,0.55 0.45,1 1,1h11l4,4V7c0,-0.55 -0.45,-1 -1,-1zm-4,6V3c0,-0.55 -0.45,-1 -1,-1H3c-0.55,0 -1,0.45 -1,1v14l4,-4h10c0.55,0 1,-0.45 1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_import_export_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_import_export_24dp.xml
new file mode 100644
index 0000000..e0004d3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_import_export_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,3L5,6.99h3V14h2V6.99h3L9,3zm7,14.01V10h-2v7.01h-3L15,21l4,-3.99h-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_invert_colors_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_invert_colors_off_24dp.xml
new file mode 100644
index 0000000..0052ad8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_invert_colors_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.65,20.87l-2.35,-2.35 -6.3,-6.29 -3.56,-3.57 -1.42,-1.41L4.27,4.5 3,5.77l2.78,2.78c-2.55,3.14 -2.36,7.76 0.56,10.69C7.9,20.8 9.95,21.58 12,21.58c1.79,0 3.57,-0.59 5.03,-1.78l2.7,2.7L21,21.23l-0.35,-0.36zM12,19.59c-1.6,0 -3.11,-0.62 -4.24,-1.76C6.62,16.69 6,15.19 6,13.59c0,-1.32 0.43,-2.57 1.21,-3.6L12,14.77v4.82zM12,5.1v4.58l7.25,7.26c1.37,-2.96 0.84,-6.57 -1.6,-9.01L12,2.27l-3.7,3.7 1.41,1.41L12,5.1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_invert_colors_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_invert_colors_on_24dp.xml
new file mode 100644
index 0000000..8d98986
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_invert_colors_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.66,7.93L12,2.27 6.34,7.93c-3.12,3.12 -3.12,8.19 0,11.31C7.9,20.8 9.95,21.58 12,21.58c2.05,0 4.1,-0.78 5.66,-2.34 3.12,-3.12 3.12,-8.19 0,-11.31zM12,19.59c-1.6,0 -3.11,-0.62 -4.24,-1.76C6.62,16.69 6,15.19 6,13.59s0.62,-3.11 1.76,-4.24L12,5.1v14.49z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_live_help_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_live_help_24dp.xml
new file mode 100644
index 0000000..de9b40a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_live_help_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,2H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h4l3,3 3,-3h4c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-6,16h-2v-2h2v2zm2.07,-7.75l-0.9,0.92C13.45,11.9 13,12.5 13,14h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2H8c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_location_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_location_off_24dp.xml
new file mode 100644
index 0000000..09a700a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_location_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,6.5c1.38,0 2.5,1.12 2.5,2.5 0,0.74 -0.33,1.39 -0.83,1.85l3.63,3.63c0.98,-1.86 1.7,-3.8 1.7,-5.48 0,-3.87 -3.13,-7 -7,-7 -1.98,0 -3.76,0.83 -5.04,2.15l3.19,3.19c0.46,-0.52 1.11,-0.84 1.85,-0.84zm4.37,9.6l-4.63,-4.63 -0.11,-0.11L3.27,3 2,4.27l3.18,3.18C5.07,7.95 5,8.47 5,9c0,5.25 7,13 7,13s1.67,-1.85 3.38,-4.35L18.73,21 20,19.73l-3.63,-3.63z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_location_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_location_on_24dp.xml
new file mode 100644
index 0000000..04b8734
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_location_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zm0,9.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_message_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_message_24dp.xml
new file mode 100644
index 0000000..afbdf64
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_message_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-2,12H6v-2h12v2zm0,-3H6V9h12v2zm0,-3H6V6h12v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_messenger_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_messenger_24dp.xml
new file mode 100644
index 0000000..5134282
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_messenger_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.1,0 -2,0.9 -2,2v18l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_no_sim_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_no_sim_24dp.xml
new file mode 100644
index 0000000..b36e505
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_no_sim_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.99,5c0,-1.1 -0.89,-2 -1.99,-2h-7L7.66,5.34 19,16.68 18.99,5zM3.65,3.88L2.38,5.15 5,7.77V19c0,1.1 0.9,2 2,2h10.01c0.35,0 0.67,-0.1 0.96,-0.26l1.88,1.88 1.27,-1.27L3.65,3.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_phone_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_phone_24dp.xml
new file mode 100644
index 0000000..3a3ba47
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_phone_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_portable_wifi_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_portable_wifi_off_24dp.xml
new file mode 100644
index 0000000..6bb84dd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_portable_wifi_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.56,14.24c0.28,-0.69 0.44,-1.45 0.44,-2.24 0,-3.31 -2.69,-6 -6,-6 -0.79,0 -1.55,0.16 -2.24,0.44l1.62,1.62c0.2,-0.03 0.41,-0.06 0.62,-0.06 2.21,0 4,1.79 4,4 0,0.21 -0.02,0.42 -0.05,0.63l1.61,1.61zM12,4c4.42,0 8,3.58 8,8 0,1.35 -0.35,2.62 -0.95,3.74l1.47,1.47C21.46,15.69 22,13.91 22,12c0,-5.52 -4.48,-10 -10,-10 -1.91,0 -3.69,0.55 -5.21,1.47l1.46,1.46C9.37,4.34 10.65,4 12,4zM3.27,2.5L2,3.77l2.1,2.1C2.79,7.57 2,9.69 2,12c0,3.7 2.01,6.92 4.99,8.65l1,-1.73C5.61,17.53 4,14.96 4,12c0,-1.76 0.57,-3.38 1.53,-4.69l1.43,1.44C6.36,9.68 6,10.8 6,12c0,2.22 1.21,4.15 3,5.19l1,-1.74c-1.19,-0.7 -2,-1.97 -2,-3.45 0,-0.65 0.17,-1.25 0.44,-1.79l1.58,1.58L10,12c0,1.1 0.9,2 2,2l0.21,-0.02 0.01,0.01 7.51,7.51L21,20.23 4.27,3.5l-1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_quick_contacts_dialer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_quick_contacts_dialer_24dp.xml
new file mode 100644
index 0000000..43baea0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_quick_contacts_dialer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,3H2C0.9,3 0,3.9 0,5v14c0,1.1 0.9,2 2,2h20c1.1,0 1.99,-0.9 1.99,-2L24,5c0,-1.1 -0.9,-2 -2,-2zM8,6c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zm6,12H2v-1c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1v1zm3.85,-4h1.64L21,16l-1.99,1.99c-1.31,-0.98 -2.28,-2.38 -2.73,-3.99 -0.18,-0.64 -0.28,-1.31 -0.28,-2s0.1,-1.36 0.28,-2c0.45,-1.62 1.42,-3.01 2.73,-3.99L21,8l-1.51,2h-1.64c-0.22,0.63 -0.35,1.3 -0.35,2s0.13,1.37 0.35,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_quick_contacts_mail_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_quick_contacts_mail_24dp.xml
new file mode 100644
index 0000000..fea2a54
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_quick_contacts_mail_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,8V7l-3,2 -3,-2v1l3,2 3,-2zm1,-5H2C0.9,3 0,3.9 0,5v14c0,1.1 0.9,2 2,2h20c1.1,0 1.99,-0.9 1.99,-2L24,5c0,-1.1 -0.9,-2 -2,-2zM8,6c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zm6,12H2v-1c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1v1zm8,-6h-8V6h8v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_ring_volume_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_ring_volume_24dp.xml
new file mode 100644
index 0000000..c76a7c2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_ring_volume_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M23.71,16.67C20.66,13.78 16.54,12 12,12 7.46,12 3.34,13.78 0.29,16.67c-0.18,0.18 -0.29,0.43 -0.29,0.71 0,0.28 0.11,0.53 0.29,0.71l2.48,2.48c0.18,0.18 0.43,0.29 0.71,0.29 0.27,0 0.52,-0.11 0.7,-0.28 0.79,-0.74 1.69,-1.36 2.66,-1.85 0.33,-0.16 0.56,-0.5 0.56,-0.9v-3.1c1.45,-0.48 3,-0.73 4.6,-0.73s3.15,0.25 4.6,0.72v3.1c0,0.39 0.23,0.74 0.56,0.9 0.98,0.49 1.87,1.12 2.66,1.85 0.18,0.18 0.43,0.28 0.7,0.28 0.28,0 0.53,-0.11 0.71,-0.29l2.48,-2.48c0.18,-0.18 0.29,-0.43 0.29,-0.71 0,-0.27 -0.11,-0.52 -0.29,-0.7zM21.16,6.26l-1.41,-1.41 -3.56,3.55 1.41,1.41s3.45,-3.52 3.56,-3.55zM13,2h-2v5h2V2zM6.4,9.81L7.81,8.4 4.26,4.84 2.84,6.26c0.11,0.03 3.56,3.55 3.56,3.55z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_current_landscape_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_current_landscape_24dp.xml
new file mode 100644
index 0000000..e765296
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_current_landscape_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M1.01,7L1,17c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2H3c-1.1,0 -1.99,0.9 -1.99,2zM19,7v10H5V7h14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_current_portrait_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_current_portrait_24dp.xml
new file mode 100644
index 0000000..5b22214
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_current_portrait_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,1.01L7,1c-1.1,0 -1.99,0.9 -1.99,2v18c0,1.1 0.89,2 1.99,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_primary_landscape_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_primary_landscape_24dp.xml
new file mode 100644
index 0000000..e765296
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_primary_landscape_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M1.01,7L1,17c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2H3c-1.1,0 -1.99,0.9 -1.99,2zM19,7v10H5V7h14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_primary_portrait_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_primary_portrait_24dp.xml
new file mode 100644
index 0000000..5b22214
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_stay_primary_portrait_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,1.01L7,1c-1.1,0 -1.99,0.9 -1.99,2v18c0,1.1 0.89,2 1.99,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_swap_calls_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_swap_calls_24dp.xml
new file mode 100644
index 0000000..723a66d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_swap_calls_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,4l-4,4h3v7c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2V8c0,-2.21 -1.79,-4 -4,-4S5,5.79 5,8v7H2l4,4 4,-4H7V8c0,-1.1 0.9,-2 2,-2s2,0.9 2,2v7c0,2.21 1.79,4 4,4s4,-1.79 4,-4V8h3l-4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_textsms_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_textsms_24dp.xml
new file mode 100644
index 0000000..796a028
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_textsms_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM9,11H7V9h2v2zm4,0h-2V9h2v2zm4,0h-2V9h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_voicemail_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_voicemail_24dp.xml
new file mode 100644
index 0000000..92a8765
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_voicemail_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.5,6C15.46,6 13,8.46 13,11.5c0,1.33 0.47,2.55 1.26,3.5H9.74c0.79,-0.95 1.26,-2.17 1.26,-3.5C11,8.46 8.54,6 5.5,6S0,8.46 0,11.5 2.46,17 5.5,17h13c3.04,0 5.5,-2.46 5.5,-5.5S21.54,6 18.5,6zm-13,9C3.57,15 2,13.43 2,11.5S3.57,8 5.5,8 9,9.57 9,11.5 7.43,15 5.5,15zm13,0c-1.93,0 -3.5,-1.57 -3.5,-3.5S16.57,8 18.5,8 22,9.57 22,11.5 20.43,15 18.5,15z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/communication/ic_vpn_key_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/communication/ic_vpn_key_24dp.xml
new file mode 100644
index 0000000..fe9c93f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/communication/ic_vpn_key_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_24dp.xml
new file mode 100644
index 0000000..059be44
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_add_box_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_box_24dp.xml
new file mode 100644
index 0000000..df3fa3a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_box_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-2,10h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_add_circle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_circle_24dp.xml
new file mode 100644
index 0000000..f4d87c1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_circle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm5,11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_add_circle_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_circle_outline_24dp.xml
new file mode 100644
index 0000000..3eaea09
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_add_circle_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1,-5C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_archive_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_archive_24dp.xml
new file mode 100644
index 0000000..5aa321c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_archive_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.54,5.23l-1.39,-1.68C18.88,3.21 18.47,3 18,3H6c-0.47,0 -0.88,0.21 -1.16,0.55L3.46,5.23C3.17,5.57 3,6.02 3,6.5V19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V6.5c0,-0.48 -0.17,-0.93 -0.46,-1.27zM12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5zM5.12,5l0.81,-1h12l0.94,1H5.12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_backspace_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_backspace_24dp.xml
new file mode 100644
index 0000000..7bc3f93
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_backspace_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,3H7c-0.69,0 -1.23,0.35 -1.59,0.88L0,12l5.41,8.11c0.36,0.53 0.9,0.89 1.59,0.89h15c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-3,12.59L17.59,17 14,13.41 10.41,17 9,15.59 12.59,12 9,8.41 10.41,7 14,10.59 17.59,7 19,8.41 15.41,12 19,15.59z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_block_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_block_24dp.xml
new file mode 100644
index 0000000..02bb4aa
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_block_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4,12c0,-4.42 3.58,-8 8,-8 1.85,0 3.55,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4,13.85 4,12zm8,8c-1.85,0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20,10.15 20,12c0,4.42 -3.58,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_clear_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_clear_24dp.xml
new file mode 100644
index 0000000..88f04ed
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_clear_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_content_copy_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_content_copy_24dp.xml
new file mode 100644
index 0000000..baa0796
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_content_copy_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,1H4c-1.1,0 -2,0.9 -2,2v14h2V3h12V1zm3,4H8c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2zm0,16H8V7h11v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_content_cut_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_content_cut_24dp.xml
new file mode 100644
index 0000000..82c28fa
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_content_cut_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9.64,7.64c0.23,-0.5 0.36,-1.05 0.36,-1.64 0,-2.21 -1.79,-4 -4,-4S2,3.79 2,6s1.79,4 4,4c0.59,0 1.14,-0.13 1.64,-0.36L10,12l-2.36,2.36C7.14,14.13 6.59,14 6,14c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4c0,-0.59 -0.13,-1.14 -0.36,-1.64L12,14l7,7h3v-1L9.64,7.64zM6,8c-1.1,0 -2,-0.89 -2,-2s0.9,-2 2,-2 2,0.89 2,2 -0.9,2 -2,2zm0,12c-1.1,0 -2,-0.89 -2,-2s0.9,-2 2,-2 2,0.89 2,2 -0.9,2 -2,2zm6,-7.5c-0.28,0 -0.5,-0.22 -0.5,-0.5s0.22,-0.5 0.5,-0.5 0.5,0.22 0.5,0.5 -0.22,0.5 -0.5,0.5zM19,3l-6,6 2,2 7,-7V3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_content_paste_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_content_paste_24dp.xml
new file mode 100644
index 0000000..cc248b5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_content_paste_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-7,0c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm7,18H5V4h2v3h10V4h2v16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_create_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_create_24dp.xml
new file mode 100644
index 0000000..0ff336a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_create_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_drafts_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_drafts_24dp.xml
new file mode 100644
index 0000000..679321c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_drafts_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21.99,8c0,-0.72 -0.37,-1.35 -0.94,-1.7L12,1 2.95,6.3C2.38,6.65 2,7.28 2,8v10c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2l-0.01,-10zM12,13L3.74,7.84 12,3l8.26,4.84L12,13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_filter_list_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_filter_list_24dp.xml
new file mode 100644
index 0000000..16ca2a1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_filter_list_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,18h4v-2h-4v2zM3,6v2h18V6H3zm3,7h12v-2H6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_flag_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_flag_24dp.xml
new file mode 100644
index 0000000..581ba88
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_flag_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14.4,6L14,4H5v17h2v-7h5.6l0.4,2h7V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_forward_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_forward_24dp.xml
new file mode 100644
index 0000000..76d901e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_forward_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,8V4l8,8 -8,8v-4H4V8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_gesture_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_gesture_24dp.xml
new file mode 100644
index 0000000..a8e4c17
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_gesture_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4.59,6.89c0.7,-0.71 1.4,-1.35 1.71,-1.22 0.5,0.2 0,1.03 -0.3,1.52 -0.25,0.42 -2.86,3.89 -2.86,6.31 0,1.28 0.48,2.34 1.34,2.98 0.75,0.56 1.74,0.73 2.64,0.46 1.07,-0.31 1.95,-1.4 3.06,-2.77 1.21,-1.49 2.83,-3.44 4.08,-3.44 1.63,0 1.65,1.01 1.76,1.79 -3.78,0.64 -5.38,3.67 -5.38,5.37 0,1.7 1.44,3.09 3.21,3.09 1.63,0 4.29,-1.33 4.69,-6.1H21v-2.5h-2.47c-0.15,-1.65 -1.09,-4.2 -4.03,-4.2 -2.25,0 -4.18,1.91 -4.94,2.84 -0.58,0.73 -2.06,2.48 -2.29,2.72 -0.25,0.3 -0.68,0.84 -1.11,0.84 -0.45,0 -0.72,-0.83 -0.36,-1.92 0.35,-1.09 1.4,-2.86 1.85,-3.52 0.78,-1.14 1.3,-1.92 1.3,-3.28C8.95,3.69 7.31,3 6.44,3 5.12,3 3.97,4 3.72,4.25c-0.36,0.36 -0.66,0.66 -0.88,0.93l1.75,1.71zm9.29,11.66c-0.31,0 -0.74,-0.26 -0.74,-0.72 0,-0.6 0.73,-2.2 2.87,-2.76 -0.3,2.69 -1.43,3.48 -2.13,3.48z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_inbox_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_inbox_24dp.xml
new file mode 100644
index 0000000..e2b49cd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_inbox_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H4.99c-1.1,0 -1.98,0.9 -1.98,2L3,19c0,1.1 0.89,2 1.99,2H19c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,12h-4c0,1.66 -1.34,3 -3,3s-3,-1.34 -3,-3H4.99V5H19v10zm-3,-5h-2V7h-4v3H8l4,4 4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_link_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_link_24dp.xml
new file mode 100644
index 0000000..1b8f436
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_link_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4V7H7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9H7c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2H8v2zm9,-6h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4V17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_mail_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_mail_24dp.xml
new file mode 100644
index 0000000..bf2ac7d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_mail_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,4l-8,5 -8,-5V6l8,5 8,-5v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_markunread_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_markunread_24dp.xml
new file mode 100644
index 0000000..bf2ac7d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_markunread_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,4l-8,5 -8,-5V6l8,5 8,-5v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_redo_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_redo_24dp.xml
new file mode 100644
index 0000000..1beacbb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_redo_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.4,10.6C16.55,8.99 14.15,8 11.5,8c-4.65,0 -8.58,3.03 -9.96,7.22L3.9,16c1.05,-3.19 4.05,-5.5 7.6,-5.5 1.95,0 3.73,0.72 5.12,1.88L13,16h9V7l-3.6,3.6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_24dp.xml
new file mode 100644
index 0000000..8757791
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,13H5v-2h14v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_circle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_circle_24dp.xml
new file mode 100644
index 0000000..98ef63c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_circle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm5,11H7v-2h10v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_circle_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_circle_outline_24dp.xml
new file mode 100644
index 0000000..42d36f9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_remove_circle_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,11v2h10v-2H7zm5,-9C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_reply_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_reply_24dp.xml
new file mode 100644
index 0000000..92ec9e1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_reply_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,9V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_reply_all_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_reply_all_24dp.xml
new file mode 100644
index 0000000..e351767
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_reply_all_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,8V5l-7,7 7,7v-3l-4,-4 4,-4zm6,1V5l-7,7 7,7v-4.1c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_report_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_report_24dp.xml
new file mode 100644
index 0000000..09d5660
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_report_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.73,3H8.27L3,8.27v7.46L8.27,21h7.46L21,15.73V8.27L15.73,3zM12,17.3c-0.72,0 -1.3,-0.58 -1.3,-1.3 0,-0.72 0.58,-1.3 1.3,-1.3 0.72,0 1.3,0.58 1.3,1.3 0,0.72 -0.58,1.3 -1.3,1.3zm1,-4.3h-2V7h2v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_save_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_save_24dp.xml
new file mode 100644
index 0000000..d36b668
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_save_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V7l-4,-4zm-5,16c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zm3,-10H5V5h10v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_select_all_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_select_all_24dp.xml
new file mode 100644
index 0000000..2dde2d4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_select_all_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,5h2V3c-1.1,0 -2,0.9 -2,2zm0,8h2v-2H3v2zm4,8h2v-2H7v2zM3,9h2V7H3v2zm10,-6h-2v2h2V3zm6,0v2h2c0,-1.1 -0.9,-2 -2,-2zM5,21v-2H3c0,1.1 0.9,2 2,2zm-2,-4h2v-2H3v2zM9,3H7v2h2V3zm2,18h2v-2h-2v2zm8,-8h2v-2h-2v2zm0,8c1.1,0 2,-0.9 2,-2h-2v2zm0,-12h2V7h-2v2zm0,8h2v-2h-2v2zm-4,4h2v-2h-2v2zm0,-16h2V3h-2v2zM7,17h10V7H7v10zm2,-8h6v6H9V9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_send_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_send_24dp.xml
new file mode 100644
index 0000000..f9dc3a1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_send_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_sort_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_sort_24dp.xml
new file mode 100644
index 0000000..1605498
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_sort_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,18h6v-2H3v2zM3,6v2h18V6H3zm0,7h12v-2H3v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_text_format_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_text_format_24dp.xml
new file mode 100644
index 0000000..09b3888
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_text_format_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5,17v2h14v-2H5zm4.5,-4.2h5l0.9,2.2h2.1L12.75,4h-1.5L6.5,15h2.1l0.9,-2.2zM12,5.98L13.87,11h-3.74L12,5.98z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/content/ic_undo_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/content/ic_undo_24dp.xml
new file mode 100644
index 0000000..36bbbcf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/content/ic_undo_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.5,8c-2.65,0 -5.05,0.99 -6.9,2.6L2,7v9h9l-3.62,-3.62c1.39,-1.16 3.16,-1.88 5.12,-1.88 3.54,0 6.55,2.31 7.6,5.5l2.37,-0.78C21.08,11.03 17.15,8 12.5,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_access_alarm_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_access_alarm_24dp.xml
new file mode 100644
index 0000000..7918b03
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_access_alarm_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM12.5,8H11v6l4.75,2.85 0.75,-1.23 -4,-2.37V8zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_access_alarms_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_access_alarms_24dp.xml
new file mode 100644
index 0000000..479b5c1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_access_alarms_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,5.7l-4.6,-3.9 -1.3,1.5 4.6,3.9L22,5.7zM7.9,3.4L6.6,1.9 2,5.7l1.3,1.5 4.6,-3.8zM12.5,8H11v6l4.7,2.9 0.8,-1.2 -4,-2.4V8zM12,4c-5,0 -9,4 -9,9s4,9 9,9 9,-4 9,-9 -4,-9 -9,-9zm0,16c-3.9,0 -7,-3.1 -7,-7s3.1,-7 7,-7 7,3.1 7,7 -3.1,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_access_time_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_access_time_24dp.xml
new file mode 100644
index 0000000..7bf67c6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_access_time_24dp.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"
+ android:fillAlpha=".9"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_add_alarm_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_add_alarm_24dp.xml
new file mode 100644
index 0000000..5e0d986
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_add_alarm_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm0,16c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7zm1,-11h-2v3H8v2h3v3h2v-3h3v-2h-3V9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_airplanemode_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_airplanemode_off_24dp.xml
new file mode 100644
index 0000000..0dfb747
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_airplanemode_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,9V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v3.68l7.83,7.83L21,16v-2l-8,-5zM3,5.27l4.99,4.99L2,14v2l8,-2.5V19l-2,1.5V22l3.5,-1 3.5,1v-1.5L13,19v-3.73L18.73,21 20,19.73 4.27,4 3,5.27z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_airplanemode_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_airplanemode_on_24dp.xml
new file mode 100644
index 0000000..3aa5af7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_airplanemode_on_24dp.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10.18,9"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,16v-2l-8,-5V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5V9l-8,5v2l8,-2.5V19l-2,1.5V22l3.5,-1 3.5,1v-1.5L13,19v-5.5l8,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_20_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_20_24dp.xml
new file mode 100644
index 0000000..81ebec3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_20_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,17v3.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V17H7z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,5.33C17,4.6 16.4,4 15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V17h10V5.33z"
+ android:fillAlpha=".3"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_30_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_30_24dp.xml
new file mode 100644
index 0000000..46f602b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_30_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,5.33C17,4.6 16.4,4 15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V15h10V5.33z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,15v5.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V15H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_50_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_50_24dp.xml
new file mode 100644
index 0000000..b4390e3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_50_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,5.33C17,4.6 16.4,4 15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V13h10V5.33z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,13v7.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V13H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_60_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_60_24dp.xml
new file mode 100644
index 0000000..e23a724
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_60_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,5.33C17,4.6 16.4,4 15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V11h10V5.33z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,11v9.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V11H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_80_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_80_24dp.xml
new file mode 100644
index 0000000..246e394
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_80_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,5.33C17,4.6 16.4,4 15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V9h10V5.33z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,9v11.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V9H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_90_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_90_24dp.xml
new file mode 100644
index 0000000..05efad9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_90_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,5.33C17,4.6 16.4,4 15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V8h10V5.33z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,8v12.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V8H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_alert_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_alert_24dp.xml
new file mode 100644
index 0000000..aeca8fb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_alert_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4zM13,18h-2v-2h2v2zm0,-4h-2V9h2v5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_20_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_20_24dp.xml
new file mode 100644
index 0000000..9018f25
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_20_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,20v-3H7v3.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V17h-4.4L11,20z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V17h4v-2.5H9L13,7v5.5h2L12.6,17H17V5.33C17,4.6 16.4,4 15.67,4z"
+ android:fillAlpha=".3"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_30_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_30_24dp.xml
new file mode 100644
index 0000000..40753fc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_30_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v9.17h2L13,7v5.5h2l-1.07,2H17V5.33C17,4.6 16.4,4 15.67,4z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,20v-5.5H7v6.17C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V14.5h-3.07L11,20z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_50_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_50_24dp.xml
new file mode 100644
index 0000000..ecd86f9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_50_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14.47,13.5L11,20v-5.5H9l0.53,-1H7v7.17C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V13.5h-2.53z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v8.17h2.53L13,7v5.5h2l-0.53,1H17V5.33C17,4.6 16.4,4 15.67,4z"
+ android:fillAlpha=".3"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_60_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_60_24dp.xml
new file mode 100644
index 0000000..1893c50
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_60_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V11h3.87L13,7v4h4V5.33C17,4.6 16.4,4 15.67,4z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,12.5h2L11,20v-5.5H9l1.87,-3.5H7v9.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V11h-4v1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_80_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_80_24dp.xml
new file mode 100644
index 0000000..8e1e8b5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_80_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V9h4.93L13,7v2h4V5.33C17,4.6 16.4,4 15.67,4z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,12.5h2L11,20v-5.5H9L11.93,9H7v11.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V9h-4v3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_90_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_90_24dp.xml
new file mode 100644
index 0000000..105f0ed
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_90_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33V8h5.47L13,7v1h4V5.33C17,4.6 16.4,4 15.67,4z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,12.5h2L11,20v-5.5H9L12.47,8H7v12.67C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V8h-4v4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_full_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_full_24dp.xml
new file mode 100644
index 0000000..e2ca7a6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_charging_full_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4zM11,20v-5.5H9L13,7v5.5h2L11,20z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_full_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_full_24dp.xml
new file mode 100644
index 0000000..92f9db6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_full_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_std_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_std_24dp.xml
new file mode 100644
index 0000000..92f9db6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_std_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_unknown_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_unknown_24dp.xml
new file mode 100644
index 0000000..4b48da8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_battery_unknown_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4zm-2.72,13.95h-1.9v-1.9h1.9v1.9zm1.35,-5.26s-0.38,0.42 -0.67,0.71c-0.48,0.48 -0.83,1.15 -0.83,1.6h-1.6c0,-0.83 0.46,-1.52 0.93,-2l0.93,-0.94c0.27,-0.27 0.44,-0.65 0.44,-1.06 0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5H9c0,-1.66 1.34,-3 3,-3s3,1.34 3,3c0,0.66 -0.27,1.26 -0.7,1.69z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_24dp.xml
new file mode 100644
index 0000000..6ce13d9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41V22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59V5.83zm1.88,10.46L13,18.17v-3.76l1.88,1.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_connected_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_connected_24dp.xml
new file mode 100644
index 0000000..27a3001
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_connected_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,12l-2,-2 -2,2 2,2 2,-2zm10.71,-4.29L12,2h-1v7.59L6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 11,14.41V22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM13,5.83l1.88,1.88L13,9.59V5.83zm1.88,10.46L13,18.17v-3.76l1.88,1.88zM19,10l-2,2 2,2 2,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_disabled_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_disabled_24dp.xml
new file mode 100644
index 0000000..02d7bc8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_disabled_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,5.83l1.88,1.88 -1.6,1.6 1.41,1.41 3.02,-3.02L12,2h-1v5.03l2,2v-3.2zM5.41,4L4,5.41 10.59,12 5,17.59 6.41,19 11,14.41V22h1l4.29,-4.29 2.3,2.29L20,18.59 5.41,4zM13,18.17v-3.76l1.88,1.88L13,18.17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_searching_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_searching_24dp.xml
new file mode 100644
index 0000000..d09fb6f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_bluetooth_searching_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14.24,12.01l2.32,2.32c0.28,-0.72 0.44,-1.51 0.44,-2.33 0,-0.82 -0.16,-1.59 -0.43,-2.31l-2.33,2.32zm5.29,-5.3l-1.26,1.26c0.63,1.21 0.98,2.57 0.98,4.02s-0.36,2.82 -0.98,4.02l1.2,1.2c0.97,-1.54 1.54,-3.36 1.54,-5.31 -0.01,-1.89 -0.55,-3.67 -1.48,-5.19zm-3.82,1L10,2H9v7.59L4.41,5 3,6.41 8.59,12 3,17.59 4.41,19 9,14.41V22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM11,5.83l1.88,1.88L11,9.59V5.83zm1.88,10.46L11,18.17v-3.76l1.88,1.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_auto_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_auto_24dp.xml
new file mode 100644
index 0000000..a7495b9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_auto_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10.85,12.65h2.3L12,9l-1.15,3.65zM20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM14.3,16l-0.7,-2h-3.2l-0.7,2H7.8L11,7h2l3.2,9h-1.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_high_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_high_24dp.xml
new file mode 100644
index 0000000..c254041
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_high_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6zm0,-10c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_low_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_low_24dp.xml
new file mode 100644
index 0000000..406a52e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_low_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_medium_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_medium_24dp.xml
new file mode 100644
index 0000000..cc63bac
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_brightness_medium_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18V6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_data_usage_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_data_usage_24dp.xml
new file mode 100644
index 0000000..69bc3ec
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_data_usage_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,2.05v3.03c3.39,0.49 6,3.39 6,6.92 0,0.9 -0.18,1.75 -0.48,2.54l2.6,1.53c0.56,-1.24 0.88,-2.62 0.88,-4.07 0,-5.18 -3.95,-9.45 -9,-9.95zM12,19c-3.87,0 -7,-3.13 -7,-7 0,-3.53 2.61,-6.43 6,-6.92V2.05c-5.06,0.5 -9,4.76 -9,9.95 0,5.52 4.47,10 9.99,10 3.31,0 6.24,-1.61 8.06,-4.09l-2.6,-1.53C16.17,17.98 14.21,19 12,19z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_developer_mode_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_developer_mode_24dp.xml
new file mode 100644
index 0000000..93288cb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_developer_mode_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,5h10v2h2V3c0,-1.1 -0.9,-1.99 -2,-1.99L7,1c-1.1,0 -2,0.9 -2,2v4h2V5zm8.41,11.59L20,12l-4.59,-4.59L14,8.83 17.17,12 14,15.17l1.41,1.42zM10,15.17L6.83,12 10,8.83 8.59,7.41 4,12l4.59,4.59L10,15.17zM17,19H7v-2H5v4c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2v-4h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_devices_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_devices_24dp.xml
new file mode 100644
index 0000000..e54c722
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_devices_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,6h18V4H4c-1.1,0 -2,0.9 -2,2v11H0v3h14v-3H4V6zm19,2h-6c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1V9c0,-0.55 -0.45,-1 -1,-1zm-1,9h-4v-7h4v7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_dvr_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_dvr_24dp.xml
new file mode 100644
index 0000000..61a2bc6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_dvr_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,3H3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h5v2h8v-2h5c1.1,0 1.99,-0.9 1.99,-2L23,5c0,-1.1 -0.9,-2 -2,-2zm0,14H3V5h18v12zm-2,-9H8v2h11V8zm0,4H8v2h11v-2zM7,8H5v2h2V8zm0,4H5v2h2v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_fixed_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_fixed_24dp.xml
new file mode 100644
index 0000000..2a7817e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_fixed_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm8.94,3c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94H23v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_not_fixed_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_not_fixed_24dp.xml
new file mode 100644
index 0000000..d8fb3b4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_not_fixed_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94H23v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_off_24dp.xml
new file mode 100644
index 0000000..68db2a9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_gps_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06c-1.13,0.12 -2.19,0.46 -3.16,0.97l1.5,1.5C10.16,5.19 11.06,5 12,5c3.87,0 7,3.13 7,7 0,0.94 -0.19,1.84 -0.52,2.65l1.5,1.5c0.5,-0.96 0.84,-2.02 0.97,-3.15H23v-2h-2.06zM3,4.27l2.04,2.04C3.97,7.62 3.25,9.23 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c1.77,-0.2 3.38,-0.91 4.69,-1.98L19.73,21 21,19.73 4.27,3 3,4.27zm13.27,13.27C15.09,18.45 13.61,19 12,19c-3.87,0 -7,-3.13 -7,-7 0,-1.61 0.55,-3.09 1.46,-4.27l9.81,9.81z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_location_disabled_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_location_disabled_24dp.xml
new file mode 100644
index 0000000..68db2a9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_location_disabled_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06c-1.13,0.12 -2.19,0.46 -3.16,0.97l1.5,1.5C10.16,5.19 11.06,5 12,5c3.87,0 7,3.13 7,7 0,0.94 -0.19,1.84 -0.52,2.65l1.5,1.5c0.5,-0.96 0.84,-2.02 0.97,-3.15H23v-2h-2.06zM3,4.27l2.04,2.04C3.97,7.62 3.25,9.23 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c1.77,-0.2 3.38,-0.91 4.69,-1.98L19.73,21 21,19.73 4.27,3 3,4.27zm13.27,13.27C15.09,18.45 13.61,19 12,19c-3.87,0 -7,-3.13 -7,-7 0,-1.61 0.55,-3.09 1.46,-4.27l9.81,9.81z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_location_searching_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_location_searching_24dp.xml
new file mode 100644
index 0000000..d8fb3b4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_location_searching_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94H23v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_multitrack_audio_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_multitrack_audio_24dp.xml
new file mode 100644
index 0000000..252e560
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_multitrack_audio_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,18h2V6H7v12zm4,4h2V2h-2v20zm-8,-8h2v-4H3v4zm12,4h2V6h-2v12zm4,-8v4h2v-4h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_network_cell_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_network_cell_24dp.xml
new file mode 100644
index 0000000..bc6f3ca
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_network_cell_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2,22h20V2z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,7L2,22h15z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_network_wifi_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_network_wifi_24dp.xml
new file mode 100644
index 0000000..3a1e2e5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_network_wifi_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3.53,10.95l8.46,10.54 0.01,0.01 0.01,-0.01 8.46,-10.54C20.04,10.62 16.81,8 12,8c-4.81,0 -8.04,2.62 -8.47,2.95z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_nfc_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_nfc_24dp.xml
new file mode 100644
index 0000000..83c3ee6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_nfc_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm0,18H4V4h16v16zM18,6h-5c-1.1,0 -2,0.9 -2,2v2.28c-0.6,0.35 -1,0.98 -1,1.72 0,1.1 0.9,2 2,2s2,-0.9 2,-2c0,-0.74 -0.4,-1.38 -1,-1.72V8h3v8H8V8h2V6H6v12h12V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_now_wallpaper_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_now_wallpaper_24dp.xml
new file mode 100644
index 0000000..6e1973e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_now_wallpaper_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,4h7V2H4c-1.1,0 -2,0.9 -2,2v7h2V4zm6,9l-4,5h12l-3,-4 -2.03,2.71L10,13zm7,-4.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S14,7.67 14,8.5s0.67,1.5 1.5,1.5S17,9.33 17,8.5zM20,2h-7v2h7v7h2V4c0,-1.1 -0.9,-2 -2,-2zm0,18h-7v2h7c1.1,0 2,-0.9 2,-2v-7h-2v7zM4,13H2v7c0,1.1 0.9,2 2,2h7v-2H4v-7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_now_widgets_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_now_widgets_24dp.xml
new file mode 100644
index 0000000..02996e0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_now_widgets_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,13v8h8v-8h-8zM3,21h8v-8H3v8zM3,3v8h8V3H3zm13.66,-1.31L11,7.34 16.66,13l5.66,-5.66 -5.66,-5.65z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_landscape_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_landscape_24dp.xml
new file mode 100644
index 0000000..1e0f44a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_landscape_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,5H3c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2zm-2,12H5V7h14v10zm-9,-1h4c0.55,0 1,-0.45 1,-1v-3c0,-0.55 -0.45,-1 -1,-1v-1c0,-1.11 -0.9,-2 -2,-2 -1.11,0 -2,0.9 -2,2v1c-0.55,0 -1,0.45 -1,1v3c0,0.55 0.45,1 1,1zm0.8,-6c0,-0.66 0.54,-1.2 1.2,-1.2 0.66,0 1.2,0.54 1.2,1.2v1h-2.4v-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_portrait_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_portrait_24dp.xml
new file mode 100644
index 0000000..d35bc48
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_portrait_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,16h4c0.55,0 1,-0.45 1,-1v-3c0,-0.55 -0.45,-1 -1,-1v-1c0,-1.11 -0.9,-2 -2,-2 -1.11,0 -2,0.9 -2,2v1c-0.55,0 -1,0.45 -1,1v3c0,0.55 0.45,1 1,1zm0.8,-6c0,-0.66 0.54,-1.2 1.2,-1.2 0.66,0 1.2,0.54 1.2,1.2v1h-2.4v-1zM17,1H7c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,18H7V5h10v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_rotation_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_rotation_24dp.xml
new file mode 100644
index 0000000..2e364cd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_lock_rotation_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M23.25,12.77l-2.57,-2.57 -1.41,1.41 2.22,2.22 -5.66,5.66L4.51,8.17l5.66,-5.66 2.1,2.1 1.41,-1.41L11.23,0.75c-0.59,-0.59 -1.54,-0.59 -2.12,0L2.75,7.11c-0.59,0.59 -0.59,1.54 0,2.12l12.02,12.02c0.59,0.59 1.54,0.59 2.12,0l6.36,-6.36c0.59,-0.59 0.59,-1.54 0,-2.12zM8.47,20.48C5.2,18.94 2.86,15.76 2.5,12H1c0.51,6.16 5.66,11 11.95,11l0.66,-0.03 -3.81,-3.82 -1.33,1.33zM16,9h5c0.55,0 1,-0.45 1,-1V4c0,-0.55 -0.45,-1 -1,-1v-0.5C21,1.12 19.88,0 18.5,0S16,1.12 16,2.5V3c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1zm0.8,-6.5c0,-0.94 0.76,-1.7 1.7,-1.7s1.7,0.76 1.7,1.7V3h-3.4v-0.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_rotation_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_rotation_24dp.xml
new file mode 100644
index 0000000..ab4d9ce
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_screen_rotation_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.48,2.52c3.27,1.55 5.61,4.72 5.97,8.48h1.5C23.44,4.84 18.29,0 12,0l-0.66,0.03 3.81,3.81 1.33,-1.32zm-6.25,-0.77c-0.59,-0.59 -1.54,-0.59 -2.12,0L1.75,8.11c-0.59,0.59 -0.59,1.54 0,2.12l12.02,12.02c0.59,0.59 1.54,0.59 2.12,0l6.36,-6.36c0.59,-0.59 0.59,-1.54 0,-2.12L10.23,1.75zm4.6,19.44L2.81,9.17l6.36,-6.36 12.02,12.02 -6.36,6.36zm-7.31,0.29C4.25,19.94 1.91,16.76 1.55,13H0.05C0.56,19.16 5.71,24 12,24l0.66,-0.03 -3.81,-3.81 -1.33,1.32z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_sd_storage_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_sd_storage_24dp.xml
new file mode 100644
index 0000000..74e0e41
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_sd_storage_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-6,6h-2V4h2v4zm3,0h-2V4h2v4zm3,0h-2V4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_settings_system_daydream_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_settings_system_daydream_24dp.xml
new file mode 100644
index 0000000..0882c81
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_settings_system_daydream_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,16h6.5c1.38,0 2.5,-1.12 2.5,-2.5S16.88,11 15.5,11h-0.05c-0.24,-1.69 -1.69,-3 -3.45,-3 -1.4,0 -2.6,0.83 -3.16,2.02h-0.16C7.17,10.18 6,11.45 6,13c0,1.66 1.34,3 3,3zM21,3H3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16.01H3V4.99h18v14.02z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_0_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_0_bar_24dp.xml
new file mode 100644
index 0000000..b385c9b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_0_bar_24dp.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2,22h20V2z"
+ android:fillAlpha=".3"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_1_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_1_bar_24dp.xml
new file mode 100644
index 0000000..4fe8b5a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_1_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2,22h20V2z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,12L2,22h10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_2_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_2_bar_24dp.xml
new file mode 100644
index 0000000..6fb8da3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_2_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2,22h20V2z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14,10L2,22h12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_3_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_3_bar_24dp.xml
new file mode 100644
index 0000000..bc6f3ca
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_3_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2,22h20V2z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,7L2,22h15z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_4_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_4_bar_24dp.xml
new file mode 100644
index 0000000..13e38ee
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_4_bar_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2,22h20V2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_0_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_0_bar_24dp.xml
new file mode 100644
index 0000000..9f74cf5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_0_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,8V2L2,22h16V8z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,22h2v-2h-2v2zm0,-12v8h2v-8h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_1_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_1_bar_24dp.xml
new file mode 100644
index 0000000..877cad6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_1_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,8V2L2,22h16V8z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,10v8h2v-8h-2zm-8,12V12L2,22h10zm8,0h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_2_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_2_bar_24dp.xml
new file mode 100644
index 0000000..3d15914
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_2_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,8V2L2,22h16V8z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14,22V10L2,22h12zm6,-12v8h2v-8h-2zm0,12h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_3_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_3_bar_24dp.xml
new file mode 100644
index 0000000..1fc9c2b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_3_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,8V2L2,22h16V8z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,22V7L2,22h15zm3,-12v8h2v-8h-2zm0,12h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_4_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_4_bar_24dp.xml
new file mode 100644
index 0000000..e836247
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_connected_no_internet_4_bar_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,18h2v-8h-2v8zm0,4h2v-2h-2v2zM2,22h16V8h4V2L2,22z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_no_sim_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_no_sim_24dp.xml
new file mode 100644
index 0000000..b36e505
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_no_sim_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.99,5c0,-1.1 -0.89,-2 -1.99,-2h-7L7.66,5.34 19,16.68 18.99,5zM3.65,3.88L2.38,5.15 5,7.77V19c0,1.1 0.9,2 2,2h10.01c0.35,0 0.67,-0.1 0.96,-0.26l1.88,1.88 1.27,-1.27L3.65,3.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_null_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_null_24dp.xml
new file mode 100644
index 0000000..f152649
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_null_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,6.83V20H6.83L20,6.83M22,2L2,22h20V2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_off_24dp.xml
new file mode 100644
index 0000000..343e9b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_cellular_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,1l-8.59,8.59L21,18.18V1zM4.77,4.5L3.5,5.77l6.36,6.36L1,21h17.73l2,2L22,21.73 4.77,4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_0_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_0_bar_24dp.xml
new file mode 100644
index 0000000..5e45e6d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_0_bar_24dp.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"
+ android:fillAlpha=".3"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_1_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_1_bar_24dp.xml
new file mode 100644
index 0000000..2fcb74a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_1_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6.67,14.86L12,21.49v0.01l0.01,-0.01 5.33,-6.63C17.06,14.65 15.03,13 12,13s-5.06,1.65 -5.33,1.86z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_2_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_2_bar_24dp.xml
new file mode 100644
index 0000000..ac24574
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_2_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4.79,12.52l7.2,8.98H12l0.01,-0.01 7.2,-8.98C18.85,12.24 16.1,10 12,10s-6.85,2.24 -7.21,2.52z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_3_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_3_bar_24dp.xml
new file mode 100644
index 0000000..3a1e2e5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_3_bar_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3.53,10.95l8.46,10.54 0.01,0.01 0.01,-0.01 8.46,-10.54C20.04,10.62 16.81,8 12,8c-4.81,0 -8.04,2.62 -8.47,2.95z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_4_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_4_bar_24dp.xml
new file mode 100644
index 0000000..f47a229
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_4_bar_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_off_24dp.xml
new file mode 100644
index 0000000..d9eaa7a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4 -1.5,0 -2.89,0.19 -4.15,0.48L18.18,13.8 23.64,7zm-6.6,8.22L3.27,1.44 2,2.72l2.05,2.06C1.91,5.76 0.59,6.82 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01 3.9,-4.86 3.32,3.32 1.27,-1.27 -3.46,-3.46z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_1_bar_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_1_bar_26x24px.xml
new file mode 100644
index 0000000..830fb67
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_1_bar_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="26dp"
+ android:height="24dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,21.99l5.66,-7.05C18.44,14.78 16.27,13 13,13s-5.44,1.78 -5.66,1.95L13,21.99z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13.01,21.99L25.58,6.32C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01z"
+ android:fillAlpha=".3"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_2_bar_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_2_bar_26x24px.xml
new file mode 100644
index 0000000..57aab89
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_2_bar_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="26dp"
+ android:height="24dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13.01,21.99L25.58,6.32C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13.01,21.99l7.54,-9.4C20.26,12.38 17.36,10 13,10c-4.36,0 -7.26,2.38 -7.55,2.59l7.54,9.4h0.02z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_3_bar_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_3_bar_26x24px.xml
new file mode 100644
index 0000000..c0b7b4a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_3_bar_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="26dp"
+ android:height="24dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13.01,21.99l9.43,-11.75C22.07,9.97 18.44,7 13,7c-5.44,0 -9.07,2.97 -9.44,3.24l9.43,11.75h0.02z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13.01,21.99L25.58,6.32C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01z"
+ android:fillAlpha=".3"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_4_bar_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_4_bar_26x24px.xml
new file mode 100644
index 0000000..6cc9ebe
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_4_bar_26x24px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="26dp"
+ android:height="24dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13.01,21.99L25.58,6.32C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_1_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_1_26x24px.xml
new file mode 100644
index 0000000..70bd006
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_1_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="26dp"
+ android:height="24dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M24.24,8l1.35,-1.68C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01L20,13.28V8h4.24z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.34,14.95L13,21.99V22v-0.01l5.66,-7.05C18.44,14.78 16.27,13 13,13s-5.44,1.78 -5.66,1.95zM22,22h2v-2h-2v2zm0,-12v8h2v-8h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_26x24px.xml
new file mode 100644
index 0000000..910bf9d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="26dp"
+ android:height="24dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M24.24,8l1.35,-1.68C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01L20,13.28V8h4.24z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,22h2v-2h-2v2zm0,-12v8h2v-8h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_2_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_2_26x24px.xml
new file mode 100644
index 0000000..e158ddf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_2_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="26dp"
+ android:height="24dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M24.24,8l1.35,-1.68C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01L20,13.28V8h4.24z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5.45,12.59l7.54,9.4 0.01,0.01 0.01,-0.01L20,13.28v-1.09c-1.07,-0.73 -3.59,-2.19 -7,-2.19 -4.36,0 -7.26,2.38 -7.55,2.59zM22,10v8h2v-8h-2zm0,12h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_3_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_3_26x24px.xml
new file mode 100644
index 0000000..c242b36
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_3_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="26dp"
+ android:height="24dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M24.24,8l1.35,-1.68C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01L20,13.28V8h4.24z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,13.28V8.71C18.35,7.87 15.94,7 13,7c-5.44,0 -9.07,2.97 -9.44,3.24l9.43,11.75 0.01,0.01 0.01,-0.01L20,13.28zM22,22h2v-2h-2v2zm0,-12v8h2v-8h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_4_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_4_26x24px.xml
new file mode 100644
index 0000000..d5f15af
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_connected_no_internet_4_26x24px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="26dp"
+ android:height="24dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,22h2v-2h-2v2zM13,2C5.74,2 0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01L20,13.28V8h4.24l1.35,-1.68C25.1,5.96 20.26,2 13,2zm9,16h2v-8h-2v8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_not_connected_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_not_connected_26x24px.xml
new file mode 100644
index 0000000..a654e58
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_not_connected_26x24px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="26dp"
+ android:height="24dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,8.5c0.85,0 1.64,0.23 2.34,0.62l2.24,-2.79C25.1,5.96 20.26,2 13,2S0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01 4.21,-5.24c-0.76,-0.87 -1.22,-2 -1.22,-3.25 0,-2.76 2.24,-5 5,-5z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,10c-1.93,0 -3.5,1.57 -3.5,3.5h1.75c0,-0.97 0.78,-1.75 1.75,-1.75s1.75,0.78 1.75,1.75c0,0.48 -0.2,0.92 -0.51,1.24l-1.09,1.1c-0.63,0.63 -1.02,1.51 -1.02,2.47v0.44h1.75c0,-1.31 0.39,-1.84 1.03,-2.47l0.78,-0.8c0.5,-0.5 0.82,-1.2 0.82,-1.97C24.5,11.57 22.93,10 21,10zm-0.95,11.95h1.9v-1.9h-1.9v1.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_null_26x24px.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_null_26x24px.xml
new file mode 100644
index 0000000..9951921
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_signal_wifi_statusbar_null_26x24px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="26dp"
+ android:height="24dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,4c4.25,0 7.62,1.51 9.68,2.75L13,18.8 3.33,6.75C5.38,5.51 8.75,4 13,4m0,-2C5.74,2 0.9,5.96 0.42,6.32l12.57,15.66 0.01,0.02 0.01,-0.01L25.58,6.32C25.1,5.96 20.26,2 13,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_storage_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_storage_24dp.xml
new file mode 100644
index 0000000..8d18d84
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_storage_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2,20h20v-4H2v4zm2,-3h2v2H4v-2zM2,4v4h20V4H2zm4,3H4V5h2v2zm-4,7h20v-4H2v4zm2,-3h2v2H4v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_usb_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_usb_24dp.xml
new file mode 100644
index 0000000..ae726d8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_usb_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,7v4h1v2h-3V5h2l-3,-4 -3,4h2v8H8v-2.07c0.7,-0.37 1.2,-1.08 1.2,-1.93 0,-1.21 -0.99,-2.2 -2.2,-2.2 -1.21,0 -2.2,0.99 -2.2,2.2 0,0.85 0.5,1.56 1.2,1.93V13c0,1.11 0.89,2 2,2h3v3.05c-0.71,0.37 -1.2,1.1 -1.2,1.95 0,1.22 0.99,2.2 2.2,2.2 1.21,0 2.2,-0.98 2.2,-2.2 0,-0.85 -0.49,-1.58 -1.2,-1.95V15h3c1.11,0 2,-0.89 2,-2v-2h1V7h-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_wifi_lock_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_wifi_lock_24dp.xml
new file mode 100644
index 0000000..ca9ce19
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_wifi_lock_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.5,9.5c0.28,0 0.55,0.04 0.81,0.08L24,6c-3.34,-2.51 -7.5,-4 -12,-4S3.34,3.49 0,6l12,16 3.5,-4.67V14.5c0,-2.76 2.24,-5 5,-5zM23,16v-1.5c0,-1.38 -1.12,-2.5 -2.5,-2.5S18,13.12 18,14.5V16c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1v-4c0,-0.55 -0.45,-1 -1,-1zm-1,0h-3v-1.5c0,-0.83 0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5V16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/device/ic_wifi_tethering_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/device/ic_wifi_tethering_24dp.xml
new file mode 100644
index 0000000..7fc86a5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/device/ic_wifi_tethering_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,11c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm6,2c0,-3.31 -2.69,-6 -6,-6s-6,2.69 -6,6c0,2.22 1.21,4.15 3,5.19l1,-1.74c-1.19,-0.7 -2,-1.97 -2,-3.45 0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,1.48 -0.81,2.75 -2,3.45l1,1.74c1.79,-1.04 3,-2.97 3,-5.19zM12,3C6.48,3 2,7.48 2,13c0,3.7 2.01,6.92 4.99,8.65l1,-1.73C5.61,18.53 4,15.96 4,13c0,-4.42 3.58,-8 8,-8s8,3.58 8,8c0,2.96 -1.61,5.53 -4,6.92l1,1.73c2.99,-1.73 5,-4.95 5,-8.65 0,-5.52 -4.48,-10 -10,-10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_attach_file_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_attach_file_24dp.xml
new file mode 100644
index 0000000..ed1aad3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_attach_file_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.5,6v11.5c0,2.21 -1.79,4 -4,4s-4,-1.79 -4,-4V5c0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5v10.5c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V6H10v9.5c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5V5c0,-2.21 -1.79,-4 -4,-4S7,2.79 7,5v12.5c0,3.04 2.46,5.5 5.5,5.5s5.5,-2.46 5.5,-5.5V6h-1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_attach_money_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_attach_money_24dp.xml
new file mode 100644
index 0000000..4a0cd0e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_attach_money_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.8,10.9c-2.27,-0.59 -3,-1.2 -3,-2.15 0,-1.09 1.01,-1.85 2.7,-1.85 1.78,0 2.44,0.85 2.5,2.1h2.21c-0.07,-1.72 -1.12,-3.3 -3.21,-3.81V3h-3v2.16c-1.94,0.42 -3.5,1.68 -3.5,3.61 0,2.31 1.91,3.46 4.7,4.13 2.5,0.6 3,1.48 3,2.41 0,0.69 -0.49,1.79 -2.7,1.79 -2.06,0 -2.87,-0.92 -2.98,-2.1h-2.2c0.12,2.19 1.76,3.42 3.68,3.83V21h3v-2.15c1.95,-0.37 3.5,-1.5 3.5,-3.55 0,-2.84 -2.43,-3.81 -4.7,-4.4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_all_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_all_24dp.xml
new file mode 100644
index 0000000..7a5ee14
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_all_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,3v18h18V3H3zm8,16H5v-6h6v6zm0,-8H5V5h6v6zm8,8h-6v-6h6v6zm0,-8h-6V5h6v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_bottom_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_bottom_24dp.xml
new file mode 100644
index 0000000..4538725
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_bottom_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,11H7v2h2v-2zm4,4h-2v2h2v-2zM9,3H7v2h2V3zm4,8h-2v2h2v-2zM5,3H3v2h2V3zm8,4h-2v2h2V7zm4,4h-2v2h2v-2zm-4,-8h-2v2h2V3zm4,0h-2v2h2V3zm2,10h2v-2h-2v2zm0,4h2v-2h-2v2zM5,7H3v2h2V7zm14,-4v2h2V3h-2zm0,6h2V7h-2v2zM5,11H3v2h2v-2zM3,21h18v-2H3v2zm2,-6H3v2h2v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_clear_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_clear_24dp.xml
new file mode 100644
index 0000000..0d9c07f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_clear_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,5h2V3H7v2zm0,8h2v-2H7v2zm0,8h2v-2H7v2zm4,-4h2v-2h-2v2zm0,4h2v-2h-2v2zm-8,0h2v-2H3v2zm0,-4h2v-2H3v2zm0,-4h2v-2H3v2zm0,-4h2V7H3v2zm0,-4h2V3H3v2zm8,8h2v-2h-2v2zm8,4h2v-2h-2v2zm0,-4h2v-2h-2v2zm0,8h2v-2h-2v2zm0,-12h2V7h-2v2zm-8,0h2V7h-2v2zm8,-6v2h2V3h-2zm-8,2h2V3h-2v2zm4,16h2v-2h-2v2zm0,-8h2v-2h-2v2zm0,-8h2V3h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_color_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_color_24dp.xml
new file mode 100644
index 0000000..c164764
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_color_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.75,7L14,3.25l-10,10V17h3.75l10,-10zm2.96,-2.96c0.39,-0.39 0.39,-1.02 0,-1.41L18.37,0.29c-0.39,-0.39 -1.02,-0.39 -1.41,0L15,2.25 18.75,6l1.96,-1.96z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0,20h24v4H0z"
+ android:fillAlpha=".36"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_horizontal_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_horizontal_24dp.xml
new file mode 100644
index 0000000..caedc37
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_horizontal_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,21h2v-2H3v2zM5,7H3v2h2V7zM3,17h2v-2H3v2zm4,4h2v-2H7v2zM5,3H3v2h2V3zm4,0H7v2h2V3zm8,0h-2v2h2V3zm-4,4h-2v2h2V7zm0,-4h-2v2h2V3zm6,14h2v-2h-2v2zm-8,4h2v-2h-2v2zm-8,-8h18v-2H3v2zM19,3v2h2V3h-2zm0,6h2V7h-2v2zm-8,8h2v-2h-2v2zm4,4h2v-2h-2v2zm4,0h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_inner_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_inner_24dp.xml
new file mode 100644
index 0000000..da4372a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_inner_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,21h2v-2H3v2zm4,0h2v-2H7v2zM5,7H3v2h2V7zM3,17h2v-2H3v2zM9,3H7v2h2V3zM5,3H3v2h2V3zm12,0h-2v2h2V3zm2,6h2V7h-2v2zm0,-6v2h2V3h-2zm-4,18h2v-2h-2v2zM13,3h-2v8H3v2h8v8h2v-8h8v-2h-8V3zm6,18h2v-2h-2v2zm0,-4h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_left_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_left_24dp.xml
new file mode 100644
index 0000000..6d2e070
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_left_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,21h2v-2h-2v2zm0,-4h2v-2h-2v2zm0,-12h2V3h-2v2zm0,4h2V7h-2v2zm0,4h2v-2h-2v2zm-4,8h2v-2H7v2zM7,5h2V3H7v2zm0,8h2v-2H7v2zm-4,8h2V3H3v18zM19,9h2V7h-2v2zm-4,12h2v-2h-2v2zm4,-4h2v-2h-2v2zm0,-14v2h2V3h-2zm0,10h2v-2h-2v2zm0,8h2v-2h-2v2zm-4,-8h2v-2h-2v2zm0,-8h2V3h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_outer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_outer_24dp.xml
new file mode 100644
index 0000000..5addeec
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_outer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,7h-2v2h2V7zm0,4h-2v2h2v-2zm4,0h-2v2h2v-2zM3,3v18h18V3H3zm16,16H5V5h14v14zm-6,-4h-2v2h2v-2zm-4,-4H7v2h2v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_right_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_right_24dp.xml
new file mode 100644
index 0000000..4a9b068
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_right_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,21h2v-2H7v2zM3,5h2V3H3v2zm4,0h2V3H7v2zm0,8h2v-2H7v2zm-4,8h2v-2H3v2zm8,0h2v-2h-2v2zm-8,-8h2v-2H3v2zm0,4h2v-2H3v2zm0,-8h2V7H3v2zm8,8h2v-2h-2v2zm4,-4h2v-2h-2v2zm4,-10v18h2V3h-2zm-4,18h2v-2h-2v2zm0,-16h2V3h-2v2zm-4,8h2v-2h-2v2zm0,-8h2V3h-2v2zm0,4h2V7h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_style_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_style_24dp.xml
new file mode 100644
index 0000000..5079af0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_style_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,21h2v-2h-2v2zm4,0h2v-2h-2v2zM7,21h2v-2H7v2zm4,0h2v-2h-2v2zm8,-4h2v-2h-2v2zm0,-4h2v-2h-2v2zM3,3v18h2V5h16V3H3zm16,6h2V7h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_top_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_top_24dp.xml
new file mode 100644
index 0000000..f38ac53
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_top_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,21h2v-2H7v2zm0,-8h2v-2H7v2zm4,0h2v-2h-2v2zm0,8h2v-2h-2v2zm-8,-4h2v-2H3v2zm0,4h2v-2H3v2zm0,-8h2v-2H3v2zm0,-4h2V7H3v2zm8,8h2v-2h-2v2zm8,-8h2V7h-2v2zm0,4h2v-2h-2v2zM3,3v2h18V3H3zm16,14h2v-2h-2v2zm-4,4h2v-2h-2v2zM11,9h2V7h-2v2zm8,12h2v-2h-2v2zm-4,-8h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_vertical_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_vertical_24dp.xml
new file mode 100644
index 0000000..45f9f94
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_border_vertical_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,9h2V7H3v2zm0,-4h2V3H3v2zm4,16h2v-2H7v2zm0,-8h2v-2H7v2zm-4,0h2v-2H3v2zm0,8h2v-2H3v2zm0,-4h2v-2H3v2zM7,5h2V3H7v2zm12,12h2v-2h-2v2zm-8,4h2V3h-2v18zm8,0h2v-2h-2v2zm0,-8h2v-2h-2v2zm0,-10v2h2V3h-2zm0,6h2V7h-2v2zm-4,-4h2V3h-2v2zm0,16h2v-2h-2v2zm0,-8h2v-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_center_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_center_24dp.xml
new file mode 100644
index 0000000..10649b2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_center_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,15v2h10v-2H7zm-4,6h18v-2H3v2zm0,-8h18v-2H3v2zm4,-6v2h10V7H7zM3,3v2h18V3H3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_justify_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_justify_24dp.xml
new file mode 100644
index 0000000..bddb3af
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_justify_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,21h18v-2H3v2zm0,-4h18v-2H3v2zm0,-4h18v-2H3v2zm0,-4h18V7H3v2zm0,-6v2h18V3H3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_left_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_left_24dp.xml
new file mode 100644
index 0000000..39cdf41
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_left_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,15H3v2h12v-2zm0,-8H3v2h12V7zM3,13h18v-2H3v2zm0,8h18v-2H3v2zM3,3v2h18V3H3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_right_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_right_24dp.xml
new file mode 100644
index 0000000..59473f3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_align_right_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,21h18v-2H3v2zm6,-4h12v-2H9v2zm-6,-4h18v-2H3v2zm6,-4h12V7H9v2zM3,3v2h18V3H3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_bold_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_bold_24dp.xml
new file mode 100644
index 0000000..8b6f679
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_bold_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.6,10.79c0.97,-0.67 1.65,-1.77 1.65,-2.79 0,-2.26 -1.75,-4 -4,-4H7v14h7.04c2.09,0 3.71,-1.7 3.71,-3.79 0,-1.52 -0.86,-2.82 -2.15,-3.42zM10,6.5h3c0.83,0 1.5,0.67 1.5,1.5s-0.67,1.5 -1.5,1.5h-3v-3zm3.5,9H10v-3h3.5c0.83,0 1.5,0.67 1.5,1.5s-0.67,1.5 -1.5,1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_clear_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_clear_24dp.xml
new file mode 100644
index 0000000..54e3092
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_clear_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3.27,5L2,6.27l6.97,6.97L6.5,19h3l1.57,-3.66L16.73,21 18,19.73 3.55,5.27 3.27,5zM6,5v0.18L8.82,8h2.4l-0.72,1.68 2.1,2.1L14.21,8H20V5H6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_fill_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_fill_24dp.xml
new file mode 100644
index 0000000..99e6f6a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_fill_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.56,8.94L7.62,0 6.21,1.41l2.38,2.38 -5.15,5.15c-0.59,0.59 -0.59,1.54 0,2.12l5.5,5.5c0.29,0.29 0.68,0.44 1.06,0.44s0.77,-0.15 1.06,-0.44l5.5,-5.5c0.59,-0.58 0.59,-1.53 0,-2.12zM5.21,10L10,5.21 14.79,10H5.21zM19,11.5s-2,2.17 -2,3.5c0,1.1 0.9,2 2,2s2,-0.9 2,-2c0,-1.33 -2,-3.5 -2,-3.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0,20h24v4H0z"
+ android:fillAlpha=".36"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_reset_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_reset_24dp.xml
new file mode 100644
index 0000000..f1153fd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_reset_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,14c0,-4 -6,-10.8 -6,-10.8s-1.33,1.51 -2.73,3.52l8.59,8.59c0.09,-0.42 0.14,-0.86 0.14,-1.31zm-0.88,3.12L12.5,12.5 5.27,5.27 4,6.55l3.32,3.32C6.55,11.32 6,12.79 6,14c0,3.31 2.69,6 6,6 1.52,0 2.9,-0.57 3.96,-1.5l2.63,2.63 1.27,-1.27 -2.74,-2.74z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_text_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_text_24dp.xml
new file mode 100644
index 0000000..abc0c96
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_color_text_24dp.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0,20h24v4H0z"
+ android:fillAlpha=".36"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,3L5.5,17h2.25l1.12,-3h6.25l1.12,3h2.25L13,3h-2zm-1.38,9L12,5.67 14.38,12H9.62z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_indent_decrease_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_indent_decrease_24dp.xml
new file mode 100644
index 0000000..3cdf4fd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_indent_decrease_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,17h10v-2H11v2zm-8,-5l4,4V8l-4,4zm0,9h18v-2H3v2zM3,3v2h18V3H3zm8,6h10V7H11v2zm0,4h10v-2H11v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_indent_increase_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_indent_increase_24dp.xml
new file mode 100644
index 0000000..d4545b8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_indent_increase_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,21h18v-2H3v2zM3,8v8l4,-4 -4,-4zm8,9h10v-2H11v2zM3,3v2h18V3H3zm8,6h10V7H11v2zm0,4h10v-2H11v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_italic_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_italic_24dp.xml
new file mode 100644
index 0000000..95cdea6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_italic_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,4v3h2.21l-3.42,8H6v3h8v-3h-2.21l3.42,-8H18V4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_line_spacing_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_line_spacing_24dp.xml
new file mode 100644
index 0000000..4bf031a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_line_spacing_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,7h2.5L5,3.5 1.5,7H4v10H1.5L5,20.5 8.5,17H6V7zm4,-2v2h12V5H10zm0,14h12v-2H10v2zm0,-6h12v-2H10v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_list_bulleted_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_list_bulleted_24dp.xml
new file mode 100644
index 0000000..6386300
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_list_bulleted_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,10.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zm0,-6c-0.83,0 -1.5,0.67 -1.5,1.5S3.17,7.5 4,7.5 5.5,6.83 5.5,6 4.83,4.5 4,4.5zm0,12.17c-0.74,0 -1.33,0.6 -1.33,1.33s0.6,1.33 1.33,1.33 1.33,-0.6 1.33,-1.33 -0.59,-1.33 -1.33,-1.33zM7,19h14v-2H7v2zm0,-6h14v-2H7v2zm0,-8v2h14V5H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_list_numbered_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_list_numbered_24dp.xml
new file mode 100644
index 0000000..1452f02
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_list_numbered_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2,17h2v0.5H3v1h1v0.5H2v1h3v-4H2v1zm1,-9h1V4H2v1h1v3zm-1,3h1.8L2,13.1v0.9h3v-1H3.2L5,10.9V10H2v1zm5,-6v2h14V5H7zm0,14h14v-2H7v2zm0,-6h14v-2H7v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_paint_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_paint_24dp.xml
new file mode 100644
index 0000000..c906069
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_paint_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,4V3c0,-0.55 -0.45,-1 -1,-1H5c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1V6h1v4H9v11c0,0.55 0.45,1 1,1h2c0.55,0 1,-0.45 1,-1v-9h8V4h-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_quote_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_quote_24dp.xml
new file mode 100644
index 0000000..7e5e327
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_quote_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,17h3l2,-4V7H5v6h3zm8,0h3l2,-4V7h-6v6h3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_size_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_size_24dp.xml
new file mode 100644
index 0000000..d8d8f35
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_size_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,4v3h5v12h3V7h5V4H9zm-6,8h3v7h3v-7h3V9H3v3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_strikethrough_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_strikethrough_24dp.xml
new file mode 100644
index 0000000..a31a531
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_strikethrough_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,19h4v-3h-4v3zM5,4v3h5v3h4V7h5V4H5zM3,14h18v-2H3v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_textdirection_l_to_r_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_textdirection_l_to_r_24dp.xml
new file mode 100644
index 0000000..67b9e4f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_textdirection_l_to_r_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,10v5h2V4h2v11h2V4h2V2H9C6.79,2 5,3.79 5,6s1.79,4 4,4zm12,8l-4,-4v3H5v2h12v3l4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_textdirection_r_to_l_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_textdirection_r_to_l_24dp.xml
new file mode 100644
index 0000000..7fa296a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_textdirection_r_to_l_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,10v5h2V4h2v11h2V4h2V2h-8C7.79,2 6,3.79 6,6s1.79,4 4,4zm-2,7v-3l-4,4 4,4v-3h12v-2H8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_underline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_underline_24dp.xml
new file mode 100644
index 0000000..0509b3e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_format_underline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,17c3.31,0 6,-2.69 6,-6V3h-2.5v8c0,1.93 -1.57,3.5 -3.5,3.5S8.5,12.93 8.5,11V3H6v8c0,3.31 2.69,6 6,6zm-7,2v2h14v-2H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_functions_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_functions_24dp.xml
new file mode 100644
index 0000000..0351ae8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_functions_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,4H6v2l6.5,6L6,18v2h12v-3h-7l5,-5 -5,-5h7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_chart_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_chart_24dp.xml
new file mode 100644
index 0000000..1d393b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_chart_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM9,17H7v-7h2v7zm4,0h-2V7h2v10zm4,0h-2v-4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_comment_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_comment_24dp.xml
new file mode 100644
index 0000000..44c6f6d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_comment_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4V4c0,-1.1 -0.9,-2 -2,-2zm-2,12H6v-2h12v2zm0,-3H6V9h12v2zm0,-3H6V6h12v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_drive_file_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_drive_file_24dp.xml
new file mode 100644
index 0000000..08e825b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_drive_file_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2V8l-6,-6H6zm7,7V3.5L18.5,9H13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_emoticon_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_emoticon_24dp.xml
new file mode 100644
index 0000000..1ef868a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_emoticon_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zm3.5,-9c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zm-7,0c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zm3.5,6.5c2.33,0 4.31,-1.46 5.11,-3.5H6.89c0.8,2.04 2.78,3.5 5.11,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_invitation_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_invitation_24dp.xml
new file mode 100644
index 0000000..e0030fd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_invitation_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,12h-5v5h5v-5zM16,1v2H8V1H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2h-1V1h-2zm3,18H5V8h14v11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_link_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_link_24dp.xml
new file mode 100644
index 0000000..1b8f436
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_link_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4V7H7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9H7c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2H8v2zm9,-6h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4V17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_photo_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_photo_24dp.xml
new file mode 100644
index 0000000..61dcb63
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_insert_photo_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_merge_type_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_merge_type_24dp.xml
new file mode 100644
index 0000000..4eee325
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_merge_type_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,20.41L18.41,19 15,15.59 13.59,17 17,20.41zM7.5,8H11v5.59L5.59,19 7,20.41l6,-6V8h3.5L12,3.5 7.5,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_mode_comment_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_mode_comment_24dp.xml
new file mode 100644
index 0000000..b275ad9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_mode_comment_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21.99,4c0,-1.1 -0.89,-2 -1.99,-2H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4 -0.01,-18z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_mode_edit_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_mode_edit_24dp.xml
new file mode 100644
index 0000000..0ff336a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_mode_edit_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_publish_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_publish_24dp.xml
new file mode 100644
index 0000000..b0651fc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_publish_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5,4v2h14V4H5zm0,10h4v6h6v-6h4l-7,-7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_bottom_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_bottom_24dp.xml
new file mode 100644
index 0000000..877284e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_bottom_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,13h-3V3h-2v10H8l4,4 4,-4zM4,19v2h16v-2H4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_center_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_center_24dp.xml
new file mode 100644
index 0000000..65df54e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_center_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8,19h3v4h2v-4h3l-4,-4 -4,4zm8,-14h-3V1h-2v4H8l4,4 4,-4zM4,11v2h16v-2H4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_top_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_top_24dp.xml
new file mode 100644
index 0000000..518a9f2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_vertical_align_top_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8,11h3v10h2V11h3l-4,-4 -4,4zM4,3v2h16V3H4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/editor/ic_wrap_text_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/editor/ic_wrap_text_24dp.xml
new file mode 100644
index 0000000..ec233dc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/editor/ic_wrap_text_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,19h6v-2H4v2zM20,5H4v2h16V5zm-3,6H4v2h13.25c1.1,0 2,0.9 2,2s-0.9,2 -2,2H15v-2l-3,3 3,3v-2h2c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_attachment_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_attachment_24dp.xml
new file mode 100644
index 0000000..be5b538
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_attachment_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.5,18C4.46,18 2,15.54 2,12.5S4.46,7 7.5,7H18c2.21,0 4,1.79 4,4s-1.79,4 -4,4H9.5C8.12,15 7,13.88 7,12.5S8.12,10 9.5,10H17v1.5H9.5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1H18c1.38,0 2.5,-1.12 2.5,-2.5S19.38,8.5 18,8.5H7.5c-2.21,0 -4,1.79 -4,4s1.79,4 4,4H17V18H7.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_24dp.xml
new file mode 100644
index 0000000..41b533d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_circle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_circle_24dp.xml
new file mode 100644
index 0000000..0d6e8c3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_circle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm4.5,14H8c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3l0.14,0.01C8.58,8.28 10.13,7 12,7c2.21,0 4,1.79 4,4h0.5c1.38,0 2.5,1.12 2.5,2.5S17.88,16 16.5,16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_done_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_done_24dp.xml
new file mode 100644
index 0000000..ad6129a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_done_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM10,17l-3.5,-3.5 1.41,-1.41L10,14.17 15.18,9l1.41,1.41L10,17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_download_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_download_24dp.xml
new file mode 100644
index 0000000..d76c1f6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_download_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_off_24dp.xml
new file mode 100644
index 0000000..cff2984
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4c-1.48,0 -2.85,0.43 -4.01,1.17l1.46,1.46C10.21,6.23 11.08,6 12,6c3.04,0 5.5,2.46 5.5,5.5v0.5H19c1.66,0 3,1.34 3,3 0,1.13 -0.64,2.11 -1.56,2.62l1.45,1.45C23.16,18.16 24,16.68 24,15c0,-2.64 -2.05,-4.78 -4.65,-4.96zM3,5.27l2.75,2.74C2.56,8.15 0,10.77 0,14c0,3.31 2.69,6 6,6h11.73l2,2L21,20.73 4.27,4 3,5.27zM7.73,10l8,8H6c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4h1.73z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_queue_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_queue_24dp.xml
new file mode 100644
index 0000000..104ec97
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_queue_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM19,18H6c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4h0.71C7.37,7.69 9.48,6 12,6c3.04,0 5.5,2.46 5.5,5.5v0.5H19c1.66,0 3,1.34 3,3s-1.34,3 -3,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_upload_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_upload_24dp.xml
new file mode 100644
index 0000000..9a79995
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_cloud_upload_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM14,13v4h-4v-4H7l5,-5 5,5h-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_file_download_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_file_download_24dp.xml
new file mode 100644
index 0000000..9c90028
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_file_download_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_file_upload_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_file_upload_24dp.xml
new file mode 100644
index 0000000..e362b93
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_file_upload_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,16h6v-6h4l-7,-7 -7,7h4zm-4,2h14v2H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_24dp.xml
new file mode 100644
index 0000000..520679b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_open_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_open_24dp.xml
new file mode 100644
index 0000000..851069c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_open_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,6h-8l-2,-2H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2zm0,12H4V8h16v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_shared_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_shared_24dp.xml
new file mode 100644
index 0000000..c4d91c6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/file/ic_folder_shared_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,6h-8l-2,-2H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2zm-5,3c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2zm4,8h-8v-1c0,-1.33 2.67,-2 4,-2s4,0.67 4,2v1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_cast_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_cast_24dp.xml
new file mode 100644
index 0000000..2082de1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_cast_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,3H3c-1.1,0 -2,0.9 -2,2v3h2V5h18v14h-7v2h7c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM1,18v3h3c0,-1.66 -1.34,-3 -3,-3zm0,-4v2c2.76,0 5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zm0,-4v2c4.97,0 9,4.03 9,9h2c0,-6.08 -4.93,-11 -11,-11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_cast_connected_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_cast_connected_24dp.xml
new file mode 100644
index 0000000..af4a654
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_cast_connected_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M1,18v3h3c0,-1.66 -1.34,-3 -3,-3zm0,-4v2c2.76,0 5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zm18,-7H5v1.63c3.96,1.28 7.09,4.41 8.37,8.37H19V7zM1,10v2c4.97,0 9,4.03 9,9h2c0,-6.08 -4.93,-11 -11,-11zm20,-7H3c-1.1,0 -2,0.9 -2,2v3h2V5h18v14h-7v2h7c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_computer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_computer_24dp.xml
new file mode 100644
index 0000000..60406f4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_computer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,18c1.1,0 1.99,-0.9 1.99,-2L22,6c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2H0v2h24v-2h-4zM4,6h16v10H4V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_desktop_mac_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_desktop_mac_24dp.xml
new file mode 100644
index 0000000..0d51046
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_desktop_mac_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,2H3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h7l-2,3v1h8v-1l-2,-3h7c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm0,12H3V4h18v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_desktop_windows_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_desktop_windows_24dp.xml
new file mode 100644
index 0000000..f4f1f3b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_desktop_windows_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,2H3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h7v2H8v2h8v-2h-2v-2h7c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm0,14H3V4h18v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_dock_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_dock_24dp.xml
new file mode 100644
index 0000000..e923ece
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_dock_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8,23h8v-2H8v2zm8,-21.99L8,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM16,15H8V5h8v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_gamepad_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_gamepad_24dp.xml
new file mode 100644
index 0000000..8891427
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_gamepad_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,7.5V2H9v5.5l3,3 3,-3zM7.5,9H2v6h5.5l3,-3 -3,-3zM9,16.5V22h6v-5.5l-3,-3 -3,3zM16.5,9l-3,3 3,3H22V9h-5.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_headset_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_headset_24dp.xml
new file mode 100644
index 0000000..320d262
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_headset_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h3c1.66,0 3,-1.34 3,-3v-7c0,-4.97 -4.03,-9 -9,-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_headset_mic_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_headset_mic_24dp.xml
new file mode 100644
index 0000000..a8e775a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_headset_mic_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h4v1h-7v2h6c1.66,0 3,-1.34 3,-3V10c0,-4.97 -4.03,-9 -9,-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_24dp.xml
new file mode 100644
index 0000000..63c10e5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,5H4c-1.1,0 -1.99,0.9 -1.99,2L2,17c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2zm-9,3h2v2h-2V8zm0,3h2v2h-2v-2zM8,8h2v2H8V8zm0,3h2v2H8v-2zm-1,2H5v-2h2v2zm0,-3H5V8h2v2zm9,7H8v-2h8v2zm0,-4h-2v-2h2v2zm0,-3h-2V8h2v2zm3,3h-2v-2h2v2zm0,-3h-2V8h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_alt_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_alt_24dp.xml
new file mode 100644
index 0000000..98a8966
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_alt_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.5,10c0.83,0 1.5,-0.67 1.5,-1.5S16.33,7 15.5,7 14,7.67 14,8.5s0.67,1.5 1.5,1.5zm-7,0c0.83,0 1.5,-0.67 1.5,-1.5S9.33,7 8.5,7 7,7.67 7,8.5 7.67,10 8.5,10zm3.5,7c2.61,0 4.83,-1.67 5.65,-4H6.35c0.82,2.33 3.04,4 5.65,4zm-0.01,-16C6.47,1 2,5.48 2,11s4.47,10 9.99,10C17.52,21 22,16.52 22,11S17.52,1 11.99,1zM12,19c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_down_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_down_24dp.xml
new file mode 100644
index 0000000..d5c447b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_down_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.41,7.84L12,12.42l4.59,-4.58L18,9.25l-6,6 -6,-6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_left_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_left_24dp.xml
new file mode 100644
index 0000000..7f27205
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_left_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.41,16.09l-4.58,-4.59 4.58,-4.59L14,5.5l-6,6 6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_right_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_right_24dp.xml
new file mode 100644
index 0000000..4bae6bb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_right_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8.59,16.34l4.58,-4.59 -4.58,-4.59L10,5.75l6,6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_up_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_up_24dp.xml
new file mode 100644
index 0000000..ebf42c3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_arrow_up_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_backspace_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_backspace_24dp.xml
new file mode 100644
index 0000000..8ada65f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_backspace_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,11H6.83l3.58,-3.59L9,6l-6,6 6,6 1.41,-1.41L6.83,13H21z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_capslock_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_capslock_24dp.xml
new file mode 100644
index 0000000..1c26f8a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_capslock_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,8.41L16.59,13 18,11.59l-6,-6 -6,6L7.41,13 12,8.41zM6,18h12v-2H6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_control_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_control_24dp.xml
new file mode 100644
index 0000000..74c9367
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_control_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm12,0c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm-6,0c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_hide_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_hide_24dp.xml
new file mode 100644
index 0000000..6ae3110
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_hide_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,3H4c-1.1,0 -1.99,0.9 -1.99,2L2,15c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-9,3h2v2h-2V6zm0,3h2v2h-2V9zM8,6h2v2H8V6zm0,3h2v2H8V9zm-1,2H5V9h2v2zm0,-3H5V6h2v2zm9,7H8v-2h8v2zm0,-4h-2V9h2v2zm0,-3h-2V6h2v2zm3,3h-2V9h2v2zm0,-3h-2V6h2v2zm-7,15l4,-4H8l4,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_return_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_return_24dp.xml
new file mode 100644
index 0000000..f74dfc7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_return_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,7v4H5.83l3.58,-3.59L8,6l-6,6 6,6 1.41,-1.41L5.83,13H21V7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_tab_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_tab_24dp.xml
new file mode 100644
index 0000000..37c5692
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_tab_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.59,7.41L15.17,11H1v2h14.17l-3.59,3.59L13,18l6,-6 -6,-6 -1.41,1.41zM20,6v12h2V6h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_voice_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_voice_24dp.xml
new file mode 100644
index 0000000..a41eedd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_keyboard_voice_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,15c1.66,0 2.99,-1.34 2.99,-3L15,6c0,-1.66 -1.34,-3 -3,-3S9,4.34 9,6v6c0,1.66 1.34,3 3,3zm5.3,-3c0,3 -2.54,5.1 -5.3,5.1S6.7,15 6.7,12H5c0,3.42 2.72,6.23 6,6.72V22h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_24dp.xml
new file mode 100644
index 0000000..87ebf54
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,18c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2H0v2h24v-2h-4zM4,6h16v10H4V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_chromebook_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_chromebook_24dp.xml
new file mode 100644
index 0000000..78101d9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_chromebook_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,18V3H2v15H0v2h24v-2h-2zm-8,0h-4v-1h4v1zm6,-3H4V5h16v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_mac_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_mac_24dp.xml
new file mode 100644
index 0000000..471cf10
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_mac_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,18c1.1,0 1.99,-0.9 1.99,-2L22,5c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -2,0.9 -2,2v11c0,1.1 0.9,2 2,2H0c0,1.1 0.9,2 2,2h20c1.1,0 2,-0.9 2,-2h-4zM4,5h16v11H4V5zm8,14c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_windows_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_windows_24dp.xml
new file mode 100644
index 0000000..e4beebb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_laptop_windows_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,18v-1c1.1,0 1.99,-0.9 1.99,-2L22,5c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2v1H0v2h24v-2h-4zM4,5h16v10H4V5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_memory_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_memory_24dp.xml
new file mode 100644
index 0000000..53a7f92
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_memory_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,9H9v6h6V9zm-2,4h-2v-2h2v2zm8,-2V9h-2V7c0,-1.1 -0.9,-2 -2,-2h-2V3h-2v2h-2V3H9v2H7c-1.1,0 -2,0.9 -2,2v2H3v2h2v2H3v2h2v2c0,1.1 0.9,2 2,2h2v2h2v-2h2v2h2v-2h2c1.1,0 2,-0.9 2,-2v-2h2v-2h-2v-2h2zm-4,6H7V7h10v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_mouse_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_mouse_24dp.xml
new file mode 100644
index 0000000..83281a8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_mouse_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,1.07V9h7c0,-4.08 -3.05,-7.44 -7,-7.93zM4,15c0,4.42 3.58,8 8,8s8,-3.58 8,-8v-4H4v4zm7,-13.93C7.05,1.56 4,4.92 4,9h7V1.07z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phone_android_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phone_android_24dp.xml
new file mode 100644
index 0000000..94b46f0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phone_android_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,1H8C6.34,1 5,2.34 5,4v16c0,1.66 1.34,3 3,3h8c1.66,0 3,-1.34 3,-3V4c0,-1.66 -1.34,-3 -3,-3zm-2,20h-4v-1h4v1zm3.25,-3H6.75V4h10.5v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phone_iphone_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phone_iphone_24dp.xml
new file mode 100644
index 0000000..d234325
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phone_iphone_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.5,1h-8C6.12,1 5,2.12 5,3.5v17C5,21.88 6.12,23 7.5,23h8c1.38,0 2.5,-1.12 2.5,-2.5v-17C18,2.12 16.88,1 15.5,1zm-4,21c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm4.5,-4H7V4h9v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phonelink_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phonelink_24dp.xml
new file mode 100644
index 0000000..e54c722
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phonelink_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,6h18V4H4c-1.1,0 -2,0.9 -2,2v11H0v3h14v-3H4V6zm19,2h-6c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1V9c0,-0.55 -0.45,-1 -1,-1zm-1,9h-4v-7h4v7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phonelink_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phonelink_off_24dp.xml
new file mode 100644
index 0000000..91b57e8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_phonelink_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,6V4H6.82l2,2H22zM1.92,1.65L0.65,2.92l1.82,1.82C2.18,5.08 2,5.52 2,6v11H0v3h17.73l2.35,2.35 1.27,-1.27L3.89,3.62 1.92,1.65zM4,6.27L14.73,17H4V6.27zM23,8h-6c-0.55,0 -1,0.45 -1,1v4.18l2,2V10h4v7h-2.18l3,3H23c0.55,0 1,-0.45 1,-1V9c0,-0.55 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_security_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_security_24dp.xml
new file mode 100644
index 0000000..5110f1e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_security_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12V5l-9,-4zm0,10.99h7c-0.53,4.12 -3.28,7.79 -7,8.94V12H5V6.3l7,-3.11v8.8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_sim_card_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_sim_card_24dp.xml
new file mode 100644
index 0000000..de928fc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_sim_card_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.99,4c0,-1.1 -0.89,-2 -1.99,-2h-8L4,8v12c0,1.1 0.9,2 2,2h12.01c1.1,0 1.99,-0.9 1.99,-2l-0.01,-16zM9,19H7v-2h2v2zm8,0h-2v-2h2v2zm-8,-4H7v-4h2v4zm4,4h-2v-4h2v4zm0,-6h-2v-2h2v2zm4,2h-2v-4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_smartphone_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_smartphone_24dp.xml
new file mode 100644
index 0000000..8b9235f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_smartphone_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_speaker_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_speaker_24dp.xml
new file mode 100644
index 0000000..f0c2944
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_speaker_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,2H7c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,1.99 2,1.99L17,22c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-5,2c1.1,0 2,0.9 2,2s-0.9,2 -2,2c-1.11,0 -2,-0.9 -2,-2s0.89,-2 2,-2zm0,16c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zm0,-8c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_24dp.xml
new file mode 100644
index 0000000..2f97c67
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,4H3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h18c1.1,0 1.99,-0.9 1.99,-2L23,6c0,-1.1 -0.9,-2 -2,-2zm-2,14H5V6h14v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_android_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_android_24dp.xml
new file mode 100644
index 0000000..7a63df9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_android_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,0H6C4.34,0 3,1.34 3,3v18c0,1.66 1.34,3 3,3h12c1.66,0 3,-1.34 3,-3V3c0,-1.66 -1.34,-3 -3,-3zm-4,22h-4v-1h4v1zm5.25,-3H4.75V3h14.5v16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_mac_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_mac_24dp.xml
new file mode 100644
index 0000000..3318071
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tablet_mac_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.5,0h-14C3.12,0 2,1.12 2,2.5v19C2,22.88 3.12,24 4.5,24h14c1.38,0 2.5,-1.12 2.5,-2.5v-19C21,1.12 19.88,0 18.5,0zm-7,23c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm7.5,-4H4V3h15v16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tv_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tv_24dp.xml
new file mode 100644
index 0000000..245642a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_tv_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,3H3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h5v2h8v-2h5c1.1,0 1.99,-0.9 1.99,-2L23,5c0,-1.1 -0.9,-2 -2,-2zm0,14H3V5h18v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/hardware/ic_watch_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_watch_24dp.xml
new file mode 100644
index 0000000..e90cf2d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/hardware/ic_watch_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,12c0,-2.54 -1.19,-4.81 -3.04,-6.27L16,0H8l-0.95,5.73C5.19,7.19 4,9.45 4,12s1.19,4.81 3.05,6.27L8,24h8l0.96,-5.73C18.81,16.81 20,14.54 20,12zM6,12c0,-3.31 2.69,-6 6,-6s6,2.69 6,6 -2.69,6 -6,6 -6,-2.69 -6,-6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_delivery_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_delivery_36px.xml
new file mode 100644
index 0000000..244971a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_delivery_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2.5,16.875L4.375,15H6.25v3H2.5V16.875z M7,26.5c0,1.932,1.556,3.5,3.491,3.5 C12.432,30,14,28.432,14,26.5c0,-1.933,-1.568,-3.5,-3.509,-3.5C8.556,23,7,24.567,7,26.5z M14,23l2,2h9l2,-2H14z M27,26.5 c0,1.932,1.566,3.5,3.5,3.5c1.933,0,3.5,-1.568,3.5,-3.5c0,-1.933,-1.567,-3.5,-3.5,-3.5C28.566,23,27,24.567,27,26.5z M9,13v9 c-0.488,0,-3,0,-3,2l-2.833,0.083L1,23l0.01,-6.5L4,13H9z M10,21V10c0,-2.128,2.03,-4,4,-4h16.423C32.392,6,34,7.739,34,9.867V21H10z "/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_flight_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_flight_36px.xml
new file mode 100644
index 0000000..33b3aac
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_flight_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.964,33l-3.234,-6.728L3,23.035l1.421,-1.422c0.173,-0.168,0.407,-0.233,0.627,-0.188l5.707,0.828l5.769,-6.188 L4.421,10.226l1.942,-1.939c0.176,-0.176,0.465,-0.28,0.688,-0.23l13.032,3.593l5.87,-5.871c1.6,-1.601,3.732,-1.604,4.805,-0.535 c1.066,1.071,1.066,3.205,-0.537,4.805l-5.873,5.87l3.592,13.034c0.055,0.223,-0.051,0.508,-0.227,0.684l-1.939,1.941l-5.836,-12.1 l-6.191,5.766l0.826,5.71c0.049,0.217,-0.016,0.451,-0.184,0.624L12.964,33z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_hangout_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_hangout_36px.xml
new file mode 100644
index 0000000..1483f09
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_hangout_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.25,3C10.209,3,4.5,8.709,4.5,15.75S10.209,28.5,17.25,28.5H18v5.25c7.289,-3.516,12,-11.25,12,-18 C30,8.709,24.291,3,17.25,3z M16.5,16.5l-1.5,3h-2.25l1.5,-3H12V12h4.5V16.5z M22.5,16.5l-1.5,3h-2.25l1.5,-3H18V12h4.5V16.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_location_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_location_36px.xml
new file mode 100644
index 0000000..95a390c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_location_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,3C12.202,3,7.5,7.701,7.5,13.5C7.5,21.375,18,33,18,33s10.5,-11.625,10.5,-19.5C28.5,7.701,23.798,3,18,3z M18,17.25 c-2.071,0,-3.75,-1.679,-3.75,-3.75S15.929,9.75,18,9.75s3.75,1.679,3.75,3.75S20.071,17.25,18,17.25z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_purchase_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_purchase_36px.xml
new file mode 100644
index 0000000..c8cea24
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_purchase_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M32.736,13h-7.056l-6.454,-9.345c-0.452,-0.677,-1.367,-0.859,-2.044,-0.407C17.016,3.36,16.881,3.5,16.776,3.656l-0.003,-0.001 L16.77,3.663l-0.002,0.001L10.319,13H3.264c-0.814,0,-1.475,0.976,-1.475,1.79c0,0.137,0.019,0.269,0.054,0.395L5.58,28.843 C5.926,30.087,7.066,31,8.421,31h19.157c1.355,0,2.496,-0.913,2.842,-2.157l3.737,-13.658c0.034,-0.126,0.053,-0.258,0.053,-0.395 C34.21,13.976,33.551,13,32.736,13z M18,25c-1.657,0,-3,-1.343,-3,-3s1.343,-3,3,-3c1.656,0,3,1.343,3,3S19.656,25,18,25z M13.579,13 L18,6.832L22.421,13H13.579z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_restaurant_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_restaurant_36px.xml
new file mode 100644
index 0000000..7e041e7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_restaurant_36px.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0.0,0.0 h36.0 v36.0 h-36.0z"
+ android:fillAlpha="0"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,10c0,0.55,-0.45,1,-1,1l0,0c-0.55,0,-1,-0.45,-1,-1V3c0,-0.55,-0.45,-1,-1,-1l0,0c-0.55,0,-1,0.45,-1,1v7c0,0.55,-0.45,1,-1,1l0,0 c-0.55,0,-1,-0.45,-1,-1V3c0,-0.55,-0.45,-1,-1,-1l0,0C7.45,2,7,2.45,7,3v9c0,0.55,0.232,1.386,0.514,1.857l1.971,3.285 c0.283,0.472,0.486,1.307,0.452,1.856L9.062,33.002C9.028,33.551,9.45,34,10,34h4c0.55,0,0.972,-0.449,0.938,-0.998l-0.875,-14.004 c-0.034,-0.549,0.169,-1.384,0.452,-1.856l1.971,-3.285C16.768,13.386,17,12.55,17,12V3c0,-0.55,-0.45,-1,-1,-1l0,0c-0.55,0,-1,0.45,-1,1V10z M22.707,5.293C22.318,5.682,22,6.45,22,7v9c0,0.55,0.25,1.374,0.555,1.832l0.891,1.336C23.75,19.626,24,20.45,24,21v12 c0,0.55,0.45,1,1,1h3c0.55,0,1,-0.45,1,-1V3c0,-0.55,-0.45,-1,-1,-1h-1c-0.55,0,-1.318,0.318,-1.707,0.707L22.707,5.293z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_ticket_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_ticket_36px.xml
new file mode 100644
index 0000000..150e96d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_ticket_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M32,15v-5c0,-1.656,-1.344,-3,-3,-3H7c-1.656,0,-3,1.344,-3,3v5c1.656,0,3,1.344,3,3s-1.344,3,-3,3v5c0,1.656,1.344,3,3,3h22 c1.656,0,3,-1.344,3,-3v-5c-1.656,0,-3,-1.344,-3,-3S30.344,15,32,15z M22.695,24.75L18,21.917l-4.695,2.833l1.242,-5.341L10.5,15.75 l5.363,-0.404L18,10.5l2.137,4.846L25.5,15.75l-4.047,3.659L22.695,24.75z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_wallet_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_wallet_36px.xml
new file mode 100644
index 0000000..352a520
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_wallet_36px.xml
@@ -0,0 +1,35 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.797,5.986c-0.082,-0.053,-0.154,-0.115,-0.238,-0.168c-0.525,-0.327,-1.203,-0.421,-1.881,-0.451l-0.156,0.062 c-0.104,0,-0.129,0,-0.152,0c-0.559,0,-1.119,0.058,-1.637,0.342c-1.576,0.868,-2.115,2.757,-1.207,4.263 c1.26,2.076,1.979,4.347,2.178,6.667c-0.025,-0.033,-0.057,-0.072,-0.08,-0.107c0.496,4.996,-1.418,8.639,-2.539,11.144 c0.004,0.035,0.014,0.068,0.016,0.104c0.094,0.988,0.666,1.92,1.637,2.456c0.518,0.285,1.086,0.42,1.645,0.42 c1.139,0,2.246,-0.565,2.857,-1.575c1.875,-3.103,2.896,-6.546,3.08,-10.022c0.008,0.017,0.018,0.033,0.027,0.051 C21.758,14.308,20.945,9.223,17.797,5.986z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14.623,16.585c-1.924,-2.673,-4.453,-4.963,-7.469,-6.643C6.633,9.651,6.061,9.514,5.498,9.514 c-1.137,0,-2.238,0.56,-2.85,1.562c-0.918,1.499,-0.387,3.427,1.186,4.3c4.678,2.607,7.75,7.22,8.25,12.356 C13.205,25.228,15.119,21.581,14.623,16.585z"
+ android:fillAlpha="0.5"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M30.631,3.937C30.051,2.718,28.799,2,27.488,2c-0.477,0,-0.957,0.093,-1.416,0.292c-1.736,0.746,-2.508,2.694,-1.725,4.348 c1.248,2.64,2.033,5.42,2.369,8.238c-0.025,-0.043,-0.062,-0.081,-0.09,-0.124c0.754,7.063,-1.365,12.161,-2.576,15.625 c-0.002,0.054,0,0.105,-0.002,0.159c-0.072,1.313,0.689,2.596,2.023,3.168C26.531,33.907,27.012,34,27.484,34 c1.314,0,2.566,-0.721,3.146,-1.938C34.857,23.125,34.857,12.873,30.631,3.937z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M26.627,14.754c-2.207,-3.453,-5.199,-6.465,-8.83,-8.768c3.148,3.236,3.961,8.321,3.549,13.183 c1.916,3.388,2.893,7.253,2.705,11.21C25.262,26.915,27.381,21.817,26.627,14.754z"
+ android:fillAlpha="0.5"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_weather_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_weather_36px.xml
new file mode 100644
index 0000000..7d347c9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_weather_36px.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9.067,15.237c0.064,0,0.129,0.012,0.194,0.012c0.706,-2.286,2.23,-4.253,4.303,-5.558 c-1.011,-1.79,-2.902,-3.017,-5.104,-3.017c-3.257,0,-5.896,2.64,-5.896,5.896c0,1.783,0.814,3.357,2.066,4.437 c0.011,-0.011,0.021,-0.022,0.032,-0.033C5.915,15.841,7.477,15.237,9.067,15.237z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M29.026,18.433c-0.454,0,-0.891,0.068,-1.306,0.186c-0.336,-4.412,-3.927,-7.886,-8.313,-7.886c-4.487,0,-8.145,3.634,-8.335,8.191 c-0.636,-0.312,-1.342,-0.492,-2.095,-0.492c-2.693,0,-4.878,2.02,-4.878,4.784S6.284,28,8.978,28c2.041,0,18.21,0,20.049,0 c2.691,0,4.874,-2.02,4.874,-4.783S31.718,18.433,29.026,18.433z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_act_youtube_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_act_youtube_36px.xml
new file mode 100644
index 0000000..394ad76
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_act_youtube_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M30.003,6.601C29.101,6.305,23.552,6,18,6C12.451,6,6.902,6.281,6,6.576C3.653,7.351,3,12.606,3,18 s0.653,10.649,3,11.424C6.902,29.72,12.451,30,18,30c5.552,0,11.101,-0.28,12.003,-0.576c2.344,-0.775,2.982,-6.031,2.982,-11.424 S32.347,7.374,30.003,6.601z M15,24V12l8.25,6L15,24z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_add_cluster_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_add_cluster_36px.xml
new file mode 100644
index 0000000..4a70cf1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_add_cluster_36px.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="35dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="35.998">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.0,8.0 h3.0 v21.0 h-3.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.0,17.0 h21.0 v3.0 h-21.0z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_check_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/ic_check_24dp.xml
new file mode 100644
index 0000000..065f56e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_check_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,16.17l-4.17,-4.17 -1.42,1.41 5.59,5.59 12,-12 -1.41,-1.41z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_chevron_down_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_chevron_down_36px.xml
new file mode 100644
index 0000000..af6708a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_chevron_down_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M24.879,12.879l-6.879000,6.879000 -6.879000,-6.879000 -2.121000,2.121000 9.000000,9.000000 9.000000,-9.000000z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_compose_popout_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_compose_popout_36px.xml
new file mode 100644
index 0000000..6019979
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_compose_popout_36px.xml
@@ -0,0 +1,31 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0.0,0.0 h36.0 v36.0 h-36.0z"
+ android:fillAlpha="0"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.41,24.832l1.094000,1.171999 6.496000,-6.004000 -6.496000,-6.000000 -1.094000,1.168000 3.086000,2.832000 -17.496000,0.000000 0.000000,4.000000 17.500000,0.000000z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8,3v11h3v-4h19v20H11v-4H8v7h25V3H8z M30,8H11V6h19V8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_custom_cluster_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_custom_cluster_36px_clr.xml
new file mode 100644
index 0000000..50a2c2b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_custom_cluster_36px_clr.xml
@@ -0,0 +1,29 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M31.954,8.458L13.313,6.02c-1.288,-0.168,-2.467,0.74,-2.637,2.029l-0.527,4.041l12.641,-1.374 c1.959,-0.212,3.708,1.218,3.917,3.157l1.157,10.673l1.961,0.257c1.287,0.169,2.465,-0.741,2.632,-2.029l1.523,-11.676 C34.148,9.809,33.242,8.627,31.954,8.458z"
+ android:fillAlpha="0.6"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22.382,12.2L4.057,14.163c-1.265,0.136,-2.181,1.256,-2.042,2.507l1.242,11.301c0.137,1.248,1.274,2.151,2.54,2.016 l18.325,-1.963c1.266,-0.137,2.18,-1.259,2.042,-2.505l-1.243,-11.303C24.784,12.967,23.647,12.064,22.382,12.2 M22.425,16.516 l-7.41,6.525v-0.002c-0.18,0.171,-0.412,0.286,-0.678,0.315c-0.263,0.027,-0.518,-0.036,-0.726,-0.166l-0.001,0.004l-8.657,-4.805 c-0.282,-0.18,-0.484,-0.48,-0.525,-0.835c-0.068,-0.626,0.389,-1.184,1.021,-1.252c0.252,-0.026,0.49,0.027,0.692,0.142l7.917,4.37 l6.782,-5.944c0.173,-0.153,0.396,-0.259,0.646,-0.286c0.632,-0.067,1.2,0.383,1.27,1.008C22.793,15.944,22.663,16.281,22.425,16.516"
+ android:fillAlpha="0.6"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_finance_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_finance_36px_clr.xml
new file mode 100644
index 0000000..2b736e1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_finance_36px_clr.xml
@@ -0,0 +1,40 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:pathData="M0.0,0.0 h36.0 v36.0 h-36.0z"
+ android:fillAlpha="0"
+ android:fillColor="#689F38"/>
+ <path
+ android:pathData="M6.0,20.0 h6.0 v13.0 h-6.0z"
+ android:fillColor="#689F38"/>
+ <path
+ android:pathData="M15.0,21.0 h6.0 v12.0 h-6.0z"
+ android:fillColor="#689F38"/>
+ <path
+ android:pathData="M24.0,14.0 h6.0 v19.0 h-6.0z"
+ android:fillColor="#689F38"/>
+ <path
+ android:pathData="M3.707,18.707l-1.414000,-1.414001 8.207000,-8.206999 6.000000,6.000000 11.500000,-11.500000 1.414000,1.414000 -12.914000,12.914000 -6.000000,-6.000000z"
+ android:fillColor="#689F38"/>
+ <path
+ android:pathData="M24.535,5.0l3.465000,0.000000 0.000000,3.464000 2.000000,-1.628000 0.000000,-3.836000 -3.836000,0.000000z"
+ android:fillColor="#689F38"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_forums_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_forums_36px_clr.xml
new file mode 100644
index 0000000..b742c9b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_forums_36px_clr.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:pathData="M24,7.111C24,5.393,22.605,4,20.889,4H7.111C5.395,4,4.016,5.393,4.016,7.111L4,25l6.223,-5H20 c2.375,0,4,-2,4,-4V7.111z M12,22v1.889C12,25.607,13.395,27,15.111,27h10.666L32,33V14.111C32,12.393,30.605,11,28.889,11H26 l0.01,5c0,4,-1.979,6,-5.01,6H12z"
+ android:fillColor="#3F51B5"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_history_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/ic_history_24dp.xml
new file mode 100644
index 0000000..4174bf2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_history_24dp.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,3c-4.97,0 -9,4.03 -9,9h-3l3.89,3.89 0.07,0.14 4.04,-4.03h-3c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42c1.63,1.63 3.87,2.64 6.36,2.64 4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zm-1,5v5l4.28,2.54 0.72,-1.21 -3.5,-2.08v-4.25h-1.5z"
+ android:fillAlpha=".9"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_inbox_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_inbox_36px_clr.xml
new file mode 100644
index 0000000..5f77a5c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_inbox_36px_clr.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:pathData="M30,6H6C4.344,6,3.015,7.343,3.015,9L3,27c0,1.657,1.344,3,3,3h24c1.656,0,3,-1.343,3,-3V9 C33,7.343,31.656,6,30,6z M29.44,13.168L18.92,20.685v-0.002C18.665,20.881,18.346,21,18,21s-0.665,-0.119,-0.917,-0.317 l-0.003,0.002L6.56,13.168C6.22,12.892,6,12.471,6,12c0,-0.83,0.671,-1.5,1.5,-1.5c0.328,0,0.63,0.104,0.876,0.283L18,17.628 l9.624,-6.845C27.87,10.604,28.172,10.5,28.5,10.5c0.829,0,1.5,0.67,1.5,1.5C30,12.471,29.783,12.892,29.44,13.168"
+ android:fillColor="#4285F4"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_low_priority_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_low_priority_36px.xml
new file mode 100644
index 0000000..ad29907
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_low_priority_36px.xml
@@ -0,0 +1,35 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4.457,25.029L0,29h14V15l-4.113,4.572C5.264,14.97,3.648,8.091,4.556,2.556C0.88,8.1,-1.096,17.663,4.457,25.029z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M35,10c0,0.55,-0.45,1,-1,1H18c-0.55,0,-1,-0.45,-1,-1V7c0,-0.55,0.45,-1,1,-1h16c0.55,0,1,0.45,1,1V10z"
+ android:fillAlpha="0.6"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M35,19c0,0.55,-0.45,1,-1,1H18c-0.55,0,-1,-0.45,-1,-1v-3c0,-0.55,0.45,-1,1,-1h16c0.55,0,1,0.45,1,1V19z"
+ android:fillAlpha="0.6"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M35,28c0,0.55,-0.45,1,-1,1H18c-0.55,0,-1,-0.45,-1,-1v-3c0,-0.55,0.45,-1,1,-1h16c0.55,0,1,0.45,1,1V28z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_multiselect_check_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_multiselect_check_36px.xml
new file mode 100644
index 0000000..0e03c3c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_multiselect_check_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M30.0,9.0l-2.551001,-1.726000 -12.448999,16.726000 -7.131000,-5.250000 -1.869000,2.540001 9.750000,7.209999z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_offers_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_offers_36px_clr.xml
new file mode 100644
index 0000000..02fe5e1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_offers_36px_clr.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:pathData="M4,28.889C4,30.605,5.395,32,7.111,32h7.777L4,21.111V28.889z M31.089,17.362L18.645,4.912 c-0.565,-0.562,-1.344,-0.91,-2.2,-0.912H7.889C5.741,4,4,5.741,4,7.889v8.556c0,0.86,0.35,1.636,0.911,2.2v0.001l12.451,12.443 C17.924,31.65,18.702,32,19.562,32c0.86,0,1.638,-0.348,2.2,-0.91l9.324,-9.326C31.65,21.202,32,20.421,32,19.562 S31.65,17.924,31.089,17.362 M9.444,12.167c-1.507,0,-2.722,-1.221,-2.722,-2.723s1.215,-2.723,2.722,-2.723 c1.504,0,2.723,1.221,2.723,2.723S10.948,12.167,9.444,12.167"
+ android:fillColor="#00BCD4"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_pin_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_pin_36px.xml
new file mode 100644
index 0000000..6fcad41
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_pin_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M24,7.091c0,0,3,-1.092,3,-3.091V2H10v2c0,1.999,3,3.091,3,3.091V18c-2.463,0,-4,1.46,-4,4h8l0.075,10.545 C17.114,33.179,17.787,34,18.5,34s1.384,-0.821,1.425,-1.455L20,22h8c0,-2.54,-1.546,-4,-4,-4V7.091z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_purchase_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_purchase_36px_clr.xml
new file mode 100644
index 0000000..f014a95
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_purchase_36px_clr.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:pathData="M31.799,11H10.939c-0.029,0,-0.05,0.015,-0.078,0.016L9.068,5H3.49C2.666,5,2,5.671,2,6.5 S2.666,8,3.49,8h3.36l4.151,13.931C11.195,22.581,11.787,23,12.43,23H29.15c0.817,0,1.636,-0.909,1.813,-1.715l2.002,-8.945 C33.143,11.534,32.617,11,31.799,11z"
+ android:fillColor="#795548"/>
+ <path
+ android:pathData="M27.5,29.0 m-3.0, 0 a 3.0,3.0 0 1,1 6.0,0 a3.0,3.0 0 1,1 -6.0,0"
+ android:fillColor="#795548"/>
+ <path
+ android:pathData="M14.0,29.0 m-3.0, 0 a 3.0,3.0 0 1,1 6.0,0 a3.0,3.0 0 1,1 -6.0,0"
+ android:fillColor="#795548"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_reminder_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_reminder_36px_clr.xml
new file mode 100644
index 0000000..ecfe620
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_reminder_36px_clr.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:pathData="M22,8v0.668C22.548,8.839,23.496,9,24.524,9c2.225,0,4.809,-0.756,4.44,-3.802 C28.771,3.583,27.944,3,26.896,3C25.291,3,23.168,4.365,22,5.49V4c0,-1.104,-0.896,-2,-2,-2s-2,0.896,-2,2v1.49 C16.832,4.365,14.709,3,13.104,3c-1.048,0,-1.876,0.583,-2.069,2.198C10.667,8.244,13.251,9,15.476,9 C16.503,9,17.452,8.839,18,8.668V8H22z M22.017,6.859C22.306,6.197,25.045,3.9,26.896,3.9c0.353,0,1.007,0,1.176,1.406 c0.097,0.795,-0.044,1.394,-0.43,1.828C26.897,7.975,25.369,8.1,24.524,8.1c-1.245,0,-2.256,-0.259,-2.427,-0.348 C22.058,7.724,22.028,7.687,22,7.65V6.906C22.007,6.891,22.01,6.876,22.017,6.859z M17.924,7.738 C17.731,7.841,16.721,8.1,15.476,8.1c-0.845,0,-2.373,-0.125,-3.117,-0.965c-0.386,-0.435,-0.526,-1.033,-0.43,-1.829 C12.098,3.9,12.753,3.9,13.105,3.9c1.85,0,4.589,2.297,4.879,2.962c0.007,0.016,0.01,0.029,0.016,0.044v0.746 C17.977,7.684,17.953,7.717,17.924,7.738z"
+ android:fillColor="#3C80F5"/>
+ <path
+ android:pathData="M29,17c-2.277,1.139,-3.736,2.739,-4.469,3.875c-0.906,1.406,-1.25,0.828,-1.25,0.828 s-0.344,-0.391,-0.719,-1.328S22,17,22,17V8.609h-4v4.641c0,-1.1,-0.9,-1.25,-2,-1.25s-2,0.9,-2,2v0.25c0,-1.1,-0.9,-1.25,-2,-1.25s-2,0.9,-2,2 v1.25c0,-1.1,-1.969,-1.562,-2.844,-0.688c-0.748,0.748,-1.031,1.744,-1.031,2.844L6,22c0,4.375,2.875,11,6.369,11 c1.631,0,5.818,0,5.818,0c3.438,0,5.23,-3,5.23,-3l2.676,-3.594c0.562,-0.547,0.634,-0.859,0.701,-1.023C28.025,22.398,32,18,32,18 S33,15,29,17z"
+ android:fillColor="#3C80F5"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_reply_all_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_reply_all_36px.xml
new file mode 100644
index 0000000..c2075dc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_reply_all_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10.828,11.672v-4.5L0,18l10.859,10.859v-4.5L4.5,18L10.828,11.672z M20,13.5v-6L9,18l11,10.5v-6.149 c7.5,0,12.25,2.399,16,7.649C34.5,22.5,30.5,15,20,13.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_rotate_24_01.xml b/asset-studio/src/main/java/images/material_design_icons/ic_rotate_24_01.xml
new file mode 100644
index 0000000..fbbd84d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_rotate_24_01.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10.25,1.75c-0.6,-0.6,-1.5,-0.6,-2.1,0l-6.4,6.4c-0.6,0.6,-0.6,1.5,0,2.1l12,12c0.6,0.6,1.5,0.6,2.1,0l6.4,-6.4 c0.6,-0.6,0.6,-1.5,0,-2.1L10.25,1.75z M14.85,21.25l-12,-12l6.4,-6.4l12,12L14.85,21.25z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.55,2.5c3.3,1.5,5.6,4.7,6,8.5h1.5c-0.6,-6.2,-5.7,-11,-12,-11c-0.2,0,-0.4,0,-0.7,0l3.8,3.8L16.55,2.5z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.55,21.5c-3.3,-1.5,-5.6,-4.7,-6,-8.5h-1.4c0.5,6.2,5.6,11,11.9,11c0.2,0,0.4,0,0.7,0l-3.8,-3.8L7.55,21.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_search_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/ic_search_24dp.xml
new file mode 100644
index 0000000..6cc717d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_search_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.5,14h-0.79l-0.28,-0.27c0.98,-1.14 1.57,-2.62 1.57,-4.23 0,-3.59 -2.91,-6.5 -6.5,-6.5s-6.5,2.91 -6.5,6.5 2.91,6.5 6.5,6.5c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99 1.49,-1.49 -4.99,-5zm-6,0c-2.49,0 -4.5,-2.01 -4.5,-4.5s2.01,-4.5 4.5,-4.5 4.5,2.01 4.5,4.5 -2.01,4.5 -4.5,4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_social_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_social_36px_clr.xml
new file mode 100644
index 0000000..5c0b9f4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_social_36px_clr.xml
@@ -0,0 +1,33 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:pathData="M12.314,23.667c-0.92,-0.513,-1.747,-0.437,-2.871,-0.437C5.879,23.23,3.015,27,3.015,29h8.142 C11.189,27,11.459,25.13,12.314,23.667z"
+ android:fillColor="#DB4437"/>
+ <path
+ android:pathData="M23.25,20c-4.549,0,-9.75,3,-9.75,9H33C33,23,27.803,20,23.25,20z"
+ android:fillColor="#DB4437"/>
+ <path
+ android:pathData="M23.0,12.0 m-5.0, 0 a 5.0,5.0 0 1,1 10.0,0 a5.0,5.0 0 1,1 -10.0,0"
+ android:fillColor="#DB4437"/>
+ <path
+ android:pathData="M10.0,16.0 m-4.0, 0 a 4.0,4.0 0 1,1 8.0,0 a4.0,4.0 0 1,1 -8.0,0"
+ android:fillColor="#DB4437"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_speed_dial_48px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_speed_dial_48px_clr.xml
new file mode 100644
index 0000000..7968925
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_speed_dial_48px_clr.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:pathData="M38,22H26V10c0,-1.1,-0.898,-2,-2,-2l0,0c-1.098,0,-2,0.9,-2,2v12H10c-1.098,0,-2,0.9,-2,2l0,0 c0,1.1,0.902,2,2,2h12v12c0,1.1,0.902,2,2,2l0,0c1.102,0,2,-0.9,2,-2V26h12c1.102,0,2,-0.9,2,-2l0,0C40,22.9,39.102,22,38,22z"
+ android:fillColor="#FFFFFF"/>
+ <path
+ android:pathData="M38,21H26V9c0,-1.1,-0.898,-2,-2,-2l0,0c-1.098,0,-2,0.9,-2,2v12H10c-1.098,0,-2,0.9,-2,2l0,0 c0,1.1,0.902,2,2,2h12v12c0,1.1,0.902,2,2,2l0,0c1.102,0,2,-0.9,2,-2V25h12c1.102,0,2,-0.9,2,-2l0,0C40,21.9,39.102,21,38,21z"
+ android:fillAlpha="0.15"
+ android:fillColor="#070707"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_sys_tty_24px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_sys_tty_24px.xml
new file mode 100644
index 0000000..53b32cf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_sys_tty_24px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22.1,4.2H1.9C1,4.2,0.2,5,0.2,5.9v12.1c0,1,0.8,1.8,1.8,1.8h20.1c1,0,1.8,-0.8,1.8,-1.8V5.9C23.8,5,23,4.2,22.1,4.2z M8,9.3H5.5V16H3.9V9.3H1.5V8.1H8V9.3z M15.3,9.3h-2.5v6.6h-1.6V9.3H8.7V8.1h6.5V9.3z M19.7,13.2V16h-1.6v-2.8l-2.7,-5h1.7 l1.7,3.7l1.7,-3.7h1.7L19.7,13.2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_travel_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_travel_36px_clr.xml
new file mode 100644
index 0000000..54d2477
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_travel_36px_clr.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:pathData="M12.964,33l-3.234,-6.728L3,23.035l1.421,-1.422c0.173,-0.168,0.407,-0.233,0.627,-0.188l5.707,0.828 l5.769,-6.188L4.421,10.226l1.942,-1.939c0.176,-0.176,0.465,-0.28,0.688,-0.23l13.032,3.593l5.87,-5.871 c1.6,-1.601,3.732,-1.604,4.805,-0.535c1.066,1.071,1.066,3.205,-0.537,4.805l-5.873,5.87l3.592,13.034 c0.055,0.223,-0.051,0.508,-0.227,0.684l-1.939,1.941l-5.836,-12.1l-6.191,5.766l0.826,5.71c0.049,0.217,-0.016,0.451,-0.184,0.624 L12.964,33z"
+ android:fillColor="#9C27B0"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_upcoming_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_upcoming_36px_clr.xml
new file mode 100644
index 0000000..33f29ed
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_upcoming_36px_clr.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:pathData="M18,5.5c6.893,0,12.5,5.607,12.5,12.5S24.893,30.5,18,30.5S5.5,24.892,5.5,18S11.107,5.5,18,5.5 M18,3C9.715,3,3,9.714,3,18c0,8.285,6.715,15,15,15s15,-6.715,15,-15C33,9.714,26.285,3,18,3L18,3z"
+ android:fillColor="#ECA403"/>
+ <path
+ android:pathData="M24.4,24.248L16,19.2V9h1.699l1.242,9l6.249,5.25L24.4,24.248z"
+ android:fillColor="#ECA403"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_updates_36px_clr.xml b/asset-studio/src/main/java/images/material_design_icons/ic_updates_36px_clr.xml
new file mode 100644
index 0000000..4c01b8f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_updates_36px_clr.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:pathData="M11,4.307v15.321c6.665,-0.811,13,3.372,20,-0.307V4C24.332,7.997,17.665,3.496,11,4.307"
+ android:fillColor="#FF5722"/>
+ <path
+ android:pathData="M9,31.5C9,32.329,8.328,33,7.5,33l0,0C6.672,33,6,32.329,6,31.5v-27C6,3.671,6.672,3,7.5,3l0,0 C8.328,3,9,3.671,9,4.5V31.5z"
+ android:fillColor="#FF5722"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_vasquette_36px.xml b/asset-studio/src/main/java/images/material_design_icons/ic_vasquette_36px.xml
new file mode 100644
index 0000000..5501f62
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_vasquette_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,12h6V6H6V12z M6,21h6v-6H6V21z M15,30h6v-6h-6V30z M6,30h6v-6H6V30z M15,12h6V6h-6V12z M24,21h6v-6h-6V21z M24,6v6h6 V6H24z M15,21h6v-6h-6V21z M24,30h6v-6h-6V30z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/ic_volume_off.xml b/asset-studio/src/main/java/images/material_design_icons/ic_volume_off.xml
new file mode 100644
index 0000000..fbccb29
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/ic_volume_off.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M33,24c0,-3.53 -2.04,-6.58 -5,-8.05v4.42l4.91,4.91c0.06,-0.42 0.09,-0.85 0.09,-1.28zm5,0c0,1.88 -0.41,3.65 -1.08,5.28l3.03,3.03C41.25,29.82 42,27 42,24c0,-8.56 -5.99,-15.72 -14,-17.54v4.13c5.78,1.72 10,7.07 10,13.41zM8.55,6L6,8.55 15.45,18H6v12h8l10,10V26.55l8.51,8.51c-1.34,1.03 -2.85,1.86 -4.51,2.36v4.13c2.75,-0.63 5.26,-1.89 7.37,-3.62L39.45,42 42,39.45l-18,-18L8.55,6zM24,8l-4.18,4.18L24,16.36V8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_add_to_photos_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_add_to_photos_24dp.xml
new file mode 100644
index 0000000..78eaca1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_add_to_photos_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zm16,-4H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-1,9h-4v4h-2v-4H9V9h4V5h2v4h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_adjust_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_adjust_24dp.xml
new file mode 100644
index 0000000..14ea87e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_adjust_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.49,2 2,6.49 2,12s4.49,10 10,10 10,-4.49 10,-10S17.51,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zm3,-8c0,1.66 -1.34,3 -3,3s-3,-1.34 -3,-3 1.34,-3 3,-3 3,1.34 3,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_assistant_photo_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_assistant_photo_24dp.xml
new file mode 100644
index 0000000..581ba88
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_assistant_photo_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14.4,6L14,4H5v17h2v-7h5.6l0.4,2h7V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_audiotrack_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_audiotrack_24dp.xml
new file mode 100644
index 0000000..ac5d072
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_audiotrack_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,3v9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12 6,14.01 6,16.5S8.01,21 10.5,21c2.31,0 4.2,-1.75 4.45,-4H15V6h4V3h-7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_circular_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_circular_24dp.xml
new file mode 100644
index 0000000..a29dbd2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_circular_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,9c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,4c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM7,9.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm3,7c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm-3,-3c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm3,-6c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM14,9c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,-1.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zm3,6c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm0,-4c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zm2,-3.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm0,-3.5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_linear_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_linear_24dp.xml
new file mode 100644
index 0000000..c52651c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_linear_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5,17.5c0.83,0 1.5,-0.67 1.5,-1.5s-0.67,-1.5 -1.5,-1.5 -1.5,0.67 -1.5,1.5 0.67,1.5 1.5,1.5zM9,13c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,-4c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zM3,21h18v-2H3v2zM5,9.5c0.83,0 1.5,-0.67 1.5,-1.5S5.83,6.5 5,6.5 3.5,7.17 3.5,8 4.17,9.5 5,9.5zm0,4c0.83,0 1.5,-0.67 1.5,-1.5s-0.67,-1.5 -1.5,-1.5 -1.5,0.67 -1.5,1.5 0.67,1.5 1.5,1.5zM9,17c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm8,-0.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM3,3v2h18V3H3zm14,5.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zm0,4c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM13,9c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,4c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,4c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_off_24dp.xml
new file mode 100644
index 0000000..8511153
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14,7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm-0.2,4.48l0.2,0.02c0.83,0 1.5,-0.67 1.5,-1.5s-0.67,-1.5 -1.5,-1.5 -1.5,0.67 -1.5,1.5l0.02,0.2c0.09,0.67 0.61,1.19 1.28,1.28zM14,3.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zm-4,0c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zm11,7c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM10,7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm8,8c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,-4c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,-4c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm-4,13.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM2.5,5.27l3.78,3.78L6,9c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1c0,-0.1 -0.03,-0.19 -0.06,-0.28l2.81,2.81c-0.71,0.11 -1.25,0.73 -1.25,1.47 0,0.83 0.67,1.5 1.5,1.5 0.74,0 1.36,-0.54 1.47,-1.25l2.81,2.81c-0.09,-0.03 -0.18,-0.06 -0.28,-0.06 -0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1c0,-0.1 -0.03,-0.19 -0.06,-0.28l3.78,3.78L20,20.23 3.77,4 2.5,5.27zM10,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm11,-3.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM6,13c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM3,9.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm7,11c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM6,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm-3,-3.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_on_24dp.xml
new file mode 100644
index 0000000..000f86e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_blur_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,13c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,4c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,-8c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm-3,0.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM6,5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm15,5.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM14,7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,-3.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zm-11,10c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm7,7c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm0,-17c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM10,7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zm0,5.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zm8,0.5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,4c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,-8c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,-4c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm3,8.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM14,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm0,3.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zm-4,-12c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zm0,8.5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zm4,-4.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zm0,-4c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_1_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_1_24dp.xml
new file mode 100644
index 0000000..cb98cb8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_1_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_2_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_2_24dp.xml
new file mode 100644
index 0000000..194732f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_2_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,2c-1.82,0 -3.53,0.5 -5,1.35C7.99,5.08 10,8.3 10,12s-2.01,6.92 -5,8.65C6.47,21.5 8.18,22 10,22c5.52,0 10,-4.48 10,-10S15.52,2 10,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_3_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_3_24dp.xml
new file mode 100644
index 0000000..c2f2587
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_3_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,2c-1.05,0 -2.05,0.16 -3,0.46 4.06,1.27 7,5.06 7,9.54 0,4.48 -2.94,8.27 -7,9.54 0.95,0.3 1.95,0.46 3,0.46 5.52,0 10,-4.48 10,-10S14.52,2 9,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_4_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_4_24dp.xml
new file mode 100644
index 0000000..92016a1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_4_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM12,18c-0.89,0 -1.74,-0.2 -2.5,-0.55C11.56,16.5 13,14.42 13,12s-1.44,-4.5 -3.5,-5.45C10.26,6.2 11.11,6 12,6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_5_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_5_24dp.xml
new file mode 100644
index 0000000..406a52e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_5_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_6_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_6_24dp.xml
new file mode 100644
index 0000000..cc63bac
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_6_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18V6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_7_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_7_24dp.xml
new file mode 100644
index 0000000..c254041
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brightness_7_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6zm0,-10c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_brush_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_brush_24dp.xml
new file mode 100644
index 0000000..3e58fe1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_brush_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,14c-1.66,0 -3,1.34 -3,3 0,1.31 -1.16,2 -2,2 0.92,1.22 2.49,2 4,2 2.21,0 4,-1.79 4,-4 0,-1.66 -1.34,-3 -3,-3zm13.71,-9.37l-1.34,-1.34c-0.39,-0.39 -1.02,-0.39 -1.41,0L9,12.25 11.75,15l8.96,-8.96c0.39,-0.39 0.39,-1.02 0,-1.41z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_24dp.xml
new file mode 100644
index 0000000..880b204
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9.4,10.5l4.77,-8.26C13.47,2.09 12.75,2 12,2c-2.4,0 -4.6,0.85 -6.32,2.25l3.66,6.35 0.06,-0.1zM21.54,9c-0.92,-2.92 -3.15,-5.26 -6,-6.34L11.88,9h9.66zm0.26,1h-7.49l0.29,0.5 4.76,8.25C21,16.97 22,14.61 22,12c0,-0.69 -0.07,-1.35 -0.2,-2zM8.54,12l-3.9,-6.75C3.01,7.03 2,9.39 2,12c0,0.69 0.07,1.35 0.2,2h7.49l-1.15,-2zm-6.08,3c0.92,2.92 3.15,5.26 6,6.34L12.12,15H2.46zm11.27,0l-3.9,6.76c0.7,0.15 1.42,0.24 2.17,0.24 2.4,0 4.6,-0.85 6.32,-2.25l-3.66,-6.35 -0.93,1.6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_alt_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_alt_24dp.xml
new file mode 100644
index 0000000..8404ea5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_alt_24dp.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,2L7.17,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2H9zm3,15c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_front_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_front_24dp.xml
new file mode 100644
index 0000000..3c55d50
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_front_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,20H5v2h5v2l3,-3 -3,-3v2zm4,0v2h5v-2h-5zM12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -1.99,0.9 -1.99,2S10.9,8 12,8zm5,-8H7C5.9,0 5,0.9 5,2v14c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V2c0,-1.1 -0.9,-2 -2,-2zM7,2h10v10.5c0,-1.67 -3.33,-2.5 -5,-2.5s-5,0.83 -5,2.5V2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_rear_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_rear_24dp.xml
new file mode 100644
index 0000000..397bbbd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_rear_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,20H5v2h5v2l3,-3 -3,-3v2zm4,0v2h5v-2h-5zm3,-20H7C5.9,0 5,0.9 5,2v14c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V2c0,-1.1 -0.9,-2 -2,-2zm-5,6c-1.11,0 -2,-0.9 -2,-2s0.89,-2 1.99,-2 2,0.9 2,2C14,5.1 13.1,6 12,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_roll_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_roll_24dp.xml
new file mode 100644
index 0000000..de29724
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_camera_roll_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14,5c0,-1.1 -0.9,-2 -2,-2h-1V2c0,-0.55 -0.45,-1 -1,-1H6c-0.55,0 -1,0.45 -1,1v1H4c-1.1,0 -2,0.9 -2,2v15c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2h8V5h-8zm-2,13h-2v-2h2v2zm0,-9h-2V7h2v2zm4,9h-2v-2h2v2zm0,-9h-2V7h2v2zm4,9h-2v-2h2v2zm0,-9h-2V7h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_center_focus_strong_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_center_focus_strong_24dp.xml
new file mode 100644
index 0000000..81c29e2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_center_focus_strong_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm-7,7H3v4c0,1.1 0.9,2 2,2h4v-2H5v-4zM5,5h4V3H5c-1.1,0 -2,0.9 -2,2v4h2V5zm14,-2h-4v2h4v4h2V5c0,-1.1 -0.9,-2 -2,-2zm0,16h-4v2h4c1.1,0 2,-0.9 2,-2v-4h-2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_center_focus_weak_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_center_focus_weak_24dp.xml
new file mode 100644
index 0000000..1a57582
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_center_focus_weak_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5,15H3v4c0,1.1 0.9,2 2,2h4v-2H5v-4zM5,5h4V3H5c-1.1,0 -2,0.9 -2,2v4h2V5zm14,-2h-4v2h4v4h2V5c0,-1.1 -0.9,-2 -2,-2zm0,16h-4v2h4c1.1,0 2,-0.9 2,-2v-4h-2v4zM12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm0,6c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_collections_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_collections_24dp.xml
new file mode 100644
index 0000000..bf9a0b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_collections_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,16V4c0,-1.1 -0.9,-2 -2,-2H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zm-11,-4l2.03,2.71L16,11l4,5H8l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2H4V6H2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_color_lens_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_color_lens_24dp.xml
new file mode 100644
index 0000000..42e5dc3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_color_lens_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9c0.83,0 1.5,-0.67 1.5,-1.5 0,-0.39 -0.15,-0.74 -0.39,-1.01 -0.23,-0.26 -0.38,-0.61 -0.38,-0.99 0,-0.83 0.67,-1.5 1.5,-1.5H16c2.76,0 5,-2.24 5,-5 0,-4.42 -4.03,-8 -9,-8zm-5.5,9c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,9 6.5,9 8,9.67 8,10.5 7.33,12 6.5,12zm3,-4C8.67,8 8,7.33 8,6.5S8.67,5 9.5,5s1.5,0.67 1.5,1.5S10.33,8 9.5,8zm5,0c-0.83,0 -1.5,-0.67 -1.5,-1.5S13.67,5 14.5,5s1.5,0.67 1.5,1.5S15.33,8 14.5,8zm3,4c-0.83,0 -1.5,-0.67 -1.5,-1.5S16.67,9 17.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_colorize_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_colorize_24dp.xml
new file mode 100644
index 0000000..1e95d1d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_colorize_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.71,5.63l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-3.12,3.12 -1.93,-1.91 -1.41,1.41 1.42,1.42L3,16.25V21h4.75l8.92,-8.92 1.42,1.42 1.41,-1.41 -1.92,-1.92 3.12,-3.12c0.4,-0.4 0.4,-1.03 0.01,-1.42zM6.92,19L5,17.08l8.06,-8.06 1.92,1.92L6.92,19z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_compare_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_compare_24dp.xml
new file mode 100644
index 0000000..cdd3c90
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_compare_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h5v2h2V1h-2v2zm0,15H5l5,-6v6zm9,-15h-5v2h5v13l-5,-6v9h5c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_control_point_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_control_point_24dp.xml
new file mode 100644
index 0000000..b378ea8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_control_point_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1,-5C6.49,2 2,6.49 2,12s4.49,10 10,10 10,-4.49 10,-10S17.51,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_control_point_duplicate_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_control_point_duplicate_24dp.xml
new file mode 100644
index 0000000..167b1f9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_control_point_duplicate_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,8h-2v3h-3v2h3v3h2v-3h3v-2h-3zM2,12c0,-2.79 1.64,-5.2 4.01,-6.32V3.52C2.52,4.76 0,8.09 0,12s2.52,7.24 6.01,8.48v-2.16C3.64,17.2 2,14.79 2,12zm13,-9c-4.96,0 -9,4.04 -9,9s4.04,9 9,9 9,-4.04 9,-9 -4.04,-9 -9,-9zm0,16c-3.86,0 -7,-3.14 -7,-7s3.14,-7 7,-7 7,3.14 7,7 -3.14,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_16_9_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_16_9_24dp.xml
new file mode 100644
index 0000000..3965d0a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_16_9_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,6H5c-1.1,0 -2,0.9 -2,2v8c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2zm0,10H5V8h14v8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_24dp.xml
new file mode 100644
index 0000000..9c3175a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,15h2V7c0,-1.1 -0.9,-2 -2,-2H9v2h8v8zM7,17V1H5v4H1v2h4v10c0,1.1 0.9,2 2,2h10v4h2v-4h4v-2H7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_3_2_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_3_2_24dp.xml
new file mode 100644
index 0000000..b78df03
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_3_2_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,4H5c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,14H5V6h14v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_5_4_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_5_4_24dp.xml
new file mode 100644
index 0000000..78bd307
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_5_4_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,5H5c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2zm0,12H5V7h14v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_7_5_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_7_5_24dp.xml
new file mode 100644
index 0000000..dd5b920
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_7_5_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,7H5c-1.1,0 -2,0.9 -2,2v6c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V9c0,-1.1 -0.9,-2 -2,-2zm0,8H5V9h14v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_din_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_din_24dp.xml
new file mode 100644
index 0000000..987f14c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_din_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V5h14v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_free_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_free_24dp.xml
new file mode 100644
index 0000000..6876754
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_free_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,5v4h2V5h4V3H5c-1.1,0 -2,0.9 -2,2zm2,10H3v4c0,1.1 0.9,2 2,2h4v-2H5v-4zm14,4h-4v2h4c1.1,0 2,-0.9 2,-2v-4h-2v4zm0,-16h-4v2h4v4h2V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_landscape_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_landscape_24dp.xml
new file mode 100644
index 0000000..78bd307
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_landscape_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,5H5c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V7c0,-1.1 -0.9,-2 -2,-2zm0,12H5V7h14v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_original_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_original_24dp.xml
new file mode 100644
index 0000000..d789be6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_original_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V5h14v14zm-5.04,-6.71l-2.75,3.54 -1.96,-2.36L6.5,17h11l-3.54,-4.71z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_portrait_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_portrait_24dp.xml
new file mode 100644
index 0000000..1e0e6bd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_portrait_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,3H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H7V5h10v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_square_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_square_24dp.xml
new file mode 100644
index 0000000..2cef016
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_crop_square_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,4H6c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,14H6V6h12v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_dehaze_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_dehaze_24dp.xml
new file mode 100644
index 0000000..88956e4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_dehaze_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2,15.5v2h20v-2H2zm0,-5v2h20v-2H2zm0,-5v2h20v-2H2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_details_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_details_24dp.xml
new file mode 100644
index 0000000..931e15e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_details_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,4l9,16 9,-16H3zm3.38,2h11.25L12,16 6.38,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_edit_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_edit_24dp.xml
new file mode 100644
index 0000000..0ff336a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_edit_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_24dp.xml
new file mode 100644
index 0000000..b40b8bd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,17v2h2v-2h2v-2h-2v-2h-2v2h-2v2h2zm5,-15H4c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM5,5h6v2H5V5zm15,15H4L20,4v16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_minus_1_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_minus_1_24dp.xml
new file mode 100644
index 0000000..bb35968
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_minus_1_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,11v2h8v-2H4zm15,7h-2V7.38L14,8.4V6.7L18.7,5h0.3v13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_minus_2_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_minus_2_24dp.xml
new file mode 100644
index 0000000..b434d4b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_minus_2_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.05,16.29l2.86,-3.07c0.38,-0.39 0.72,-0.79 1.04,-1.18 0.32,-0.39 0.59,-0.78 0.82,-1.17 0.23,-0.39 0.41,-0.78 0.54,-1.17s0.19,-0.79 0.19,-1.18c0,-0.53 -0.09,-1.02 -0.27,-1.46 -0.18,-0.44 -0.44,-0.81 -0.78,-1.11 -0.34,-0.31 -0.77,-0.54 -1.26,-0.71 -0.51,-0.16 -1.08,-0.24 -1.72,-0.24 -0.69,0 -1.31,0.11 -1.85,0.32 -0.54,0.21 -1,0.51 -1.36,0.88 -0.37,0.37 -0.65,0.8 -0.84,1.3 -0.18,0.47 -0.27,0.97 -0.28,1.5h2.14c0.01,-0.31 0.05,-0.6 0.13,-0.87 0.09,-0.29 0.23,-0.54 0.4,-0.75 0.18,-0.21 0.41,-0.37 0.68,-0.49 0.27,-0.12 0.6,-0.18 0.96,-0.18 0.31,0 0.58,0.05 0.81,0.15 0.23,0.1 0.43,0.25 0.59,0.43 0.16,0.18 0.28,0.4 0.37,0.65 0.08,0.25 0.13,0.52 0.13,0.81 0,0.22 -0.03,0.43 -0.08,0.65 -0.06,0.22 -0.15,0.45 -0.29,0.7 -0.14,0.25 -0.32,0.53 -0.56,0.83 -0.23,0.3 -0.52,0.65 -0.88,1.03l-4.17,4.55V18H21v-1.71h-5.95zM2,11v2h8v-2H2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_plus_1_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_plus_1_24dp.xml
new file mode 100644
index 0000000..3ba74f7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_plus_1_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,7H8v4H4v2h4v4h2v-4h4v-2h-4V7zm10,11h-2V7.38L15,8.4V6.7L19.7,5h0.3v13z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_plus_2_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_plus_2_24dp.xml
new file mode 100644
index 0000000..97d7424
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_plus_2_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.05,16.29l2.86,-3.07c0.38,-0.39 0.72,-0.79 1.04,-1.18 0.32,-0.39 0.59,-0.78 0.82,-1.17 0.23,-0.39 0.41,-0.78 0.54,-1.17 0.13,-0.39 0.19,-0.79 0.19,-1.18 0,-0.53 -0.09,-1.02 -0.27,-1.46 -0.18,-0.44 -0.44,-0.81 -0.78,-1.11 -0.34,-0.31 -0.77,-0.54 -1.26,-0.71 -0.51,-0.16 -1.08,-0.24 -1.72,-0.24 -0.69,0 -1.31,0.11 -1.85,0.32 -0.54,0.21 -1,0.51 -1.36,0.88 -0.37,0.37 -0.65,0.8 -0.84,1.3 -0.18,0.47 -0.27,0.97 -0.28,1.5h2.14c0.01,-0.31 0.05,-0.6 0.13,-0.87 0.09,-0.29 0.23,-0.54 0.4,-0.75 0.18,-0.21 0.41,-0.37 0.68,-0.49 0.27,-0.12 0.6,-0.18 0.96,-0.18 0.31,0 0.58,0.05 0.81,0.15 0.23,0.1 0.43,0.25 0.59,0.43 0.16,0.18 0.28,0.4 0.37,0.65 0.08,0.25 0.13,0.52 0.13,0.81 0,0.22 -0.03,0.43 -0.08,0.65 -0.06,0.22 -0.15,0.45 -0.29,0.7 -0.14,0.25 -0.32,0.53 -0.56,0.83 -0.23,0.3 -0.52,0.65 -0.88,1.03l-4.17,4.55V18H22v-1.71h-5.95zM8,7H6v4H2v2h4v4h2v-4h4v-2H8V7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_zero_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_zero_24dp.xml
new file mode 100644
index 0000000..8ae50ac
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_exposure_zero_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.14,12.5c0,1 -0.1,1.85 -0.3,2.55 -0.2,0.7 -0.48,1.27 -0.83,1.7 -0.36,0.44 -0.79,0.75 -1.3,0.95 -0.51,0.2 -1.07,0.3 -1.7,0.3 -0.62,0 -1.18,-0.1 -1.69,-0.3 -0.51,-0.2 -0.95,-0.51 -1.31,-0.95 -0.36,-0.44 -0.65,-1.01 -0.85,-1.7 -0.2,-0.7 -0.3,-1.55 -0.3,-2.55v-2.04c0,-1 0.1,-1.85 0.3,-2.55 0.2,-0.7 0.48,-1.26 0.84,-1.69 0.36,-0.43 0.8,-0.74 1.31,-0.93C10.81,5.1 11.38,5 12,5c0.63,0 1.19,0.1 1.7,0.29 0.51,0.19 0.95,0.5 1.31,0.93 0.36,0.43 0.64,0.99 0.84,1.69 0.2,0.7 0.3,1.54 0.3,2.55v2.04zm-2.11,-2.36c0,-0.64 -0.05,-1.18 -0.13,-1.62 -0.09,-0.44 -0.22,-0.79 -0.4,-1.06 -0.17,-0.27 -0.39,-0.46 -0.64,-0.58 -0.25,-0.13 -0.54,-0.19 -0.86,-0.19 -0.32,0 -0.61,0.06 -0.86,0.18s-0.47,0.31 -0.64,0.58c-0.17,0.27 -0.31,0.62 -0.4,1.06s-0.13,0.98 -0.13,1.62v2.67c0,0.64 0.05,1.18 0.14,1.62 0.09,0.45 0.23,0.81 0.4,1.09s0.39,0.48 0.64,0.61 0.54,0.19 0.87,0.19c0.33,0 0.62,-0.06 0.87,-0.19s0.46,-0.33 0.63,-0.61c0.17,-0.28 0.3,-0.64 0.39,-1.09 0.09,-0.45 0.13,-0.99 0.13,-1.62v-2.66z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_1_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_1_24dp.xml
new file mode 100644
index 0000000..e108b9e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_1_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm11,10h2V5h-4v2h2v8zm7,-14H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_24dp.xml
new file mode 100644
index 0000000..667505c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.96,10.29l-2.75,3.54 -1.96,-2.36L8.5,15h11l-3.54,-4.71zM3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_2_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_2_24dp.xml
new file mode 100644
index 0000000..957c4b6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_2_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14zm-4,-4h-4v-2h2c1.1,0 2,-0.89 2,-2V7c0,-1.11 -0.9,-2 -2,-2h-4v2h4v2h-2c-1.1,0 -2,0.89 -2,2v4h6v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_3_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_3_24dp.xml
new file mode 100644
index 0000000..aaf5f01
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_3_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,1H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14zM3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm14,8v-1.5c0,-0.83 -0.67,-1.5 -1.5,-1.5 0.83,0 1.5,-0.67 1.5,-1.5V7c0,-1.11 -0.9,-2 -2,-2h-4v2h4v2h-2v2h2v2h-4v2h4c1.1,0 2,-0.89 2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_4_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_4_24dp.xml
new file mode 100644
index 0000000..86e0431
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_4_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm12,10h2V5h-2v4h-2V5h-2v6h4v4zm6,-14H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_5_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_5_24dp.xml
new file mode 100644
index 0000000..5e6a677
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_5_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,1H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14zM3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm14,8v-2c0,-1.11 -0.9,-2 -2,-2h-2V7h4V5h-6v6h4v2h-4v2h4c1.1,0 2,-0.89 2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_6_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_6_24dp.xml
new file mode 100644
index 0000000..c347253
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_6_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14zm-8,-2h2c1.1,0 2,-0.89 2,-2v-2c0,-1.11 -0.9,-2 -2,-2h-2V7h4V5h-4c-1.1,0 -2,0.89 -2,2v6c0,1.11 0.9,2 2,2zm0,-4h2v2h-2v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_7_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_7_24dp.xml
new file mode 100644
index 0000000..0045c2c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_7_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14zm-8,-2l4,-8V5h-6v2h4l-4,8h2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_8_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_8_24dp.xml
new file mode 100644
index 0000000..8916519
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_8_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14zm-8,-2h2c1.1,0 2,-0.89 2,-2v-1.5c0,-0.83 -0.67,-1.5 -1.5,-1.5 0.83,0 1.5,-0.67 1.5,-1.5V7c0,-1.11 -0.9,-2 -2,-2h-2c-1.1,0 -2,0.89 -2,2v1.5c0,0.83 0.67,1.5 1.5,1.5 -0.83,0 -1.5,0.67 -1.5,1.5V13c0,1.11 0.9,2 2,2zm0,-8h2v2h-2V7zm0,4h2v2h-2v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_9_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_9_24dp.xml
new file mode 100644
index 0000000..670eb1b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_9_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14zM15,5h-2c-1.1,0 -2,0.89 -2,2v2c0,1.11 0.9,2 2,2h2v2h-4v2h4c1.1,0 2,-0.89 2,-2V7c0,-1.11 -0.9,-2 -2,-2zm0,4h-2V7h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_9_plus_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_9_plus_24dp.xml
new file mode 100644
index 0000000..8dc1f04
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_9_plus_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm11,7V8c0,-1.11 -0.9,-2 -2,-2h-1c-1.1,0 -2,0.89 -2,2v1c0,1.11 0.9,2 2,2h1v1H9v2h3c1.1,0 2,-0.89 2,-2zm-3,-3V8h1v1h-1zm10,-8H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,8h-2V7h-2v2h-2v2h2v2h2v-2h2v6H7V3h14v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_b_and_w_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_b_and_w_24dp.xml
new file mode 100644
index 0000000..0fdc812
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_b_and_w_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16l-7,-8v8H5l7,-8V5h7v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_center_focus_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_center_focus_24dp.xml
new file mode 100644
index 0000000..8a185cd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_center_focus_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5,15H3v4c0,1.1 0.9,2 2,2h4v-2H5v-4zM5,5h4V3H5c-1.1,0 -2,0.9 -2,2v4h2V5zm14,-2h-4v2h4v4h2V5c0,-1.1 -0.9,-2 -2,-2zm0,16h-4v2h4c1.1,0 2,-0.9 2,-2v-4h-2v4zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_drama_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_drama_24dp.xml
new file mode 100644
index 0000000..695c11b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_drama_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.61,5.64 5.36,8.04 2.35,8.36 0,10.9 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM19,18H6c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4h2c0,-2.76 -1.86,-5.08 -4.4,-5.78C8.61,6.88 10.2,6 12,6c3.03,0 5.5,2.47 5.5,5.5v0.5H19c1.65,0 3,1.35 3,3s-1.35,3 -3,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_frames_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_frames_24dp.xml
new file mode 100644
index 0000000..ffcab6a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_frames_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,4h-4l-4,-4 -4,4H4c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,16H4V6h4.52l3.52,-3.5L15.52,6H20v14zM18,8H6v10h12"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_hdr_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_hdr_24dp.xml
new file mode 100644
index 0000000..c3ad94b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_hdr_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14,6l-3.75,5 2.85,3.8 -1.6,1.2C9.81,13.75 7,10 7,10l-6,8h22L14,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_none_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_none_24dp.xml
new file mode 100644
index 0000000..b1d1542
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_none_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,5H1v16c0,1.1 0.9,2 2,2h16v-2H3V5zm18,-4H7c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2zm0,16H7V3h14v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_tilt_shift_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_tilt_shift_24dp.xml
new file mode 100644
index 0000000..7b4116a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_tilt_shift_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,4.07V2.05c-2.01,0.2 -3.84,1 -5.32,2.21L7.1,5.69c1.11,-0.86 2.44,-1.44 3.9,-1.62zm7.32,0.19C16.84,3.05 15.01,2.25 13,2.05v2.02c1.46,0.18 2.79,0.76 3.9,1.62l1.42,-1.43zM19.93,11h2.02c-0.2,-2.01 -1,-3.84 -2.21,-5.32L18.31,7.1c0.86,1.11 1.44,2.44 1.62,3.9zM5.69,7.1L4.26,5.68C3.05,7.16 2.25,8.99 2.05,11h2.02c0.18,-1.46 0.76,-2.79 1.62,-3.9zM4.07,13H2.05c0.2,2.01 1,3.84 2.21,5.32l1.43,-1.43c-0.86,-1.1 -1.44,-2.43 -1.62,-3.89zM15,12c0,-1.66 -1.34,-3 -3,-3s-3,1.34 -3,3 1.34,3 3,3 3,-1.34 3,-3zm3.31,4.9l1.43,1.43c1.21,-1.48 2.01,-3.32 2.21,-5.32h-2.02c-0.18,1.45 -0.76,2.78 -1.62,3.89zM13,19.93v2.02c2.01,-0.2 3.84,-1 5.32,-2.21l-1.43,-1.43c-1.1,0.86 -2.43,1.44 -3.89,1.62zm-7.32,-0.19C7.16,20.95 9,21.75 11,21.95v-2.02c-1.46,-0.18 -2.79,-0.76 -3.9,-1.62l-1.42,1.43z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_vintage_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_vintage_24dp.xml
new file mode 100644
index 0000000..1fdbd6a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_filter_vintage_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.7,12.4c-0.28,-0.16 -0.57,-0.29 -0.86,-0.4 0.29,-0.11 0.58,-0.24 0.86,-0.4 1.92,-1.11 2.99,-3.12 3,-5.19 -1.79,-1.03 -4.07,-1.11 -6,0 -0.28,0.16 -0.54,0.35 -0.78,0.54 0.05,-0.31 0.08,-0.63 0.08,-0.95 0,-2.22 -1.21,-4.15 -3,-5.19C10.21,1.85 9,3.78 9,6c0,0.32 0.03,0.64 0.08,0.95 -0.24,-0.2 -0.5,-0.39 -0.78,-0.55 -1.92,-1.11 -4.2,-1.03 -6,0 0,2.07 1.07,4.08 3,5.19 0.28,0.16 0.57,0.29 0.86,0.4 -0.29,0.11 -0.58,0.24 -0.86,0.4 -1.92,1.11 -2.99,3.12 -3,5.19 1.79,1.03 4.07,1.11 6,0 0.28,-0.16 0.54,-0.35 0.78,-0.54 -0.05,0.32 -0.08,0.64 -0.08,0.96 0,2.22 1.21,4.15 3,5.19 1.79,-1.04 3,-2.97 3,-5.19 0,-0.32 -0.03,-0.64 -0.08,-0.95 0.24,0.2 0.5,0.38 0.78,0.54 1.92,1.11 4.2,1.03 6,0 -0.01,-2.07 -1.08,-4.08 -3,-5.19zM12,16c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_flare_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_flare_24dp.xml
new file mode 100644
index 0000000..9827247
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_flare_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,11H1v2h6v-2zm2.17,-3.24L7.05,5.64 5.64,7.05l2.12,2.12 1.41,-1.41zM13,1h-2v6h2V1zm5.36,6.05l-1.41,-1.41 -2.12,2.12 1.41,1.41 2.12,-2.12zM17,11v2h6v-2h-6zm-5,-2c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zm2.83,7.24l2.12,2.12 1.41,-1.41 -2.12,-2.12 -1.41,1.41zm-9.19,0.71l1.41,1.41 2.12,-2.12 -1.41,-1.41 -2.12,2.12zM11,23h2v-6h-2v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_auto_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_auto_24dp.xml
new file mode 100644
index 0000000..e422d4c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_auto_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,2v12h3v9l7,-12H9l4,-9H3zm16,0h-2l-3.2,9h1.9l0.7,-2h3.2l0.7,2h1.9L19,2zm-2.15,5.65L18,4l1.15,3.65h-2.3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_off_24dp.xml
new file mode 100644
index 0000000..04d719e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3.27,3L2,4.27l5,5V13h3v9l3.58,-6.14L17.73,20 19,18.73 3.27,3zM17,10h-4l4,-8H7v2.18l8.46,8.46L17,10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_on_24dp.xml
new file mode 100644
index 0000000..8bf924f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_flash_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,2v11h3v9l7,-12h-4l4,-8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_flip_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_flip_24dp.xml
new file mode 100644
index 0000000..ef0bbde
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_flip_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,21h2v-2h-2v2zm4,-12h2V7h-2v2zM3,5v14c0,1.1 0.9,2 2,2h4v-2H5V5h4V3H5c-1.1,0 -2,0.9 -2,2zm16,-2v2h2c0,-1.1 -0.9,-2 -2,-2zm-8,20h2V1h-2v22zm8,-6h2v-2h-2v2zM15,5h2V3h-2v2zm4,8h2v-2h-2v2zm0,8c1.1,0 2,-0.9 2,-2h-2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_gradient_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_gradient_24dp.xml
new file mode 100644
index 0000000..2d9fb13
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_gradient_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,9h2v2h-2zm-2,2h2v2H9zm4,0h2v2h-2zm2,-2h2v2h-2zM7,9h2v2H7zm12,-6H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM9,18H7v-2h2v2zm4,0h-2v-2h2v2zm4,0h-2v-2h2v2zm2,-7h-2v2h2v2h-2v-2h-2v2h-2v-2h-2v2H9v-2H7v2H5v-2h2v-2H5V5h14v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_grain_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_grain_24dp.xml
new file mode 100644
index 0000000..185e80d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_grain_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,12c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM6,8c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm0,8c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm12,-8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zm-4,8c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm4,-4c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm-4,-4c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm-4,-4c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_grid_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_grid_off_24dp.xml
new file mode 100644
index 0000000..1ab4ca5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_grid_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8,4v1.45l2,2V4h4v4h-3.45l2,2H14v1.45l2,2V10h4v4h-3.45l2,2H20v1.45l2,2V4c0,-1.1 -0.9,-2 -2,-2H4.55l2,2H8zm8,0h4v4h-4V4zM1.27,1.27L0,2.55l2,2V20c0,1.1 0.9,2 2,2h15.46l2,2 1.27,-1.27L1.27,1.27zM10,12.55L11.45,14H10v-1.45zm-6,-6L5.45,8H4V6.55zM8,20H4v-4h4v4zm0,-6H4v-4h3.45l0.55,0.55V14zm6,6h-4v-4h3.45l0.55,0.54V20zm2,0v-1.46L17.46,20H16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_grid_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_grid_on_24dp.xml
new file mode 100644
index 0000000..6cc5c86
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_grid_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM8,20H4v-4h4v4zm0,-6H4v-4h4v4zm0,-6H4V4h4v4zm6,12h-4v-4h4v4zm0,-6h-4v-4h4v4zm0,-6h-4V4h4v4zm6,12h-4v-4h4v4zm0,-6h-4v-4h4v4zm0,-6h-4V4h4v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_off_24dp.xml
new file mode 100644
index 0000000..dc47ada
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,17L3.27,2.27 2,3.55l4,4V11H4V7H2v10h2v-4h2v4h2V9.55l1,1V17h4c0.67,0 1.26,-0.33 1.62,-0.84l6.34,6.34 1.27,-1.27L18,17zm-5,-2h-2v-2.45l2,2V15zm5,-2h1l0.82,3.27 0.73,0.73H22l-1.19,-4.17c0.7,-0.31 1.19,-1.01 1.19,-1.83V9c0,-1.1 -0.9,-2 -2,-2h-4v5.45l2,2V13zm0,-4h2v2h-2V9zm-3,2.45V9c0,-1.1 -0.9,-2 -2,-2h-2.45L15,11.45z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_on_24dp.xml
new file mode 100644
index 0000000..891f5bd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,11H4V7H2v10h2v-4h2v4h2V7H6v4zm7,-4H9v10h4c1.1,0 2,-0.9 2,-2V9c0,-1.1 -0.9,-2 -2,-2zm0,8h-2V9h2v6zm9,-4V9c0,-1.1 -0.9,-2 -2,-2h-4v10h2v-4h1l1,4h2l-1.19,-4.17c0.7,-0.31 1.19,-1.01 1.19,-1.83zm-2,0h-2V9h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_strong_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_strong_24dp.xml
new file mode 100644
index 0000000..c37d1dc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_strong_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6 6,-2.69 6,-6 -2.69,-6 -6,-6zM5,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm0,6c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_weak_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_weak_24dp.xml
new file mode 100644
index 0000000..bd64aee
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_hdr_weak_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm12,-2c-3.31,0 -6,2.69 -6,6s2.69,6 6,6 6,-2.69 6,-6 -2.69,-6 -6,-6zm0,10c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_healing_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_healing_24dp.xml
new file mode 100644
index 0000000..a3e5418
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_healing_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.73,12.02l3.98,-3.98c0.39,-0.39 0.39,-1.02 0,-1.41l-4.34,-4.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-3.98,3.98L8,2.29C7.8,2.1 7.55,2 7.29,2c-0.25,0 -0.51,0.1 -0.7,0.29L2.25,6.63c-0.39,0.39 -0.39,1.02 0,1.41l3.98,3.98L2.25,16c-0.39,0.39 -0.39,1.02 0,1.41l4.34,4.34c0.39,0.39 1.02,0.39 1.41,0l3.98,-3.98 3.98,3.98c0.2,0.2 0.45,0.29 0.71,0.29 0.26,0 0.51,-0.1 0.71,-0.29l4.34,-4.34c0.39,-0.39 0.39,-1.02 0,-1.41l-3.99,-3.98zM12,9c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm-4.71,1.96L3.66,7.34l3.63,-3.63 3.62,3.62 -3.62,3.63zM10,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zm2,2c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zm2,-4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm2.66,9.34l-3.63,-3.62 3.63,-3.63 3.62,3.62 -3.62,3.63z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_image_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_image_24dp.xml
new file mode 100644
index 0000000..61dcb63
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_image_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_image_aspect_ratio_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_image_aspect_ratio_24dp.xml
new file mode 100644
index 0000000..a59eb87
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_image_aspect_ratio_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,10h-2v2h2v-2zm0,4h-2v2h2v-2zm-8,-4H6v2h2v-2zm4,0h-2v2h2v-2zm8,-6H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,14H4V6h16v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_iso_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_iso_24dp.xml
new file mode 100644
index 0000000..33fffdc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_iso_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM5.5,7.5h2v-2H9v2h2V9H9v2H7.5V9h-2V7.5zM19,19H5L19,5v14zm-2,-2v-1.5h-5V17h5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_landscape_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_landscape_24dp.xml
new file mode 100644
index 0000000..c3ad94b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_landscape_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14,6l-3.75,5 2.85,3.8 -1.6,1.2C9.81,13.75 7,10 7,10l-6,8h22L14,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_leak_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_leak_add_24dp.xml
new file mode 100644
index 0000000..f6ada4e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_leak_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,3H3v3c1.66,0 3,-1.34 3,-3zm8,0h-2c0,4.97 -4.03,9 -9,9v2c6.08,0 11,-4.93 11,-11zm-4,0H8c0,2.76 -2.24,5 -5,5v2c3.87,0 7,-3.13 7,-7zm0,18h2c0,-4.97 4.03,-9 9,-9v-2c-6.07,0 -11,4.93 -11,11zm8,0h3v-3c-1.66,0 -3,1.34 -3,3zm-4,0h2c0,-2.76 2.24,-5 5,-5v-2c-3.87,0 -7,3.13 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_leak_remove_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_leak_remove_24dp.xml
new file mode 100644
index 0000000..1612b52
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_leak_remove_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,3H8c0,0.37 -0.04,0.72 -0.12,1.06l1.59,1.59C9.81,4.84 10,3.94 10,3zM3,4.27l2.84,2.84C5.03,7.67 4.06,8 3,8v2c1.61,0 3.09,-0.55 4.27,-1.46L8.7,9.97C7.14,11.24 5.16,12 3,12v2c2.71,0 5.19,-0.99 7.11,-2.62l2.5,2.5C10.99,15.81 10,18.29 10,21h2c0,-2.16 0.76,-4.14 2.03,-5.69l1.43,1.43C14.55,17.91 14,19.39 14,21h2c0,-1.06 0.33,-2.03 0.89,-2.84L19.73,21 21,19.73 4.27,3 3,4.27zM14,3h-2c0,1.5 -0.37,2.91 -1.02,4.16l1.46,1.46C13.42,6.98 14,5.06 14,3zm5.94,13.12c0.34,-0.08 0.69,-0.12 1.06,-0.12v-2c-0.94,0 -1.84,0.19 -2.66,0.52l1.6,1.6zm-4.56,-4.56l1.46,1.46C18.09,12.37 19.5,12 21,12v-2c-2.06,0 -3.98,0.58 -5.62,1.56z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_lens_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_lens_24dp.xml
new file mode 100644
index 0000000..5093856
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_lens_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_24dp.xml
new file mode 100644
index 0000000..e242ded
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,10c-3.86,0 -7,3.14 -7,7h2c0,-2.76 2.24,-5 5,-5s5,2.24 5,5h2c0,-3.86 -3.14,-7 -7,-7zm0,-4C5.93,6 1,10.93 1,17h2c0,-4.96 4.04,-9 9,-9s9,4.04 9,9h2c0,-6.07 -4.93,-11 -11,-11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_3_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_3_24dp.xml
new file mode 100644
index 0000000..d7c35c9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_3_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.01,3h-14c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-4,7.5c0,0.83 -0.67,1.5 -1.5,1.5 0.83,0 1.5,0.67 1.5,1.5V15c0,1.11 -0.9,2 -2,2h-4v-2h4v-2h-2v-2h2V9h-4V7h4c1.1,0 2,0.89 2,2v1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_4_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_4_24dp.xml
new file mode 100644
index 0000000..d35d6b2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_4_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-4,14h-2v-4H9V7h2v4h2V7h2v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_5_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_5_24dp.xml
new file mode 100644
index 0000000..a19ee0b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_5_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-4,6h-4v2h2c1.1,0 2,0.89 2,2v2c0,1.11 -0.9,2 -2,2H9v-2h4v-2H9V7h6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_6_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_6_24dp.xml
new file mode 100644
index 0000000..94eeb9c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_6_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,15h2v-2h-2v2zm8,-12H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-4,6h-4v2h2c1.1,0 2,0.89 2,2v2c0,1.11 -0.9,2 -2,2h-2c-1.1,0 -2,-0.89 -2,-2V9c0,-1.11 0.9,-2 2,-2h4v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_one_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_one_24dp.xml
new file mode 100644
index 0000000..1b731ab
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_one_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-5,14h-2V9h-2V7h4v10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_two_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_two_24dp.xml
new file mode 100644
index 0000000..96d3dfd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_looks_two_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-4,8c0,1.11 -0.9,2 -2,2h-2v2h4v2H9v-4c0,-1.11 0.9,-2 2,-2h2V9H9V7h4c1.1,0 2,0.89 2,2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_loupe_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_loupe_24dp.xml
new file mode 100644
index 0000000..7677b3b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_loupe_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1,-5C6.49,2 2,6.49 2,12s4.49,10 10,10h8c1.1,0 2,-0.9 2,-2v-8c0,-5.51 -4.49,-10 -10,-10zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_movie_creation_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_movie_creation_24dp.xml
new file mode 100644
index 0000000..6d4c52e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_movie_creation_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,4l2,4h-3l-2,-4h-2l2,4h-3l-2,-4H8l2,4H7L5,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4h-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_nature_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_nature_24dp.xml
new file mode 100644
index 0000000..6239dbe
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_nature_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,16.12c3.47,-0.41 6.17,-3.36 6.17,-6.95 0,-3.87 -3.13,-7 -7,-7s-7,3.13 -7,7c0,3.47 2.52,6.34 5.83,6.89V20H5v2h14v-2h-6v-3.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_nature_people_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_nature_people_24dp.xml
new file mode 100644
index 0000000..caeb451
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_nature_people_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22.17,9.17c0,-3.87 -3.13,-7 -7,-7s-7,3.13 -7,7c0,3.47 2.52,6.34 5.83,6.89V20H6v-3h1v-4c0,-0.55 -0.45,-1 -1,-1H3c-0.55,0 -1,0.45 -1,1v4h1v5h16v-2h-3v-3.88c3.47,-0.41 6.17,-3.36 6.17,-6.95zM4.5,11c0.83,0 1.5,-0.67 1.5,-1.5S5.33,8 4.5,8 3,8.67 3,9.5 3.67,11 4.5,11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_navigate_before_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_navigate_before_24dp.xml
new file mode 100644
index 0000000..ed84de6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_navigate_before_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_navigate_next_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_navigate_next_24dp.xml
new file mode 100644
index 0000000..2ff2653
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_navigate_next_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_palette_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_palette_24dp.xml
new file mode 100644
index 0000000..42e5dc3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_palette_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9c0.83,0 1.5,-0.67 1.5,-1.5 0,-0.39 -0.15,-0.74 -0.39,-1.01 -0.23,-0.26 -0.38,-0.61 -0.38,-0.99 0,-0.83 0.67,-1.5 1.5,-1.5H16c2.76,0 5,-2.24 5,-5 0,-4.42 -4.03,-8 -9,-8zm-5.5,9c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,9 6.5,9 8,9.67 8,10.5 7.33,12 6.5,12zm3,-4C8.67,8 8,7.33 8,6.5S8.67,5 9.5,5s1.5,0.67 1.5,1.5S10.33,8 9.5,8zm5,0c-0.83,0 -1.5,-0.67 -1.5,-1.5S13.67,5 14.5,5s1.5,0.67 1.5,1.5S15.33,8 14.5,8zm3,4c-0.83,0 -1.5,-0.67 -1.5,-1.5S16.67,9 17.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_24dp.xml
new file mode 100644
index 0000000..297abbd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M23,18V6c0,-1.1 -0.9,-2 -2,-2H3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2zM8.5,12.5l2.5,3.01L14.5,11l4.5,6H5l3.5,-4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_fisheye_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_fisheye_24dp.xml
new file mode 100644
index 0000000..671946f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_fisheye_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zm0,18c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_horizontal_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_horizontal_24dp.xml
new file mode 100644
index 0000000..7fe1598
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_horizontal_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,6.54v10.91c-2.6,-0.77 -5.28,-1.16 -8,-1.16 -2.72,0 -5.4,0.39 -8,1.16V6.54c2.6,0.77 5.28,1.16 8,1.16 2.72,0.01 5.4,-0.38 8,-1.16M21.43,4c-0.1,0 -0.2,0.02 -0.31,0.06C18.18,5.16 15.09,5.7 12,5.7c-3.09,0 -6.18,-0.55 -9.12,-1.64 -0.11,-0.04 -0.22,-0.06 -0.31,-0.06 -0.34,0 -0.57,0.23 -0.57,0.63v14.75c0,0.39 0.23,0.62 0.57,0.62 0.1,0 0.2,-0.02 0.31,-0.06 2.94,-1.1 6.03,-1.64 9.12,-1.64 3.09,0 6.18,0.55 9.12,1.64 0.11,0.04 0.21,0.06 0.31,0.06 0.33,0 0.57,-0.23 0.57,-0.63V4.63c0,-0.4 -0.24,-0.63 -0.57,-0.63z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_vertical_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_vertical_24dp.xml
new file mode 100644
index 0000000..f5282ec
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_vertical_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.94,21.12c-1.1,-2.94 -1.64,-6.03 -1.64,-9.12 0,-3.09 0.55,-6.18 1.64,-9.12 0.04,-0.11 0.06,-0.22 0.06,-0.31 0,-0.34 -0.23,-0.57 -0.63,-0.57H4.63c-0.4,0 -0.63,0.23 -0.63,0.57 0,0.1 0.02,0.2 0.06,0.31C5.16,5.82 5.71,8.91 5.71,12c0,3.09 -0.55,6.18 -1.64,9.12 -0.05,0.11 -0.07,0.22 -0.07,0.31 0,0.33 0.23,0.57 0.63,0.57h14.75c0.39,0 0.63,-0.24 0.63,-0.57 -0.01,-0.1 -0.03,-0.2 -0.07,-0.31zM6.54,20c0.77,-2.6 1.16,-5.28 1.16,-8 0,-2.72 -0.39,-5.4 -1.16,-8h10.91c-0.77,2.6 -1.16,5.28 -1.16,8 0,2.72 0.39,5.4 1.16,8H6.54z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_wide_angle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_wide_angle_24dp.xml
new file mode 100644
index 0000000..eebf829
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_panorama_wide_angle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,6c2.45,0 4.71,0.2 7.29,0.64 0.47,1.78 0.71,3.58 0.71,5.36 0,1.78 -0.24,3.58 -0.71,5.36 -2.58,0.44 -4.84,0.64 -7.29,0.64s-4.71,-0.2 -7.29,-0.64C4.24,15.58 4,13.78 4,12c0,-1.78 0.24,-3.58 0.71,-5.36C7.29,6.2 9.55,6 12,6m0,-2c-2.73,0 -5.22,0.24 -7.95,0.72l-0.93,0.16 -0.25,0.9C2.29,7.85 2,9.93 2,12s0.29,4.15 0.87,6.22l0.25,0.89 0.93,0.16c2.73,0.49 5.22,0.73 7.95,0.73s5.22,-0.24 7.95,-0.72l0.93,-0.16 0.25,-0.89c0.58,-2.08 0.87,-4.16 0.87,-6.23s-0.29,-4.15 -0.87,-6.22l-0.25,-0.89 -0.93,-0.16C17.22,4.24 14.73,4 12,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_24dp.xml
new file mode 100644
index 0000000..61dcb63
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_album_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_album_24dp.xml
new file mode 100644
index 0000000..f8ff7dc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_album_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,2H6c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,4h5v8l-2.5,-1.5L6,12V4zm0,15l3,-3.86 2.14,2.58 3,-3.86L18,19H6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_camera_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_camera_24dp.xml
new file mode 100644
index 0000000..8404ea5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_camera_24dp.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,2L7.17,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2H9zm3,15c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_library_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_library_24dp.xml
new file mode 100644
index 0000000..bf9a0b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_photo_library_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,16V4c0,-1.1 -0.9,-2 -2,-2H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zm-11,-4l2.03,2.71L16,11l4,5H8l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2H4V6H2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_portrait_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_portrait_24dp.xml
new file mode 100644
index 0000000..dd73aee
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_portrait_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,12.25c1.24,0 2.25,-1.01 2.25,-2.25S13.24,7.75 12,7.75 9.75,8.76 9.75,10s1.01,2.25 2.25,2.25zm4.5,4c0,-1.5 -3,-2.25 -4.5,-2.25s-4.5,0.75 -4.5,2.25V17h9v-0.75zM19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V5h14v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_remove_red_eye_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_remove_red_eye_24dp.xml
new file mode 100644
index 0000000..46a571c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_remove_red_eye_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zm0,-8c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_rotate_left_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_rotate_left_24dp.xml
new file mode 100644
index 0000000..3f0a0eb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_rotate_left_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.11,8.53L5.7,7.11C4.8,8.27 4.24,9.61 4.07,11h2.02c0.14,-0.87 0.49,-1.72 1.02,-2.47zM6.09,13H4.07c0.17,1.39 0.72,2.73 1.62,3.89l1.41,-1.42c-0.52,-0.75 -0.87,-1.59 -1.01,-2.47zm1.01,5.32c1.16,0.9 2.51,1.44 3.9,1.61V17.9c-0.87,-0.15 -1.71,-0.49 -2.46,-1.03L7.1,18.32zM13,4.07V1L8.45,5.55 13,10V6.09c2.84,0.48 5,2.94 5,5.91s-2.16,5.43 -5,5.91v2.02c3.95,-0.49 7,-3.85 7,-7.93s-3.05,-7.44 -7,-7.93z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_rotate_right_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_rotate_right_24dp.xml
new file mode 100644
index 0000000..d45fea1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_rotate_right_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.55,5.55L11,1v3.07C7.06,4.56 4,7.92 4,12s3.05,7.44 7,7.93v-2.02c-2.84,-0.48 -5,-2.94 -5,-5.91s2.16,-5.43 5,-5.91V10l4.55,-4.45zM19.93,11c-0.17,-1.39 -0.72,-2.73 -1.62,-3.89l-1.42,1.42c0.54,0.75 0.88,1.6 1.02,2.47h2.02zM13,17.9v2.02c1.39,-0.17 2.74,-0.71 3.9,-1.61l-1.44,-1.44c-0.75,0.54 -1.59,0.89 -2.46,1.03zm3.89,-2.42l1.42,1.41c0.9,-1.16 1.45,-2.5 1.62,-3.89h-2.02c-0.14,0.87 -0.48,1.72 -1.02,2.48z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_slideshow_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_slideshow_24dp.xml
new file mode 100644
index 0000000..9b26770
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_slideshow_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,8v8l5,-4 -5,-4zm9,-5H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V5h14v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_straighten_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_straighten_24dp.xml
new file mode 100644
index 0000000..f10b101
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_straighten_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,6H3c-1.1,0 -2,0.9 -2,2v8c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2zm0,10H3V8h2v4h2V8h2v4h2V8h2v4h2V8h2v4h2V8h2v8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_style_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_style_24dp.xml
new file mode 100644
index 0000000..14d351e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_style_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2.53,19.65l1.34,0.56v-9.03l-2.43,5.86c-0.41,1.02 0.08,2.19 1.09,2.61zm19.5,-3.7L17.07,3.98c-0.31,-0.75 -1.04,-1.21 -1.81,-1.23 -0.26,0 -0.53,0.04 -0.79,0.15L7.1,5.95c-0.75,0.31 -1.21,1.03 -1.23,1.8 -0.01,0.27 0.04,0.54 0.15,0.8l4.96,11.97c0.31,0.76 1.05,1.22 1.83,1.23 0.26,0 0.52,-0.05 0.77,-0.15l7.36,-3.05c1.02,-0.42 1.51,-1.59 1.09,-2.6zM7.88,8.75c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zm-2,11c0,1.1 0.9,2 2,2h1.45l-3.45,-8.34v6.34z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_switch_camera_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_switch_camera_24dp.xml
new file mode 100644
index 0000000..f36ba70
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_switch_camera_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,4h-3.17L15,2H9L7.17,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm-5,11.5V13H9v2.5L5.5,12 9,8.5V11h6V8.5l3.5,3.5 -3.5,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_switch_video_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_switch_video_24dp.xml
new file mode 100644
index 0000000..b29205f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_switch_video_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,9.5V6c0,-0.55 -0.45,-1 -1,-1H3c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h14c0.55,0 1,-0.45 1,-1v-3.5l4,4v-13l-4,4zm-5,6V13H7v2.5L3.5,12 7,8.5V11h6V8.5l3.5,3.5 -3.5,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_tag_faces_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_tag_faces_24dp.xml
new file mode 100644
index 0000000..1ef868a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_tag_faces_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zm3.5,-9c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zm-7,0c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zm3.5,6.5c2.33,0 4.31,-1.46 5.11,-3.5H6.89c0.8,2.04 2.78,3.5 5.11,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_texture_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_texture_24dp.xml
new file mode 100644
index 0000000..098a5a5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_texture_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.51,3.08L3.08,19.51c0.09,0.34 0.27,0.65 0.51,0.9 0.25,0.24 0.56,0.42 0.9,0.51L20.93,4.49c-0.19,-0.69 -0.73,-1.23 -1.42,-1.41zM11.88,3L3,11.88v2.83L14.71,3h-2.83zM5,3c-1.1,0 -2,0.9 -2,2v2l4,-4H5zm14,18c0.55,0 1.05,-0.22 1.41,-0.59 0.37,-0.36 0.59,-0.86 0.59,-1.41v-2l-4,4h2zm-9.71,0h2.83L21,12.12V9.29L9.29,21z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_timelapse_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_timelapse_24dp.xml
new file mode 100644
index 0000000..20d4ee6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_timelapse_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.24,7.76C15.07,6.59 13.54,6 12,6v6l-4.24,4.24c2.34,2.34 6.14,2.34 8.49,0 2.34,-2.34 2.34,-6.14 -0.01,-8.48zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_10_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_10_24dp.xml
new file mode 100644
index 0000000..ce73d5d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_10_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0,7.72V9.4l3,-1V18h2V6h-0.25L0,7.72zm23.78,6.65c-0.14,-0.28 -0.35,-0.53 -0.63,-0.74 -0.28,-0.21 -0.61,-0.39 -1.01,-0.53s-0.85,-0.27 -1.35,-0.38c-0.35,-0.07 -0.64,-0.15 -0.87,-0.23 -0.23,-0.08 -0.41,-0.16 -0.55,-0.25 -0.14,-0.09 -0.23,-0.19 -0.28,-0.3 -0.05,-0.11 -0.08,-0.24 -0.08,-0.39 0,-0.14 0.03,-0.28 0.09,-0.41 0.06,-0.13 0.15,-0.25 0.27,-0.34 0.12,-0.1 0.27,-0.18 0.45,-0.24s0.4,-0.09 0.64,-0.09c0.25,0 0.47,0.04 0.66,0.11 0.19,0.07 0.35,0.17 0.48,0.29 0.13,0.12 0.22,0.26 0.29,0.42 0.06,0.16 0.1,0.32 0.1,0.49h1.95c0,-0.39 -0.08,-0.75 -0.24,-1.09 -0.16,-0.34 -0.39,-0.63 -0.69,-0.88 -0.3,-0.25 -0.66,-0.44 -1.09,-0.59C21.49,9.07 21,9 20.46,9c-0.51,0 -0.98,0.07 -1.39,0.21 -0.41,0.14 -0.77,0.33 -1.06,0.57 -0.29,0.24 -0.51,0.52 -0.67,0.84 -0.16,0.32 -0.23,0.65 -0.23,1.01s0.08,0.69 0.23,0.96c0.15,0.28 0.36,0.52 0.64,0.73 0.27,0.21 0.6,0.38 0.98,0.53 0.38,0.14 0.81,0.26 1.27,0.36 0.39,0.08 0.71,0.17 0.95,0.26s0.43,0.19 0.57,0.29c0.13,0.1 0.22,0.22 0.27,0.34 0.05,0.12 0.07,0.25 0.07,0.39 0,0.32 -0.13,0.57 -0.4,0.77 -0.27,0.2 -0.66,0.29 -1.17,0.29 -0.22,0 -0.43,-0.02 -0.64,-0.08 -0.21,-0.05 -0.4,-0.13 -0.56,-0.24 -0.17,-0.11 -0.3,-0.26 -0.41,-0.44 -0.11,-0.18 -0.17,-0.41 -0.18,-0.67h-1.89c0,0.36 0.08,0.71 0.24,1.05 0.16,0.34 0.39,0.65 0.7,0.93 0.31,0.27 0.69,0.49 1.15,0.66 0.46,0.17 0.98,0.25 1.58,0.25 0.53,0 1.01,-0.06 1.44,-0.19 0.43,-0.13 0.8,-0.31 1.11,-0.54 0.31,-0.23 0.54,-0.51 0.71,-0.83 0.17,-0.32 0.25,-0.67 0.25,-1.06 -0.02,-0.4 -0.09,-0.74 -0.24,-1.02zm-9.96,-7.32c-0.34,-0.4 -0.75,-0.7 -1.23,-0.88 -0.47,-0.18 -1.01,-0.27 -1.59,-0.27 -0.58,0 -1.11,0.09 -1.59,0.27 -0.48,0.18 -0.89,0.47 -1.23,0.88 -0.34,0.41 -0.6,0.93 -0.79,1.59 -0.18,0.65 -0.28,1.45 -0.28,2.39v1.92c0,0.94 0.09,1.74 0.28,2.39 0.19,0.66 0.45,1.19 0.8,1.6 0.34,0.41 0.75,0.71 1.23,0.89 0.48,0.18 1.01,0.28 1.59,0.28 0.59,0 1.12,-0.09 1.59,-0.28 0.48,-0.18 0.88,-0.48 1.22,-0.89 0.34,-0.41 0.6,-0.94 0.78,-1.6 0.18,-0.65 0.28,-1.45 0.28,-2.39v-1.92c0,-0.94 -0.09,-1.74 -0.28,-2.39 -0.18,-0.66 -0.44,-1.19 -0.78,-1.59zm-0.92,6.17c0,0.6 -0.04,1.11 -0.12,1.53 -0.08,0.42 -0.2,0.76 -0.36,1.02 -0.16,0.26 -0.36,0.45 -0.59,0.57 -0.23,0.12 -0.51,0.18 -0.82,0.18 -0.3,0 -0.58,-0.06 -0.82,-0.18s-0.44,-0.31 -0.6,-0.57c-0.16,-0.26 -0.29,-0.6 -0.38,-1.02 -0.09,-0.42 -0.13,-0.93 -0.13,-1.53v-2.5c0,-0.6 0.04,-1.11 0.13,-1.52 0.09,-0.41 0.21,-0.74 0.38,-1 0.16,-0.25 0.36,-0.43 0.6,-0.55 0.24,-0.11 0.51,-0.17 0.81,-0.17 0.31,0 0.58,0.06 0.81,0.17 0.24,0.11 0.44,0.29 0.6,0.55 0.16,0.25 0.29,0.58 0.37,0.99 0.08,0.41 0.13,0.92 0.13,1.52v2.51z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_24dp.xml
new file mode 100644
index 0000000..0f1425c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,1H9v2h6V1zm-4,13h2V8h-2v6zm8.03,-6.61l1.42,-1.42c-0.43,-0.51 -0.9,-0.99 -1.41,-1.41l-1.42,1.42C16.07,4.74 14.12,4 12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9 9,-4.03 9,-9c0,-2.12 -0.74,-4.07 -1.97,-5.61zM12,20c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_3_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_3_24dp.xml
new file mode 100644
index 0000000..a9b95fe
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_3_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.61,12.97c-0.16,-0.24 -0.36,-0.46 -0.62,-0.65 -0.25,-0.19 -0.56,-0.35 -0.93,-0.48 0.3,-0.14 0.57,-0.3 0.8,-0.5 0.23,-0.2 0.42,-0.41 0.57,-0.64 0.15,-0.23 0.27,-0.46 0.34,-0.71 0.08,-0.24 0.11,-0.49 0.11,-0.73 0,-0.55 -0.09,-1.04 -0.28,-1.46 -0.18,-0.42 -0.44,-0.77 -0.78,-1.06 -0.33,-0.28 -0.73,-0.5 -1.2,-0.64 -0.45,-0.13 -0.97,-0.2 -1.53,-0.2 -0.55,0 -1.06,0.08 -1.52,0.24 -0.47,0.17 -0.87,0.4 -1.2,0.69 -0.33,0.29 -0.6,0.63 -0.78,1.03 -0.2,0.39 -0.29,0.83 -0.29,1.29h1.98c0,-0.26 0.05,-0.49 0.14,-0.69 0.09,-0.2 0.22,-0.38 0.38,-0.52 0.17,-0.14 0.36,-0.25 0.58,-0.33 0.22,-0.08 0.46,-0.12 0.73,-0.12 0.61,0 1.06,0.16 1.36,0.47 0.3,0.31 0.44,0.75 0.44,1.32 0,0.27 -0.04,0.52 -0.12,0.74 -0.08,0.22 -0.21,0.41 -0.38,0.57 -0.17,0.16 -0.38,0.28 -0.63,0.37 -0.25,0.09 -0.55,0.13 -0.89,0.13H6.72v1.57H7.9c0.34,0 0.64,0.04 0.91,0.11 0.27,0.08 0.5,0.19 0.69,0.35 0.19,0.16 0.34,0.36 0.44,0.61 0.1,0.24 0.16,0.54 0.16,0.87 0,0.62 -0.18,1.09 -0.53,1.42 -0.35,0.33 -0.84,0.49 -1.45,0.49 -0.29,0 -0.56,-0.04 -0.8,-0.13 -0.24,-0.08 -0.44,-0.2 -0.61,-0.36 -0.17,-0.16 -0.3,-0.34 -0.39,-0.56 -0.09,-0.22 -0.14,-0.46 -0.14,-0.72H4.19c0,0.55 0.11,1.03 0.32,1.45 0.21,0.42 0.5,0.77 0.86,1.05s0.77,0.49 1.24,0.63 0.96,0.21 1.48,0.21c0.57,0 1.09,-0.08 1.58,-0.23 0.49,-0.15 0.91,-0.38 1.26,-0.68 0.36,-0.3 0.64,-0.66 0.84,-1.1 0.2,-0.43 0.3,-0.93 0.3,-1.48 0,-0.29 -0.04,-0.58 -0.11,-0.86 -0.08,-0.25 -0.19,-0.51 -0.35,-0.76zm9.26,1.4c-0.14,-0.28 -0.35,-0.53 -0.63,-0.74 -0.28,-0.21 -0.61,-0.39 -1.01,-0.53s-0.85,-0.27 -1.35,-0.38c-0.35,-0.07 -0.64,-0.15 -0.87,-0.23 -0.23,-0.08 -0.41,-0.16 -0.55,-0.25 -0.14,-0.09 -0.23,-0.19 -0.28,-0.3 -0.05,-0.11 -0.08,-0.24 -0.08,-0.39s0.03,-0.28 0.09,-0.41c0.06,-0.13 0.15,-0.25 0.27,-0.34 0.12,-0.1 0.27,-0.18 0.45,-0.24s0.4,-0.09 0.64,-0.09c0.25,0 0.47,0.04 0.66,0.11 0.19,0.07 0.35,0.17 0.48,0.29 0.13,0.12 0.22,0.26 0.29,0.42 0.06,0.16 0.1,0.32 0.1,0.49h1.95c0,-0.39 -0.08,-0.75 -0.24,-1.09 -0.16,-0.34 -0.39,-0.63 -0.69,-0.88 -0.3,-0.25 -0.66,-0.44 -1.09,-0.59 -0.43,-0.15 -0.92,-0.22 -1.46,-0.22 -0.51,0 -0.98,0.07 -1.39,0.21 -0.41,0.14 -0.77,0.33 -1.06,0.57 -0.29,0.24 -0.51,0.52 -0.67,0.84 -0.16,0.32 -0.23,0.65 -0.23,1.01s0.08,0.68 0.23,0.96c0.15,0.28 0.37,0.52 0.64,0.73 0.27,0.21 0.6,0.38 0.98,0.53 0.38,0.14 0.81,0.26 1.27,0.36 0.39,0.08 0.71,0.17 0.95,0.26s0.43,0.19 0.57,0.29c0.13,0.1 0.22,0.22 0.27,0.34 0.05,0.12 0.07,0.25 0.07,0.39 0,0.32 -0.13,0.57 -0.4,0.77 -0.27,0.2 -0.66,0.29 -1.17,0.29 -0.22,0 -0.43,-0.02 -0.64,-0.08 -0.21,-0.05 -0.4,-0.13 -0.56,-0.24 -0.17,-0.11 -0.3,-0.26 -0.41,-0.44 -0.11,-0.18 -0.17,-0.41 -0.18,-0.67h-1.89c0,0.36 0.08,0.71 0.24,1.05 0.16,0.34 0.39,0.65 0.7,0.93 0.31,0.27 0.69,0.49 1.15,0.66 0.46,0.17 0.98,0.25 1.58,0.25 0.53,0 1.01,-0.06 1.44,-0.19 0.43,-0.13 0.8,-0.31 1.11,-0.54 0.31,-0.23 0.54,-0.51 0.71,-0.83 0.17,-0.32 0.25,-0.67 0.25,-1.06 -0.02,-0.4 -0.09,-0.74 -0.24,-1.02z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_auto_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_auto_24dp.xml
new file mode 100644
index 0000000..25160fe
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_auto_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,4C9.79,4 8,5.79 8,8s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm0,10c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.67 -5.33,-4 -8,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_off_24dp.xml
new file mode 100644
index 0000000..9b58d048
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_timer_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.04,4.55l-1.42,1.42C16.07,4.74 14.12,4 12,4c-1.83,0 -3.53,0.55 -4.95,1.48l1.46,1.46C9.53,6.35 10.73,6 12,6c3.87,0 7,3.13 7,7 0,1.27 -0.35,2.47 -0.94,3.49l1.45,1.45C20.45,16.53 21,14.83 21,13c0,-2.12 -0.74,-4.07 -1.97,-5.61l1.42,-1.42 -1.41,-1.42zM15,1H9v2h6V1zm-4,8.44l2,2V8h-2v1.44zM3.02,4L1.75,5.27 4.5,8.03C3.55,9.45 3,11.16 3,13c0,4.97 4.02,9 9,9 1.84,0 3.55,-0.55 4.98,-1.5l2.5,2.5 1.27,-1.27 -7.71,-7.71L3.02,4zM12,20c-3.87,0 -7,-3.13 -7,-7 0,-1.28 0.35,-2.48 0.95,-3.52l9.56,9.56c-1.03,0.61 -2.23,0.96 -3.51,0.96z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_tonality_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_tonality_24dp.xml
new file mode 100644
index 0000000..ece3cb2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_tonality_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm-1,17.93c-3.94,-0.49 -7,-3.85 -7,-7.93s3.05,-7.44 7,-7.93v15.86zm2,-15.86c1.03,0.13 2,0.45 2.87,0.93H13v-0.93zM13,7h5.24c0.25,0.31 0.48,0.65 0.68,1H13V7zm0,3h6.74c0.08,0.33 0.15,0.66 0.19,1H13v-1zm0,9.93V19h2.87c-0.87,0.48 -1.84,0.8 -2.87,0.93zM18.24,17H13v-1h5.92c-0.2,0.35 -0.43,0.69 -0.68,1zm1.5,-3H13v-1h6.93c-0.04,0.34 -0.11,0.67 -0.19,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_transform_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_transform_24dp.xml
new file mode 100644
index 0000000..ac407b7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_transform_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,18v-2H8V4h2L7,1 4,4h2v2H2v2h4v8c0,1.1 0.9,2 2,2h8v2h-2l3,3 3,-3h-2v-2h4zM10,8h6v6h2V8c0,-1.1 -0.9,-2 -2,-2h-6v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_tune_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_tune_24dp.xml
new file mode 100644
index 0000000..553b593
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_tune_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,17v2h6v-2H3zM3,5v2h10V5H3zm10,16v-2h8v-2h-8v-2h-2v6h2zM7,9v2H3v2h4v2h2V9H7zm14,4v-2H11v2h10zm-6,-4h2V7h4V5h-4V3h-2v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_auto_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_auto_24dp.xml
new file mode 100644
index 0000000..15887e7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_auto_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6.85,12.65h2.3L8,9l-1.15,3.65zM22,7l-1.2,6.29L19.3,7h-1.6l-1.49,6.29L15,7h-0.76C12.77,5.17 10.53,4 8,4c-4.42,0 -8,3.58 -8,8s3.58,8 8,8c3.13,0 5.84,-1.81 7.15,-4.43l0.1,0.43H17l1.5,-6.1L20,16h1.75l2.05,-9H22zm-11.7,9l-0.7,-2H6.4l-0.7,2H3.8L7,7h2l3.2,9h-1.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_cloudy_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_cloudy_24dp.xml
new file mode 100644
index 0000000..3502765
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_cloudy_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.36,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.64,-4.96z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_incandescent_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_incandescent_24dp.xml
new file mode 100644
index 0000000..e82fd89
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_incandescent_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3.55,18.54l1.41,1.41 1.79,-1.8 -1.41,-1.41 -1.79,1.8zM11,22.45h2V19.5h-2v2.95zM4,10.5H1v2h3v-2zm11,-4.19V1.5H9v4.81C7.21,7.35 6,9.28 6,11.5c0,3.31 2.69,6 6,6s6,-2.69 6,-6c0,-2.22 -1.21,-4.15 -3,-5.19zm5,4.19v2h3v-2h-3zm-2.76,7.66l1.79,1.8 1.41,-1.41 -1.8,-1.79 -1.4,1.4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_irradescent_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_irradescent_24dp.xml
new file mode 100644
index 0000000..a943060
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_irradescent_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5,14.5h14v-6H5v6zM11,0.55V3.5h2V0.55h-2zm8.04,2.5l-1.79,1.79 1.41,1.41 1.8,-1.79 -1.42,-1.41zM13,22.45V19.5h-2v2.95h2zm7.45,-3.91l-1.8,-1.79 -1.41,1.41 1.79,1.8 1.42,-1.42zM3.55,4.46l1.79,1.79 1.41,-1.41 -1.79,-1.79 -1.41,1.41zm1.41,15.49l1.79,-1.8 -1.41,-1.41 -1.79,1.79 1.41,1.42z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_sunny_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_sunny_24dp.xml
new file mode 100644
index 0000000..b8c40fb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/image/ic_wb_sunny_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6.76,4.84l-1.8,-1.79 -1.41,1.41 1.79,1.79 1.42,-1.41zM4,10.5H1v2h3v-2zm9,-9.95h-2V3.5h2V0.55zm7.45,3.91l-1.41,-1.41 -1.79,1.79 1.41,1.41 1.79,-1.79zm-3.21,13.7l1.79,1.8 1.41,-1.41 -1.8,-1.79 -1.4,1.4zM20,10.5v2h3v-2h-3zm-8,-5c-3.31,0 -6,2.69 -6,6s2.69,6 6,6 6,-2.69 6,-6 -2.69,-6 -6,-6zm-1,16.95h2V19.5h-2v2.95zm-7.45,-3.91l1.41,1.41 1.79,-1.8 -1.41,-1.41 -1.79,1.8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_beenhere_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_beenhere_24dp.xml
new file mode 100644
index 0000000..bd622df
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_beenhere_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,1H5c-1.1,0 -1.99,0.9 -1.99,2L3,15.93c0,0.69 0.35,1.3 0.88,1.66L12,23l8.11,-5.41c0.53,-0.36 0.88,-0.97 0.88,-1.66L21,3c0,-1.1 -0.9,-2 -2,-2zm-9,15l-5,-5 1.41,-1.41L10,13.17l7.59,-7.59L19,7l-9,9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_24dp.xml
new file mode 100644
index 0000000..ced648c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21.71,11.29l-9,-9c-0.39,-0.39 -1.02,-0.39 -1.41,0l-9,9c-0.39,0.39 -0.39,1.02 0,1.41l9,9c0.39,0.39 1.02,0.39 1.41,0l9,-9c0.39,-0.38 0.39,-1.01 0,-1.41zM14,14.5V12h-4v3H8v-4c0,-0.55 0.45,-1 1,-1h5V7.5l3.5,3.5 -3.5,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_bike_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_bike_24dp.xml
new file mode 100644
index 0000000..d7c0992
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_bike_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,4.8c0.99,0 1.8,-0.81 1.8,-1.8s-0.81,-1.8 -1.8,-1.8c-1,0 -1.8,0.81 -1.8,1.8S15,4.8 16,4.8zm3,7.2c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5zm0,8.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5zM14.8,10H19V8.2h-3.2l-1.93,-3.27c-0.3,-0.5 -0.84,-0.83 -1.46,-0.83 -0.47,0 -0.89,0.19 -1.2,0.5l-3.7,3.7c-0.32,0.3 -0.51,0.73 -0.51,1.2 0,0.63 0.33,1.16 0.85,1.47L11.2,13v5H13v-6.48l-2.25,-1.67 2.32,-2.33L14.8,10zM5,12c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5zm0,8.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_bus_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_bus_24dp.xml
new file mode 100644
index 0000000..ca91981
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_bus_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,16c0,0.88 0.39,1.67 1,2.22V20c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h8v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1.78c0.61,-0.55 1,-1.34 1,-2.22V6c0,-3.5 -3.58,-4 -8,-4s-8,0.5 -8,4v10zm3.5,1c-0.83,0 -1.5,-0.67 -1.5,-1.5S6.67,14 7.5,14s1.5,0.67 1.5,1.5S8.33,17 7.5,17zm9,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm1.5,-6H6V6h12v5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_car_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_car_24dp.xml
new file mode 100644
index 0000000..252a94f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_car_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.92,6.01C18.72,5.42 18.16,5 17.5,5h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,12v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,13 6.5,13s1.5,0.67 1.5,1.5S7.33,16 6.5,16zm11,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,11l1.5,-4.5h11L19,11H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_ferry_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_ferry_24dp.xml
new file mode 100644
index 0000000..c304e8a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_ferry_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,21c-1.39,0 -2.78,-0.47 -4,-1.32 -2.44,1.71 -5.56,1.71 -8,0C6.78,20.53 5.39,21 4,21H2v2h2c1.38,0 2.74,-0.35 4,-0.99 2.52,1.29 5.48,1.29 8,0 1.26,0.65 2.62,0.99 4,0.99h2v-2h-2zM3.95,19H4c1.6,0 3.02,-0.88 4,-2 0.98,1.12 2.4,2 4,2s3.02,-0.88 4,-2c0.98,1.12 2.4,2 4,2h0.05l1.89,-6.68c0.08,-0.26 0.06,-0.54 -0.06,-0.78s-0.34,-0.42 -0.6,-0.5L20,10.62V6c0,-1.1 -0.9,-2 -2,-2h-3V1H9v3H6c-1.1,0 -2,0.9 -2,2v4.62l-1.29,0.42c-0.26,0.08 -0.48,0.26 -0.6,0.5s-0.15,0.52 -0.06,0.78L3.95,19zM6,6h12v3.97L12,8 6,9.97V6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_subway_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_subway_24dp.xml
new file mode 100644
index 0000000..a40cea8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_subway_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2c-4.42,0 -8,0.5 -8,4v9.5C4,17.43 5.57,19 7.5,19L6,20.5v0.5h12v-0.5L16.5,19c1.93,0 3.5,-1.57 3.5,-3.5V6c0,-3.5 -3.58,-4 -8,-4zM7.5,17c-0.83,0 -1.5,-0.67 -1.5,-1.5S6.67,14 7.5,14s1.5,0.67 1.5,1.5S8.33,17 7.5,17zm3.5,-6H6V6h5v5zm5.5,6c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm1.5,-6h-5V6h5v5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_train_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_train_24dp.xml
new file mode 100644
index 0000000..a029014
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_train_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,15.5C4,17.43 5.57,19 7.5,19L6,20.5v0.5h12v-0.5L16.5,19c1.93,0 3.5,-1.57 3.5,-3.5V5c0,-3.5 -3.58,-4 -8,-4s-8,0.5 -8,4v10.5zm8,1.5c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zm6,-7H6V5h12v5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_transit_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_transit_24dp.xml
new file mode 100644
index 0000000..a40cea8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_transit_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2c-4.42,0 -8,0.5 -8,4v9.5C4,17.43 5.57,19 7.5,19L6,20.5v0.5h12v-0.5L16.5,19c1.93,0 3.5,-1.57 3.5,-3.5V6c0,-3.5 -3.58,-4 -8,-4zM7.5,17c-0.83,0 -1.5,-0.67 -1.5,-1.5S6.67,14 7.5,14s1.5,0.67 1.5,1.5S8.33,17 7.5,17zm3.5,-6H6V6h5v5zm5.5,6c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm1.5,-6h-5V6h5v5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_walk_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_walk_24dp.xml
new file mode 100644
index 0000000..3b92462
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_directions_walk_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14,3.8c0.99,0 1.8,-0.81 1.8,-1.8 0,-1 -0.81,-1.8 -1.8,-1.8 -1,0 -1.8,0.81 -1.8,1.8S13,3.8 14,3.8zm0.12,6.2H19V8.2h-3.62l-2,-3.33c-0.3,-0.5 -0.84,-0.83 -1.46,-0.83 -0.17,0 -0.34,0.03 -0.49,0.07L6,5.8V11h1.8V7.33l2.11,-0.66L6,22h1.8l2.87,-8.11L13,17v5h1.8v-6.41l-2.49,-4.54 0.73,-2.87L14.12,10z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_flight_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_flight_24dp.xml
new file mode 100644
index 0000000..3aa5af7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_flight_24dp.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10.18,9"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,16v-2l-8,-5V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5V9l-8,5v2l8,-2.5V19l-2,1.5V22l3.5,-1 3.5,1v-1.5L13,19v-5.5l8,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_hotel_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_hotel_24dp.xml
new file mode 100644
index 0000000..3d2e1a0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_hotel_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,13c1.66,0 3,-1.34 3,-3S8.66,7 7,7s-3,1.34 -3,3 1.34,3 3,3zm12,-6h-8v7H3V5H1v15h2v-3h18v3h2v-9c0,-2.21 -1.79,-4 -4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_layers_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_layers_24dp.xml
new file mode 100644
index 0000000..430ec18
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_layers_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.99,18.54l-7.37,-5.73L3,14.07l9,7 9,-7 -1.63,-1.27 -7.38,5.74zM12,16l7.36,-5.73L21,9l-9,-7 -9,7 1.63,1.27L12,16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_layers_clear_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_layers_clear_24dp.xml
new file mode 100644
index 0000000..8516033
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_layers_clear_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.81,14.99l1.19,-0.92 -1.43,-1.43 -1.19,0.92 1.43,1.43zm-0.45,-4.72L21,9l-9,-7 -2.91,2.27 7.87,7.88 2.4,-1.88zM3.27,1L2,2.27l4.22,4.22L3,9l1.63,1.27L12,16l2.1,-1.63 1.43,1.43L12,18.54l-7.37,-5.73L3,14.07l9,7 4.95,-3.85L20.73,21 22,19.73 3.27,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_airport_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_airport_24dp.xml
new file mode 100644
index 0000000..c63e2de
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_airport_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,16v-2l-8,-5V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5V9l-8,5v2l8,-2.5V19l-2,1.5V22l3.5,-1 3.5,1v-1.5L13,19v-5.5l8,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_atm_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_atm_24dp.xml
new file mode 100644
index 0000000..eb1dc04
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_atm_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,17h2v-1h1c0.55,0 1,-0.45 1,-1v-3c0,-0.55 -0.45,-1 -1,-1h-3v-1h4V8h-2V7h-2v1h-1c-0.55,0 -1,0.45 -1,1v3c0,0.55 0.45,1 1,1h3v1H9v2h2v1zm9,-13H4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V6c0,-1.11 -0.89,-2 -2,-2zm0,14H4V6h16v12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_attraction_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_attraction_24dp.xml
new file mode 100644
index 0000000..a75c432
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_attraction_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,12c0,-1.1 0.9,-2 2,-2V6c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -1.99,0.9 -1.99,2v4c1.1,0 1.99,0.9 1.99,2s-0.89,2 -2,2v4c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2v-4c-1.1,0 -2,-0.9 -2,-2zm-4.42,4.8L12,14.5l-3.58,2.3 1.08,-4.12 -3.29,-2.69 4.24,-0.25L12,5.8l1.54,3.95 4.24,0.25 -3.29,2.69 1.09,4.11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_bar_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_bar_24dp.xml
new file mode 100644
index 0000000..e6eba39
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_bar_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11,13v6H6v2h12v-2h-5v-6l8,-8V3H3v2l8,8zM7.5,7l-2,-2h13l-2,2h-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_cafe_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_cafe_24dp.xml
new file mode 100644
index 0000000..1b0a6d6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_cafe_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,3H4v10c0,2.21 1.79,4 4,4h6c2.21,0 4,-1.79 4,-4v-3h2c1.11,0 2,-0.89 2,-2V5c0,-1.11 -0.89,-2 -2,-2zm0,5h-2V5h2v3zM2,21h18v-2H2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_car_wash_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_car_wash_24dp.xml
new file mode 100644
index 0000000..f94b7ff
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_car_wash_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,5c0.83,0 1.5,-0.67 1.5,-1.5 0,-1 -1.5,-2.7 -1.5,-2.7s-1.5,1.7 -1.5,2.7c0,0.83 0.67,1.5 1.5,1.5zm-5,0c0.83,0 1.5,-0.67 1.5,-1.5 0,-1 -1.5,-2.7 -1.5,-2.7s-1.5,1.7 -1.5,2.7c0,0.83 0.67,1.5 1.5,1.5zM7,5c0.83,0 1.5,-0.67 1.5,-1.5C8.5,2.5 7,0.8 7,0.8S5.5,2.5 5.5,3.5C5.5,4.33 6.17,5 7,5zm11.92,3.01C18.72,7.42 18.16,7 17.5,7h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,14v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,18c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,15 6.5,15s1.5,0.67 1.5,1.5S7.33,18 6.5,18zm11,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,13l1.5,-4.5h11L19,13H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_convenience_store_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_convenience_store_24dp.xml
new file mode 100644
index 0000000..b1259d2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_convenience_store_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,7V4H5v3H2v13h8v-4h4v4h8V7h-3zm-8,3H9v1h2v1H8V9h2V8H8V7h3v3zm5,2h-1v-2h-2V7h1v2h1V7h1v5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_drink_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_drink_24dp.xml
new file mode 100644
index 0000000..36a980a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_drink_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,2l2.01,18.23C5.13,21.23 5.97,22 7,22h10c1.03,0 1.87,-0.77 1.99,-1.77L21,2H3zm9,17c-1.66,0 -3,-1.34 -3,-3 0,-2 3,-5.4 3,-5.4s3,3.4 3,5.4c0,1.66 -1.34,3 -3,3zm6.33,-11H5.67l-0.44,-4h13.53l-0.43,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_florist_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_florist_24dp.xml
new file mode 100644
index 0000000..0932396
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_florist_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,22c4.97,0 9,-4.03 9,-9 -4.97,0 -9,4.03 -9,9zM5.6,10.25c0,1.38 1.12,2.5 2.5,2.5 0.53,0 1.01,-0.16 1.42,-0.44l-0.02,0.19c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5l-0.02,-0.19c0.4,0.28 0.89,0.44 1.42,0.44 1.38,0 2.5,-1.12 2.5,-2.5 0,-1 -0.59,-1.85 -1.43,-2.25 0.84,-0.4 1.43,-1.25 1.43,-2.25 0,-1.38 -1.12,-2.5 -2.5,-2.5 -0.53,0 -1.01,0.16 -1.42,0.44l0.02,-0.19C14.5,2.12 13.38,1 12,1S9.5,2.12 9.5,3.5l0.02,0.19c-0.4,-0.28 -0.89,-0.44 -1.42,-0.44 -1.38,0 -2.5,1.12 -2.5,2.5 0,1 0.59,1.85 1.43,2.25 -0.84,0.4 -1.43,1.25 -1.43,2.25zM12,5.5c1.38,0 2.5,1.12 2.5,2.5s-1.12,2.5 -2.5,2.5S9.5,9.38 9.5,8s1.12,-2.5 2.5,-2.5zM3,13c0,4.97 4.03,9 9,9 0,-4.97 -4.03,-9 -9,-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_gas_station_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_gas_station_24dp.xml
new file mode 100644
index 0000000..9c10a2c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_gas_station_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.77,7.23l0.01,-0.01 -3.72,-3.72L15,4.56l2.11,2.11c-0.94,0.36 -1.61,1.26 -1.61,2.33 0,1.38 1.12,2.5 2.5,2.5 0.36,0 0.69,-0.08 1,-0.21v7.21c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V14c0,-1.1 -0.9,-2 -2,-2h-1V5c0,-1.1 -0.9,-2 -2,-2H6c-1.1,0 -2,0.9 -2,2v16h10v-7.5h1.5v5c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5V9c0,-0.69 -0.28,-1.32 -0.73,-1.77zM12,10H6V5h6v5zm6,0c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_grocery_store_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_grocery_store_24dp.xml
new file mode 100644
index 0000000..f28015e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_grocery_store_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,18c-1.1,0 -1.99,0.9 -1.99,2S5.9,22 7,22s2,-0.9 2,-2 -0.9,-2 -2,-2zM1,2v2h2l3.6,7.59 -1.35,2.45c-0.16,0.28 -0.25,0.61 -0.25,0.96 0,1.1 0.9,2 2,2h12v-2H7.42c-0.14,0 -0.25,-0.11 -0.25,-0.25l0.03,-0.12 0.9,-1.63h7.45c0.75,0 1.41,-0.41 1.75,-1.03l3.58,-6.49c0.08,-0.14 0.12,-0.31 0.12,-0.48 0,-0.55 -0.45,-1 -1,-1H5.21l-0.94,-2H1zm16,16c-1.1,0 -1.99,0.9 -1.99,2s0.89,2 1.99,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_hospital_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_hospital_24dp.xml
new file mode 100644
index 0000000..5ae2b34
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_hospital_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.1,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm-1,11h-4v4h-4v-4H6v-4h4V6h4v4h4v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_hotel_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_hotel_24dp.xml
new file mode 100644
index 0000000..3d2e1a0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_hotel_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,13c1.66,0 3,-1.34 3,-3S8.66,7 7,7s-3,1.34 -3,3 1.34,3 3,3zm12,-6h-8v7H3V5H1v15h2v-3h18v3h2v-9c0,-2.21 -1.79,-4 -4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_laundry_service_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_laundry_service_24dp.xml
new file mode 100644
index 0000000..7f03ce3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_laundry_service_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9.17,16.83c1.56,1.56 4.1,1.56 5.66,0 1.56,-1.56 1.56,-4.1 0,-5.66l-5.66,5.66zM18,2.01L6,2c-1.11,0 -2,0.89 -2,2v16c0,1.11 0.89,2 2,2h12c1.11,0 2,-0.89 2,-2V4c0,-1.11 -0.89,-1.99 -2,-1.99zM10,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM7,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zm5,16c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_library_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_library_24dp.xml
new file mode 100644
index 0000000..a396913
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_library_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,11.55C9.64,9.35 6.48,8 3,8v11c3.48,0 6.64,1.35 9,3.55 2.36,-2.19 5.52,-3.55 9,-3.55V8c-3.48,0 -6.64,1.35 -9,3.55zM12,8c1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3 1.34,3 3,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_mall_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_mall_24dp.xml
new file mode 100644
index 0000000..bce7f6f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_mall_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,6h-2c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6H5c-1.1,0 -1.99,0.9 -1.99,2L3,20c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2zm-7,-3c1.66,0 3,1.34 3,3H9c0,-1.66 1.34,-3 3,-3zm0,10c-2.76,0 -5,-2.24 -5,-5h2c0,1.66 1.34,3 3,3s3,-1.34 3,-3h2c0,2.76 -2.24,5 -5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_movies_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_movies_24dp.xml
new file mode 100644
index 0000000..7c662e7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_movies_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,3v2h-2V3H8v2H6V3H4v18h2v-2h2v2h8v-2h2v2h2V3h-2zM8,17H6v-2h2v2zm0,-4H6v-2h2v2zm0,-4H6V7h2v2zm10,8h-2v-2h2v2zm0,-4h-2v-2h2v2zm0,-4h-2V7h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_offer_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_offer_24dp.xml
new file mode 100644
index 0000000..7b14cab
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_offer_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21.41,11.58l-9,-9C12.05,2.22 11.55,2 11,2H4c-1.1,0 -2,0.9 -2,2v7c0,0.55 0.22,1.05 0.59,1.42l9,9c0.36,0.36 0.86,0.58 1.41,0.58 0.55,0 1.05,-0.22 1.41,-0.59l7,-7c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-0.55 -0.23,-1.06 -0.59,-1.42zM5.5,7C4.67,7 4,6.33 4,5.5S4.67,4 5.5,4 7,4.67 7,5.5 6.33,7 5.5,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_parking_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_parking_24dp.xml
new file mode 100644
index 0000000..145b78f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_parking_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,3H6v18h4v-6h3c3.31,0 6,-2.69 6,-6s-2.69,-6 -6,-6zm0.2,8H10V7h3.2c1.1,0 2,0.9 2,2s-0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_pharmacy_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_pharmacy_24dp.xml
new file mode 100644
index 0000000..68b81de
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_pharmacy_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21,5h-2.64l1.14,-3.14L17.15,1l-1.46,4H3v2l2,6 -2,6v2h18v-2l-2,-6 2,-6V5zm-5,9h-3v3h-2v-3H8v-2h3V9h2v3h3v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_phone_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_phone_24dp.xml
new file mode 100644
index 0000000..3a3ba47
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_phone_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_pizza_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_pizza_24dp.xml
new file mode 100644
index 0000000..15efc5e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_pizza_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C8.43,2 5.23,3.54 3.01,6L12,22l8.99,-16C18.78,3.55 15.57,2 12,2zM7,7c0,-1.1 0.9,-2 2,-2s2,0.9 2,2 -0.9,2 -2,2 -2,-0.9 -2,-2zm5,8c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_play_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_play_24dp.xml
new file mode 100644
index 0000000..a75c432
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_play_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,12c0,-1.1 0.9,-2 2,-2V6c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -1.99,0.9 -1.99,2v4c1.1,0 1.99,0.9 1.99,2s-0.89,2 -2,2v4c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2v-4c-1.1,0 -2,-0.9 -2,-2zm-4.42,4.8L12,14.5l-3.58,2.3 1.08,-4.12 -3.29,-2.69 4.24,-0.25L12,5.8l1.54,3.95 4.24,0.25 -3.29,2.69 1.09,4.11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_post_office_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_post_office_24dp.xml
new file mode 100644
index 0000000..bf2ac7d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_post_office_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm0,4l-8,5 -8,-5V6l8,5 8,-5v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_print_shop_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_print_shop_24dp.xml
new file mode 100644
index 0000000..9bfb690
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_print_shop_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,8H5c-1.66,0 -3,1.34 -3,3v6h4v4h12v-4h4v-6c0,-1.66 -1.34,-3 -3,-3zm-3,11H8v-5h8v5zm3,-7c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zm-1,-9H6v4h12V3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_restaurant_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_restaurant_24dp.xml
new file mode 100644
index 0000000..4c41998
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_restaurant_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8.1,13.34l2.83,-2.83L3.91,3.5c-1.56,1.56 -1.56,4.09 0,5.66l4.19,4.18zm6.78,-1.81c1.53,0.71 3.68,0.21 5.27,-1.38 1.91,-1.91 2.28,-4.65 0.81,-6.12 -1.46,-1.46 -4.2,-1.1 -6.12,0.81 -1.59,1.59 -2.09,3.74 -1.38,5.27L3.7,19.87l1.41,1.41L12,14.41l6.88,6.88 1.41,-1.41L13.41,13l1.47,-1.47z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_see_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_see_24dp.xml
new file mode 100644
index 0000000..8404ea5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_see_24dp.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,2L7.17,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2H9zm3,15c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_shipping_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_shipping_24dp.xml
new file mode 100644
index 0000000..57d8b29
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_shipping_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,8h-3V4H3c-1.1,0 -2,0.9 -2,2v11h2c0,1.66 1.34,3 3,3s3,-1.34 3,-3h6c0,1.66 1.34,3 3,3s3,-1.34 3,-3h2v-5l-3,-4zM6,18.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm13.5,-9l1.96,2.5H17V9.5h2.5zm-1.5,9c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_taxi_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_taxi_24dp.xml
new file mode 100644
index 0000000..bf051cf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_local_taxi_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.92,6.01C18.72,5.42 18.16,5 17.5,5H15V3H9v2H6.5c-0.66,0 -1.21,0.42 -1.42,1.01L3,12v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,13 6.5,13s1.5,0.67 1.5,1.5S7.33,16 6.5,16zm11,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,11l1.5,-4.5h11L19,11H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_location_history_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_location_history_24dp.xml
new file mode 100644
index 0000000..f72c0cd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_location_history_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,2H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h4l3,3 3,-3h4c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-7,3.3c1.49,0 2.7,1.21 2.7,2.7 0,1.49 -1.21,2.7 -2.7,2.7 -1.49,0 -2.7,-1.21 -2.7,-2.7 0,-1.49 1.21,-2.7 2.7,-2.7zM18,16H6v-0.9c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1v0.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_map_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_map_24dp.xml
new file mode 100644
index 0000000..da420ca
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_map_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.5,3l-0.16,0.03L15,5.1 9,3 3.36,4.9c-0.21,0.07 -0.36,0.25 -0.36,0.48V20.5c0,0.28 0.22,0.5 0.5,0.5l0.16,-0.03L9,18.9l6,2.1 5.64,-1.9c0.21,-0.07 0.36,-0.25 0.36,-0.48V3.5c0,-0.28 -0.22,-0.5 -0.5,-0.5zM15,19l-6,-2.11V5l6,2.11V19z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_my_location_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_my_location_24dp.xml
new file mode 100644
index 0000000..2a7817e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_my_location_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm8.94,3c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94V1h-2v2.06C6.83,3.52 3.52,6.83 3.06,11H1v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94V23h2v-2.06c4.17,-0.46 7.48,-3.77 7.94,-7.94H23v-2h-2.06zM12,19c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_navigation_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_navigation_24dp.xml
new file mode 100644
index 0000000..bb6a1de
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_navigation_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2L4.5,20.29l0.71,0.71L12,18l6.79,3 0.71,-0.71z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_pin_drop_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_pin_drop_24dp.xml
new file mode 100644
index 0000000..f2c728d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_pin_drop_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,8c0,-3.31 -2.69,-6 -6,-6S6,4.69 6,8c0,4.5 6,11 6,11s6,-6.5 6,-11zm-8,0c0,-1.1 0.9,-2 2,-2s2,0.9 2,2 -0.89,2 -2,2c-1.1,0 -2,-0.9 -2,-2zM5,20v2h14v-2H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_place_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_place_24dp.xml
new file mode 100644
index 0000000..04b8734
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_place_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zm0,9.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_rate_review_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_rate_review_24dp.xml
new file mode 100644
index 0000000..740409d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_rate_review_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,14v-2.47l6.88,-6.88c0.2,-0.2 0.51,-0.2 0.71,0l1.77,1.77c0.2,0.2 0.2,0.51 0,0.71L8.47,14H6zm12,0h-7.5l2,-2H18v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_restaurant_menu_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_restaurant_menu_24dp.xml
new file mode 100644
index 0000000..4c41998
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_restaurant_menu_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8.1,13.34l2.83,-2.83L3.91,3.5c-1.56,1.56 -1.56,4.09 0,5.66l4.19,4.18zm6.78,-1.81c1.53,0.71 3.68,0.21 5.27,-1.38 1.91,-1.91 2.28,-4.65 0.81,-6.12 -1.46,-1.46 -4.2,-1.1 -6.12,0.81 -1.59,1.59 -2.09,3.74 -1.38,5.27L3.7,19.87l1.41,1.41L12,14.41l6.88,6.88 1.41,-1.41L13.41,13l1.47,-1.47z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_satellite_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_satellite_24dp.xml
new file mode 100644
index 0000000..1f89d3c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_satellite_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM5,4.99h3C8,6.65 6.66,8 5,8V4.99zM5,12v-2c2.76,0 5,-2.25 5,-5.01h2C12,8.86 8.87,12 5,12zm0,6l3.5,-4.5 2.5,3.01L14.5,12l4.5,6H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_store_mall_directory_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_store_mall_directory_24dp.xml
new file mode 100644
index 0000000..cef78e8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_store_mall_directory_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,4H4v2h16V4zm1,10v-2l-1,-5H4l-1,5v2h1v6h10v-6h4v6h2v-6h1zm-9,4H6v-4h6v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_terrain_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_terrain_24dp.xml
new file mode 100644
index 0000000..c3ad94b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_terrain_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14,6l-3.75,5 2.85,3.8 -1.6,1.2C9.81,13.75 7,10 7,10l-6,8h22L14,6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/maps/ic_traffic_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/maps/ic_traffic_24dp.xml
new file mode 100644
index 0000000..61f7523
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/maps/ic_traffic_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,10h-3V8.86c1.72,-0.45 3,-2 3,-3.86h-3V4c0,-0.55 -0.45,-1 -1,-1H8c-0.55,0 -1,0.45 -1,1v1H4c0,1.86 1.28,3.41 3,3.86V10H4c0,1.86 1.28,3.41 3,3.86V15H4c0,1.86 1.28,3.41 3,3.86V20c0,0.55 0.45,1 1,1h8c0.55,0 1,-0.45 1,-1v-1.14c1.72,-0.45 3,-2 3,-3.86h-3v-1.14c1.72,-0.45 3,-2 3,-3.86zm-8,9c-1.11,0 -2,-0.9 -2,-2s0.89,-2 2,-2c1.1,0 2,0.9 2,2s-0.89,2 -2,2zm0,-5c-1.11,0 -2,-0.9 -2,-2s0.89,-2 2,-2c1.1,0 2,0.9 2,2s-0.89,2 -2,2zm0,-5c-1.11,0 -2,-0.9 -2,-2 0,-1.11 0.89,-2 2,-2 1.1,0 2,0.89 2,2 0,1.1 -0.89,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/microphone.xml b/asset-studio/src/main/java/images/material_design_icons/microphone.xml
new file mode 100644
index 0000000..b999099
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/microphone.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="m19,11l-1.7,0c0,0.74 -0.16,1.43 -0.43,2.05l1.23,1.23c0.56,-0.98 0.9,-2.09 0.9,-3.28zm-4.02,0.17c0,-0.06 0.02,-0.11 0.02,-0.17l0,-6c0,-1.66 -1.34,-3 -3,-3s-3,1.34 -3,3l0,0.18l5.98,5.99zm-10.71,-8.17l-1.27,1.27l6.01,6.01l0,0.72c0,1.66 1.33,3 2.99,3c0.22,0 0.44,-0.03 0.65,-0.08l1.66,1.66c-0.71,0.33 -1.5,0.52 -2.31,0.52c-2.76,0 -5.3,-2.1 -5.3,-5.1l-1.7,0c0,3.41 2.72,6.23 6,6.72l0,3.28l2,0l0,-3.28c0.91,-0.13 1.77,-0.45 2.54,-0.9l4.19,4.18l1.27,-1.27l-16.73,-16.73z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_apps_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_apps_24dp.xml
new file mode 100644
index 0000000..9bb7960
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_apps_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,8h4V4H4v4zm6,12h4v-4h-4v4zm-6,0h4v-4H4v4zm0,-6h4v-4H4v4zm6,0h4v-4h-4v4zm6,-10v4h4V4h-4zm-6,4h4V4h-4v4zm6,6h4v-4h-4v4zm0,6h4v-4h-4v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_apps_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_apps_36px.xml
new file mode 100644
index 0000000..b0a62d1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_apps_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,12h6V6H6v6zm9,18h6v-6h-6v6zm-9,0h6v-6H6v6zm0,-9h6v-6H6v6zm9,0h6v-6h-6v6zm9,-15v6h6V6h-6zm-9,6h6V6h-6v6zm9,9h6v-6h-6v6zm0,9h6v-6h-6v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_back_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_back_24dp.xml
new file mode 100644
index 0000000..2ac850b
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_back_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_back_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_back_36px.xml
new file mode 100644
index 0000000..97f4c9e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_back_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M30,16.5H11.74l8.38,-8.38L18,6 6,18l12,12 2.12,-2.12 -8.38,-8.38H30v-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_24dp.xml
new file mode 100644
index 0000000..4ec66aa
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,10l5,5 5,-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_36px.xml
new file mode 100644
index 0000000..1a6474a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10.5,15l7.5,7.5 7.5,-7.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_circle_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_circle_24dp.xml
new file mode 100644
index 0000000..beb8569
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_down_circle_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,12l-4,-4h8l-4,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_up_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_up_24dp.xml
new file mode 100644
index 0000000..33c90ee
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_up_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,14l5,-5 5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_up_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_up_36px.xml
new file mode 100644
index 0000000..0c2f255
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_drop_up_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10.5,21l7.5,-7.5 7.5,7.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_forward_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_forward_24dp.xml
new file mode 100644
index 0000000..4087dcd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_forward_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_forward_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_forward_36px.xml
new file mode 100644
index 0000000..a2aa943
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_arrow_forward_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,6l-2.12,2.12 8.38,8.38H6v3h18.26l-8.38,8.38L18,30l12,-12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_cancel_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_cancel_24dp.xml
new file mode 100644
index 0000000..8ea1d98
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_cancel_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zm5,13.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_cancel_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_cancel_36px.xml
new file mode 100644
index 0000000..c682aef
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_cancel_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,3C9.71,3 3,9.71 3,18s6.71,15 15,15 15,-6.71 15,-15S26.29,3 18,3zm7.5,20.38l-2.12,2.12L18,20.12l-5.38,5.38 -2.12,-2.12L15.88,18l-5.38,-5.38 2.12,-2.12L18,15.88l5.38,-5.38 2.12,2.12L20.12,18l5.38,5.38z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_check_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_check_24dp.xml
new file mode 100644
index 0000000..02e8321
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_check_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_check_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_check_36px.xml
new file mode 100644
index 0000000..8b6a857
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_check_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13.5,24.26L7.24,18l-2.12,2.12 8.38,8.38 18,-18 -2.12,-2.12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_left_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_left_24dp.xml
new file mode 100644
index 0000000..ed84de6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_left_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_left_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_left_36px.xml
new file mode 100644
index 0000000..bd4dcaa
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_left_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M23.12,11.12L21,9l-9,9 9,9 2.12,-2.12L16.24,18z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_right_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_right_24dp.xml
new file mode 100644
index 0000000..2ff2653
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_right_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_right_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_right_36px.xml
new file mode 100644
index 0000000..c6a6a74
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_chevron_right_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,9l-2.12,2.12L19.76,18l-6.88,6.88L15,27l9,-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_close_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_close_24dp.xml
new file mode 100644
index 0000000..88f04ed
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_close_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_close_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_close_36px.xml
new file mode 100644
index 0000000..ad67228
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_close_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M28.5,9.62L26.38,7.5 18,15.88 9.62,7.5 7.5,9.62 15.88,18 7.5,26.38l2.12,2.12L18,20.12l8.38,8.38 2.12,-2.12L20.12,18z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_less_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_less_24dp.xml
new file mode 100644
index 0000000..6f1c0d5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_less_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,8l-6,6 1.41,1.41L12,10.83l4.59,4.58L18,14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_less_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_less_36px.xml
new file mode 100644
index 0000000..12bcbc0
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_less_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,12l-9,9 2.12,2.12L18,16.24l6.88,6.88L27,21z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_more_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_more_24dp.xml
new file mode 100644
index 0000000..98b7d7c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_more_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_more_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_more_36px.xml
new file mode 100644
index 0000000..e9d17e7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_expand_more_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M24.88,12.88L18,19.76l-6.88,-6.88L9,15l9,9 9,-9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_24dp.xml
new file mode 100644
index 0000000..e35574f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,14H5v5h5v-2H7v-3zm-2,-4h2V7h3V5H5v5zm12,7h-3v2h5v-5h-2v3zM14,5v2h3v3h2V5h-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_36px.xml
new file mode 100644
index 0000000..f0d60c4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,21H7v8h8v-3h-5v-5zm-3,-6h3v-5h5V7H7v8zm19,11h-5v3h8v-8h-3v5zM21,7v3h5v5h3V7h-8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_exit_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_exit_24dp.xml
new file mode 100644
index 0000000..ad11915
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_exit_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5,16h3v3h2v-5H5v2zm3,-8H5v2h5V5H8v3zm6,11h2v-3h3v-2h-5v5zm2,-11V5h-2v5h5V8h-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_exit_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_exit_36px.xml
new file mode 100644
index 0000000..9e779e1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_fullscreen_exit_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7,24h5v5h3v-8H7v3zm5,-12H7v3h8V7h-3v5zm9,17h3v-5h5v-3h-8v8zm3,-17V7h-3v8h8v-3h-5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_menu_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_menu_24dp.xml
new file mode 100644
index 0000000..60e9e71
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_menu_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,18h18v-2H3v2zm0,-5h18v-2H3v2zm0,-7v2h18V6H3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_menu_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_menu_36px.xml
new file mode 100644
index 0000000..b90b53a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_menu_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,27h28v-3H4v3zm0,-8h28v-3H4v3zM4,8v3h28V8H4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_horiz_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_horiz_24dp.xml
new file mode 100644
index 0000000..74c9367
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_horiz_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm12,0c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm-6,0c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_horiz_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_horiz_36px.xml
new file mode 100644
index 0000000..6a3165e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_horiz_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,15c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zm18,0c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zm-9,0c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_vert_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_vert_24dp.xml
new file mode 100644
index 0000000..9c7516e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_vert_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zm0,2c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zm0,6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_vert_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_vert_36px.xml
new file mode 100644
index 0000000..43afde6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_more_vert_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,12c1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3 1.34,3 3,3zm0,3c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zm0,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_refresh_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_refresh_24dp.xml
new file mode 100644
index 0000000..19991a9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_refresh_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_refresh_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_refresh_36px.xml
new file mode 100644
index 0000000..a4d1eed
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_refresh_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M26.47,9.53C24.3,7.35 21.32,6 18,6 11.37,6 6,11.37 6,18s5.37,12 12,12c5.94,0 10.85,-4.33 11.81,-10h-3.04c-0.91,4.01 -4.49,7 -8.77,7 -4.97,0 -9,-4.03 -9,-9s4.03,-9 9,-9c2.49,0 4.71,1.03 6.34,2.66L20,16h10V6l-3.53,3.53z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_less_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_less_24dp.xml
new file mode 100644
index 0000000..459c655
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_less_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.41,18.59L8.83,20 12,16.83 15.17,20l1.41,-1.41L12,14l-4.59,4.59zm9.18,-13.18L15.17,4 12,7.17 8.83,4 7.41,5.41 12,10l4.59,-4.59z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_less_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_less_36px.xml
new file mode 100644
index 0000000..d7cf4e9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_less_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.12,27.88L13.24,30 18,25.24 22.76,30l2.12,-2.12L18,21l-6.88,6.88zM24.88,8.12L22.76,6 18,10.76 13.24,6l-2.12,2.12L18,15l6.88,-6.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_more_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_more_24dp.xml
new file mode 100644
index 0000000..b912880
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_more_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,5.83L15.17,9l1.41,-1.41L12,3 7.41,7.59 8.83,9 12,5.83zm0,12.34L8.83,15l-1.41,1.41L12,21l4.59,-4.59L15.17,15 12,18.17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_more_36px.xml b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_more_36px.xml
new file mode 100644
index 0000000..61f093a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/navigation/ic_unfold_more_36px.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,9.24L22.76,14l2.12,-2.12L18,5l-6.88,6.88L13.24,14 18,9.24zm0,17.52L13.24,22l-2.12,2.12L18,31l6.88,-6.88L22.76,22 18,26.76z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_adb_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_adb_24dp.xml
new file mode 100644
index 0000000..b34e4d4
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_adb_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5,16c0,3.87 3.13,7 7,7s7,-3.13 7,-7v-4H5v4zM16.12,4.37l2.1,-2.1 -0.82,-0.83 -2.3,2.31C14.16,3.28 13.12,3 12,3s-2.16,0.28 -3.09,0.75L6.6,1.44l-0.82,0.83 2.1,2.1C6.14,5.64 5,7.68 5,10v1h14v-1c0,-2.32 -1.14,-4.36 -2.88,-5.63zM9,9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zm6,0c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_bluetooth_audio_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_bluetooth_audio_24dp.xml
new file mode 100644
index 0000000..d09fb6f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_bluetooth_audio_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14.24,12.01l2.32,2.32c0.28,-0.72 0.44,-1.51 0.44,-2.33 0,-0.82 -0.16,-1.59 -0.43,-2.31l-2.33,2.32zm5.29,-5.3l-1.26,1.26c0.63,1.21 0.98,2.57 0.98,4.02s-0.36,2.82 -0.98,4.02l1.2,1.2c0.97,-1.54 1.54,-3.36 1.54,-5.31 -0.01,-1.89 -0.55,-3.67 -1.48,-5.19zm-3.82,1L10,2H9v7.59L4.41,5 3,6.41 8.59,12 3,17.59 4.41,19 9,14.41V22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM11,5.83l1.88,1.88L11,9.59V5.83zm1.88,10.46L11,18.17v-3.76l1.88,1.88z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_disc_full_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_disc_full_24dp.xml
new file mode 100644
index 0000000..1bc6710
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_disc_full_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,16h2v-2h-2v2zm0,-9v5h2V7h-2zM10,4c-4.42,0 -8,3.58 -8,8s3.58,8 8,8 8,-3.58 8,-8 -3.58,-8 -8,-8zm0,10c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_dnd_forwardslash_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_dnd_forwardslash_24dp.xml
new file mode 100644
index 0000000..b8dafd2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_dnd_forwardslash_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.5,2 2,6.5 2,12s4.5,10 10,10 10,-4.5 10,-10S17.5,2 12,2zM4,12c0,-4.4 3.6,-8 8,-8 1.8,0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4,13.8 4,12zm8,8c-1.8,0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20,10.2 20,12c0,4.4 -3.6,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_do_not_disturb_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_do_not_disturb_24dp.xml
new file mode 100644
index 0000000..90eae2a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_do_not_disturb_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8 0,-1.85 0.63,-3.55 1.69,-4.9L16.9,18.31C15.55,19.37 13.85,20 12,20zm6.31,-3.1L7.1,5.69C8.45,4.63 10.15,4 12,4c4.42,0 8,3.58 8,8 0,1.85 -0.63,3.55 -1.69,4.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_drive_eta_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_drive_eta_24dp.xml
new file mode 100644
index 0000000..8550cf2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_drive_eta_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.92,5.01C18.72,4.42 18.16,4 17.5,4h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,11v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,15c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,12 6.5,12s1.5,0.67 1.5,1.5S7.33,15 6.5,15zm11,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,10l1.5,-4.5h11L19,10H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_available_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_available_24dp.xml
new file mode 100644
index 0000000..3071e64
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_available_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.53,11.06L15.47,10l-4.88,4.88 -2.12,-2.12 -1.06,1.06L10.59,17l5.94,-5.94zM19,3h-1V1h-2v2H8V1H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V8h14v11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_busy_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_busy_24dp.xml
new file mode 100644
index 0000000..6fb38ce
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_busy_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9.31,17l2.44,-2.44L14.19,17l1.06,-1.06 -2.44,-2.44 2.44,-2.44L14.19,10l-2.44,2.44L9.31,10l-1.06,1.06 2.44,2.44 -2.44,2.44L9.31,17zM19,3h-1V1h-2v2H8V1H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V8h14v11z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_note_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_note_24dp.xml
new file mode 100644
index 0000000..7aea2ac
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_event_note_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,10H7v2h10v-2zm2,-7h-1V1h-2v2H8V1H6v2H5c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zm0,16H5V8h14v11zm-5,-5H7v2h7v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_folder_special_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_folder_special_24dp.xml
new file mode 100644
index 0000000..d62bb4f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_folder_special_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,6h-8l-2,-2H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2zm-6.42,12L10,15.9 6.42,18l0.95,-4.07 -3.16,-2.74 4.16,-0.36L10,7l1.63,3.84 4.16,0.36 -3.16,2.74 0.95,4.06z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_mms_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_mms_24dp.xml
new file mode 100644
index 0000000..4f8b65f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_mms_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM5,14l3.5,-4.5 2.5,3.01L14.5,8l4.5,6H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_more_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_more_24dp.xml
new file mode 100644
index 0000000..060b18d
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_more_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,3H7c-0.69,0 -1.23,0.35 -1.59,0.88L0,12l5.41,8.11c0.36,0.53 0.97,0.89 1.66,0.89H22c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM9,13.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm5,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zm5,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_network_locked_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_network_locked_24dp.xml
new file mode 100644
index 0000000..cb260cc
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_network_locked_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19.5,10c0.17,0 0.33,0.03 0.5,0.05V1L1,20h13v-3c0,-0.89 0.39,-1.68 1,-2.23v-0.27c0,-2.48 2.02,-4.5 4.5,-4.5zm2.5,6v-1.5c0,-1.38 -1.12,-2.5 -2.5,-2.5S17,13.12 17,14.5V16c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1v-4c0,-0.55 -0.45,-1 -1,-1zm-1,0h-3v-1.5c0,-0.83 0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5V16z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_bluetooth_speaker_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_bluetooth_speaker_24dp.xml
new file mode 100644
index 0000000..58978a5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_bluetooth_speaker_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14.71,9.5L17,7.21V11h0.5l2.85,-2.85L18.21,6l2.15,-2.15L17.5,1H17v3.79L14.71,2.5l-0.71,0.71L16.79,6 14,8.79l0.71,0.71zM18,2.91l0.94,0.94 -0.94,0.94V2.91zm0,4.3l0.94,0.94 -0.94,0.94V7.21zm2,8.29c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.59l2.2,-2.21c0.28,-0.26 0.36,-0.65 0.25,-1C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_forwarded_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_forwarded_24dp.xml
new file mode 100644
index 0000000..7a544b5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_forwarded_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,11l5,-5 -5,-5v3h-4v4h4v3zm2,4.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.59l2.2,-2.21c0.28,-0.26 0.36,-0.65 0.25,-1C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_in_talk_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_in_talk_24dp.xml
new file mode 100644
index 0000000..6e22cac
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_in_talk_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,15.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.59l2.2,-2.21c0.28,-0.26 0.36,-0.65 0.25,-1C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1zM19,12h2c0,-4.97 -4.03,-9 -9,-9v2c3.87,0 7,3.13 7,7zm-4,0h2c0,-2.76 -2.24,-5 -5,-5v2c1.66,0 3,1.34 3,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_locked_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_locked_24dp.xml
new file mode 100644
index 0000000..236bef8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_locked_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,15.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.59l2.2,-2.21c0.28,-0.26 0.36,-0.65 0.25,-1C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1zM20,4v-0.5C20,2.12 18.88,1 17.5,1S15,2.12 15,3.5V4c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1V5c0,-0.55 -0.45,-1 -1,-1zm-0.8,0h-3.4v-0.5c0,-0.94 0.76,-1.7 1.7,-1.7s1.7,0.76 1.7,1.7V4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_missed_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_missed_24dp.xml
new file mode 100644
index 0000000..c03efaf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_missed_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6.5,5.5L12,11l7,-7 -1,-1 -6,6 -4.5,-4.5H11V3H5v6h1.5V5.5zm17.21,11.17C20.66,13.78 16.54,12 12,12 7.46,12 3.34,13.78 0.29,16.67c-0.18,0.18 -0.29,0.43 -0.29,0.71s0.11,0.53 0.29,0.71l2.48,2.48c0.18,0.18 0.43,0.29 0.71,0.29 0.27,0 0.52,-0.11 0.7,-0.28 0.79,-0.74 1.69,-1.36 2.66,-1.85 0.33,-0.16 0.56,-0.5 0.56,-0.9v-3.1c1.45,-0.48 3,-0.73 4.6,-0.73 1.6,0 3.15,0.25 4.6,0.72v3.1c0,0.39 0.23,0.74 0.56,0.9 0.98,0.49 1.87,1.12 2.67,1.85 0.18,0.18 0.43,0.28 0.7,0.28 0.28,0 0.53,-0.11 0.71,-0.29l2.48,-2.48c0.18,-0.18 0.29,-0.43 0.29,-0.71s-0.12,-0.52 -0.3,-0.7z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_paused_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_paused_24dp.xml
new file mode 100644
index 0000000..b4d51d8
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_phone_paused_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,3h-2v7h2V3zm3,12.5c-1.25,0 -2.45,-0.2 -3.57,-0.57 -0.35,-0.11 -0.74,-0.03 -1.02,0.24l-2.2,2.2c-2.83,-1.44 -5.15,-3.75 -6.59,-6.59l2.2,-2.21c0.28,-0.26 0.36,-0.65 0.25,-1C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.5c0,-0.55 -0.45,-1 -1,-1zM19,3v7h2V3h-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_play_download_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_play_download_24dp.xml
new file mode 100644
index 0000000..f18f833
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_play_download_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,6h-4V4l-2,-2h-4L8,4v2H4c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8c0,-1.11 -0.89,-2 -2,-2zM10,4h4v2h-4V4zm2,15l-5,-5h3v-4h4v4h3l-5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_play_install_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_play_install_24dp.xml
new file mode 100644
index 0000000..a14f860
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_play_install_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,6h-4V4l-2,-2h-4L8,4v2H4c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8c0,-1.11 -0.89,-2 -2,-2zM10,4h4v2h-4V4zm0.5,13.5L7,14l1.41,-1.41 2.09,2.09 5.18,-5.18 1.41,1.41 -6.59,6.59z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_sd_card_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sd_card_24dp.xml
new file mode 100644
index 0000000..74e0e41
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sd_card_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-6,6h-2V4h2v4zm3,0h-2V4h2v4zm3,0h-2V4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_sim_card_alert_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sim_card_alert_24dp.xml
new file mode 100644
index 0000000..ef5a545
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sim_card_alert_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-5,15h-2v-2h2v2zm0,-4h-2V8h2v5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_sms_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sms_24dp.xml
new file mode 100644
index 0000000..796a028
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sms_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM9,11H7V9h2v2zm4,0h-2V9h2v2zm4,0h-2V9h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_sms_failed_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sms_failed_24dp.xml
new file mode 100644
index 0000000..d9adbb3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sms_failed_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-7,12h-2v-2h2v2zm0,-4h-2V6h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_24dp.xml
new file mode 100644
index 0000000..5ad1561
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,4V1L8,5l4,4V6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zm0,14c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_disabled_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_disabled_24dp.xml
new file mode 100644
index 0000000..14e2932
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_disabled_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,6.35V4.26c-0.8,0.21 -1.55,0.54 -2.23,0.96l1.46,1.46c0.25,-0.12 0.5,-0.24 0.77,-0.33zm-7.14,-0.94l2.36,2.36C4.45,8.99 4,10.44 4,12c0,2.21 0.91,4.2 2.36,5.64L4,20h6v-6l-2.24,2.24C6.68,15.15 6,13.66 6,12c0,-1 0.25,-1.94 0.68,-2.77l8.08,8.08c-0.25,0.13 -0.5,0.25 -0.77,0.34v2.09c0.8,-0.21 1.55,-0.54 2.23,-0.96l2.36,2.36 1.27,-1.27L4.14,4.14 2.86,5.41zM20,4h-6v6l2.24,-2.24C17.32,8.85 18,10.34 18,12c0,1 -0.25,1.94 -0.68,2.77l1.46,1.46C19.55,15.01 20,13.56 20,12c0,-2.21 -0.91,-4.2 -2.36,-5.64L20,4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_problem_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_problem_24dp.xml
new file mode 100644
index 0000000..53a6dcd
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_sync_problem_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,12c0,2.21 0.91,4.2 2.36,5.64L3,20h6v-6l-2.24,2.24C5.68,15.15 5,13.66 5,12c0,-2.61 1.67,-4.83 4,-5.65V4.26C5.55,5.15 3,8.27 3,12zm8,5h2v-2h-2v2zM21,4h-6v6l2.24,-2.24C18.32,8.85 19,10.34 19,12c0,2.61 -1.67,4.83 -4,5.65v2.09c3.45,-0.89 6,-4.01 6,-7.74 0,-2.21 -0.91,-4.2 -2.36,-5.64L21,4zm-10,9h2V7h-2v6z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_system_update_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_system_update_24dp.xml
new file mode 100644
index 0000000..9f2103e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_system_update_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14zm-1,-6h-3V8h-2v5H8l4,4 4,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_tap_and_play_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_tap_and_play_24dp.xml
new file mode 100644
index 0000000..df5e24a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_tap_and_play_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2,16v2c2.76,0 5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zm0,4v3h3c0,-1.66 -1.34,-3 -3,-3zm0,-8v2c4.97,0 9,4.03 9,9h2c0,-6.08 -4.92,-11 -11,-11zM17,1.01L7,1c-1.1,0 -2,0.9 -2,2v7.37c0.69,0.16 1.36,0.37 2,0.64V5h10v13h-3.03c0.52,1.25 0.84,2.59 0.95,4H17c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_time_to_leave_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_time_to_leave_24dp.xml
new file mode 100644
index 0000000..8550cf2
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_time_to_leave_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.92,5.01C18.72,4.42 18.16,4 17.5,4h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,11v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,15c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,12 6.5,12s1.5,0.67 1.5,1.5S7.33,15 6.5,15zm11,0c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,10l1.5,-4.5h11L19,10H5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_vibration_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_vibration_24dp.xml
new file mode 100644
index 0000000..ce1d2c5
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_vibration_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M0,15h2V9H0v6zm3,2h2V7H3v10zm19,-8v6h2V9h-2zm-3,8h2V7h-2v10zM16.5,3h-9C6.67,3 6,3.67 6,4.5v15c0,0.83 0.67,1.5 1.5,1.5h9c0.83,0 1.5,-0.67 1.5,-1.5v-15c0,-0.83 -0.67,-1.5 -1.5,-1.5zM16,19H8V5h8v14z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_voice_chat_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_voice_chat_24dp.xml
new file mode 100644
index 0000000..51c8fb6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_voice_chat_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,2H4c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-2,12l-4,-3.2V14H6V6h8v3.2L18,6v8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/notification/ic_vpn_lock_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/notification/ic_vpn_lock_24dp.xml
new file mode 100644
index 0000000..8a4cff7
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/notification/ic_vpn_lock_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,4v-0.5C22,2.12 20.88,1 19.5,1S17,2.12 17,3.5V4c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h5c0.55,0 1,-0.45 1,-1V5c0,-0.55 -0.45,-1 -1,-1zm-0.8,0h-3.4v-0.5c0,-0.94 0.76,-1.7 1.7,-1.7s1.7,0.76 1.7,1.7V4zm-2.28,8c0.04,0.33 0.08,0.66 0.08,1 0,2.08 -0.8,3.97 -2.1,5.39 -0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1H7v-2h2c0.55,0 1,-0.45 1,-1V8h2c1.1,0 2,-0.9 2,-2V3.46c-0.95,-0.3 -1.95,-0.46 -3,-0.46C5.48,3 1,7.48 1,13s4.48,10 10,10 10,-4.48 10,-10c0,-0.34 -0.02,-0.67 -0.05,-1h-2.03zM10,20.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L8,16v1c0,1.1 0.9,2 2,2v1.93z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_cake_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_cake_24dp.xml
new file mode 100644
index 0000000..19280f1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_cake_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,6c1.11,0 2,-0.9 2,-2 0,-0.38 -0.1,-0.73 -0.29,-1.03L12,0l-1.71,2.97c-0.19,0.3 -0.29,0.65 -0.29,1.03 0,1.1 0.9,2 2,2zm4.6,9.99l-1.07,-1.07 -1.08,1.07c-1.3,1.3 -3.58,1.31 -4.89,0l-1.07,-1.07 -1.09,1.07C6.75,16.64 5.88,17 4.96,17c-0.73,0 -1.4,-0.23 -1.96,-0.61V21c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1v-4.61c-0.56,0.38 -1.23,0.61 -1.96,0.61 -0.92,0 -1.79,-0.36 -2.44,-1.01zM18,9h-5V7h-2v2H6c-1.66,0 -3,1.34 -3,3v1.54c0,1.08 0.88,1.96 1.96,1.96 0.52,0 1.02,-0.2 1.38,-0.57l2.14,-2.13 2.13,2.13c0.74,0.74 2.03,0.74 2.77,0l2.14,-2.13 2.13,2.13c0.37,0.37 0.86,0.57 1.38,0.57 1.08,0 1.96,-0.88 1.96,-1.96V12C21,10.34 19.66,9 18,9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_domain_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_domain_24dp.xml
new file mode 100644
index 0000000..62bc39f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_domain_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,7V3H2v18h20V7H12zM6,19H4v-2h2v2zm0,-4H4v-2h2v2zm0,-4H4V9h2v2zm0,-4H4V5h2v2zm4,12H8v-2h2v2zm0,-4H8v-2h2v2zm0,-4H8V9h2v2zm0,-4H8V5h2v2zm10,12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2,-8h-2v2h2v-2zm0,4h-2v2h2v-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_group_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_group_24dp.xml
new file mode 100644
index 0000000..8b0643f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_group_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zm-8,0c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zm0,2c-2.33,0 -7,1.17 -7,3.5V19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zm8,0c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45V19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_group_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_group_add_24dp.xml
new file mode 100644
index 0000000..ba42e71
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_group_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M8,10H5V7H3v3H0v2h3v3h2v-3h3v-2zm10,1c1.66,0 2.99,-1.34 2.99,-3S19.66,5 18,5c-0.32,0 -0.63,0.05 -0.91,0.14 0.57,0.81 0.9,1.79 0.9,2.86s-0.34,2.04 -0.9,2.86c0.28,0.09 0.59,0.14 0.91,0.14zm-5,0c1.66,0 2.99,-1.34 2.99,-3S14.66,5 13,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zm6.62,2.16c0.83,0.73 1.38,1.66 1.38,2.84v2h3v-2c0,-1.54 -2.37,-2.49 -4.38,-2.84zM13,13c-2,0 -6,1 -6,3v2h12v-2c0,-2 -4,-3 -6,-3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_location_city_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_location_city_24dp.xml
new file mode 100644
index 0000000..0b0e88f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_location_city_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,11V5l-3,-3 -3,3v2H3v14h18V11h-6zm-8,8H5v-2h2v2zm0,-4H5v-2h2v2zm0,-4H5V9h2v2zm6,8h-2v-2h2v2zm0,-4h-2v-2h2v2zm0,-4h-2V9h2v2zm0,-4h-2V5h2v2zm6,12h-2v-2h2v2zm0,-4h-2v-2h2v2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_mood_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_mood_24dp.xml
new file mode 100644
index 0000000..1ef868a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_mood_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zm3.5,-9c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zm-7,0c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zm3.5,6.5c2.33,0 4.31,-1.46 5.11,-3.5H6.89c0.8,2.04 2.78,3.5 5.11,3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_24dp.xml
new file mode 100644
index 0000000..8902d2a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zm6.5,-6v-5.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_none_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_none_24dp.xml
new file mode 100644
index 0000000..6fbff40
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_none_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zm6.5,-6v-5.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2zm-2,1H7v-6.5C7,8.01 9.01,6 11.5,6S16,8.01 16,10.5V17z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_off_24dp.xml
new file mode 100644
index 0000000..86c52d9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zM18,10.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-0.51,0.12 -0.99,0.32 -1.45,0.56L18,14.18V10.5zm-0.27,8.5l2,2L21,19.73 4.27,3 3,4.27l2.92,2.92C5.34,8.16 5,9.29 5,10.5V16l-2,2v1h14.73z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_on_24dp.xml
new file mode 100644
index 0000000..d484b8a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M6.58,3.58L5.15,2.15C2.76,3.97 1.18,6.8 1.03,10h2c0.15,-2.65 1.51,-4.97 3.55,-6.42zM19.97,10h2c-0.15,-3.2 -1.73,-6.03 -4.13,-7.85l-1.43,1.43c2.05,1.45 3.41,3.77 3.56,6.42zm-1.97,0.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2v-5.5zM11.5,22c0.14,0 0.27,-0.01 0.4,-0.04 0.65,-0.13 1.19,-0.58 1.44,-1.18 0.1,-0.24 0.16,-0.5 0.16,-0.78h-4c0,1.1 0.9,2 2,2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_paused_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_paused_24dp.xml
new file mode 100644
index 0000000..5a4d741
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_notifications_paused_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zm6.5,-6v-5.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2zm-4,-6.2l-2.8,3.4H14V15H9v-1.8l2.8,-3.4H9V8h5v1.8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_pages_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_pages_24dp.xml
new file mode 100644
index 0000000..65b27bf
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_pages_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,5v6h5L7,7l4,1V3H5c-1.1,0 -2,0.9 -2,2zm5,8H3v6c0,1.1 0.9,2 2,2h6v-5l-4,1 1,-4zm9,4l-4,-1v5h6c1.1,0 2,-0.9 2,-2v-6h-5l1,4zm2,-14h-6v5l4,-1 -1,4h5V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_party_mode_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_party_mode_24dp.xml
new file mode 100644
index 0000000..cfb41d9
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_party_mode_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,4h-3.17L15,2H9L7.17,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2zm-8,3c1.63,0 3.06,0.79 3.98,2H12c-1.66,0 -3,1.34 -3,3 0,0.35 0.07,0.69 0.18,1H7.1c-0.06,-0.32 -0.1,-0.66 -0.1,-1 0,-2.76 2.24,-5 5,-5zm0,10c-1.63,0 -3.06,-0.79 -3.98,-2H12c1.66,0 3,-1.34 3,-3 0,-0.35 -0.07,-0.69 -0.18,-1h2.08c0.07,0.32 0.1,0.66 0.1,1 0,2.76 -2.24,5 -5,5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_people_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_people_24dp.xml
new file mode 100644
index 0000000..8b0643f
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_people_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zm-8,0c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zm0,2c-2.33,0 -7,1.17 -7,3.5V19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zm8,0c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45V19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_people_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_people_outline_24dp.xml
new file mode 100644
index 0000000..6cea149
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_people_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16.5,13c-1.2,0 -3.07,0.34 -4.5,1 -1.43,-0.67 -3.3,-1 -4.5,-1C5.33,13 1,14.08 1,16.25V19h22v-2.75c0,-2.17 -4.33,-3.25 -6.5,-3.25zm-4,4.5h-10v-1.25c0,-0.54 2.56,-1.75 5,-1.75s5,1.21 5,1.75v1.25zm9,0H14v-1.25c0,-0.46 -0.2,-0.86 -0.52,-1.22 0.88,-0.3 1.96,-0.53 3.02,-0.53 2.44,0 5,1.21 5,1.75v1.25zM7.5,12c1.93,0 3.5,-1.57 3.5,-3.5S9.43,5 7.5,5 4,6.57 4,8.5 5.57,12 7.5,12zm0,-5.5c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2zm9,5.5c1.93,0 3.5,-1.57 3.5,-3.5S18.43,5 16.5,5 13,6.57 13,8.5s1.57,3.5 3.5,3.5zm0,-5.5c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_person_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_person_24dp.xml
new file mode 100644
index 0000000..587e14c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_person_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zm0,2c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_person_add_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_person_add_24dp.xml
new file mode 100644
index 0000000..e43603c
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_person_add_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zm-9,-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9,4c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_person_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_person_outline_24dp.xml
new file mode 100644
index 0000000..a34bd73
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_person_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,5.9c1.16,0 2.1,0.94 2.1,2.1s-0.94,2.1 -2.1,2.1S9.9,9.16 9.9,8s0.94,-2.1 2.1,-2.1m0,9c2.97,0 6.1,1.46 6.1,2.1v1.1H5.9V17c0,-0.64 3.13,-2.1 6.1,-2.1M12,4C9.79,4 8,5.79 8,8s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zm0,9c-2.67,0 -8,1.34 -8,4v3h16v-3c0,-2.66 -5.33,-4 -8,-4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_plus_one_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_plus_one_24dp.xml
new file mode 100644
index 0000000..9b8344a
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_plus_one_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,8H8v4H4v2h4v4h2v-4h4v-2h-4zm4.5,-1.92V7.9l2.5,-0.5V18h2V5z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_poll_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_poll_24dp.xml
new file mode 100644
index 0000000..1d393b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_poll_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM9,17H7v-7h2v7zm4,0h-2V7h2v10zm4,0h-2v-4h2v4z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_public_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_public_24dp.xml
new file mode 100644
index 0000000..d6e43bb
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_public_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm-1,17.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zm6.9,-2.54c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1H8v-2h2c0.55,0 1,-0.45 1,-1V7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_school_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_school_24dp.xml
new file mode 100644
index 0000000..4ed1f28
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_school_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5,13.18v4L12,21l7,-3.82v-4L12,17l-7,-3.82zM12,3L1,9l11,6 9,-4.91V17h2V9L12,3z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_share_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_share_24dp.xml
new file mode 100644
index 0000000..737da72
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_share_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/social/ic_whatshot_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/social/ic_whatshot_24dp.xml
new file mode 100644
index 0000000..b2dd494
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/social/ic_whatshot_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13.5,0.67s0.74,2.65 0.74,4.8c0,2.06 -1.35,3.73 -3.41,3.73 -2.07,0 -3.63,-1.67 -3.63,-3.73l0.03,-0.36C5.21,7.51 4,10.62 4,14c0,4.42 3.58,8 8,8s8,-3.58 8,-8C20,8.61 17.41,3.8 13.5,0.67zM11.71,19c-1.78,0 -3.22,-1.4 -3.22,-3.14 0,-1.62 1.05,-2.76 2.81,-3.12 1.77,-0.36 3.6,-1.21 4.62,-2.58 0.39,1.29 0.59,2.65 0.59,4.04 0,2.65 -2.15,4.8 -4.8,4.8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/test_path_01.xml b/asset-studio/src/main/java/images/material_design_icons/test_path_01.xml
new file mode 100644
index 0000000..5ec99ab
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/test_path_01.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="800dp"
+ android:height="800dp"
+ android:viewportWidth="800.0"
+ android:viewportHeight="800.0">
+ <path
+ android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299"
+ android:fillColor="#00000000"
+ android:strokeColor="#FF0000"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/test_path_02.xml b/asset-studio/src/main/java/images/material_design_icons/test_path_02.xml
new file mode 100644
index 0000000..7db7f33
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/test_path_02.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="800dp"
+ android:height="800dp"
+ android:viewportWidth="800.0"
+ android:viewportHeight="800.0">
+ <path
+ android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299"
+ android:fillColor="#00000000"
+ android:strokeColor="#FF0000"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/test_qt_01.xml b/asset-studio/src/main/java/images/material_design_icons/test_qt_01.xml
new file mode 100644
index 0000000..3d1e007
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/test_qt_01.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="800dp"
+ android:height="600dp"
+ android:viewportWidth="800.0"
+ android:viewportHeight="600.0">
+ <path
+ android:pathData="m0,200q200,250 200,0t200,0Q500,400,600,200T800,200"
+ android:fillColor="#000000"
+ android:strokeColor="#000000"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/toggle/ic_check_box_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_check_box_24dp.xml
new file mode 100644
index 0000000..7c34d3e
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_check_box_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2V5c0,-1.1 -0.89,-2 -2,-2zm-9,14l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/toggle/ic_check_box_outline_blank_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_check_box_outline_blank_24dp.xml
new file mode 100644
index 0000000..ce7d078
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_check_box_outline_blank_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/toggle/ic_radio_button_off_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_radio_button_off_24dp.xml
new file mode 100644
index 0000000..3ea75d6
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_radio_button_off_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/toggle/ic_radio_button_on_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_radio_button_on_24dp.xml
new file mode 100644
index 0000000..7eb8099
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_radio_button_on_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5zm0,-5C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_24dp.xml
new file mode 100644
index 0000000..e2c53b1
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_half_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_half_24dp.xml
new file mode 100644
index 0000000..80f11db
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_half_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,9.74l-7.19,-0.62L12,2.5 9.19,9.13 2,9.74l5.46,4.73 -1.64,7.03L12,17.77l6.18,3.73 -1.63,-7.03L22,9.74zM12,15.9V6.6l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.9z"/>
+</vector>
diff --git a/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_outline_24dp.xml b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_outline_24dp.xml
new file mode 100644
index 0000000..4ca7ae3
--- /dev/null
+++ b/asset-studio/src/main/java/images/material_design_icons/toggle/ic_star_outline_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
+</vector>
diff --git a/asset-studio/src/test/java/com/android/assetstudiolib/ActionBarIconGeneratorTest.java b/asset-studio/src/test/java/com/android/assetstudiolib/ActionBarIconGeneratorTest.java
index ed5ea04..b18912f 100644
--- a/asset-studio/src/test/java/com/android/assetstudiolib/ActionBarIconGeneratorTest.java
+++ b/asset-studio/src/test/java/com/android/assetstudiolib/ActionBarIconGeneratorTest.java
@@ -22,7 +22,7 @@
import java.io.IOException;
@SuppressWarnings("javadoc")
-public class ActionBarIconGeneratorTest extends GeneratorTest {
+public class ActionBarIconGeneratorTest extends BitmapGeneratorTest {
private void checkGraphic(String baseName, Theme theme) throws IOException {
ActionBarOptions options = new ActionBarOptions();
options.theme = theme;
diff --git a/asset-studio/src/test/java/com/android/assetstudiolib/BitmapGeneratorTest.java b/asset-studio/src/test/java/com/android/assetstudiolib/BitmapGeneratorTest.java
new file mode 100644
index 0000000..c10a778
--- /dev/null
+++ b/asset-studio/src/test/java/com/android/assetstudiolib/BitmapGeneratorTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.assetstudiolib;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+
+/**
+ * Shared test infrastructure for bitmap generator
+ */
+public abstract class BitmapGeneratorTest extends GeneratorTest implements GraphicGeneratorContext {
+ private static final String TEST_DATA_REL_PATH =
+ "tools/base/asset-studio/src/test/java/com/android/assetstudiolib/testdata";
+
+ @Override
+ protected String getTestDataRelPath() {
+ return TEST_DATA_REL_PATH;
+ };
+
+ protected void checkGraphic(int expectedFileCount, String folderName, String baseName,
+ GraphicGenerator generator, GraphicGenerator.Options options)
+ throws IOException {
+ Map<String, Map<String, BufferedImage>> categoryMap =
+ new HashMap<String, Map<String, BufferedImage>>();
+ options.sourceImage = GraphicGenerator.getClipartImage("android.png");
+ generator.generate(null, categoryMap, this, options, baseName);
+
+ File targetDir = getTargetDir();
+
+ List<String> errors = new ArrayList<String>();
+ int fileCount = 0;
+ for (Map<String, BufferedImage> previews : categoryMap.values()) {
+ for (Map.Entry<String, BufferedImage> entry : previews.entrySet()) {
+ String relativePath = entry.getKey();
+ BufferedImage image = entry.getValue();
+
+ if (image == null) continue;
+
+ String path = "testdata" + File.separator + folderName + File.separator
+ + relativePath;
+ InputStream is = BitmapGeneratorTest.class.getResourceAsStream(path);
+ if (is == null) {
+ String filePath = folderName + File.separator + relativePath;
+ String generatedFilePath = generateGoldenImage(targetDir, image, path, filePath);
+ errors.add("File did not exist, created " + generatedFilePath);
+ } else {
+ BufferedImage goldenImage = ImageIO.read(is);
+ assertImageSimilar(relativePath, goldenImage, image, 5.0f);
+ }
+ }
+
+ fileCount += previews.values().size();
+ }
+ if (!errors.isEmpty()) {
+ fail(errors.toString());
+ }
+
+ assertEquals("Wrong number of generated files", expectedFileCount, fileCount);
+ }
+
+ @Override
+ public BufferedImage loadImageResource(String path) {
+ try {
+ return GraphicGenerator.getStencilImage(path);
+ } catch (IOException e) {
+ fail(e.toString());
+ }
+
+ return null;
+ }
+}
diff --git a/asset-studio/src/test/java/com/android/assetstudiolib/GeneratorTest.java b/asset-studio/src/test/java/com/android/assetstudiolib/GeneratorTest.java
index c8e2b3a..32cba47 100644
--- a/asset-studio/src/test/java/com/android/assetstudiolib/GeneratorTest.java
+++ b/asset-studio/src/test/java/com/android/assetstudiolib/GeneratorTest.java
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
*
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.eclipse.org/org/documents/epl-v10.php
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,216 +13,166 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.assetstudiolib;
-import java.awt.Color;
-import java.awt.Graphics;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.imageio.ImageIO;
-
import junit.framework.TestCase;
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
/**
- * Shared test infrastructure for code generator
+ * Shared test infrastructure for asset (either bitmap or vector) generator.
+ * TODO: Merge this file with the duplicated one in sdk-common.
*/
-public abstract class GeneratorTest extends TestCase implements GraphicGeneratorContext {
- private static final String TEST_DATA_REL_PATH =
- "tools/base/asset-studio/src/test/java/com/android/assetstudiolib/testdata";
+public abstract class GeneratorTest extends TestCase {
+ protected abstract String getTestDataRelPath();
- protected void checkGraphic(int expectedFileCount, String folderName, String baseName,
- GraphicGenerator generator, GraphicGenerator.Options options)
- throws IOException {
- Map<String, Map<String, BufferedImage>> categoryMap =
- new HashMap<String, Map<String, BufferedImage>>();
- options.sourceImage = GraphicGenerator.getClipartImage("android.png");
- generator.generate(null, categoryMap, this, options, baseName);
-
- File targetDir = getTargetDir();
-
- List<String> errors = new ArrayList<String>();
- int fileCount = 0;
- for (Map<String, BufferedImage> previews : categoryMap.values()) {
- for (Map.Entry<String, BufferedImage> entry : previews.entrySet()) {
- String relativePath = entry.getKey();
- BufferedImage image = entry.getValue();
-
- if (image == null) continue;
-
- String path = "testdata" + File.separator + folderName + File.separator
- + relativePath;
- InputStream is = GeneratorTest.class.getResourceAsStream(path);
- if (is == null) {
- if (targetDir == null) {
- fail("Did not find " + path
- + ". Set $ANDROID_SRC to have it created automatically");
- }
- File fileName = new File(targetDir, folderName + File.separator
- + relativePath);
- assertFalse(fileName.exists());
- if (!fileName.getParentFile().exists()) {
- boolean mkdir = fileName.getParentFile().mkdirs();
- assertTrue(fileName.getParent(), mkdir);
- }
-
- ImageIO.write(image, "PNG", fileName);
- errors.add("File did not exist, created " + fileName.getPath());
- } else {
- BufferedImage goldenImage = ImageIO.read(is);
- assertImageSimilar(relativePath, goldenImage, image, 5.0f);
- }
- }
-
- fileCount += previews.values().size();
- }
- if (errors.size() > 0) {
- fail(errors.toString());
- }
-
- assertEquals("Wrong number of generated files", expectedFileCount, fileCount);
+ protected String generateGoldenImage(File targetDir,
+ BufferedImage goldenImage,
+ String missingFilePath,
+ String filePath) throws IOException {
+ if (targetDir == null) {
+ fail(
+ "Did not find " + missingFilePath + ". Set $ANDROID_SRC to have it created automatically");
+ }
+ File fileName = new File(targetDir, filePath);
+ assertFalse(fileName.exists());
+ if (!fileName.getParentFile().exists()) {
+ boolean mkdir = fileName.getParentFile().mkdirs();
+ assertTrue(fileName.getParent(), mkdir);
}
- public static void assertImageSimilar(String imageName, BufferedImage goldenImage,
- BufferedImage image, float maxPercentDifferent) throws IOException {
- assertTrue("Widths differ too much for " + imageName, Math.abs(goldenImage.getWidth()
- - image.getWidth()) < 2);
- assertTrue("Widths differ too much for " + imageName, Math.abs(goldenImage.getHeight()
- - image.getHeight()) < 2);
+ ImageIO.write(goldenImage, "PNG", fileName);
+ return fileName.getPath();
+ }
- assertEquals(BufferedImage.TYPE_INT_ARGB, image.getType());
+ public static void assertImageSimilar(String imageName,
+ BufferedImage goldenImage,
+ BufferedImage image,
+ float maxPercentDifferent) throws IOException {
+ assertTrue("Widths differ too much for " + imageName,
+ Math.abs(goldenImage.getWidth() - image.getWidth()) < 2);
+ assertTrue("Widths differ too much for " + imageName,
+ Math.abs(goldenImage.getHeight() - image.getHeight()) < 2);
- if (goldenImage.getType() != BufferedImage.TYPE_INT_ARGB) {
- BufferedImage temp = new BufferedImage(goldenImage.getWidth(), goldenImage.getHeight(),
- BufferedImage.TYPE_INT_ARGB);
- temp.getGraphics().drawImage(goldenImage, 0, 0, null);
- goldenImage = temp;
- }
- assertEquals(BufferedImage.TYPE_INT_ARGB, goldenImage.getType());
+ assertEquals(BufferedImage.TYPE_INT_ARGB, image.getType());
- int imageWidth = Math.min(goldenImage.getWidth(), image.getWidth());
- int imageHeight = Math.min(goldenImage.getHeight(), image.getHeight());
+ if (goldenImage.getType() != BufferedImage.TYPE_INT_ARGB) {
+ BufferedImage temp = new BufferedImage(goldenImage.getWidth(), goldenImage.getHeight(),
+ BufferedImage.TYPE_INT_ARGB);
+ temp.getGraphics().drawImage(goldenImage, 0, 0, null);
+ goldenImage = temp;
+ }
+ assertEquals(BufferedImage.TYPE_INT_ARGB, goldenImage.getType());
- // Blur the images to account for the scenarios where there are pixel
- // differences
- // in where a sharp edge occurs
- // goldenImage = blur(goldenImage, 6);
- // image = blur(image, 6);
+ int imageWidth = Math.min(goldenImage.getWidth(), image.getWidth());
+ int imageHeight = Math.min(goldenImage.getHeight(), image.getHeight());
- int width = 3 * imageWidth;
- int height = imageHeight;
- BufferedImage deltaImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
- Graphics g = deltaImage.getGraphics();
+ // Blur the images to account for the scenarios where there are pixel
+ // differences
+ // in where a sharp edge occurs
+ // goldenImage = blur(goldenImage, 6);
+ // image = blur(image, 6);
- // Compute delta map
- long delta = 0;
- for (int y = 0; y < imageHeight; y++) {
- for (int x = 0; x < imageWidth; x++) {
- int goldenRgb = goldenImage.getRGB(x, y);
- int rgb = image.getRGB(x, y);
- if (goldenRgb == rgb) {
- deltaImage.setRGB(imageWidth + x, y, 0x00808080);
- continue;
- }
+ int width = 3 * imageWidth;
+ int height = imageHeight;
+ BufferedImage deltaImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ Graphics g = deltaImage.getGraphics();
- // If the pixels have no opacity, don't delta colors at all
- if (((goldenRgb & 0xFF000000) == 0) && (rgb & 0xFF000000) == 0) {
- deltaImage.setRGB(imageWidth + x, y, 0x00808080);
- continue;
- }
-
- int deltaR = ((rgb & 0xFF0000) >>> 16) - ((goldenRgb & 0xFF0000) >>> 16);
- int newR = 128 + deltaR & 0xFF;
- int deltaG = ((rgb & 0x00FF00) >>> 8) - ((goldenRgb & 0x00FF00) >>> 8);
- int newG = 128 + deltaG & 0xFF;
- int deltaB = (rgb & 0x0000FF) - (goldenRgb & 0x0000FF);
- int newB = 128 + deltaB & 0xFF;
-
- int avgAlpha = ((((goldenRgb & 0xFF000000) >>> 24)
- + ((rgb & 0xFF000000) >>> 24)) / 2) << 24;
-
- int newRGB = avgAlpha | newR << 16 | newG << 8 | newB;
- deltaImage.setRGB(imageWidth + x, y, newRGB);
-
- delta += Math.abs(deltaR);
- delta += Math.abs(deltaG);
- delta += Math.abs(deltaB);
- }
+ // Compute delta map
+ long delta = 0;
+ for (int y = 0; y < imageHeight; y++) {
+ for (int x = 0; x < imageWidth; x++) {
+ int goldenRgb = goldenImage.getRGB(x, y);
+ int rgb = image.getRGB(x, y);
+ if (goldenRgb == rgb) {
+ deltaImage.setRGB(imageWidth + x, y, 0x00808080);
+ continue;
}
- // 3 different colors, 256 color levels
- long total = imageHeight * imageWidth * 3L * 256L;
- float percentDifference = (float) (delta * 100 / (double) total);
-
- if (percentDifference > maxPercentDifferent) {
- // Expected on the left
- // Golden on the right
- g.drawImage(goldenImage, 0, 0, null);
- g.drawImage(image, 2 * imageWidth, 0, null);
-
- // Labels
- if (imageWidth > 80) {
- g.setColor(Color.RED);
- g.drawString("Expected", 10, 20);
- g.drawString("Actual", 2 * imageWidth + 10, 20);
- }
-
- File output = new File(getTempDir(), "delta-"
- + imageName.replace(File.separatorChar, '_'));
- if (output.exists()) {
- output.delete();
- }
- ImageIO.write(deltaImage, "PNG", output);
- String message = String.format("Images differ (by %.1f%%) - see details in %s",
- percentDifference, output);
- System.out.println(message);
- fail(message);
+ // If the pixels have no opacity, don't delta colors at all
+ if (((goldenRgb & 0xFF000000) == 0) && (rgb & 0xFF000000) == 0) {
+ deltaImage.setRGB(imageWidth + x, y, 0x00808080);
+ continue;
}
- g.dispose();
+ int deltaR = ((rgb & 0xFF0000) >>> 16) - ((goldenRgb & 0xFF0000) >>> 16);
+ int newR = 128 + deltaR & 0xFF;
+ int deltaG = ((rgb & 0x00FF00) >>> 8) - ((goldenRgb & 0x00FF00) >>> 8);
+ int newG = 128 + deltaG & 0xFF;
+ int deltaB = (rgb & 0x0000FF) - (goldenRgb & 0x0000FF);
+ int newB = 128 + deltaB & 0xFF;
+
+ int avgAlpha =
+ ((((goldenRgb & 0xFF000000) >>> 24) + ((rgb & 0xFF000000) >>> 24)) / 2) << 24;
+
+ int newRGB = avgAlpha | newR << 16 | newG << 8 | newB;
+ deltaImage.setRGB(imageWidth + x, y, newRGB);
+
+ delta += Math.abs(deltaR);
+ delta += Math.abs(deltaG);
+ delta += Math.abs(deltaB);
+ }
}
- protected static File getTempDir() {
- if (System.getProperty("os.name").equals("Mac OS X")) {
- return new File("/tmp"); //$NON-NLS-1$
- }
+ // 3 different colors, 256 color levels
+ long total = imageHeight * imageWidth * 3L * 256L;
+ float percentDifference = (float)(delta * 100 / (double)total);
- return new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
+ if (percentDifference > maxPercentDifferent) {
+ // Expected on the left
+ // Golden on the right
+ g.drawImage(goldenImage, 0, 0, null);
+ g.drawImage(image, 2 * imageWidth, 0, null);
+
+ // Labels
+ if (imageWidth > 80) {
+ g.setColor(Color.RED);
+ g.drawString("Expected", 10, 20);
+ g.drawString("Actual", 2 * imageWidth + 10, 20);
+ }
+
+ File output = new File(getTempDir(), "delta-" + imageName.replace(File.separatorChar, '_'));
+ if (output.exists()) {
+ output.delete();
+ }
+ ImageIO.write(deltaImage, "PNG", output);
+ String message =
+ String.format("Images differ (by %.1f%%) - see details in %s", percentDifference, output);
+ System.out.println(message);
+ fail(message);
}
- @Override
- public BufferedImage loadImageResource(String path) {
- try {
- return GraphicGenerator.getStencilImage(path);
- } catch (IOException e) {
- fail(e.toString());
- }
+ g.dispose();
+ }
- return null;
+ protected static File getTempDir() {
+ if (System.getProperty("os.name").equals("Mac OS X")) {
+ return new File("/tmp"); //$NON-NLS-1$
}
- /** Get the location to write missing golden files to */
- protected File getTargetDir() {
- // Set $ANDROID_SRC to point to your git AOSP working tree
- String sdk = System.getenv("ANDROID_SRC");
- if (sdk != null) {
- File root = new File(sdk);
- if (root.exists()) {
- File testData = new File(root, TEST_DATA_REL_PATH.replace('/',
- File.separatorChar));
- if (testData.exists()) {
- return testData;
- }
- }
- }
+ return new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
+ }
- return null;
+ /**
+ * Get the location to write missing golden files to
+ */
+ protected File getTargetDir() {
+ // Set $ANDROID_SRC to point to your git AOSP working tree
+ String sdk = System.getenv("ANDROID_SRC");
+ if (sdk != null) {
+ File root = new File(sdk);
+ if (root.exists()) {
+ File testData = new File(root, getTestDataRelPath().replace('/', File.separatorChar));
+ if (testData.exists()) {
+ return testData;
+ }
+ }
}
+
+ return null;
+ }
}
diff --git a/asset-studio/src/test/java/com/android/assetstudiolib/LauncherIconGeneratorTest.java b/asset-studio/src/test/java/com/android/assetstudiolib/LauncherIconGeneratorTest.java
index 085221f..4bcba0f 100644
--- a/asset-studio/src/test/java/com/android/assetstudiolib/LauncherIconGeneratorTest.java
+++ b/asset-studio/src/test/java/com/android/assetstudiolib/LauncherIconGeneratorTest.java
@@ -21,7 +21,7 @@
import java.io.IOException;
@SuppressWarnings("javadoc")
-public class LauncherIconGeneratorTest extends GeneratorTest {
+public class LauncherIconGeneratorTest extends BitmapGeneratorTest {
private void checkGraphic(String baseName,
GraphicGenerator.Shape shape, GraphicGenerator.Style style,
boolean crop, int background, boolean isWebGraphic) throws IOException {
diff --git a/asset-studio/src/test/java/com/android/assetstudiolib/MenuIconGeneratorTest.java b/asset-studio/src/test/java/com/android/assetstudiolib/MenuIconGeneratorTest.java
index 700be4b..2ba8f55 100644
--- a/asset-studio/src/test/java/com/android/assetstudiolib/MenuIconGeneratorTest.java
+++ b/asset-studio/src/test/java/com/android/assetstudiolib/MenuIconGeneratorTest.java
@@ -19,7 +19,7 @@
import java.io.IOException;
@SuppressWarnings("javadoc")
-public class MenuIconGeneratorTest extends GeneratorTest {
+public class MenuIconGeneratorTest extends BitmapGeneratorTest {
private void checkGraphic(String baseName) throws IOException {
MenuIconGenerator generator = new MenuIconGenerator();
checkGraphic(4, "menus", baseName, generator, new GraphicGenerator.Options());
diff --git a/asset-studio/src/test/java/com/android/assetstudiolib/NotificationIconGeneratorTest.java b/asset-studio/src/test/java/com/android/assetstudiolib/NotificationIconGeneratorTest.java
index 1880d2b..d37a96d 100644
--- a/asset-studio/src/test/java/com/android/assetstudiolib/NotificationIconGeneratorTest.java
+++ b/asset-studio/src/test/java/com/android/assetstudiolib/NotificationIconGeneratorTest.java
@@ -21,7 +21,7 @@
import java.io.IOException;
@SuppressWarnings("javadoc")
-public class NotificationIconGeneratorTest extends GeneratorTest {
+public class NotificationIconGeneratorTest extends BitmapGeneratorTest {
private void checkGraphic(String baseName, int minSdk, String folderName,
int expectedCount) throws IOException {
NotificationOptions options = new NotificationOptions();
diff --git a/asset-studio/src/test/java/com/android/assetstudiolib/TabIconGeneratorTest.java b/asset-studio/src/test/java/com/android/assetstudiolib/TabIconGeneratorTest.java
index 4231f54..bf3fe28 100644
--- a/asset-studio/src/test/java/com/android/assetstudiolib/TabIconGeneratorTest.java
+++ b/asset-studio/src/test/java/com/android/assetstudiolib/TabIconGeneratorTest.java
@@ -19,7 +19,7 @@
import java.io.IOException;
@SuppressWarnings("javadoc")
-public class TabIconGeneratorTest extends GeneratorTest {
+public class TabIconGeneratorTest extends BitmapGeneratorTest {
private void checkGraphic(String folderName, String baseName, int minSdk,
int expectedFileCount) throws IOException {
TabIconGenerator generator = new TabIconGenerator();
diff --git a/build-system/builder-model/build.gradle b/build-system/builder-model/build.gradle
index 0f8e9ec..0f2fc5f 100644
--- a/build-system/builder-model/build.gradle
+++ b/build-system/builder-model/build.gradle
@@ -1,12 +1,3 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
apply plugin: 'clone-artifacts'
apply plugin: 'sdk-java-lib'
diff --git a/build-system/builder-model/src/main/java/com/android/build/OutputFile.java b/build-system/builder-model/src/main/java/com/android/build/OutputFile.java
index 1a382a8..bcfd8cd 100644
--- a/build-system/builder-model/src/main/java/com/android/build/OutputFile.java
+++ b/build-system/builder-model/src/main/java/com/android/build/OutputFile.java
@@ -17,7 +17,6 @@
package com.android.build;
import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
import java.io.File;
import java.util.Collection;
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/AdbOptions.java b/build-system/builder-model/src/main/java/com/android/builder/model/AdbOptions.java
index 483a208..9f92de9 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/AdbOptions.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/AdbOptions.java
@@ -16,8 +16,6 @@
package com.android.builder.model;
-import com.android.annotations.NonNull;
-
import java.util.Collection;
/**
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/AndroidArtifact.java b/build-system/builder-model/src/main/java/com/android/builder/model/AndroidArtifact.java
index 53ba933..4350079 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/AndroidArtifact.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/AndroidArtifact.java
@@ -102,6 +102,12 @@
Set<String> getAbiFilters();
/**
+ * Returns the native libraries associated with the artifact.
+ */
+ @Nullable
+ Collection<NativeLibrary> getNativeLibraries();
+
+ /**
* Map of Build Config Fields where the key is the field name.
*
* @return a non-null map of class fields (possibly empty).
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/AndroidLibrary.java b/build-system/builder-model/src/main/java/com/android/builder/model/AndroidLibrary.java
index 6ef2155..3e8f5ca 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/AndroidLibrary.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/AndroidLibrary.java
@@ -156,4 +156,15 @@
*/
@NonNull
File getPublicResources();
+
+ /**
+ * Returns whether the library is considered optional, meaning that it may or may not
+ * be present in the final APK.
+ *
+ * If the library is optional, then:
+ * - if the consumer is a library, it'll get skipped from resource merging and won't show up
+ * in the consumer R.txt
+ * - if the consumer is a separate test project, all the resources gets skipped from merging.
+ */
+ boolean isOptional();
}
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java b/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
index 9747e41..068f365 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/AndroidProject.java
@@ -165,6 +165,14 @@
Collection<File> getFrameworkSources();
/**
+ * Returns the collection of toolchains used to create any native libraries.
+ *
+ * @return collection of toolchains.
+ */
+ @NonNull
+ Collection<NativeToolchain> getNativeToolchains();
+
+ /**
* Returns a list of {@link SigningConfig}.
*/
@NonNull
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/BaseConfig.java b/build-system/builder-model/src/main/java/com/android/builder/model/BaseConfig.java
index 1057cf2..aa6da40 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/BaseConfig.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/BaseConfig.java
@@ -21,6 +21,7 @@
import java.io.File;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
/**
@@ -48,28 +49,28 @@
Map<String, ClassField> getResValues();
/**
- * Returns the list of proguard rule files.
+ * Returns the collection of proguard rule files.
*
* <p>These files are only applied to the production code.
*
- * @return a non-null list of files.
+ * @return a non-null collection of files.
* @see #getTestProguardFiles()
*/
@NonNull
Collection<File> getProguardFiles();
/**
- * Returns the list of proguard rule files for consumers of the library to use.
+ * Returns the collection of proguard rule files for consumers of the library to use.
*
- * @return a non-null list of files.
+ * @return a non-null collection of files.
*/
@NonNull
Collection<File> getConsumerProguardFiles();
/**
- * Returns the list of proguard rule files to use for the test APK.
+ * Returns the collection of proguard rule files to use for the test APK.
*
- * @return a non-null list of files.
+ * @return a non-null collection of files.
*/
@NonNull
Collection<File> getTestProguardFiles();
@@ -96,4 +97,14 @@
@Nullable
File getMultiDexKeepProguard();
+
+ /**
+ * Returns the optional jarjar rule files, or empty if jarjar should be skipped.
+ * If more than one file is provided, the rule files will be merged in order with last one
+ * win in case of rule redefinition.
+ * Can only be used with Jack toolchain.
+ * @return the optional jarjar rule file.
+ */
+ @NonNull
+ List<File> getJarJarRuleFiles();
}
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/DimensionAware.java b/build-system/builder-model/src/main/java/com/android/builder/model/DimensionAware.java
new file mode 100644
index 0000000..be34319
--- /dev/null
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/DimensionAware.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.builder.model;
+
+import com.android.annotations.Nullable;
+
+/**
+ * Interface for objects has a dimension.
+ */
+public interface DimensionAware {
+
+ /**
+ * Returns the dimension or null if the object do not have a dimension.
+ */
+ @Nullable
+ String getDimension();
+
+}
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/JavaArtifact.java b/build-system/builder-model/src/main/java/com/android/builder/model/JavaArtifact.java
index 2e6466b..4fd39e9 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/JavaArtifact.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/JavaArtifact.java
@@ -16,8 +16,18 @@
package com.android.builder.model;
+import com.android.annotations.Nullable;
+
+import java.io.File;
+
/**
* The information for a generated Java artifact.
*/
public interface JavaArtifact extends BaseArtifact {
+
+ /**
+ * Path to the mockable platform jar generated for this {@link JavaArtifact}, if present.
+ */
+ @Nullable
+ File getMockablePlatformJar();
}
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/NativeLibrary.java b/build-system/builder-model/src/main/java/com/android/builder/model/NativeLibrary.java
new file mode 100644
index 0000000..6e9148e
--- /dev/null
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/NativeLibrary.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.builder.model;
+
+import com.android.annotations.NonNull;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * A Native Library. The configurations used to create a shared object.
+ */
+public interface NativeLibrary {
+
+ /**
+ * Returns the name of the native library.
+ *
+ * A native library "libfoo.so" would have the name of "foo".
+ *
+ * @return name of the native library.
+ */
+ @NonNull
+ String getName();
+
+ /**
+ * Returns the ABI of the library.
+ *
+ * @return abi of the library.
+ */
+ @NonNull
+ String getAbi();
+
+ /**
+ * Returns the name of the toolchain used to compile the native library.
+ *
+ * @return name of the toolchain.
+ */
+ @NonNull
+ String getToolchainName();
+
+ /**
+ * A list of include directories for compiling C code.
+ *
+ * @return list of include directories.
+ */
+ @NonNull
+ List<File> getCIncludeDirs();
+
+ /**
+ * A list of include directories for compiling C++ code.
+ *
+ * @return list of include directories.
+ */
+ @NonNull
+ List<File> getCppIncludeDirs();
+
+ /**
+ * A list of system include directories for compiling C code.
+ *
+ * @return list of include directories.
+ */
+ @NonNull
+ List<File> getCSystemIncludeDirs();
+
+ /**
+ * A list of system include directories for compiling C++ code.
+ *
+ * @return list of include directories.
+ */
+ @NonNull
+ List<File> getCppSystemIncludeDirs();
+
+ /**
+ * A list of defines for C code.
+ *
+ * @return list of defines.
+ */
+ @NonNull
+ List<String> getCDefines();
+
+ /**
+ * A list of defines for C++ code.
+ *
+ * @return list of defines.
+ */
+ @NonNull
+ List<String> getCppDefines();
+
+ /**
+ * A list of compiler flags for C code.
+ *
+ * @return list of compiler flags.
+ */
+ @NonNull
+ List<String> getCCompilerFlags();
+
+ /**
+ * A list of compiler flags for C++ code.
+ *
+ * @return list of compiler flags.
+ */
+ @NonNull
+ List<String> getCppCompilerFlags();
+
+ /**
+ * The folders containing built libraries with debug information.
+ *
+ * @return list of paths to locate shared objects with debug information.
+ */
+ @NonNull
+ List<File> getDebuggableLibraryFolders();
+
+}
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/NativeToolchain.java b/build-system/builder-model/src/main/java/com/android/builder/model/NativeToolchain.java
new file mode 100644
index 0000000..e1c387c
--- /dev/null
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/NativeToolchain.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.builder.model;
+
+import com.android.annotations.NonNull;
+
+import java.io.File;
+
+/**
+ * Toolchain for building a native library.
+ */
+public interface NativeToolchain {
+
+ /**
+ * Returns the name of the toolchain.
+ *
+ * e.g. "x86_64", "arm-linux-androideabi"
+ *
+ * @return name of the toolchain.
+ */
+ @NonNull
+ String getName();
+
+ /**
+ * Returns the full path of the C compiler.
+ *
+ * @return the C compiler path.
+ */
+ @NonNull
+ File getCCompilerExecutable();
+
+ /**
+ * Returns the full path of the C++ compiler.
+ *
+ * @return the C++ compiler path.
+ */
+ @NonNull
+ File getCppCompilerExecutable();
+}
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/PackagingOptions.java b/build-system/builder-model/src/main/java/com/android/builder/model/PackagingOptions.java
index b69e00a..2ea4f0e 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/PackagingOptions.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/PackagingOptions.java
@@ -29,4 +29,7 @@
@NonNull
Set<String> getPickFirsts();
+
+ @NonNull
+ Set<String> getMerges();
}
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/ProductFlavor.java b/build-system/builder-model/src/main/java/com/android/builder/model/ProductFlavor.java
index a252842..04f408a 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/ProductFlavor.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/ProductFlavor.java
@@ -20,6 +20,7 @@
import com.android.annotations.Nullable;
import java.util.Collection;
+import java.util.Map;
/**
* a Product Flavor. This is only the configuration of the flavor.
@@ -30,7 +31,7 @@
* @see ProductFlavorContainer
* @see BaseArtifact#getDependencies()
*/
-public interface ProductFlavor extends BaseConfig {
+public interface ProductFlavor extends BaseConfig, DimensionAware {
/**
* Returns the name of the flavor.
@@ -42,12 +43,6 @@
String getName();
/**
- * Returns the flavor dimension or null if not applicable.
- */
- @Nullable
- String getDimension();
-
- /**
* Returns the name of the product flavor. This is only the value set on this product flavor.
* To get the final application id name, use {@link AndroidArtifact#getApplicationId()}.
*
@@ -146,6 +141,12 @@
String getTestInstrumentationRunner();
/**
+ * Returns the arguments for the test instrumentation runner.
+ */
+ @NonNull
+ Map<String, String> getTestInstrumentationRunnerArguments();
+
+ /**
* Returns the handlingProfile value. This is only the value set on this product flavor.
*
* @return the handlingProfile value.
diff --git a/build-system/builder-model/src/main/java/com/android/builder/model/SyncIssue.java b/build-system/builder-model/src/main/java/com/android/builder/model/SyncIssue.java
index 59d12ec..85f0d1e 100644
--- a/build-system/builder-model/src/main/java/com/android/builder/model/SyncIssue.java
+++ b/build-system/builder-model/src/main/java/com/android/builder/model/SyncIssue.java
@@ -59,7 +59,10 @@
*/
int TYPE_MISMATCH_DEP = 9;
- int TYPE_MAX = 10; // increment when adding new types.
+ // data is dependency coordinate
+ int TYPE_OPTIONAL_LIB_NOT_FOUND = 10;
+
+ int TYPE_MAX = 11; // increment when adding new types.
/**
* Returns the severity of the issue.
diff --git a/build-system/builder-test-api/build.gradle b/build-system/builder-test-api/build.gradle
index b41a68c..d24b00f 100644
--- a/build-system/builder-test-api/build.gradle
+++ b/build-system/builder-test-api/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
+apply plugin: 'jacoco'
apply plugin: 'clone-artifacts'
dependencies {
diff --git a/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceConfig.java b/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceConfig.java
index 3a261ce..3f19bc6 100644
--- a/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceConfig.java
+++ b/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceConfig.java
@@ -23,7 +23,6 @@
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
diff --git a/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceException.java b/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceException.java
index b93d79a..a60d214 100644
--- a/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceException.java
+++ b/build-system/builder-test-api/src/main/java/com/android/builder/testing/api/DeviceException.java
@@ -17,7 +17,6 @@
package com.android.builder.testing.api;
import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
import com.google.common.annotations.Beta;
/**
diff --git a/build-system/builder-test-api/src/test/java/com/android/builder/testing/api/DeviceConfigTest.java b/build-system/builder-test-api/src/test/java/com/android/builder/testing/api/DeviceConfigTest.java
index 8d473bc..6bbe759 100644
--- a/build-system/builder-test-api/src/test/java/com/android/builder/testing/api/DeviceConfigTest.java
+++ b/build-system/builder-test-api/src/test/java/com/android/builder/testing/api/DeviceConfigTest.java
@@ -21,8 +21,6 @@
import com.google.common.collect.ImmutableList;
-import junit.framework.TestCase;
-
import org.junit.Test;
import java.util.ArrayList;
diff --git a/build-system/builder/build.gradle b/build-system/builder/build.gradle
index 05e41d0..050a4a1 100644
--- a/build-system/builder/build.gradle
+++ b/build-system/builder/build.gradle
@@ -1,14 +1,6 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
apply plugin: 'clone-artifacts'
+apply plugin: 'jacoco'
evaluationDependsOn(':base:builder-model')
evaluationDependsOn(':base:builder-test-api')
@@ -23,12 +15,14 @@
compile project(':base:manifest-merger')
compile project(':base:ddmlib')
+ compile project(':base:jack:jack-api')
+ compile project(':base:jack:jill-api')
+
compile 'com.squareup:javawriter:2.5.0'
compile 'org.bouncycastle:bcpkix-jdk15on:1.48'
compile 'org.bouncycastle:bcprov-jdk15on:1.48'
compile 'org.ow2.asm:asm:5.0.3'
compile 'org.ow2.asm:asm-tree:5.0.3'
- compile 'com.android.tools.jack:jack-api:0.1'
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-all:1.9.5'
@@ -99,6 +93,7 @@
}
sourceSets.main.compileClasspath += [configurations.provided]
+sourceSets.test.runtimeClasspath += [configurations.provided]
tasks.compileJava.dependsOn(configurations.provided)
tasks.sourcesJar.dependsOn(configurations.sourcesProvided)
diff --git a/build-system/builder/builder.iml b/build-system/builder/builder.iml
index 7c62a1c..0484483 100644
--- a/build-system/builder/builder.iml
+++ b/build-system/builder/builder.iml
@@ -25,6 +25,8 @@
<orderEntry type="module" module-name="manifest-merger-base" />
<orderEntry type="library" exported="" name="asm-tools" level="project" />
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
- <orderEntry type="library" name="jack-api-0.1" level="project" />
+ <orderEntry type="module" module-name="assetstudio-base" />
+ <orderEntry type="module" module-name="jack-api" exported="" />
+ <orderEntry type="module" module-name="jill-api" exported="" />
</component>
</module>
\ No newline at end of file
diff --git a/build-system/builder/src/main/java/com/android/builder/compiling/DependencyFileProcessor.java b/build-system/builder/src/main/java/com/android/builder/compiling/DependencyFileProcessor.java
index fa62aeb..0e5d399 100644
--- a/build-system/builder/src/main/java/com/android/builder/compiling/DependencyFileProcessor.java
+++ b/build-system/builder/src/main/java/com/android/builder/compiling/DependencyFileProcessor.java
@@ -20,6 +20,7 @@
import com.android.builder.internal.incremental.DependencyData;
import java.io.File;
+import java.io.IOException;
/**
* A Class that processes a dependency file after a compilation.
@@ -31,8 +32,8 @@
*
* The instance will be called for each dependency file that is created during compilation.
*
- * @see com.android.builder.core.AndroidBuilder#compileAllAidlFiles(java.util.List, java.io.File, java.io.File, java.util.List, DependencyFileProcessor)
- * @see com.android.builder.core.AndroidBuilder#compileAidlFile(java.io.File, java.io.File, java.io.File, java.io.File, java.util.List, DependencyFileProcessor)
+ * @see com.android.builder.core.AndroidBuilder#compileAllAidlFiles(java.util.List, File, File, java.util.List, DependencyFileProcessor, com.android.ide.common.process.ProcessOutputHandler)
+ * @see com.android.builder.core.AndroidBuilder#compileAidlFile(File, File, File, File, java.util.List, DependencyFileProcessor, com.android.ide.common.process.ProcessOutputHandler)
*/
public interface DependencyFileProcessor {
@@ -41,5 +42,5 @@
* @param dependencyFile the dependency file.
* @return the dependency data that was created.
*/
- DependencyData processFile(@NonNull File dependencyFile);
+ DependencyData processFile(@NonNull File dependencyFile) throws IOException;
}
diff --git a/build-system/builder/src/main/java/com/android/builder/compiling/ResValueGenerator.java b/build-system/builder/src/main/java/com/android/builder/compiling/ResValueGenerator.java
index 23f21bf..5b6aed6 100644
--- a/build-system/builder/src/main/java/com/android/builder/compiling/ResValueGenerator.java
+++ b/build-system/builder/src/main/java/com/android/builder/compiling/ResValueGenerator.java
@@ -16,10 +16,11 @@
package com.android.builder.compiling;
import static com.android.SdkConstants.ATTR_NAME;
+import static com.android.SdkConstants.ATTR_TRANSLATABLE;
import static com.android.SdkConstants.ATTR_TYPE;
-import static com.android.SdkConstants.TAG_STRING;
import static com.android.SdkConstants.TAG_ITEM;
import static com.android.SdkConstants.TAG_RESOURCES;
+import static com.android.SdkConstants.VALUE_FALSE;
import static com.google.common.base.Preconditions.checkNotNull;
import com.android.annotations.NonNull;
@@ -148,6 +149,12 @@
itemNode.getAttributes().setNamedItem(typeAttr);
}
+ if (type == ResourceType.STRING) {
+ Attr translatable = document.createAttribute(ATTR_TRANSLATABLE);
+ translatable.setValue(VALUE_FALSE);
+ itemNode.getAttributes().setNamedItem(translatable);
+ }
+
if (!field.getValue().isEmpty()) {
itemNode.appendChild(document.createTextNode(field.getValue()));
}
@@ -164,7 +171,7 @@
try {
content = XmlPrettyPrinter.prettyPrint(document, true);
} catch (Throwable t) {
- content = XmlUtils.toXml(document, false);
+ content = XmlUtils.toXml(document);
}
Files.write(content, resFile, Charsets.UTF_8);
diff --git a/build-system/builder/src/main/java/com/android/builder/core/AaptPackageProcessBuilder.java b/build-system/builder/src/main/java/com/android/builder/core/AaptPackageProcessBuilder.java
index 69dac44..b64fb82 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/AaptPackageProcessBuilder.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/AaptPackageProcessBuilder.java
@@ -379,7 +379,6 @@
resourceConfigs.add(mPreferredDensity);
// when adding a density filter, also always add the nodpi option.
resourceConfigs.add(Density.NODPI.getResourceValue());
- resourceConfigs.add(Density.ANYDPI.getResourceValue());
}
diff --git a/build-system/builder/src/main/java/com/android/builder/core/AndroidBuilder.java b/build-system/builder/src/main/java/com/android/builder/core/AndroidBuilder.java
index 22f69f1..ce77d74 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/AndroidBuilder.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/AndroidBuilder.java
@@ -29,7 +29,6 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.annotations.VisibleForTesting;
import com.android.builder.compiling.DependencyFileProcessor;
import com.android.builder.core.BuildToolsServiceLoader.BuildToolServiceLoader;
import com.android.builder.dependency.ManifestDependency;
@@ -45,11 +44,11 @@
import com.android.builder.internal.compiler.RenderScriptProcessor;
import com.android.builder.internal.compiler.SourceSearcher;
import com.android.builder.internal.incremental.DependencyData;
-import com.android.builder.internal.packaging.JavaResourceProcessor;
import com.android.builder.internal.packaging.Packager;
import com.android.builder.model.ClassField;
import com.android.builder.model.PackagingOptions;
import com.android.builder.model.SigningConfig;
+import com.android.builder.model.SyncIssue;
import com.android.builder.packaging.DuplicateFileException;
import com.android.builder.packaging.PackagerException;
import com.android.builder.packaging.SealedPackageException;
@@ -58,7 +57,6 @@
import com.android.builder.sdk.TargetInfo;
import com.android.builder.signing.SignedJarBuilder;
import com.android.ide.common.internal.AaptCruncher;
-import com.android.ide.common.internal.CommandLineRunner;
import com.android.ide.common.internal.LoggedErrorException;
import com.android.ide.common.internal.PngCruncher;
import com.android.ide.common.process.CachedProcessOutputHandler;
@@ -82,6 +80,9 @@
import com.android.jack.api.v01.MultiDexKind;
import com.android.jack.api.v01.ReporterKind;
import com.android.jack.api.v01.UnrecoverableException;
+import com.android.jill.api.JillProvider;
+import com.android.jill.api.v01.Api01TranslationTask;
+import com.android.jill.api.v01.TranslationException;
import com.android.manifmerger.ManifestMerger2;
import com.android.manifmerger.MergingReport;
import com.android.manifmerger.PlaceholderEncoder;
@@ -89,6 +90,7 @@
import com.android.manifmerger.XmlDocument;
import com.android.sdklib.BuildToolInfo;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.IAndroidTarget.OptionalLibrary;
import com.android.sdklib.repository.FullRevision;
import com.android.utils.ILogger;
import com.android.utils.Pair;
@@ -98,11 +100,10 @@
import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
-import com.google.common.hash.HashCode;
-import com.google.common.hash.Hashing;
import com.google.common.io.Files;
import java.io.ByteArrayOutputStream;
@@ -116,10 +117,12 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Enumeration;
import java.util.List;
import java.util.Map;
-import java.util.ServiceLoader;
import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
/**
* This is the main builder class. It is given all the data to process the build (such as
@@ -127,15 +130,15 @@
* build steps.
*
* To use:
- * create a builder with {@link #AndroidBuilder(String, CommandLineRunner, ProcessExecutor, JavaProcessExecutor, ProcessOutputHandler, ILogger, boolean)}
+ * create a builder with {@link #AndroidBuilder(String, String, ProcessExecutor, JavaProcessExecutor, ErrorReporter, ILogger, boolean)}
*
* then build steps can be done with
* {@link #mergeManifests(File, List, List, String, int, String, String, String, Integer, String, String, ManifestMerger2.MergeType, Map, File)}
- * {@link #processTestManifest(String, String, String, String, String, Boolean, Boolean, java.io.File, java.util.List, java.io.File, java.io.File)}
- * {@link #processResources(AaptPackageProcessBuilder, boolean)}
- * {@link #compileAllAidlFiles(java.util.List, java.io.File, java.io.File, java.util.List, com.android.builder.compiling.DependencyFileProcessor)}
- * {@link #convertByteCode(Collection, Collection, File, boolean, boolean, File, DexOptions, List, File, boolean, boolean)}
- * {@link #packageApk(String, java.io.File, java.util.Collection, java.util.Collection, String, java.util.Collection, java.util.Set, boolean, com.android.builder.model.SigningConfig, com.android.builder.model.PackagingOptions, String)}
+ * {@link #processTestManifest(String, String, String, String, String, Boolean, Boolean, File, List, Map, File, File)}
+ * {@link #processResources(AaptPackageProcessBuilder, boolean, ProcessOutputHandler)}
+ * {@link #compileAllAidlFiles(List, File, File, List, DependencyFileProcessor, ProcessOutputHandler)}
+ * {@link #convertByteCode(Collection, Collection, File, boolean, File, DexOptions, List, File, boolean, boolean, ProcessOutputHandler)}
+ * {@link #packageApk(String, File, Collection, Collection, String, Collection, File, Set, boolean, SigningConfig, PackagingOptions, SignedJarBuilder.IZipEntryFilter, String)}
*
* Java compilation is not handled but the builder provides the bootclasspath with
* {@link #getBootClasspath()}.
@@ -150,12 +153,18 @@
}
};
- @NonNull private final String mProjectId;
- @NonNull private final ILogger mLogger;
+ @NonNull
+ private final String mProjectId;
+ @NonNull
+ private final ILogger mLogger;
- @NonNull private final ProcessExecutor mProcessExecutor;
- @NonNull private final JavaProcessExecutor mJavaProcessExecutor;
- @NonNull private final ProcessOutputHandler mProcessOutputHandler;
+ @NonNull
+ private final ProcessExecutor mProcessExecutor;
+ @NonNull
+ private final JavaProcessExecutor mJavaProcessExecutor;
+ @NonNull
+ private final ErrorReporter mErrorReporter;
+
private final boolean mVerboseExec;
@Nullable private String mCreatedBy;
@@ -164,6 +173,10 @@
private SdkInfo mSdkInfo;
private TargetInfo mTargetInfo;
+ private List<File> mBootClasspath;
+ @NonNull
+ private List<LibraryRequest> mLibraryRequests = ImmutableList.of();
+
/**
* Creates an AndroidBuilder.
* <p/>
@@ -179,31 +192,14 @@
@Nullable String createdBy,
@NonNull ProcessExecutor processExecutor,
@NonNull JavaProcessExecutor javaProcessExecutor,
- @NonNull ProcessOutputHandler processOutputHandler,
+ @NonNull ErrorReporter errorReporter,
@NonNull ILogger logger,
boolean verboseExec) {
- mProjectId = projectId;
+ mProjectId = checkNotNull(projectId);
mCreatedBy = createdBy;
- mProcessExecutor = processExecutor;
- mJavaProcessExecutor = javaProcessExecutor;
- mProcessOutputHandler = processOutputHandler;
- mLogger = checkNotNull(logger);
- mVerboseExec = verboseExec;
- }
-
- @VisibleForTesting
- AndroidBuilder(
- @NonNull String projectId,
- @NonNull CommandLineRunner cmdLineRunner,
- @NonNull ProcessExecutor processExecutor,
- @NonNull JavaProcessExecutor javaProcessExecutor,
- @NonNull ProcessOutputHandler processOutputHandler,
- @NonNull ILogger logger,
- boolean verboseExec) {
- mProjectId = projectId;
mProcessExecutor = checkNotNull(processExecutor);
mJavaProcessExecutor = checkNotNull(javaProcessExecutor);
- mProcessOutputHandler = processOutputHandler;
+ mErrorReporter = checkNotNull(errorReporter);
mLogger = checkNotNull(logger);
mVerboseExec = verboseExec;
}
@@ -217,7 +213,10 @@
*
* @see com.android.builder.sdk.SdkLoader
*/
- public void setTargetInfo(@NonNull SdkInfo sdkInfo, @NonNull TargetInfo targetInfo) {
+ public void setTargetInfo(
+ @NonNull SdkInfo sdkInfo,
+ @NonNull TargetInfo targetInfo,
+ @NonNull Collection<LibraryRequest> libraryRequests) {
mSdkInfo = sdkInfo;
mTargetInfo = targetInfo;
@@ -226,6 +225,8 @@
"The SDK Build Tools revision (%1$s) is too low for project '%2$s'. Minimum required is %3$s",
mTargetInfo.getBuildTools().getRevision(), mProjectId, MIN_BUILD_TOOLS_REV));
}
+
+ mLibraryRequests = ImmutableList.copyOf(libraryRequests);
}
/**
@@ -249,6 +250,11 @@
return mLogger;
}
+ @NonNull
+ public ErrorReporter getErrorReporter() {
+ return mErrorReporter;
+ }
+
/**
* Returns the compilation target, if set.
*/
@@ -286,31 +292,75 @@
*/
@NonNull
public List<File> getBootClasspath() {
- checkState(mTargetInfo != null,
- "Cannot call getBootClasspath() before setTargetInfo() is called.");
+ if (mBootClasspath == null) {
+ checkState(mTargetInfo != null,
+ "Cannot call getBootClasspath() before setTargetInfo() is called.");
- List<File> classpath = Lists.newArrayList();
+ List<File> classpath = Lists.newArrayList();
- IAndroidTarget target = mTargetInfo.getTarget();
+ IAndroidTarget target = mTargetInfo.getTarget();
- for (String p : target.getBootClasspath()) {
- classpath.add(new File(p));
+ for (String p : target.getBootClasspath()) {
+ classpath.add(new File(p));
+ }
+
+ List<LibraryRequest> requestedLibs = Lists.newArrayList(mLibraryRequests);
+
+ // add additional libraries if any
+ List<OptionalLibrary> libs = target.getAdditionalLibraries();
+ for (OptionalLibrary lib : libs) {
+ // add it always for now
+ classpath.add(lib.getJar());
+
+ // remove from list of requested if match
+ LibraryRequest requestedLib = findMatchingLib(lib.getName(), requestedLibs);
+ if (requestedLib != null) {
+ requestedLibs.remove(requestedLib);
+ }
+ }
+
+ // add optional libraries if needed.
+ List<OptionalLibrary> optionalLibraries = target.getOptionalLibraries();
+ for (OptionalLibrary lib : optionalLibraries) {
+ // search if requested
+ LibraryRequest requestedLib = findMatchingLib(lib.getName(), requestedLibs);
+ if (requestedLib != null) {
+ // add to classpath
+ classpath.add(lib.getJar());
+
+ // remove from requested list.
+ requestedLibs.remove(requestedLib);
+ }
+ }
+
+ // look for not found requested libraries.
+ for (LibraryRequest library : requestedLibs) {
+ mErrorReporter.handleSyncError(
+ library.getName(),
+ SyncIssue.TYPE_OPTIONAL_LIB_NOT_FOUND,
+ "Unable to find optional library: " + library.getName());
+ }
+
+ // add annotations.jar if needed.
+ if (target.getVersion().getApiLevel() <= 15) {
+ classpath.add(mSdkInfo.getAnnotationsJar());
+ }
+
+ mBootClasspath = ImmutableList.copyOf(classpath);
}
- // add optional libraries if any
- IAndroidTarget.IOptionalLibrary[] libs = target.getOptionalLibraries();
- if (libs != null) {
- for (IAndroidTarget.IOptionalLibrary lib : libs) {
- classpath.add(new File(lib.getJarPath()));
+ return mBootClasspath;
+ }
+
+ @Nullable
+ private static LibraryRequest findMatchingLib(@NonNull String name, @NonNull List<LibraryRequest> libraries) {
+ for (LibraryRequest library : libraries) {
+ if (name.equals(library.getName())) {
+ return library;
}
}
- // add annotations.jar if needed.
- if (target.getVersion().getApiLevel() <= 15) {
- classpath.add(mSdkInfo.getAnnotationsJar());
- }
-
- return classpath;
+ return null;
}
/**
@@ -318,29 +368,15 @@
*/
@NonNull
public List<String> getBootClasspathAsStrings() {
- checkState(mTargetInfo != null,
- "Cannot call getBootClasspath() before setTargetInfo() is called.");
+ List<File> classpath = getBootClasspath();
- List<String> classpath = Lists.newArrayList();
-
- IAndroidTarget target = mTargetInfo.getTarget();
-
- classpath.addAll(target.getBootClasspath());
-
- // add optional libraries if any
- IAndroidTarget.IOptionalLibrary[] libs = target.getOptionalLibraries();
- if (libs != null) {
- for (IAndroidTarget.IOptionalLibrary lib : libs) {
- classpath.add(lib.getJarPath());
- }
+ // convert to Strings.
+ List<String> results = Lists.newArrayListWithCapacity(classpath.size());
+ for (File f : classpath) {
+ results.add(f.getAbsolutePath());
}
- // add annotations.jar if needed.
- if (target.getVersion().getApiLevel() <= 15) {
- classpath.add(mSdkInfo.getAnnotationsJar().getPath());
- }
-
- return classpath;
+ return results;
}
/**
@@ -350,7 +386,7 @@
*
* @return the jar file, or null.
*
- * @see #setTargetInfo(com.android.builder.sdk.SdkInfo, com.android.builder.sdk.TargetInfo)
+ * @see #setTargetInfo(SdkInfo, TargetInfo, Collection)
*/
@Nullable
public File getRenderScriptSupportJar() {
@@ -418,7 +454,7 @@
*
* @return the folder, or null.
*
- * @see #setTargetInfo(com.android.builder.sdk.SdkInfo, com.android.builder.sdk.TargetInfo)
+ * @see #setTargetInfo(SdkInfo, TargetInfo, Collection)
*/
@Nullable
public File getSupportNativeLibFolder() {
@@ -435,13 +471,13 @@
* @return an PngCruncher object
*/
@NonNull
- public PngCruncher getAaptCruncher() {
+ public PngCruncher getAaptCruncher(ProcessOutputHandler processOutputHandler) {
checkState(mTargetInfo != null,
"Cannot call getAaptCruncher() before setTargetInfo() is called.");
return new AaptCruncher(
mTargetInfo.getBuildTools().getPath(BuildToolInfo.PathId.AAPT),
mProcessExecutor,
- mProcessOutputHandler);
+ processOutputHandler);
}
@NonNull
@@ -450,11 +486,6 @@
}
@NonNull
- public ProcessResult executeProcess(@NonNull ProcessInfo processInfo) {
- return executeProcess(processInfo, mProcessOutputHandler);
- }
-
- @NonNull
public ProcessResult executeProcess(@NonNull ProcessInfo processInfo,
@NonNull ProcessOutputHandler handler) {
return mProcessExecutor.execute(processInfo, handler);
@@ -780,7 +811,8 @@
*/
public void processResources(
@NonNull AaptPackageProcessBuilder aaptCommand,
- boolean enforceUniquePackageName)
+ boolean enforceUniquePackageName,
+ @NonNull ProcessOutputHandler processOutputHandler)
throws IOException, InterruptedException, ProcessException {
checkState(mTargetInfo != null,
@@ -790,7 +822,7 @@
ProcessInfo processInfo = aaptCommand.build(
mTargetInfo.getBuildTools(), mTargetInfo.getTarget(), mLogger);
- ProcessResult result = mProcessExecutor.execute(processInfo, mProcessOutputHandler);
+ ProcessResult result = mProcessExecutor.execute(processInfo, processOutputHandler);
result.rethrowFailure().assertNormalExitValue();
// now if the project has libraries, R needs to be created for each libraries,
@@ -812,6 +844,9 @@
Multimap<String, SymbolLoader> libMap = ArrayListMultimap.create();
for (SymbolFileProvider lib : aaptCommand.getLibraries()) {
+ if (lib.isOptional()) {
+ continue;
+ }
String packageName = VariantConfiguration.getManifestPackage(lib.getManifest());
if (appPackageName == null) {
continue;
@@ -833,7 +868,6 @@
// if the library has no resource, this file won't exist.
if (rFile.isFile()) {
-
// load the full values if that's not already been done.
// Doing it lazily allow us to support the case where there's no
// resources anywhere.
@@ -958,7 +992,8 @@
@NonNull File sourceOutputDir,
@Nullable File parcelableOutputDir,
@NonNull List<File> importFolders,
- @Nullable DependencyFileProcessor dependencyFileProcessor)
+ @Nullable DependencyFileProcessor dependencyFileProcessor,
+ @NonNull ProcessOutputHandler processOutputHandler)
throws IOException, InterruptedException, LoggedErrorException, ProcessException {
checkNotNull(sourceFolders, "sourceFolders cannot be null.");
checkNotNull(sourceOutputDir, "sourceOutputDir cannot be null.");
@@ -988,7 +1023,7 @@
dependencyFileProcessor != null ?
dependencyFileProcessor : sNoOpDependencyFileProcessor,
mProcessExecutor,
- mProcessOutputHandler);
+ processOutputHandler);
SourceSearcher searcher = new SourceSearcher(sourceFolders, "aidl");
searcher.setUseExecutor(true);
@@ -1012,7 +1047,8 @@
@NonNull File sourceOutputDir,
@Nullable File parcelableOutputDir,
@NonNull List<File> importFolders,
- @Nullable DependencyFileProcessor dependencyFileProcessor)
+ @Nullable DependencyFileProcessor dependencyFileProcessor,
+ @NonNull ProcessOutputHandler processOutputHandler)
throws IOException, InterruptedException, LoggedErrorException, ProcessException {
checkNotNull(aidlFile, "aidlFile cannot be null.");
checkNotNull(sourceOutputDir, "sourceOutputDir cannot be null.");
@@ -1037,7 +1073,7 @@
dependencyFileProcessor != null ?
dependencyFileProcessor : sNoOpDependencyFileProcessor,
mProcessExecutor,
- mProcessOutputHandler);
+ processOutputHandler);
processor.processFile(sourceFolder, aidlFile);
}
@@ -1076,7 +1112,8 @@
int optimLevel,
boolean ndkMode,
boolean supportMode,
- @Nullable Set<String> abiFilters)
+ @Nullable Set<String> abiFilters,
+ @NonNull ProcessOutputHandler processOutputHandler)
throws InterruptedException, ProcessException, LoggedErrorException, IOException {
checkNotNull(sourceFolders, "sourceFolders cannot be null.");
checkNotNull(importFolders, "importFolders cannot be null.");
@@ -1106,7 +1143,7 @@
ndkMode,
supportMode,
abiFilters);
- processor.build(mProcessExecutor, mProcessOutputHandler);
+ processor.build(mProcessExecutor, processOutputHandler);
}
/**
@@ -1170,13 +1207,14 @@
@NonNull Collection<File> preDexedLibraries,
@NonNull File outDexFolder,
boolean multidex,
- boolean multidexLegacy,
@Nullable File mainDexList,
@NonNull DexOptions dexOptions,
@Nullable List<String> additionalParameters,
@NonNull File tmpFolder,
boolean incremental,
- boolean optimize) throws IOException, InterruptedException, ProcessException {
+ boolean optimize,
+ @NonNull ProcessOutputHandler processOutputHandler)
+ throws IOException, InterruptedException, ProcessException {
checkNotNull(inputs, "inputs cannot be null.");
checkNotNull(preDexedLibraries, "preDexedLibraries cannot be null.");
checkNotNull(outDexFolder, "outDexFolder cannot be null.");
@@ -1187,6 +1225,13 @@
checkState(mTargetInfo != null,
"Cannot call convertByteCode() before setTargetInfo() is called.");
+ ImmutableList.Builder<File> verifiedInputs = ImmutableList.builder();
+ for (File input : inputs) {
+ if (checkLibraryClassesJar(input)) {
+ verifiedInputs.add(input);
+ }
+ }
+
BuildToolInfo buildToolInfo = mTargetInfo.getBuildTools();
DexProcessBuilder builder = new DexProcessBuilder(outDexFolder);
@@ -1196,7 +1241,7 @@
.setMultiDex(multidex)
.setMainDexList(mainDexList)
.addInputs(preDexedLibraries)
- .addInputs(inputs);
+ .addInputs(verifiedInputs.build());
if (additionalParameters != null) {
builder.additionalParameters(additionalParameters);
@@ -1204,7 +1249,7 @@
JavaProcessInfo javaProcessInfo = builder.build(buildToolInfo, dexOptions);
- ProcessResult result = mJavaProcessExecutor.execute(javaProcessInfo, mProcessOutputHandler);
+ ProcessResult result = mJavaProcessExecutor.execute(javaProcessInfo, processOutputHandler);
result.rethrowFailure().assertNormalExitValue();
}
@@ -1252,7 +1297,8 @@
@NonNull File inputFile,
@NonNull File outFile,
boolean multiDex,
- @NonNull DexOptions dexOptions)
+ @NonNull DexOptions dexOptions,
+ @NonNull ProcessOutputHandler processOutputHandler)
throws IOException, InterruptedException, ProcessException {
checkState(mTargetInfo != null,
"Cannot call preDexLibrary() before setTargetInfo() is called.");
@@ -1267,7 +1313,7 @@
buildToolInfo,
mVerboseExec,
mJavaProcessExecutor,
- mProcessOutputHandler);
+ processOutputHandler);
}
/**
@@ -1284,7 +1330,7 @@
* @throws ProcessException
*/
@NonNull
- public static List<File> preDexLibrary(
+ public static ImmutableList<File> preDexLibrary(
@NonNull File inputFile,
@NonNull File outFile,
boolean multiDex,
@@ -1298,6 +1344,15 @@
checkNotNull(outFile, "outFile cannot be null.");
checkNotNull(dexOptions, "dexOptions cannot be null.");
+
+
+ try {
+ if (!checkLibraryClassesJar(inputFile)) {
+ return ImmutableList.of();
+ }
+ } catch(IOException e) {
+ throw new RuntimeException("Exception while checking library jar", e);
+ }
DexProcessBuilder builder = new DexProcessBuilder(outFile);
builder.setVerbose(verbose)
@@ -1321,13 +1376,64 @@
throw new RuntimeException("No dex files created at " + outFile.getAbsolutePath());
}
- return Lists.newArrayList(files);
+ return ImmutableList.copyOf(files);
} else {
- return Collections.singletonList(outFile);
+ return ImmutableList.of(outFile);
}
}
/**
+ * Returns true if the library (jar or folder) contains class files, false otherwise.
+ */
+ private static boolean checkLibraryClassesJar(@NonNull File input) throws IOException {
+
+ if (!input.exists()) {
+ return false;
+ }
+
+ if (input.isDirectory()) {
+ return checkFolder(input);
+ }
+
+ ZipFile zipFile = null;
+ try {
+ zipFile = new ZipFile(input);
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while(entries.hasMoreElements()) {
+ if (entries.nextElement().getName().endsWith(".class")) {
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ if (zipFile != null) {
+ zipFile.close();
+ }
+ }
+ }
+
+ /**
+ * Returns true if this folder or one of its subfolder contains a class file, false otherwise.
+ */
+ private static boolean checkFolder(@NonNull File folder) {
+ File[] subFolders = folder.listFiles();
+ if (subFolders != null) {
+ for (File childFolder : subFolders) {
+ if (childFolder.isFile() && childFolder.getName().endsWith(".class")) {
+ return true;
+ }
+ if (childFolder.isDirectory()) {
+ // if childFolder returns false, continue search otherwise return success.
+ if (checkFolder(childFolder)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Converts java source code into android byte codes using the jack integration APIs.
* Jack will run in memory.
*/
@@ -1339,6 +1445,9 @@
@NonNull Collection<File> sourceFiles,
@Nullable Collection<File> proguardFiles,
@Nullable File mappingFile,
+ @NonNull Collection<File> jarJarRulesFiles,
+ @Nullable File incrementalDir,
+ @Nullable File javaResourcesFolder,
boolean multiDex,
int minSdkVersion) {
@@ -1349,7 +1458,7 @@
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
Optional<JackProvider> jackProvider = buildToolServiceLoader
- .getSingleService(BuildToolsServiceLoader.JACK);
+ .getSingleService(getLogger(), BuildToolsServiceLoader.JACK);
if (jackProvider.isPresent()) {
Api01Config config;
@@ -1366,6 +1475,10 @@
config.setProguardConfigFiles(new ArrayList<File>(proguardFiles));
}
+ if (!jarJarRulesFiles.isEmpty()) {
+ config.setJarJarConfigFiles(ImmutableList.copyOf(jarJarRulesFiles));
+ }
+
if (multiDex) {
if (minSdkVersion < BuildToolInfo.SDK_LEVEL_FOR_MULTIDEX_NATIVE_SUPPORT) {
config.setMultiDexKind(MultiDexKind.LEGACY);
@@ -1384,6 +1497,22 @@
config.setReporter(ReporterKind.DEFAULT, outputStream);
+ // set the incremental dir if set and either already exists or can be created.
+ if (incrementalDir != null) {
+ if (!incrementalDir.exists() && !incrementalDir.mkdirs()) {
+ mLogger.warning("Cannot create %1$s directory, "
+ + "jack incremental support disabled", incrementalDir);
+ }
+ if (incrementalDir.exists()) {
+ config.setIncrementalDir(incrementalDir);
+ }
+ }
+ if (javaResourcesFolder != null) {
+ ArrayList<File> folders = Lists.newArrayListWithExpectedSize(3);
+ folders.add(javaResourcesFolder);
+ config.setResourceDirs(folders);
+ }
+
compilationTask = config.getTask();
} catch (ConfigNotSupportedException e1) {
mLogger.warning("Jack APIs v01 not supported");
@@ -1425,10 +1554,12 @@
@NonNull File ecjOptionFile,
@Nullable Collection<File> proguardFiles,
@Nullable File mappingFile,
+ @NonNull Collection<File> jarJarRuleFiles,
boolean multiDex,
int minSdkVersion,
boolean debugLog,
- String javaMaxHeapSize) throws ProcessException {
+ String javaMaxHeapSize,
+ @NonNull ProcessOutputHandler processOutputHandler) throws ProcessException {
JackProcessBuilder builder = new JackProcessBuilder();
builder.setDebugLog(debugLog)
@@ -1448,8 +1579,12 @@
builder.setMultiDex(true).setMinSdkVersion(minSdkVersion);
}
+ if (jarJarRuleFiles != null) {
+ builder.setJarJarRuleFiles(jarJarRuleFiles);
+ }
+
mJavaProcessExecutor.execute(
- builder.build(mTargetInfo.getBuildTools()), mProcessOutputHandler)
+ builder.build(mTargetInfo.getBuildTools()), processOutputHandler)
.rethrowFailure().assertNormalExitValue();
}
@@ -1466,7 +1601,8 @@
public void convertLibraryToJack(
@NonNull File inputFile,
@NonNull File outFile,
- @NonNull DexOptions dexOptions)
+ @NonNull DexOptions dexOptions,
+ @NonNull ProcessOutputHandler processOutputHandler)
throws ProcessException, IOException, InterruptedException {
checkState(mTargetInfo != null,
"Cannot call preJackLibrary() before setTargetInfo() is called.");
@@ -1480,7 +1616,54 @@
buildToolInfo,
mVerboseExec,
mJavaProcessExecutor,
- mProcessOutputHandler);
+ processOutputHandler,
+ mLogger);
+ }
+
+ public static List<File> convertLibaryToJackUsingApis(
+ @NonNull File inputFile,
+ @NonNull File outFile,
+ @NonNull DexOptions dexOptions,
+ @NonNull BuildToolInfo buildToolInfo,
+ boolean verbose,
+ @NonNull JavaProcessExecutor processExecutor,
+ @NonNull ProcessOutputHandler processOutputHandler,
+ @NonNull ILogger logger) throws ProcessException {
+
+ BuildToolServiceLoader buildToolServiceLoader = BuildToolsServiceLoader.INSTANCE
+ .forVersion(buildToolInfo);
+ if (System.getenv("USE_JACK_API") != null) {
+ try {
+ Optional<JillProvider> jillProviderOptional = buildToolServiceLoader
+ .getSingleService(logger, BuildToolsServiceLoader.JILL);
+
+ if (jillProviderOptional.isPresent()) {
+ com.android.jill.api.v01.Api01Config config =
+ jillProviderOptional.get().createConfig(
+ com.android.jill.api.v01.Api01Config.class);
+
+ config.setInputJavaBinaryFile(inputFile);
+ config.setOutputJackFile(outFile);
+ config.setVerbose(verbose);
+
+ Api01TranslationTask translationTask = config.getTask();
+ translationTask.run();
+
+ return ImmutableList.of(outFile);
+ }
+
+ } catch (ClassNotFoundException e) {
+ logger.warning("Cannot find the jill tool in the classpath, reverting to native");
+ } catch (com.android.jill.api.ConfigNotSupportedException e) {
+ logger.warning(e.getMessage() + ", reverting to native");
+ } catch (com.android.jill.api.v01.ConfigurationException e) {
+ logger.warning(e.getMessage() + ", reverting to native");
+ } catch (TranslationException e) {
+ logger.error(e, "In process translation failed, reverting to native, file a bug");
+ }
+ }
+ return convertLibraryToJack(inputFile, outFile, dexOptions, buildToolInfo, verbose,
+ processExecutor, processOutputHandler, logger);
}
public static List<File> convertLibraryToJack(
@@ -1490,7 +1673,8 @@
@NonNull BuildToolInfo buildToolInfo,
boolean verbose,
@NonNull JavaProcessExecutor processExecutor,
- @NonNull ProcessOutputHandler processOutputHandler)
+ @NonNull ProcessOutputHandler processOutputHandler,
+ @NonNull ILogger logger)
throws ProcessException {
checkNotNull(inputFile, "inputFile cannot be null.");
checkNotNull(outFile, "outFile cannot be null.");
@@ -1518,6 +1702,7 @@
builder.addArgs("--verbose");
}
+ logger.verbose(builder.toString());
JavaProcessInfo javaProcessInfo = builder.createJavaProcess();
ProcessResult result = processExecutor.execute(javaProcessInfo, processOutputHandler);
result.rethrowFailure().assertNormalExitValue();
@@ -1534,6 +1719,7 @@
* @param packagedJars the jars that are packaged (libraries + jar dependencies)
* @param javaResourcesLocation the processed Java resource folder
* @param jniLibsFolders the folders containing jni shared libraries
+ * @param mergingFolder folder to contain files that are being merged
* @param abiFilters optional ABI filter
* @param jniDebugBuild whether the app should include jni debug data
* @param signingConfig the signing configuration
@@ -1554,10 +1740,12 @@
@NonNull Collection<File> packagedJars,
@Nullable String javaResourcesLocation,
@Nullable Collection<File> jniLibsFolders,
+ @NonNull File mergingFolder,
@Nullable Set<String> abiFilters,
boolean jniDebugBuild,
@Nullable SigningConfig signingConfig,
@Nullable PackagingOptions packagingOptions,
+ @Nullable SignedJarBuilder.IZipEntryFilter packagingOptionsFilter,
@NonNull String outApkLocation)
throws DuplicateFileException, FileNotFoundException,
KeytoolException, PackagerException, SigningException {
@@ -1566,7 +1754,7 @@
CertificateInfo certificateInfo = null;
if (signingConfig != null && signingConfig.isSigningReady()) {
- //noinspection ConstantConditions
+ //noinspection ConstantConditions - isSigningReady() called above.
certificateInfo = KeystoreHelper.getCertificateInfo(signingConfig.getStoreType(),
signingConfig.getStoreFile(), signingConfig.getStorePassword(),
signingConfig.getKeyPassword(), signingConfig.getKeyAlias());
@@ -1577,8 +1765,8 @@
try {
Packager packager = new Packager(
- outApkLocation, androidResPkgLocation,
- certificateInfo, mCreatedBy, packagingOptions, mLogger);
+ outApkLocation, androidResPkgLocation, mergingFolder,
+ certificateInfo, mCreatedBy, packagingOptions, packagingOptionsFilter, mLogger);
// add dex folder to the apk root.
if (dexFolder != null) {
@@ -1590,29 +1778,12 @@
packager.setJniDebugMode(jniDebugBuild);
- // figure out conflicts!
- JavaResourceProcessor resProcessor = new JavaResourceProcessor(packager);
-
- if (javaResourcesLocation != null) {
- resProcessor.addSourceFolder(javaResourcesLocation);
+ if (javaResourcesLocation != null && !packagedJars.isEmpty()) {
+ throw new PackagerException("javaResourcesLocation and packagedJars both provided");
}
-
- // add the resources from the jar files.
- Set<String> hashs = Sets.newHashSet();
-
- for (File jar : packagedJars) {
- // TODO remove once we can properly add a library as a dependency of its test.
- String hash = getFileHash(jar);
- if (hash == null) {
- throw new PackagerException("Unable to compute hash of " + jar.getAbsolutePath());
- }
- if (hashs.contains(hash)) {
- continue;
- }
-
- hashs.add(hash);
-
- packager.addResourcesFromJar(jar);
+ if (javaResourcesLocation != null || !packagedJars.isEmpty()) {
+ packager.addResources(javaResourcesLocation != null
+ ? new File(javaResourcesLocation) : Iterables.getOnlyElement(packagedJars));
}
// also add resources from library projects and jars
@@ -1650,6 +1821,7 @@
CertificateInfo certificateInfo = null;
if (signingConfig != null && signingConfig.isSigningReady()) {
+ //noinspection ConstantConditions - isSigningReady() called above.
certificateInfo = KeystoreHelper.getCertificateInfo(signingConfig.getStoreType(),
signingConfig.getStoreFile(), signingConfig.getStorePassword(),
signingConfig.getKeyPassword(), signingConfig.getKeyAlias());
@@ -1665,26 +1837,8 @@
Packager.getLocalVersion(), mCreatedBy);
- signedJarBuilder.writeZip(new FileInputStream(in), null);
+ signedJarBuilder.writeZip(new FileInputStream(in));
signedJarBuilder.close();
}
-
- /**
- * Returns the hash of a file.
- * @param file the file to hash
- * @return the hash or null if an error happened
- */
- @Nullable
- private static String getFileHash(@NonNull File file) {
- try {
- HashCode hashCode = Files.hash(file, Hashing.sha1());
- return hashCode.toString();
- } catch (IOException ignored) {
-
- }
-
- return null;
- }
-
}
diff --git a/build-system/builder/src/main/java/com/android/builder/core/BuildToolsServiceLoader.java b/build-system/builder/src/main/java/com/android/builder/core/BuildToolsServiceLoader.java
index 56e683e..bdbafb9 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/BuildToolsServiceLoader.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/BuildToolsServiceLoader.java
@@ -17,10 +17,13 @@
package com.android.builder.core;
import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
import com.android.jack.api.JackProvider;
+import com.android.jill.api.JillProvider;
import com.android.sdklib.BuildToolInfo;
import com.android.sdklib.repository.FullRevision;
+import com.android.utils.ILogger;
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
@@ -101,7 +104,7 @@
*
* @param <T> the type of service.
*/
- public abstract static class Service<T> {
+ public static class Service<T> {
private final Collection<String> classpath;
private final Class<T> serviceClass;
@@ -117,18 +120,27 @@
public Class<T> getServiceClass() {
return serviceClass;
}
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this)
+ .add("serviceClass", serviceClass)
+ .add("classpath", Joiner.on(",").join(classpath))
+ .toString();
+ }
}
/**
* Jack service description.
*/
- public static final Service<JackProvider> JACK = new Jack();
+ public static final Service<JackProvider> JACK =
+ new Service<JackProvider>(ImmutableList.of("jack.jar"), JackProvider.class);
- private static final class Jack extends Service<JackProvider> {
- Jack() {
- super(ImmutableList.of("jack.jar", "jill.jar"), JackProvider.class);
- }
- }
+ /**
+ * Jill service description.
+ */
+ public static final Service<JillProvider> JILL =
+ new Service<JillProvider>(ImmutableList.of("jill.jar"), JillProvider.class);
/**
* build-tools version specific {@link ServiceLoader} helper.
@@ -191,7 +203,7 @@
throw new RuntimeException(e);
}
}
- ClassLoader cl = new URLClassLoader(urls, getClass().getClassLoader());
+ ClassLoader cl = new URLClassLoader(urls, serviceType.getServiceClass().getClassLoader());
ServiceLoader<T> serviceLoader = ServiceLoader.load(serviceType.getServiceClass(), cl);
loadedServicesLoaders.add(new LoadedServiceLoader<T>(
serviceType.getServiceClass(), serviceLoader));
@@ -201,16 +213,31 @@
/**
* Return the first service instance for the requested service type or
* {@link Optional#absent()} if none exist.
+ * @param logger to log resolution.
* @param serviceType the requested service type encapsulation.
* @param <T> the requested service class type.
* @return the instance of T or null of none exist in this context.
* @throws ClassNotFoundException
*/
@NonNull
- public <T> Optional<T> getSingleService(Service<T> serviceType) throws ClassNotFoundException {
+ public synchronized <T> Optional<T> getSingleService(
+ ILogger logger,
+ Service<T> serviceType) throws ClassNotFoundException {
+ logger.verbose("Looking for %1$s", serviceType);
ServiceLoader<T> serviceLoader = getServiceLoader(serviceType);
+ logger.verbose("Got a serviceLoader %1$d",
+ Integer.toHexString(System.identityHashCode(serviceLoader)));
Iterator<T> serviceIterator = serviceLoader.iterator();
- return Optional.fromNullable(serviceIterator.hasNext() ? serviceIterator.next() : null);
+ logger.verbose("Service Iterator = %1$s ", serviceIterator);
+ if (serviceIterator.hasNext()) {
+ T service = serviceIterator.next();
+ logger.verbose("Got it from %1$s, loaded service = %2$s, type = %3$s",
+ serviceIterator, service, service.getClass());
+ return Optional.of(service);
+ } else {
+ logger.info("Cannot find service implementation %1$s" + serviceType);
+ return Optional.absent();
+ }
}
@NonNull
diff --git a/build-system/builder/src/main/java/com/android/builder/core/DefaultBuildType.java b/build-system/builder/src/main/java/com/android/builder/core/DefaultBuildType.java
index f072845..412d460 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/DefaultBuildType.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/DefaultBuildType.java
@@ -64,6 +64,9 @@
return this;
}
+ /**
+ * Name of this build type.
+ */
@Override
@NonNull
public String getName() {
@@ -89,6 +92,22 @@
mTestCoverageEnabled = testCoverageEnabled;
}
+ /**
+ * Whether test coverage is enabled for this build type.
+ *
+ * <p>If enabled this uses Jacoco to capture coverage and creates a report in the build
+ * directory.
+ *
+ * <p>The version of Jacoco can be configured with:
+ * <pre>
+ * android {
+ * jacoco {
+ * version = '0.6.2.201302030002'
+ * }
+ * }
+ * </pre>
+ *
+ */
@Override
public boolean isTestCoverageEnabled() {
return mTestCoverageEnabled;
@@ -98,6 +117,12 @@
mPseudoLocalesEnabled = pseudoLocalesEnabled;
}
+ /**
+ * Whether to generate pseudo locale in the APK.
+ *
+ * <p>If enabled, 2 fake pseudo locales (en-XA and ar-XB) will be added to the APK to help
+ * test internationalization support in the app.
+ */
@Override
public boolean isPseudoLocalesEnabled() {
return mPseudoLocalesEnabled;
@@ -136,7 +161,9 @@
return this;
}
- /** Optimization level to use by the renderscript compiler. */
+ /**
+ * Optimization level to use by the renderscript compiler.
+ */
@Override
public int getRenderscriptOptimLevel() {
return mRenderscriptOptimLevel;
@@ -220,6 +247,18 @@
return mSigningConfig;
}
+ /**
+ * Whether a linked Android Wear app should be embedded in variant using this build type.
+ *
+ * <p>Wear apps can be linked with the following code:
+ *
+ * <pre>
+ * dependencies {
+ * freeWearApp project(:wear:free') // applies to variant using the free flavor
+ * wearApp project(':wear:base') // applies to all other variants
+ * }
+ * </pre>
+ */
@Override
public boolean isEmbedMicroApp() {
return mEmbedMicroApp;
@@ -237,49 +276,38 @@
DefaultBuildType buildType = (DefaultBuildType) o;
- if (!mName.equals(buildType.mName)) return false;
- if (mDebuggable != buildType.mDebuggable) return false;
- if (mTestCoverageEnabled != buildType.mTestCoverageEnabled) return false;
- if (mJniDebuggable != buildType.mJniDebuggable) return false;
- if (mPseudoLocalesEnabled != buildType.mPseudoLocalesEnabled) return false;
- if (mRenderscriptDebuggable != buildType.mRenderscriptDebuggable) return false;
- if (mRenderscriptOptimLevel != buildType.mRenderscriptOptimLevel) return false;
- if (mMinifyEnabled != buildType.mMinifyEnabled) return false;
- if (mZipAlignEnabled != buildType.mZipAlignEnabled) return false;
- if (mApplicationIdSuffix != null ?
- !mApplicationIdSuffix.equals(buildType.mApplicationIdSuffix) :
- buildType.mApplicationIdSuffix != null)
- return false;
- if (mVersionNameSuffix != null ?
- !mVersionNameSuffix.equals(buildType.mVersionNameSuffix) :
- buildType.mVersionNameSuffix != null)
- return false;
- if (mSigningConfig != null ?
- !mSigningConfig.equals(buildType.mSigningConfig) :
- buildType.mSigningConfig != null)
- return false;
- if (mEmbedMicroApp != buildType.mEmbedMicroApp) return false;
-
- return true;
+ return Objects.equal(mName, buildType.mName) &&
+ mDebuggable == buildType.mDebuggable &&
+ mTestCoverageEnabled == buildType.mTestCoverageEnabled &&
+ mJniDebuggable == buildType.mJniDebuggable &&
+ mPseudoLocalesEnabled == buildType.mPseudoLocalesEnabled &&
+ mRenderscriptDebuggable == buildType.mRenderscriptDebuggable &&
+ mRenderscriptOptimLevel == buildType.mRenderscriptOptimLevel &&
+ mMinifyEnabled == buildType.mMinifyEnabled &&
+ mZipAlignEnabled == buildType.mZipAlignEnabled &&
+ mEmbedMicroApp == buildType.mEmbedMicroApp &&
+ Objects.equal(mApplicationIdSuffix, buildType.mApplicationIdSuffix) &&
+ Objects.equal(mVersionNameSuffix, buildType.mVersionNameSuffix) &&
+ Objects.equal(mSigningConfig, buildType.mSigningConfig);
}
@Override
public int hashCode() {
- int result = super.hashCode();
- result = 31 * result + (mName.hashCode());
- result = 31 * result + (mDebuggable ? 1 : 0);
- result = 31 * result + (mTestCoverageEnabled ? 1 : 0);
- result = 31 * result + (mJniDebuggable ? 1 : 0);
- result = 31 * result + (mPseudoLocalesEnabled ? 1 : 0);
- result = 31 * result + (mRenderscriptDebuggable ? 1 : 0);
- result = 31 * result + mRenderscriptOptimLevel;
- result = 31 * result + (mApplicationIdSuffix != null ? mApplicationIdSuffix.hashCode() : 0);
- result = 31 * result + (mVersionNameSuffix != null ? mVersionNameSuffix.hashCode() : 0);
- result = 31 * result + (mMinifyEnabled ? 1 : 0);
- result = 31 * result + (mZipAlignEnabled ? 1 : 0);
- result = 31 * result + (mSigningConfig != null ? mSigningConfig.hashCode() : 0);
- result = 31 * result + (mEmbedMicroApp ? 1 : 0);
- return result;
+ return Objects.hashCode(
+ super.hashCode(),
+ mName,
+ mDebuggable,
+ mTestCoverageEnabled,
+ mJniDebuggable,
+ mPseudoLocalesEnabled,
+ mRenderscriptDebuggable,
+ mRenderscriptOptimLevel,
+ mApplicationIdSuffix,
+ mVersionNameSuffix,
+ mMinifyEnabled,
+ mZipAlignEnabled,
+ mSigningConfig,
+ mEmbedMicroApp);
}
@Override
diff --git a/build-system/builder/src/main/java/com/android/builder/core/DefaultManifestParser.java b/build-system/builder/src/main/java/com/android/builder/core/DefaultManifestParser.java
index 184c81c..0d1f9f1 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/DefaultManifestParser.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/DefaultManifestParser.java
@@ -66,13 +66,13 @@
@NonNull
public synchronized int getVersionCode(@NonNull File manifestFile) {
if (mVersionCode == null) {
+ mVersionCode = Optional.absent();
try {
String value = getStringValue(manifestFile, "/manifest/@android:versionCode");
if (value != null) {
mVersionCode = Optional.of(Integer.valueOf(value));
}
} catch (NumberFormatException ignored) {
- mVersionCode = Optional.absent();
}
}
return mVersionCode.or(-1);
diff --git a/build-system/builder/src/main/java/com/android/builder/core/DefaultProductFlavor.java b/build-system/builder/src/main/java/com/android/builder/core/DefaultProductFlavor.java
index 84984e3..6bed799 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/DefaultProductFlavor.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/DefaultProductFlavor.java
@@ -16,6 +16,8 @@
package com.android.builder.core;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.builder.internal.BaseConfigImpl;
@@ -23,10 +25,14 @@
import com.android.builder.model.ProductFlavor;
import com.android.builder.model.SigningConfig;
import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+import java.io.File;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Map;
import java.util.Set;
/**
@@ -63,6 +69,8 @@
private String mTestApplicationId;
@Nullable
private String mTestInstrumentationRunner;
+ @NonNull
+ private Map<String, String> mTestInstrumentationRunnerArguments = Maps.newHashMap();
@Nullable
private Boolean mTestHandleProfiling;
@Nullable
@@ -187,7 +195,7 @@
/** Sets the targetSdkVersion to the given value. */
@NonNull
- public ProductFlavor setTargetSdkVersion(ApiVersion targetSdkVersion) {
+ public ProductFlavor setTargetSdkVersion(@Nullable ApiVersion targetSdkVersion) {
mTargetSdkVersion = targetSdkVersion;
return this;
}
@@ -292,6 +300,36 @@
return mTestInstrumentationRunner;
}
+ /** Sets the test instrumentation runner custom arguments. */
+ @NonNull
+ public ProductFlavor setTestInstrumentationRunnerArguments(
+ @NonNull Map<String, String> testInstrumentationRunnerArguments) {
+ mTestInstrumentationRunnerArguments = checkNotNull(testInstrumentationRunnerArguments);
+ return this;
+ }
+
+ /**
+ * Test instrumentation runner custom arguments.
+ *
+ * e.g. <code>[key: "value"]</code> will give
+ * <code>adb shell am instrument -w <b>-e key value</b> com.example</code>...".
+ *
+ * <p>See <a href="http://developer.android.com/guide/topics/manifest/instrumentation-element.html">
+ * instrumentation</a>.
+ *
+ * <p>Test runner arguments can also be specified from the command line:
+ *
+ * <p><pre>
+ * INSTRUMENTATION_TEST_RUNNER_ARGS=size=medium,foo=bar ./gradlew connectedAndroidTest
+ * ./gradlew connectedAndroidTest -Pcom.android.tools.instrumentationTestRunnerArgs=size=medium,foo=bar
+ * </pre>
+ */
+ @Override
+ @NonNull
+ public Map<String, String> getTestInstrumentationRunnerArguments() {
+ return mTestInstrumentationRunnerArguments;
+ }
+
/**
* See <a href="http://developer.android.com/guide/topics/manifest/instrumentation-element.html">
* instrumentation</a>.
@@ -340,6 +378,9 @@
return this;
}
+ /**
+ * Adds a res config filter (for instance 'hdpi')
+ */
public void addResourceConfiguration(@NonNull String configuration) {
if (mResourceConfiguration == null) {
mResourceConfiguration = Sets.newHashSet();
@@ -348,6 +389,9 @@
mResourceConfiguration.add(configuration);
}
+ /**
+ * Adds a res config filter (for instance 'hdpi')
+ */
public void addResourceConfigurations(@NonNull String... configurations) {
if (mResourceConfiguration == null) {
mResourceConfiguration = Sets.newHashSet();
@@ -356,6 +400,9 @@
mResourceConfiguration.addAll(Arrays.asList(configurations));
}
+ /**
+ * Adds a res config filter (for instance 'hdpi')
+ */
public void addResourceConfigurations(@NonNull Collection<String> configurations) {
if (mResourceConfiguration == null) {
mResourceConfiguration = Sets.newHashSet();
@@ -364,6 +411,9 @@
mResourceConfiguration.addAll(configurations);
}
+ /**
+ * Adds a res config filter (for instance 'hdpi')
+ */
@NonNull
@Override
public Collection<String> getResourceConfigurations() {
@@ -421,6 +471,11 @@
overlay.getTestInstrumentationRunner(),
base.getTestInstrumentationRunner());
+ flavor.mTestInstrumentationRunnerArguments.putAll(
+ base.getTestInstrumentationRunnerArguments());
+ flavor.mTestInstrumentationRunnerArguments.putAll(
+ overlay.getTestInstrumentationRunnerArguments());
+
flavor.mTestHandleProfiling = chooseNotNull(
overlay.getTestHandleProfiling(),
base.getTestHandleProfiling());
@@ -453,6 +508,12 @@
flavor.setMultiDexKeepProguard(chooseNotNull(
overlay.getMultiDexKeepProguard(), base.getMultiDexKeepProguard()));
+
+ flavor.setJarJarRuleFiles(ImmutableList.<File>builder()
+ .addAll(overlay.getJarJarRuleFiles())
+ .addAll(base.getJarJarRuleFiles())
+ .build());
+
return flavor;
}
@@ -482,6 +543,7 @@
flavor.mTestApplicationId = productFlavor.getTestApplicationId();
flavor.mTestInstrumentationRunner = productFlavor.getTestInstrumentationRunner();
+ flavor.mTestInstrumentationRunnerArguments = productFlavor.getTestInstrumentationRunnerArguments();
flavor.mTestHandleProfiling = productFlavor.getTestHandleProfiling();
flavor.mTestFunctionalTest = productFlavor.getTestFunctionalTest();
@@ -497,6 +559,7 @@
flavor.setMultiDexKeepFile(productFlavor.getMultiDexKeepFile());
flavor.setMultiDexKeepProguard(productFlavor.getMultiDexKeepProguard());
+ flavor.setJarJarRuleFiles(ImmutableList.copyOf(productFlavor.getJarJarRuleFiles()));
return flavor;
}
@@ -519,109 +582,50 @@
DefaultProductFlavor that = (DefaultProductFlavor) o;
- if (mDimension !=null ? !mDimension.equals(that.mDimension) :
- that.mDimension != null) {
- return false;
- }
-
- if (mApplicationId != null ? !mApplicationId.equals(that.mApplicationId)
- : that.mApplicationId != null) {
- return false;
- }
- if (mMaxSdkVersion != null ? !mMaxSdkVersion.equals(that.mMaxSdkVersion)
- : that.mMaxSdkVersion != null) {
- return false;
- }
- if (mMinSdkVersion != null ? !mMinSdkVersion.equals(that.mMinSdkVersion)
- : that.mMinSdkVersion != null) {
- return false;
- }
- if (!mName.equals(that.mName)) {
- return false;
- }
- if (mRenderscriptNdkModeEnabled != null ? !mRenderscriptNdkModeEnabled
- .equals(that.mRenderscriptNdkModeEnabled)
- : that.mRenderscriptNdkModeEnabled != null) {
- return false;
- }
- if (mRenderscriptSupportModeEnabled != null ? !mRenderscriptSupportModeEnabled
- .equals(that.mRenderscriptSupportModeEnabled) : that.mRenderscriptSupportModeEnabled
- != null) {
- return false;
- }
- if (mRenderscriptTargetApi != null ? !mRenderscriptTargetApi
- .equals(that.mRenderscriptTargetApi)
- : that.mRenderscriptTargetApi != null) {
- return false;
- }
- if (mResourceConfiguration != null ? !mResourceConfiguration
- .equals(that.mResourceConfiguration)
- : that.mResourceConfiguration != null) {
- return false;
- }
- if (mSigningConfig != null ? !mSigningConfig.equals(that.mSigningConfig)
- : that.mSigningConfig != null) {
- return false;
- }
- if (mTargetSdkVersion != null ? !mTargetSdkVersion.equals(that.mTargetSdkVersion)
- : that.mTargetSdkVersion != null) {
- return false;
- }
- if (mTestApplicationId != null ? !mTestApplicationId.equals(that.mTestApplicationId)
- : that.mTestApplicationId != null) {
- return false;
- }
- if (mTestFunctionalTest != null ? !mTestFunctionalTest.equals(that.mTestFunctionalTest)
- : that.mTestFunctionalTest != null) {
- return false;
- }
- if (mTestHandleProfiling != null ? !mTestHandleProfiling.equals(that.mTestHandleProfiling)
- : that.mTestHandleProfiling != null) {
- return false;
- }
- if (mTestInstrumentationRunner != null ? !mTestInstrumentationRunner
- .equals(that.mTestInstrumentationRunner)
- : that.mTestInstrumentationRunner != null) {
- return false;
- }
- if (mVersionCode != null ? !mVersionCode.equals(that.mVersionCode)
- : that.mVersionCode != null) {
- return false;
- }
- if (mVersionName != null ? !mVersionName.equals(that.mVersionName)
- : that.mVersionName != null) {
- return false;
- }
-
- return true;
+ return Objects.equal(mDimension, that.mDimension) &&
+ Objects.equal(mApplicationId, that.mApplicationId) &&
+ Objects.equal(mMaxSdkVersion, that.mMaxSdkVersion) &&
+ Objects.equal(mMinSdkVersion, that.mMinSdkVersion) &&
+ Objects.equal(mName, that.mName) &&
+ Objects.equal(mRenderscriptNdkModeEnabled, that.mRenderscriptNdkModeEnabled) &&
+ Objects.equal(mRenderscriptSupportModeEnabled,
+ that.mRenderscriptSupportModeEnabled) &&
+ Objects.equal(mRenderscriptTargetApi, that.mRenderscriptTargetApi) &&
+ Objects.equal(mResourceConfiguration, that.mResourceConfiguration) &&
+ Objects.equal(mSigningConfig, that.mSigningConfig) &&
+ Objects.equal(mTargetSdkVersion, that.mTargetSdkVersion) &&
+ Objects.equal(mTestApplicationId, that.mTestApplicationId) &&
+ Objects.equal(mTestFunctionalTest, that.mTestFunctionalTest) &&
+ Objects.equal(mTestHandleProfiling, that.mTestHandleProfiling) &&
+ Objects.equal(mTestInstrumentationRunner, that.mTestInstrumentationRunner) &&
+ Objects.equal(mTestInstrumentationRunnerArguments,
+ that.mTestInstrumentationRunnerArguments) &&
+ Objects.equal(mVersionCode, that.mVersionCode) &&
+ Objects.equal(mVersionName, that.mVersionName);
}
@Override
public int hashCode() {
- int result = super.hashCode();
- result = 31 * result + mName.hashCode();
- result = 31 * result + (mDimension != null ? mDimension.hashCode() : 0);
- result = 31 * result + (mMinSdkVersion != null ? mMinSdkVersion.hashCode() : 0);
- result = 31 * result + (mTargetSdkVersion != null ? mTargetSdkVersion.hashCode() : 0);
- result = 31 * result + (mMaxSdkVersion != null ? mMaxSdkVersion.hashCode() : 0);
- result = 31 * result + (mRenderscriptTargetApi != null ? mRenderscriptTargetApi.hashCode()
- : 0);
- result = 31 * result + (mRenderscriptSupportModeEnabled != null ? mRenderscriptSupportModeEnabled
- .hashCode()
- : 0);
- result = 31 * result + (mRenderscriptNdkModeEnabled != null ? mRenderscriptNdkModeEnabled.hashCode() : 0);
- result = 31 * result + (mVersionCode != null ? mVersionCode.hashCode() : 0);
- result = 31 * result + (mVersionName != null ? mVersionName.hashCode() : 0);
- result = 31 * result + (mApplicationId != null ? mApplicationId.hashCode() : 0);
- result = 31 * result + (mTestApplicationId != null ? mTestApplicationId.hashCode() : 0);
- result = 31 * result + (mTestInstrumentationRunner != null ? mTestInstrumentationRunner
- .hashCode() : 0);
- result = 31 * result + (mTestHandleProfiling != null ? mTestHandleProfiling.hashCode() : 0);
- result = 31 * result + (mTestFunctionalTest != null ? mTestFunctionalTest.hashCode() : 0);
- result = 31 * result + (mSigningConfig != null ? mSigningConfig.hashCode() : 0);
- result = 31 * result + (mResourceConfiguration != null ? mResourceConfiguration.hashCode()
- : 0);
- return result;
+ return Objects.hashCode(
+ super.hashCode(),
+ mName,
+ mDimension,
+ mMinSdkVersion,
+ mTargetSdkVersion,
+ mMaxSdkVersion,
+ mRenderscriptTargetApi,
+ mRenderscriptSupportModeEnabled,
+ mRenderscriptNdkModeEnabled,
+ mVersionCode,
+ mVersionName,
+ mApplicationId,
+ mTestApplicationId,
+ mTestInstrumentationRunner,
+ mTestInstrumentationRunnerArguments,
+ mTestHandleProfiling,
+ mTestFunctionalTest,
+ mSigningConfig,
+ mResourceConfiguration);
}
@Override
@@ -640,6 +644,7 @@
.add("applicationId", mApplicationId)
.add("testApplicationId", mTestApplicationId)
.add("testInstrumentationRunner", mTestInstrumentationRunner)
+ .add("testInstrumentationRunnerArguments", mTestInstrumentationRunnerArguments)
.add("testHandleProfiling", mTestHandleProfiling)
.add("testFunctionalTest", mTestFunctionalTest)
.add("signingConfig", mSigningConfig)
diff --git a/build-system/builder/src/main/java/com/android/builder/core/ErrorReporter.java b/build-system/builder/src/main/java/com/android/builder/core/ErrorReporter.java
new file mode 100644
index 0000000..359920f
--- /dev/null
+++ b/build-system/builder/src/main/java/com/android/builder/core/ErrorReporter.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.builder.core;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.SyncIssue;
+import com.android.ide.common.blame.MessageReceiver;
+
+/**
+ * An error reporter for project evaluation and execution.
+ *
+ * The behavior of the reporter must vary depending on the evaluation mode
+ * ({@link ErrorReporter.EvaluationMode}), indicating whether
+ * the IDE is querying the project or not.
+ */
+public abstract class ErrorReporter implements MessageReceiver {
+
+ public enum EvaluationMode {
+ /** Standard mode, errors should be breaking */
+ STANDARD,
+ /**
+ * IDE mode. Errors should not be breaking and should generate a SyncIssue instead.
+ */
+ IDE,
+ /** Legacy IDE mode (Studio 1.0), where SyncIssue are not understood by the IDE. */
+ IDE_LEGACY
+ }
+
+ @NonNull
+ private final EvaluationMode mMode;
+
+ protected ErrorReporter(@NonNull EvaluationMode mode) {
+ mMode = mode;
+ }
+
+ @NonNull
+ public EvaluationMode getMode() {
+ return mMode;
+ }
+
+ @NonNull
+ public abstract SyncIssue handleSyncError(@NonNull String data, int type, @NonNull String msg);
+}
diff --git a/build-system/builder/src/main/java/com/android/builder/core/JackProcessBuilder.java b/build-system/builder/src/main/java/com/android/builder/core/JackProcessBuilder.java
index 0beb384..6d6cbff 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/JackProcessBuilder.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/JackProcessBuilder.java
@@ -48,6 +48,8 @@
private boolean mMultiDex = false;
private int mMinSdkVersion = 21;
private File mEcjOptionFile = null;
+ private Collection<File> mJarJarRuleFiles = null;
+ private File mIncrementalDir = null;
public JackProcessBuilder() {
}
@@ -133,6 +135,12 @@
}
@NonNull
+ public JackProcessBuilder setJarJarRuleFiles(@NonNull Collection<File> jarJarRuleFiles) {
+ mJarJarRuleFiles = jarJarRuleFiles;
+ return this;
+ }
+
+ @NonNull
public JavaProcessInfo build(@NonNull BuildToolInfo buildToolInfo) throws ProcessException {
FullRevision revision = buildToolInfo.getRevision();
@@ -201,6 +209,12 @@
}
}
+ if (mJarJarRuleFiles != null) {
+ for (File jarjarRuleFile : mJarJarRuleFiles) {
+ builder.addArgs("--config-jarjar", jarjarRuleFile.getAbsolutePath());
+ }
+ }
+
builder.addArgs("@" + mEcjOptionFile.getAbsolutePath());
return builder.createJavaProcess();
diff --git a/build-system/builder/src/main/java/com/android/builder/core/LibraryRequest.java b/build-system/builder/src/main/java/com/android/builder/core/LibraryRequest.java
new file mode 100644
index 0000000..7e0451b
--- /dev/null
+++ b/build-system/builder/src/main/java/com/android/builder/core/LibraryRequest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.builder.core;
+
+import com.android.annotations.NonNull;
+
+/**
+ * a request for an optional library.
+ */
+public class LibraryRequest {
+
+ @NonNull
+ private final String mName;
+ private final boolean mRequired;
+
+ public LibraryRequest(@NonNull String name, boolean required) {
+ mName = name;
+ mRequired = required;
+ }
+
+ /**
+ * The name of the library. This is the unique name that will show up in the manifest.
+ */
+ @NonNull
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Whether the library is required by the app or just optional.
+ */
+ public boolean isRequired() {
+ return mRequired;
+ }
+}
diff --git a/build-system/builder/src/main/java/com/android/builder/core/VariantConfiguration.java b/build-system/builder/src/main/java/com/android/builder/core/VariantConfiguration.java
index 6bfc479..afa5e43 100644
--- a/build-system/builder/src/main/java/com/android/builder/core/VariantConfiguration.java
+++ b/build-system/builder/src/main/java/com/android/builder/core/VariantConfiguration.java
@@ -26,7 +26,6 @@
import com.android.builder.dependency.DependencyContainer;
import com.android.builder.dependency.JarDependency;
import com.android.builder.dependency.LibraryDependency;
-import com.android.utils.StringHelper;
import com.android.builder.model.ApiVersion;
import com.android.builder.model.BuildType;
import com.android.builder.model.ClassField;
@@ -35,15 +34,14 @@
import com.android.builder.model.SourceProvider;
import com.android.ide.common.res2.AssetSet;
import com.android.ide.common.res2.ResourceSet;
-import com.google.common.collect.ArrayListMultimap;
+import com.android.utils.StringHelper;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import java.io.File;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -398,8 +396,10 @@
}
if (!mFlavors.isEmpty()) {
+ boolean first = true;
for (F flavor : mFlavors) {
- sb.append(flavor.getName());
+ sb.append(first ? flavor.getName() : StringHelper.capitalize(flavor.getName()));
+ first = false;
}
sb.append('/').append(mBuildType.getName());
@@ -511,114 +511,6 @@
}
/**
- * Checks the SourceProviders to ensure that they don't use the same folders.
- *
- * This can't be allowed as it can break incremental support, or
- * generate dup files in resulting APK.
- *
- * @throws java.lang.RuntimeException if a dup is found.
- */
- public void checkSourceProviders() {
- List<SourceProvider> providers = Lists.newArrayListWithExpectedSize(
- 4 + mFlavorSourceProviders.size());
-
- providers.add(mDefaultSourceProvider);
- if (mBuildTypeSourceProvider != null) {
- providers.add(mBuildTypeSourceProvider);
- }
-
- providers.addAll(mFlavorSourceProviders);
- if (mVariantSourceProvider != null) {
- providers.add(mVariantSourceProvider);
- }
- if (mMultiFlavorSourceProvider != null) {
- providers.add(mMultiFlavorSourceProvider);
- }
-
- try {
- checkFileSourceSet(providers, SourceProvider.class.getMethod("getManifestFile"), "manifest");
- checkFileCollectionSourceSet(providers,
- SourceProvider.class.getMethod("getJavaDirectories"), "java");
- checkFileCollectionSourceSet(providers,
- SourceProvider.class.getMethod("getResourcesDirectories"), "resources");
- checkFileCollectionSourceSet(providers,
- SourceProvider.class.getMethod("getAidlDirectories"), "aidl");
- checkFileCollectionSourceSet(providers,
- SourceProvider.class.getMethod("getRenderscriptDirectories"), "rs");
- checkFileCollectionSourceSet(providers,
- SourceProvider.class.getMethod("getCDirectories"), "c");
- checkFileCollectionSourceSet(providers,
- SourceProvider.class.getMethod("getCppDirectories"), "cpp");
- checkFileCollectionSourceSet(providers,
- SourceProvider.class.getMethod("getResDirectories"), "res");
- checkFileCollectionSourceSet(providers,
- SourceProvider.class.getMethod("getAssetsDirectories"), "assets");
- checkFileCollectionSourceSet(providers,
- SourceProvider.class.getMethod("getJniLibsDirectories"), "jniLibs");
-
- } catch (NoSuchMethodException ignored) {
- } catch (InvocationTargetException ignored) {
- } catch (IllegalAccessException ignored) {
- }
- }
-
- private static void checkFileSourceSet(
- List<SourceProvider> providers,
- Method m,
- @NonNull String name)
- throws IllegalAccessException, InvocationTargetException {
- Map<String, File> map = Maps.newHashMap();
- for (SourceProvider sourceProvider : providers) {
- File file = (File) m.invoke(sourceProvider);
-
- if (!map.isEmpty()) {
- for (Map.Entry<String, File> entry : map.entrySet()) {
- if (file.equals(entry.getValue())) {
- throw new RuntimeException(String.format(
- "SourceSets '%s' and '%s' use the same file for '%s': %s",
- sourceProvider.getName(),
- entry.getKey(),
- name,
- file));
- }
- }
- }
-
- map.put(sourceProvider.getName(), file);
- }
- }
-
- private static void checkFileCollectionSourceSet(
- List<SourceProvider> providers,
- Method m,
- @NonNull String name)
- throws IllegalAccessException, InvocationTargetException {
- Multimap<String, File> map = ArrayListMultimap.create();
- for (SourceProvider sourceProvider : providers) {
- //noinspection unchecked
- Collection<File> list = (Collection<File>) m.invoke(sourceProvider);
-
- if (!map.isEmpty()) {
- for (Map.Entry<String, Collection<File>> entry : map.asMap().entrySet()) {
- Collection<File> existingFiles = entry.getValue();
- for (File f : list) {
- if (existingFiles.contains(f)) {
- throw new RuntimeException(String.format(
- "SourceSets '%s' and '%s' use the same file/folder for '%s': %s",
- sourceProvider.getName(),
- entry.getKey(),
- name,
- f));
- }
- }
- }
- }
-
- map.putAll(sourceProvider.getName(), list);
- }
- }
-
- /**
* Sets the variant-specific source provider.
* @param sourceProvider the source provider for the product flavor
*
@@ -678,9 +570,6 @@
resolveIndirectLibraryDependencies(mDirectLibraries, mFlatLibraries);
- for (LibraryDependency libraryDependency : mFlatLibraries) {
- mLocalJars.addAll(libraryDependency.getLocalDependencies());
- }
return this;
}
@@ -947,16 +836,12 @@
String versionName = mMergedFlavor.getVersionName();
String versionSuffix = mBuildType.getVersionNameSuffix();
- if (versionSuffix != null && !versionSuffix.isEmpty()) {
- if (versionName == null) {
- if (!mType.isForTesting()) {
- versionName = getVersionNameFromManifest();
- } else {
- versionName = "";
- }
- }
+ if (versionName == null && !mType.isForTesting()) {
+ versionName = getVersionNameFromManifest();
+ }
- versionName = versionName + versionSuffix;
+ if (versionSuffix != null && !versionSuffix.isEmpty()) {
+ versionName = Strings.nullToEmpty(versionName) + versionSuffix;
}
return versionName;
@@ -1001,6 +886,20 @@
}
/**
+ * Returns the instrumentationRunner arguments to use to test this variant, or if the
+ * variant is a test, the ones to use to test the tested variant
+ */
+ @NonNull
+ public Map<String, String> getInstrumentationRunnerArguments() {
+ VariantConfiguration config = this;
+ if (mType.isForTesting()) {
+ config = getTestedConfig();
+ checkState(config != null);
+ }
+ return config.mMergedFlavor.getTestInstrumentationRunnerArguments();
+ }
+
+ /**
* Returns handleProfiling value to use to test this variant, or if the
* variant is a test, the one to use to test the tested variant.
* @return the handleProfiling value
@@ -1217,11 +1116,13 @@
if (includeDependencies) {
for (int n = mFlatLibraries.size() - 1 ; n >= 0 ; n--) {
LibraryDependency dependency = mFlatLibraries.get(n);
- File resFolder = dependency.getResFolder();
- if (resFolder.isDirectory()) {
- ResourceSet resourceSet = new ResourceSet(dependency.getFolder().getName());
- resourceSet.addSource(resFolder);
- resourceSets.add(resourceSet);
+ if (!dependency.isOptional()) {
+ File resFolder = dependency.getResFolder();
+ if (resFolder.isDirectory()) {
+ ResourceSet resourceSet = new ResourceSet(dependency.getFolder().getName());
+ resourceSet.addSource(resFolder);
+ resourceSets.add(resourceSet);
+ }
}
}
}
@@ -1523,13 +1424,15 @@
}
for (LibraryDependency libraryDependency : mFlatLibraries) {
- File libJar = libraryDependency.getJarFile();
- if (libJar.exists()) {
- jars.add(libJar);
- }
- for (File jarFile : libraryDependency.getLocalJars()) {
- if (jarFile.isFile()) {
- jars.add(jarFile);
+ if (!libraryDependency.isOptional()) {
+ File libJar = libraryDependency.getJarFile();
+ if (libJar.exists()) {
+ jars.add(libJar);
+ }
+ for (File jarFile : libraryDependency.getLocalJars()) {
+ if (jarFile.isFile()) {
+ jars.add(jarFile);
+ }
}
}
}
@@ -1560,6 +1463,21 @@
}
}
+ for (LibraryDependency libraryDependency : mFlatLibraries) {
+ if (libraryDependency.isOptional()) {
+ File libJar = libraryDependency.getJarFile();
+ if (libJar.exists()) {
+ jars.add(libJar);
+ }
+ for (File jarFile : libraryDependency.getLocalJars()) {
+ if (jarFile.isFile()) {
+ jars.add(jarFile);
+ }
+ }
+ }
+ }
+
+
return Lists.newArrayList(jars);
}
@@ -1926,4 +1844,12 @@
// default is false.
return false;
}
+
+ public Collection<File> getJarJarRuleFiles() {
+
+ ImmutableList.Builder<File> jarjarRuleFiles = ImmutableList.builder();
+ jarjarRuleFiles.addAll(getMergedFlavor().getJarJarRuleFiles());
+ jarjarRuleFiles.addAll(mBuildType.getJarJarRuleFiles());
+ return jarjarRuleFiles.build();
+ }
}
diff --git a/build-system/builder/src/main/java/com/android/builder/dependency/LibraryBundle.java b/build-system/builder/src/main/java/com/android/builder/dependency/LibraryBundle.java
index 0beb612..e9a6129 100644
--- a/build-system/builder/src/main/java/com/android/builder/dependency/LibraryBundle.java
+++ b/build-system/builder/src/main/java/com/android/builder/dependency/LibraryBundle.java
@@ -185,7 +185,7 @@
@Override
@NonNull
public File getLintJar() {
- return new File(mBundleFolder, "lint.jar");
+ return new File(getJarsRootFolder(), "lint.jar");
}
@Override
diff --git a/build-system/builder/src/main/java/com/android/builder/dependency/SymbolFileProvider.java b/build-system/builder/src/main/java/com/android/builder/dependency/SymbolFileProvider.java
index bf77caf..07eaaff 100644
--- a/build-system/builder/src/main/java/com/android/builder/dependency/SymbolFileProvider.java
+++ b/build-system/builder/src/main/java/com/android/builder/dependency/SymbolFileProvider.java
@@ -30,4 +30,15 @@
*/
@NonNull
File getSymbolFile();
+
+ /**
+ * Returns whether the library is considered optional, meaning that it may or may not
+ * be present in the final APK.
+ *
+ * If the library is optional, then:
+ * - if the consumer is a library, it'll get skipped from resource merging and won't show up
+ * in the consumer R.txt
+ * - if the consumer is a separate test project, all the resources gets skipped from merging.
+ */
+ boolean isOptional();
}
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/BaseConfigImpl.java b/build-system/builder/src/main/java/com/android/builder/internal/BaseConfigImpl.java
index acd6134..52298f4 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/BaseConfigImpl.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/BaseConfigImpl.java
@@ -20,6 +20,7 @@
import com.android.annotations.Nullable;
import com.android.builder.model.BaseConfig;
import com.android.builder.model.ClassField;
+import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -49,40 +50,83 @@
@Nullable
private File mMultiDexKeepFile;
+ @NonNull
+ private List<File> mJarJarRuleFiles = Lists.newArrayList();
+
+ /**
+ * Adds a BuildConfig field.
+ */
public void addBuildConfigField(@NonNull ClassField field) {
mBuildConfigFields.put(field.getName(), field);
}
- public void addResValue(@NonNull ClassField field) {
- mResValues.put(field.getName(), field);
+ /**
+ * Adds a generated resource value.
+ */
+ public void addResValue(@NonNull ClassField field) { mResValues.put(field.getName(), field);
}
+ /**
+ * Adds a generated resource value.
+ */
public void addResValues(@NonNull Map<String, ClassField> values) {
mResValues.putAll(values);
}
+ /**
+ * Returns the BuildConfig fields.
+ */
@Override
@NonNull
public Map<String, ClassField> getBuildConfigFields() {
return mBuildConfigFields;
}
+ /**
+ * Adds BuildConfig fields.
+ */
public void addBuildConfigFields(@NonNull Map<String, ClassField> fields) {
mBuildConfigFields.putAll(fields);
}
+ /**
+ * Returns the generated resource values.
+ */
@NonNull
@Override
public Map<String, ClassField> getResValues() {
return mResValues;
}
+ /**
+ * Returns ProGuard configuration files to be used.
+ *
+ * <p>There are 2 default rules files
+ * <ul>
+ * <li>proguard-android.txt
+ * <li>proguard-android-optimize.txt
+ * </ul>
+ * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the
+ * full path to the files. They are identical except for enabling optimizations.
+ *
+ * <p>See similarly named methods to specify the files.
+ */
@Override
@NonNull
public List<File> getProguardFiles() {
return mProguardFiles;
}
+ /**
+ * ProGuard rule files to be included in the published AAR.
+ *
+ * <p>These proguard rule files will then be used by any application project that consumes the
+ * AAR (if ProGuard is enabled).
+ *
+ * <p>This allows AAR to specify shrinking or obfuscation exclude rules.
+ *
+ * <p>This is only valid for Library project. This is ignored in Application project.
+ */
@Override
@NonNull
public List<File> getConsumerProguardFiles() {
@@ -95,16 +139,34 @@
return mTestProguardFiles;
}
+ /**
+ * Returns the manifest placeholders.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger#TOC-Placeholder-support">
+ * Manifest merger</a>.
+ */
@NonNull
@Override
public Map<String, Object> getManifestPlaceholders() {
return mManifestPlaceholders;
}
+ /**
+ * Adds manifest placeholders.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger#TOC-Placeholder-support">
+ * Manifest merger</a>.
+ */
public void addManifestPlaceholders(@NonNull Map<String, Object> manifestPlaceholders) {
mManifestPlaceholders.putAll(manifestPlaceholders);
}
+ /**
+ * Sets a new set of manifest placeholders.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/manifest-merger#TOC-Placeholder-support">
+ * Manifest merger</a>.
+ */
public void setManifestPlaceholders(@NonNull Map<String, Object> manifestPlaceholders) {
mManifestPlaceholders.clear();
this.mManifestPlaceholders.putAll(manifestPlaceholders);
@@ -130,6 +192,8 @@
mMultiDexKeepFile = that.getMultiDexKeepFile();
mMultiDexKeepProguard = that.getMultiDexKeepProguard();
+
+ mJarJarRuleFiles = that.getJarJarRuleFiles();
}
private void setBuildConfigFields(@NonNull Map<String, ClassField> fields) {
@@ -175,6 +239,16 @@
mMultiDexKeepProguard = file;
}
+ public void setJarJarRuleFiles(@NonNull List<File> files) {
+ mJarJarRuleFiles = files;
+ }
+
+ @NonNull
+ @Override
+ public List<File> getJarJarRuleFiles() {
+ return mJarJarRuleFiles;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -186,48 +260,30 @@
BaseConfigImpl that = (BaseConfigImpl) o;
- if (!mBuildConfigFields.equals(that.mBuildConfigFields)) {
- return false;
- }
- if (!mConsumerProguardFiles.equals(that.mConsumerProguardFiles)) {
- return false;
- }
- if (!mManifestPlaceholders.equals(that.mManifestPlaceholders)) {
- return false;
- }
- if (mMultiDexEnabled != null ? !mMultiDexEnabled.equals(that.mMultiDexEnabled) :
- that.mMultiDexEnabled != null) {
- return false;
- }
- if (mMultiDexKeepFile != null ? !mMultiDexKeepFile.equals(that.mMultiDexKeepFile) :
- that.mMultiDexKeepFile != null) {
- return false;
- }
- if (mMultiDexKeepProguard != null ? !mMultiDexKeepProguard.equals(that.mMultiDexKeepProguard) :
- that.mMultiDexKeepProguard != null) {
- return false;
- }
- if (!mProguardFiles.equals(that.mProguardFiles)) {
- return false;
- }
- if (!mResValues.equals(that.mResValues)) {
- return false;
- }
+ return Objects.equal(mBuildConfigFields, that.mBuildConfigFields) &&
+ Objects.equal(mConsumerProguardFiles, that.mConsumerProguardFiles) &&
+ Objects.equal(mManifestPlaceholders, that.mManifestPlaceholders) &&
+ Objects.equal(mMultiDexEnabled, that.mMultiDexEnabled) &&
+ Objects.equal(mMultiDexKeepFile, that.mMultiDexKeepFile) &&
+ Objects.equal(mMultiDexKeepProguard, that.mMultiDexKeepProguard) &&
+ Objects.equal(mProguardFiles, that.mProguardFiles) &&
+ Objects.equal(mResValues, that.mResValues) &&
+ Objects.equal(mJarJarRuleFiles, that.mJarJarRuleFiles);
- return true;
}
@Override
public int hashCode() {
- int result = mBuildConfigFields.hashCode();
- result = 31 * result + mResValues.hashCode();
- result = 31 * result + mProguardFiles.hashCode();
- result = 31 * result + mConsumerProguardFiles.hashCode();
- result = 31 * result + mManifestPlaceholders.hashCode();
- result = 31 * result + (mMultiDexEnabled != null ? mMultiDexEnabled.hashCode() : 0);
- result = 31 * result + (mMultiDexKeepFile != null ? mMultiDexKeepFile.hashCode() : 0);
- result = 31 * result + (mMultiDexKeepProguard != null ? mMultiDexKeepProguard.hashCode() : 0);
- return result;
+ return Objects.hashCode(
+ mBuildConfigFields,
+ mResValues,
+ mProguardFiles,
+ mConsumerProguardFiles,
+ mManifestPlaceholders,
+ mMultiDexEnabled,
+ mMultiDexKeepFile,
+ mMultiDexKeepProguard,
+ mJarJarRuleFiles);
}
@Override
@@ -241,6 +297,7 @@
", mMultiDexEnabled=" + mMultiDexEnabled +
", mMultiDexKeepFile=" + mMultiDexKeepFile +
", mMultiDexKeepProguard=" + mMultiDexKeepProguard +
+ ", mJarJarRuleFiles=" + mJarJarRuleFiles +
'}';
}
}
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/FakeAndroidTarget.java b/build-system/builder/src/main/java/com/android/builder/internal/FakeAndroidTarget.java
index a9a7edc..2338c95 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/FakeAndroidTarget.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/FakeAndroidTarget.java
@@ -24,6 +24,7 @@
import com.android.sdklib.ISystemImage;
import com.android.sdklib.repository.descriptors.IdDisplay;
import com.android.utils.SparseArray;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.io.File;
@@ -139,6 +140,7 @@
return "android";
}
+ @NonNull
@Override
public AndroidVersion getVersion() {
return new AndroidVersion(mApiLevel, null);
@@ -169,6 +171,7 @@
return false;
}
+ @NonNull
@Override
public File[] getSkins() {
return new File[0];
@@ -179,9 +182,16 @@
return null;
}
+ @NonNull
@Override
- public IOptionalLibrary[] getOptionalLibraries() {
- return new IOptionalLibrary[0];
+ public List<OptionalLibrary> getAdditionalLibraries() {
+ return ImmutableList.of();
+ }
+
+ @NonNull
+ @Override
+ public List<OptionalLibrary> getOptionalLibraries() {
+ return ImmutableList.of();
}
@Override
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/compiler/JackConversionCache.java b/build-system/builder/src/main/java/com/android/builder/internal/compiler/JackConversionCache.java
index 9c159b1..8adbcc3 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/compiler/JackConversionCache.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/compiler/JackConversionCache.java
@@ -24,6 +24,7 @@
import com.android.ide.common.process.ProcessOutputHandler;
import com.android.sdklib.BuildToolInfo;
import com.android.sdklib.repository.FullRevision;
+import com.android.utils.ILogger;
import com.android.utils.Pair;
import com.google.common.io.Files;
@@ -90,7 +91,8 @@
@NonNull BuildToolInfo buildToolInfo,
boolean verbose,
@NonNull JavaProcessExecutor processExecutor,
- @NonNull ProcessOutputHandler processOutputHandler)
+ @NonNull ProcessOutputHandler processOutputHandler,
+ @NonNull ILogger logger)
throws ProcessException, InterruptedException, IOException {
Key itemKey = Key.of(inputFile, buildToolInfo.getRevision());
@@ -102,14 +104,15 @@
if (pair.getSecond()) {
try {
// haven't process this file yet so do it and record it.
- List<File> files = AndroidBuilder.convertLibraryToJack(
+ List<File> files = AndroidBuilder.convertLibaryToJackUsingApis(
inputFile,
outFile,
dexOptions,
buildToolInfo,
verbose,
processExecutor,
- processOutputHandler);
+ processOutputHandler,
+ logger);
item.getOutputFiles().addAll(files);
incrementMisses();
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/compiler/RenderScriptProcessor.java b/build-system/builder/src/main/java/com/android/builder/internal/compiler/RenderScriptProcessor.java
index 3bee2fc..da566c1 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/compiler/RenderScriptProcessor.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/compiler/RenderScriptProcessor.java
@@ -101,9 +101,7 @@
private final int mTargetApi;
- private final boolean mDebugBuild;
-
- private final int mOptimLevel;
+ private final int mOptimizationLevel;
private final boolean mNdkMode;
@@ -123,7 +121,7 @@
@NonNull BuildToolInfo buildToolInfo,
int targetApi,
boolean debugBuild,
- int optimLevel,
+ int optimizationLevel,
boolean ndkMode,
boolean supportMode,
@Nullable Set<String> abiFilters) {
@@ -135,8 +133,7 @@
mLibOutputDir = libOutputDir;
mBuildToolInfo = buildToolInfo;
mTargetApi = targetApi;
- mDebugBuild = debugBuild;
- mOptimLevel = optimLevel;
+ mOptimizationLevel = optimizationLevel;
mNdkMode = ndkMode;
mSupportMode = supportMode;
mAbiFilters = abiFilters;
@@ -226,7 +223,7 @@
// }
builder.addArgs("-O");
- builder.addArgs(Integer.toString(mOptimLevel));
+ builder.addArgs(Integer.toString(mOptimizationLevel));
// add all import paths
builder.addArgs("-I");
@@ -349,7 +346,7 @@
builder.setExecutable(mBuildToolInfo.getPath(BuildToolInfo.PathId.BCC_COMPAT));
builder.addEnvironments(env);
- builder.addArgs("-O" + Integer.toString(mOptimLevel));
+ builder.addArgs("-O" + Integer.toString(mOptimizationLevel));
File outFile = new File(objAbiFolder, objName);
builder.addArgs("-o", outFile.getAbsolutePath());
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/incremental/DependencyData.java b/build-system/builder/src/main/java/com/android/builder/internal/incremental/DependencyData.java
index 2648b6a..1356d4d 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/incremental/DependencyData.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/incremental/DependencyData.java
@@ -98,7 +98,7 @@
return processDependencyData(content);
}
- private static enum ParseMode {
+ private enum ParseMode {
OUTPUT, MAIN, SECONDARY, DONE
}
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/packaging/Packager.java b/build-system/builder/src/main/java/com/android/builder/internal/packaging/Packager.java
index 22917f8..f3db39d 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/packaging/Packager.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/packaging/Packager.java
@@ -28,12 +28,13 @@
import com.android.builder.packaging.SealedPackageException;
import com.android.builder.signing.SignedJarBuilder;
import com.android.builder.signing.SignedJarBuilder.IZipEntryFilter;
-import com.android.ide.common.packaging.PackagingUtils;
import com.android.ide.common.signing.CertificateInfo;
+import com.android.utils.FileUtils;
import com.android.utils.ILogger;
-import com.google.common.collect.Sets;
import com.google.common.io.Closeables;
+import com.google.common.io.Files;
+import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -44,11 +45,9 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
@@ -69,10 +68,10 @@
Pattern.CASE_INSENSITIVE);
/**
- * A No-op zip filter. It's used to detect conflicts.
+ * Filter to detect duplicate entries
*
*/
- private final class NullZipFilter implements IZipEntryFilter {
+ private final class DuplicateZipFilter implements IZipEntryFilter {
private File mInputFile;
void reset(File inputFile) {
@@ -85,7 +84,13 @@
File duplicate = checkFileForDuplicate(archivePath);
if (duplicate != null) {
- throw new DuplicateFileException(archivePath, duplicate, mInputFile);
+ // we have a duplicate but it might be the same source file, in this case,
+ // we just ignore the duplicate, and of course, we don't add it again.
+ File potentialDuplicate = new File(mInputFile, archivePath);
+ if (!duplicate.getAbsolutePath().equals(potentialDuplicate.getAbsolutePath())) {
+ throw new DuplicateFileException(archivePath, duplicate, mInputFile);
+ }
+ return false;
} else {
mAddedFiles.put(archivePath, mInputFile);
}
@@ -95,118 +100,20 @@
}
/**
- * Custom {@link IZipEntryFilter} to filter out everything that is not a standard java
- * resources, and also record whether the zip file contains native libraries.
- * <p/>Used in {@link SignedJarBuilder#writeZip(java.io.InputStream, IZipEntryFilter)} when
- * we only want the java resources from external jars.
+ * A filter to filter out binary files like .class
*/
- private final class JavaAndNativeResourceFilter implements IZipEntryFilter {
- private final List<String> mNativeLibs = new ArrayList<String>();
- private Set<String> mUsedPickFirsts = null;
-
- @Nullable
- private final PackagingOptions mPackagingOptions;
-
+ private static final class NoBinaryZipFilter implements IZipEntryFilter {
@NonNull
- private final Set<String> mExcludes;
- @NonNull
- private final Set<String> mPickFirsts;
+ private final IZipEntryFilter parentFilter;
- private boolean mNativeLibsConflict = false;
- private File mInputFile;
-
- private JavaAndNativeResourceFilter(@Nullable PackagingOptions packagingOptions) {
- mPackagingOptions = packagingOptions;
-
- mExcludes = mPackagingOptions != null ? mPackagingOptions.getExcludes() :
- Collections.<String>emptySet();
- mPickFirsts = mPackagingOptions != null ? mPackagingOptions.getPickFirsts() :
- Collections.<String>emptySet();
-
+ private NoBinaryZipFilter(@NonNull IZipEntryFilter parentFilter) {
+ this.parentFilter = parentFilter;
}
+
@Override
public boolean checkEntry(String archivePath) throws ZipAbortException {
- // split the path into segments.
- String[] segments = archivePath.split("/");
-
- // empty path? skip to next entry.
- if (segments.length == 0) {
- return false;
- }
-
- //noinspection VariableNotUsedInsideIf
- if (mPackagingOptions != null) {
- if (mExcludes.contains(archivePath)) {
- return false;
- }
-
- if (mPickFirsts.contains(archivePath)) {
- if (mUsedPickFirsts == null) {
- mUsedPickFirsts = Sets.newHashSetWithExpectedSize(mPickFirsts.size());
- }
-
- if (mUsedPickFirsts.contains(archivePath)) {
- return false;
- } else {
- mUsedPickFirsts.add(archivePath);
- }
- }
- }
-
- // Check each folders to make sure they should be included.
- // Folders like CVS, .svn, etc.. should already have been excluded from the
- // jar file, but we need to exclude some other folder (like /META-INF) so
- // we check anyway.
- for (int i = 0 ; i < segments.length - 1; i++) {
- if (!PackagingUtils.checkFolderForPackaging(segments[i])) {
- return false;
- }
- }
-
- // get the file name from the path
- String fileName = segments[segments.length-1];
-
- boolean check = PackagingUtils.checkFileForPackaging(fileName);
-
- // only do additional checks if the file passes the default checks.
- if (check) {
- mLogger.verbose("=> %s", archivePath);
-
- File duplicate = checkFileForDuplicate(archivePath);
- if (duplicate != null) {
- throw new DuplicateFileException(archivePath, duplicate, mInputFile);
- } else {
- mAddedFiles.put(archivePath, mInputFile);
- }
-
- if (archivePath.endsWith(".so")) {
- mNativeLibs.add(archivePath);
-
- // only .so located in lib/ will interfere with the installation
- if (archivePath.startsWith(SdkConstants.FD_APK_NATIVE_LIBS + "/")) {
- mNativeLibsConflict = true;
- }
- } else if (archivePath.endsWith(".jnilib")) {
- mNativeLibs.add(archivePath);
- }
- }
-
- return check;
- }
-
- List<String> getNativeLibs() {
- return mNativeLibs;
- }
-
- boolean getNativeLibsConflict() {
- return mNativeLibsConflict;
- }
-
- void reset(File inputFile) {
- mInputFile = inputFile;
- mNativeLibs.clear();
- mNativeLibsConflict = false;
+ return parentFilter.checkEntry(archivePath) && !archivePath.endsWith(".class");
}
}
@@ -215,48 +122,11 @@
private boolean mJniDebugMode = false;
private boolean mIsSealed = false;
- private final NullZipFilter mNullFilter = new NullZipFilter();
- private final JavaAndNativeResourceFilter mFilter;
+ private final DuplicateZipFilter mNoDuplicateFilter = new DuplicateZipFilter();
+ private final NoBinaryZipFilter mNoBinaryZipFilter = new NoBinaryZipFilter(mNoDuplicateFilter);
+ @Nullable private final SignedJarBuilder.IZipEntryFilter mPackagingOptionsFilter;
private final HashMap<String, File> mAddedFiles = new HashMap<String, File>();
-
- /**
- * Status for the addition of a jar file resources into the APK.
- * This indicates possible issues with native library inside the jar file.
- */
- public interface JarStatus {
- /**
- * Returns the list of native libraries found in the jar file.
- */
- List<String> getNativeLibs();
-
- /**
- * Returns whether some of those libraries were located in the location that Android
- * expects its native libraries.
- */
- boolean hasNativeLibsConflicts();
-
- }
-
- /** Internal implementation of {@link JarStatus}. */
- private static final class JarStatusImpl implements JarStatus {
- public final List<String> mLibs;
- public final boolean mNativeLibsConflict;
-
- private JarStatusImpl(List<String> libs, boolean nativeLibsConflict) {
- mLibs = libs;
- mNativeLibsConflict = nativeLibsConflict;
- }
-
- @Override
- public List<String> getNativeLibs() {
- return mLibs;
- }
-
- @Override
- public boolean hasNativeLibsConflicts() {
- return mNativeLibsConflict;
- }
- }
+ private final HashMap<String, File> mMergeFiles = new HashMap<String, File>();
/**
* Creates a new instance.
@@ -272,6 +142,7 @@
*
* @param apkLocation the file to create
* @param resLocation the file representing the packaged resource file.
+ * @param mergingFolder the folder to store files that are being merged.
* @param certificateInfo the signing information used to sign the package. Optional the OS path to the debug keystore, if needed or null.
* @param logger the logger.
* @throws com.android.builder.packaging.PackagerException
@@ -279,11 +150,12 @@
public Packager(
@NonNull String apkLocation,
@NonNull String resLocation,
+ @NonNull File mergingFolder,
CertificateInfo certificateInfo,
@Nullable String createdBy,
@Nullable PackagingOptions packagingOptions,
+ @Nullable SignedJarBuilder.IZipEntryFilter packagingOptionsFilter,
ILogger logger) throws PackagerException {
- mFilter = new JavaAndNativeResourceFilter(packagingOptions);
try {
File apkFile = new File(apkLocation);
@@ -292,6 +164,14 @@
File resFile = new File(resLocation);
checkInputFile(resFile);
+ checkMergingFolder(mergingFolder);
+ if (packagingOptions != null) {
+ for (String merge : packagingOptions.getMerges()) {
+ mMergeFiles.put(merge, new File(mergingFolder, merge));
+ }
+ }
+
+ mPackagingOptionsFilter = packagingOptionsFilter;
mLogger = logger;
mBuilder = new SignedJarBuilder(
@@ -409,11 +289,11 @@
mLogger.verbose("%s:", zipFile);
// reset the filter with this input.
- mNullFilter.reset(zipFile);
+ mNoDuplicateFilter.reset(zipFile);
// ask the builder to add the content of the file.
fis = new FileInputStream(zipFile);
- mBuilder.writeZip(fis, mNullFilter);
+ mBuilder.writeZip(fis, mNoDuplicateFilter, null /* ZipEntryExtractor */);
} catch (DuplicateFileException e) {
mBuilder.cleanUp();
throw e;
@@ -430,51 +310,59 @@
}
/**
- * Adds the resources from a jar file.
- * @param jarFile the jar File.
- * @return a {@link JarStatus} object indicating if native libraries where found in
- * the jar file.
- * @throws PackagerException if an error occurred
- * @throws SealedPackageException if the APK is already sealed.
- * @throws DuplicateFileException if a file conflicts with another already added to the APK
- * at the same location inside the APK archive.
+ * Adds all resources from a merged folder or jar file. There cannot be any duplicates and all
+ * files present must be added unless it is a "binary" file like a .class or .dex (jack
+ * produces the classes.dex in the same location as the obfuscated resources).
+ * @param jarFileOrDirectory a jar file or directory reference.
+ * @throws PackagerException could not add an entry to the package.
+ * @throws DuplicateFileException if an entry with the same name was already present in the
+ * package being built while adding the jarFileOrDirectory content.
*/
- public JarStatus addResourcesFromJar(File jarFile) throws PackagerException,
- SealedPackageException, DuplicateFileException {
- if (mIsSealed) {
- throw new SealedPackageException("APK is already sealed");
- }
+ public void addResources(@NonNull File jarFileOrDirectory)
+ throws PackagerException, DuplicateFileException {
- FileInputStream fis = null;
+ mNoDuplicateFilter.reset(jarFileOrDirectory);
+ InputStream fis = null;
try {
- mLogger.verbose("%s:", jarFile);
-
- // reset the filter with this input.
- mFilter.reset(jarFile);
-
- // ask the builder to add the content of the file, filtered to only let through
- // the java resources.
- fis = new FileInputStream(jarFile);
- mBuilder.writeZip(fis, mFilter);
-
- // check if native libraries were found in the external library. This should
- // constitutes an error or warning depending on if they are in lib/
- return new JarStatusImpl(mFilter.getNativeLibs(), mFilter.getNativeLibsConflict());
+ if (jarFileOrDirectory.isDirectory()) {
+ addResourcesFromDirectory(jarFileOrDirectory, "");
+ } else {
+ fis = new BufferedInputStream(new FileInputStream(jarFileOrDirectory));
+ mBuilder.writeZip(fis, mNoBinaryZipFilter, null /* ZipEntryExtractor */);
+ }
} catch (DuplicateFileException e) {
mBuilder.cleanUp();
throw e;
} catch (Exception e) {
mBuilder.cleanUp();
- throw new PackagerException(e, "Failed to add %s", jarFile);
+ throw new PackagerException(e, "Failed to add %s", jarFileOrDirectory);
} finally {
try {
- Closeables.close(fis, true /* swallowIOException */);
+ if (fis != null) {
+ Closeables.close(fis, true /* swallowIOException */);
+ }
} catch (IOException e) {
// ignore.
}
}
}
+ private void addResourcesFromDirectory(@NonNull File directory, String path)
+ throws IOException, IZipEntryFilter.ZipAbortException {
+ File[] directoryFiles = directory.listFiles();
+ if (directoryFiles == null) {
+ return;
+ }
+ for (File file : directoryFiles) {
+ String entryName = path.isEmpty() ? file.getName() : path + "/" + file.getName();
+ if (file.isDirectory()) {
+ addResourcesFromDirectory(file, entryName);
+ } else {
+ doAddFile(file, entryName);
+ }
+ }
+ }
+
/**
* Adds the native libraries from the top native folder.
* The content of this folder must be the various ABI folders.
@@ -535,8 +423,12 @@
abi.getName() + "/" + libName;
try {
- doAddFile(lib, path);
- } catch (IOException e) {
+
+ if (mPackagingOptionsFilter == null
+ || mPackagingOptionsFilter.checkEntry(path)) {
+ doAddFile(lib, path);
+ }
+ } catch (Exception e) {
mBuilder.cleanUp();
throw new PackagerException(e, "Failed to add %s", lib);
}
@@ -559,6 +451,20 @@
throw new SealedPackageException("APK is already sealed");
}
+ // Add all the merged files that are pending to be packaged.
+ for (Map.Entry<String, File>entry : mMergeFiles.entrySet()) {
+ File inputFile = entry.getValue();
+ String archivePath = entry.getKey();
+ try {
+ if (inputFile.exists()) {
+ mBuilder.writeFile(inputFile, archivePath);
+ }
+ } catch (IOException e) {
+ mBuilder.cleanUp();
+ throw new PackagerException(e, "Failed to add merged file %s", inputFile);
+ }
+ }
+
// close and sign the application package.
try {
mBuilder.close();
@@ -570,17 +476,26 @@
}
}
- private void doAddFile(File file, String archivePath) throws DuplicateFileException,
+ private void doAddFile(File file, String archivePath) throws IZipEntryFilter.ZipAbortException,
IOException {
- mLogger.verbose("%1$s => %2$s", file, archivePath);
- File duplicate = checkFileForDuplicate(archivePath);
- if (duplicate != null) {
- throw new DuplicateFileException(archivePath, duplicate, file);
+ // If a file has to be merged, write it to a file in the merging folder and add it later.
+ if (mMergeFiles.keySet().contains(archivePath)) {
+ File mergingFile = mMergeFiles.get(archivePath);
+ Files.createParentDirs(mergingFile);
+ FileOutputStream fos = new FileOutputStream(mMergeFiles.get(archivePath), true);
+ try {
+ fos.write(Files.toByteArray(file));
+ } finally {
+ fos.close();
+ }
+ } else {
+ if (!mNoBinaryZipFilter.checkEntry(archivePath)) {
+ return;
+ }
+ mAddedFiles.put(archivePath, file);
+ mBuilder.writeFile(file, archivePath);
}
-
- mAddedFiles.put(archivePath, file);
- mBuilder.writeFile(file, archivePath);
}
/**
@@ -603,7 +518,7 @@
* @param file the File to check
* @throws PackagerException If the check fails
*/
- private void checkOutputFile(File file) throws PackagerException {
+ private static void checkOutputFile(File file) throws PackagerException {
if (file.isDirectory()) {
throw new PackagerException("%s is a directory!", file);
}
@@ -625,6 +540,32 @@
}
/**
+ * Checks the merger folder is:
+ * - a directory.
+ * - if the folder exists, that it can be modified.
+ * - if it doesn't exists, that a new folder can be created.
+ * @param file the File to check
+ * @throws PackagerException If the check fails
+ */
+ private static void checkMergingFolder(File file) throws PackagerException {
+ if (file.isFile()) {
+ throw new PackagerException("%s is a file!", file);
+ }
+
+ if (file.exists()) { // will be a directory in this case.
+ if (!file.canWrite()) {
+ throw new PackagerException("Cannot write %s", file);
+ }
+ }
+
+ try {
+ FileUtils.emptyFolder(file);
+ } catch (IOException e) {
+ throw new PackagerException(e);
+ }
+ }
+
+ /**
* Checks an input {@link File} object.
* This checks the following:
* - the file is not an existing directory.
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/testing/CustomTestRunListener.java b/build-system/builder/src/main/java/com/android/builder/internal/testing/CustomTestRunListener.java
index 62eb862..0716d46 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/testing/CustomTestRunListener.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/testing/CustomTestRunListener.java
@@ -87,8 +87,7 @@
@Override
public void testRunStarted(String runName, int testCount) {
if (mLogger != null) {
- mLogger.info(
- String.format("Starting %1$d tests on %2$s", testCount, mDeviceName));
+ mLogger.info("Starting %1$d tests on %2$s", testCount, mDeviceName);
}
super.testRunStarted(runName, testCount);
}
@@ -96,9 +95,8 @@
@Override
public void testFailed(TestIdentifier test, String trace) {
if (mLogger != null) {
- mLogger.warning(
- String.format("\n%1$s > %2$s[%3$s] \033[31mFAILED \033[0m",
- test.getClassName(), test.getTestName(), mDeviceName));
+ mLogger.warning("\n%1$s > %2$s[%3$s] \033[31mFAILED \033[0m",
+ test.getClassName(), test.getTestName(), mDeviceName);
mLogger.warning(getModifiedTrace(trace));
}
@@ -109,7 +107,11 @@
@Override
public void testAssumptionFailure(TestIdentifier test, String trace) {
- testFailed(test, trace);
+ if (mLogger != null) {
+ mLogger.warning("\n%1$s > %2$s[%3$s] \033[33mSKIPPED \033[0m\n%4$s",
+ test.getClassName(), test.getTestName(), mDeviceName, getModifiedTrace(trace));
+ }
+ super.testAssumptionFailure(test, trace);
}
@Override
@@ -117,13 +119,11 @@
if (!mFailedTests.remove(test)) {
// if wasn't present in the list, then the test succeeded.
if (mLogger != null) {
- mLogger.info(
- String.format("\n%1$s > %2$s[%3$s] \033[32mSUCCESS \033[0m",
- test.getClassName(), test.getTestName(), mDeviceName));
+ mLogger.info("\n%1$s > %2$s[%3$s] \033[32mSUCCESS \033[0m",
+ test.getClassName(), test.getTestName(), mDeviceName);
}
}
-
super.testEnded(test, testMetrics);
}
@@ -135,6 +135,15 @@
super.testRunFailed(errorMessage);
}
+ @Override
+ public void testIgnored(TestIdentifier test) {
+ if (mLogger != null) {
+ mLogger.warning("\n%1$s > %2$s[%3$s] \033[33mSKIPPED \033[0m",
+ test.getClassName(), test.getTestName(), mDeviceName);
+ }
+ super.testIgnored(test);
+ }
+
private String getModifiedTrace(String trace) {
// split lines
String[] lines = trace.split("\n");
diff --git a/build-system/builder/src/main/java/com/android/builder/internal/testing/SimpleTestCallable.java b/build-system/builder/src/main/java/com/android/builder/internal/testing/SimpleTestCallable.java
index f6b4985..46cbc13 100644
--- a/build-system/builder/src/main/java/com/android/builder/internal/testing/SimpleTestCallable.java
+++ b/build-system/builder/src/main/java/com/android/builder/internal/testing/SimpleTestCallable.java
@@ -33,7 +33,6 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintWriter;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -67,27 +66,21 @@
@NonNull
private final List<File> testedApks;
@NonNull
- private final File adbExec;
- @NonNull
- private final Collection<String> installOptions;
-
- private final int timeoutInMs;
- @NonNull
private final ILogger logger;
+ private final int timeoutInMs;
+
public SimpleTestCallable(
- @NonNull DeviceConnector device,
- @NonNull String projectName,
- @NonNull String flavorName,
- @NonNull File testApk,
+ @NonNull DeviceConnector device,
+ @NonNull String projectName,
+ @NonNull String flavorName,
+ @NonNull File testApk,
@NonNull List<File> testedApks,
- @NonNull File adbExec,
- @NonNull TestData testData,
- @NonNull File resultsDir,
- @NonNull File coverageDir,
- int timeoutInMs,
- @NonNull Collection<String> installOptions,
- @NonNull ILogger logger) {
+ @NonNull TestData testData,
+ @NonNull File resultsDir,
+ @NonNull File coverageDir,
+ int timeoutInMs,
+ @NonNull ILogger logger) {
this.projectName = projectName;
this.device = device;
this.flavorName = flavorName;
@@ -96,9 +89,7 @@
this.testApk = testApk;
this.testedApks = testedApks;
this.testData = testData;
- this.adbExec = adbExec;
this.timeoutInMs = timeoutInMs;
- this.installOptions = installOptions;
this.logger = logger;
}
@@ -150,6 +141,11 @@
testData.getInstrumentationRunner(),
device);
+ for (Map.Entry<String, String> argument:
+ testData.getInstrumentationRunnerArguments().entrySet()) {
+ runner.addInstrumentationArg(argument.getKey(), argument.getValue());
+ }
+
if (testData.isTestCoverageEnabled()) {
runner.addInstrumentationArg("coverage", "true");
runner.addInstrumentationArg("coverageFile", coverageFile);
diff --git a/build-system/builder/src/main/java/com/android/builder/png/QueuedCruncher.java b/build-system/builder/src/main/java/com/android/builder/png/QueuedCruncher.java
index db45a6b..ee852ce 100644
--- a/build-system/builder/src/main/java/com/android/builder/png/QueuedCruncher.java
+++ b/build-system/builder/src/main/java/com/android/builder/png/QueuedCruncher.java
@@ -29,7 +29,6 @@
import java.io.File;
import java.io.IOException;
-import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
diff --git a/build-system/builder/src/main/java/com/android/builder/png/VectorDrawableRenderer.java b/build-system/builder/src/main/java/com/android/builder/png/VectorDrawableRenderer.java
index d17ac44..83db810 100644
--- a/build-system/builder/src/main/java/com/android/builder/png/VectorDrawableRenderer.java
+++ b/build-system/builder/src/main/java/com/android/builder/png/VectorDrawableRenderer.java
@@ -17,60 +17,175 @@
package com.android.builder.png;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
import com.android.annotations.NonNull;
+import com.android.ide.common.res2.ResourcePreprocessor;
+import com.android.ide.common.resources.configuration.DensityQualifier;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.VersionQualifier;
+import com.android.ide.common.vectordrawable.VdPreview;
import com.android.resources.Density;
+import com.android.resources.ResourceFolderType;
+import com.android.utils.ILogger;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
-/**
- * Generates PNG images from VectorDrawable files.
- */
-public class VectorDrawableRenderer {
+import javax.imageio.ImageIO;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+/**
+ * Generates PNG images (and XML copies) from VectorDrawable files.
+ */
+@SuppressWarnings("MethodMayBeStatic")
+public class VectorDrawableRenderer implements ResourcePreprocessor {
/** Projects with minSdk set to this or higher don't need to generate PNGs. */
public static final int MIN_SDK_WITH_VECTOR_SUPPORT = 21;
+ private final ILogger mLogger;
+ private final File mOutputDir;
+ private final Collection<Density> mDensities;
+
+ public VectorDrawableRenderer(File outputDir, Collection<Density> densities, ILogger logger) {
+ mOutputDir = outputDir;
+ mDensities = densities;
+ mLogger = logger;
+ }
+
public Collection<File> createPngFiles(
@NonNull File inputXmlFile,
@NonNull File outputResDirectory,
@NonNull Collection<Density> densities) throws IOException {
- checkArgument(inputXmlFile.exists());
- checkArgument(outputResDirectory.exists());
-
- Collection<File> createdFiles = Lists.newArrayList();
-
- for (Density density : densities) {
- // Sketch implementation.
-
- // TODO: add the density to all other qualifiers of the original file.
- File directory = new File(outputResDirectory, "drawable-" + density.getResourceValue());
- File pngFile = new File(directory, inputXmlFile.getName().replace(".xml", ".png"));
-
- Files.createParentDirs(pngFile);
- Files.write("PNG for " + density.getResourceValue(), pngFile, Charsets.UTF_8);
- createdFiles.add(pngFile);
- }
-
- File v21Dir = new File(outputResDirectory, "drawable-v21");
- File v21Copy = new File(v21Dir, inputXmlFile.getName());
- Files.createParentDirs(v21Copy);
- Files.copy(inputXmlFile, v21Copy);
- createdFiles.add(v21Copy);
-
- // TODO: make all the drawable-hdpi-v21 aliases.
-
- return createdFiles;
+ throw new RuntimeException("Replaced by the new way to generate PNGs");
}
- public boolean isVectorDrawable(File resourceFile) {
- // TODO: parse the root element of the file to check.
- return resourceFile.getPath().endsWith(".xml")
- && resourceFile.getParentFile().getName().equals("drawable");
+ @Override
+ public boolean needsPreprocessing(File resourceFile) {
+ return isXml(resourceFile)
+ && isInDrawable(resourceFile)
+ && getEffectiveVersion(resourceFile) < MIN_SDK_WITH_VECTOR_SUPPORT
+ && isRootVector(resourceFile);
+ }
+
+ @Override
+ public Collection<File> getFilesToBeGenerated(File inputXmlFile) {
+ FolderConfiguration originalConfiguration = getFolderConfiguration(inputXmlFile);
+
+ // Create all the PNG files and duplicate the XML into folders with the version qualifier.
+ Collection<File> filesToBeGenerated = Lists.newArrayList();
+ for (Density density : mDensities) {
+ FolderConfiguration newConfiguration = FolderConfiguration.copyOf(originalConfiguration);
+ newConfiguration.setDensityQualifier(new DensityQualifier(density));
+
+ File directory = new File(
+ mOutputDir,
+ newConfiguration.getFolderName(ResourceFolderType.DRAWABLE));
+ File pngFile = new File(
+ directory,
+ inputXmlFile.getName().replace(".xml", ".png"));
+
+ filesToBeGenerated.add(pngFile);
+
+ newConfiguration.setVersionQualifier(new VersionQualifier(MIN_SDK_WITH_VECTOR_SUPPORT));
+ File destination = new File(
+ mOutputDir,
+ newConfiguration.getFolderName(ResourceFolderType.DRAWABLE));
+ File xmlCopy = new File(destination, inputXmlFile.getName());
+ filesToBeGenerated.add(xmlCopy);
+ }
+
+ return filesToBeGenerated;
+ }
+
+ @Override
+ public void generateFile(File toBeGenerated, File original) throws IOException {
+ Files.createParentDirs(toBeGenerated);
+
+ if (isXml(toBeGenerated)) {
+ Files.copy(original, toBeGenerated);
+ } else {
+ mLogger.info(
+ "Generating PNG: [%s] from [%s]",
+ toBeGenerated.getAbsolutePath(),
+ original.getAbsolutePath());
+
+ FolderConfiguration folderConfiguration = getFolderConfiguration(toBeGenerated);
+ checkState(folderConfiguration.getDensityQualifier() != null);
+ Density density = folderConfiguration.getDensityQualifier().getValue();
+
+ String xmlContent = Files.toString(original, Charsets.UTF_8);
+ float scaleFactor = density.getDpiValue() / (float) Density.MEDIUM.getDpiValue();
+ if (scaleFactor <= 0) {
+ scaleFactor = 1.0f;
+ }
+
+ final VdPreview.TargetSize imageSize = VdPreview.TargetSize.createSizeFromScale(scaleFactor);
+ BufferedImage image = VdPreview.getPreviewFromVectorXml(imageSize, xmlContent, null);
+ checkState(image != null, "Generating the image failed.");
+ ImageIO.write(image, "png", toBeGenerated);
+ }
+ }
+
+ @NonNull
+ private FolderConfiguration getFolderConfiguration(@NonNull File inputXmlFile) {
+ String parentName = inputXmlFile.getParentFile().getName();
+ FolderConfiguration originalConfiguration =
+ FolderConfiguration.getConfigForFolder(parentName);
+ checkArgument(
+ originalConfiguration != null,
+ "Invalid resource folder name [%s].",
+ parentName);
+ return originalConfiguration;
+ }
+
+ private boolean isInDrawable(@NonNull File inputXmlFile) {
+ ResourceFolderType folderType =
+ ResourceFolderType.getFolderType(inputXmlFile.getParentFile().getName());
+
+ return folderType == ResourceFolderType.DRAWABLE;
+ }
+
+ /**
+ * Parse the root element of the file, return true if it is a vector.
+ * TODO: Combine this with the drawing code, such that we don't parse the file twice.
+ */
+ private boolean isRootVector(File resourceFile) {
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ boolean result = false;
+ try {
+ DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+ Document doc = dBuilder.parse(resourceFile);
+ Element root = doc.getDocumentElement();
+ if (root != null && root.getNodeName().equalsIgnoreCase("vector")) {
+ result = true;
+ }
+ } catch (Exception e) {
+ mLogger.error(e, "Exception in parsing the XML resource" + e.getMessage());
+ }
+
+ return result;
+ }
+
+ private boolean isXml(File resourceFile) {
+ return resourceFile.getPath().endsWith(".xml");
+ }
+
+ private int getEffectiveVersion(File resourceFile) {
+ FolderConfiguration configuration = getFolderConfiguration(resourceFile);
+ if (configuration.getVersionQualifier() == null) {
+ configuration.createDefault();
+ }
+ //noinspection ConstantConditions - handled above.
+ return configuration.getVersionQualifier().getVersion();
}
}
diff --git a/build-system/builder/src/main/java/com/android/builder/sdk/DefaultSdkLoader.java b/build-system/builder/src/main/java/com/android/builder/sdk/DefaultSdkLoader.java
index 3517c85..5a2fe7f 100644
--- a/build-system/builder/src/main/java/com/android/builder/sdk/DefaultSdkLoader.java
+++ b/build-system/builder/src/main/java/com/android/builder/sdk/DefaultSdkLoader.java
@@ -85,7 +85,7 @@
IAndroidTarget target = mSdkManager.getTargetFromHashString(targetHash);
if (target == null) {
- throw new IllegalStateException("failed to find target " + targetHash + " : " + mSdkLocation);
+ throw new IllegalStateException("failed to find target with hash string '" + targetHash + "' in: " + mSdkLocation);
}
BuildToolInfo buildToolInfo = mSdkManager.getBuildTool(buildToolRevision);
diff --git a/build-system/builder/src/main/java/com/android/builder/signing/SignedJarBuilder.java b/build-system/builder/src/main/java/com/android/builder/signing/SignedJarBuilder.java
index d8a3dde..b41c9ff 100644
--- a/build-system/builder/src/main/java/com/android/builder/signing/SignedJarBuilder.java
+++ b/build-system/builder/src/main/java/com/android/builder/signing/SignedJarBuilder.java
@@ -148,6 +148,18 @@
boolean checkEntry(String archivePath) throws ZipAbortException;
}
+
+ /**
+ * Classes which implement this interface provides a method to check whether a file should
+ * be merged and extracted from the zip.
+ */
+ public interface ZipEntryExtractor {
+
+ boolean checkEntry(String archivePath);
+
+ void extract(String archivePath, InputStream zis) throws IOException;
+ }
+
/**
* Creates a {@link SignedJarBuilder} with a given output stream, and signing information.
* <p/>If either <code>key</code> or <code>certificate</code> is <code>null</code> then
@@ -209,6 +221,17 @@
/**
* Copies the content of a Jar/Zip archive into the receiver archive.
+ * @param input the {@link InputStream} for the Jar/Zip to copy.
+ * @throws IOException
+ * @throws ZipAbortException if the {@link IZipEntryFilter} filter indicated that the write
+ * must be aborted.
+ */
+ public void writeZip(InputStream input) throws IOException, ZipAbortException {
+ writeZip(input, null, null);
+ }
+
+ /**
+ * Copies the content of a Jar/Zip archive into the receiver archive.
* <p/>An optional {@link IZipEntryFilter} allows to selectively choose which files
* to copy over.
* @param input the {@link InputStream} for the Jar/Zip to copy.
@@ -217,7 +240,7 @@
* @throws ZipAbortException if the {@link IZipEntryFilter} filter indicated that the write
* must be aborted.
*/
- public void writeZip(InputStream input, IZipEntryFilter filter)
+ public void writeZip(InputStream input, IZipEntryFilter filter, ZipEntryExtractor extractor)
throws IOException, ZipAbortException {
ZipInputStream zis = new ZipInputStream(input);
@@ -256,6 +279,12 @@
}
}
+ // if we have a filter, we check the entry to see if it's a file that should be extracted.
+ if (extractor != null && extractor.checkEntry(name)) {
+ extractor.extract(name, zis);
+ continue;
+ }
+
// if we have a filter, we check the entry against it
if (filter != null && !filter.checkEntry(name)) {
continue;
diff --git a/build-system/builder/src/main/java/com/android/builder/testing/ConnectedDeviceProvider.java b/build-system/builder/src/main/java/com/android/builder/testing/ConnectedDeviceProvider.java
index ab99df7..b1371d3 100644
--- a/build-system/builder/src/main/java/com/android/builder/testing/ConnectedDeviceProvider.java
+++ b/build-system/builder/src/main/java/com/android/builder/testing/ConnectedDeviceProvider.java
@@ -23,10 +23,16 @@
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.IDevice;
import com.android.utils.ILogger;
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
import java.io.File;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* DeviceProvider for locally connected devices. Basically returns the list of devices that
@@ -87,18 +93,29 @@
throw new DeviceException("No connected devices!");
}
- final String androidSerial = System.getenv("ANDROID_SERIAL");
- final Boolean isValidSerial = androidSerial != null && !androidSerial.isEmpty();
+ final String androidSerialsEnv = System.getenv("ANDROID_SERIAL");
+ final boolean isValidSerial = androidSerialsEnv != null && !androidSerialsEnv.isEmpty();
+
+ final Set<String> serials;
+ if (isValidSerial) {
+ serials = Sets.newHashSet(Splitter.on(',').split(androidSerialsEnv));
+ } else {
+ serials = Collections.emptySet();
+ }
+
final List<IDevice> filteredDevices = Lists.newArrayListWithCapacity(devices.length);
for (IDevice iDevice : devices) {
- if (!isValidSerial || iDevice.getSerialNumber().equals(androidSerial)) {
+ if (!isValidSerial || serials.contains(iDevice.getSerialNumber())) {
+ serials.remove(iDevice.getSerialNumber());
filteredDevices.add(iDevice);
}
}
- if (filteredDevices.isEmpty()) {
+ if (!serials.isEmpty()) {
throw new DeviceException(String.format(
- "Connected device with serial %s not found!", androidSerial));
+ "Connected device with serial%s '%s' not found!",
+ serials.size() == 1 ? "" : "s",
+ Joiner.on("', '").join(serials)));
}
for (IDevice device : filteredDevices) {
@@ -117,7 +134,7 @@
if (isValidSerial) {
throw new DeviceException(String.format(
"Connected device with serial $1%s is not online.",
- androidSerial));
+ androidSerialsEnv));
} else {
throw new DeviceException("No online devices found.");
}
diff --git a/build-system/builder/src/main/java/com/android/builder/testing/SimpleTestRunner.java b/build-system/builder/src/main/java/com/android/builder/testing/SimpleTestRunner.java
index 4486a30..8e048b7 100755
--- a/build-system/builder/src/main/java/com/android/builder/testing/SimpleTestRunner.java
+++ b/build-system/builder/src/main/java/com/android/builder/testing/SimpleTestRunner.java
@@ -21,7 +21,6 @@
import com.android.builder.internal.InstallUtils;
import com.android.builder.internal.testing.CustomTestRunListener;
import com.android.builder.internal.testing.SimpleTestCallable;
-import com.android.builder.testing.api.DeviceConfig;
import com.android.builder.testing.api.DeviceConfigProviderImpl;
import com.android.builder.testing.api.DeviceConnector;
import com.android.builder.testing.api.DeviceException;
@@ -46,17 +45,14 @@
*/
public class SimpleTestRunner implements TestRunner {
- @NonNull
- private final File mAdbExec;
@Nullable
private final File mSplitSelectExec;
@NonNull
private final ProcessExecutor mProcessExecutor;
- public SimpleTestRunner(@NonNull File adbExec,
+ public SimpleTestRunner(
@Nullable File splitSelectExec,
@NonNull ProcessExecutor processExecutor) {
- mAdbExec = adbExec;
mSplitSelectExec = splitSelectExec;
mProcessExecutor = processExecutor;
}
@@ -114,9 +110,10 @@
}
compatibleDevices++;
- executor.execute(new SimpleTestCallable(device, projectName, variantName,
- testApk, testedApks, mAdbExec, testData,
- resultsDir, coverageDir, timeoutInMs, installOptions, logger));
+ executor.execute(
+ new SimpleTestCallable(
+ device, projectName, variantName, testApk, testedApks, testData,
+ resultsDir, coverageDir, timeoutInMs, logger));
}
} else {
unauthorizedDevices++;
diff --git a/build-system/builder/src/main/java/com/android/builder/testing/TestData.java b/build-system/builder/src/main/java/com/android/builder/testing/TestData.java
index 065cfc4..64fdaef 100644
--- a/build-system/builder/src/main/java/com/android/builder/testing/TestData.java
+++ b/build-system/builder/src/main/java/com/android/builder/testing/TestData.java
@@ -27,6 +27,7 @@
import java.io.File;
import java.util.List;
+import java.util.Map;
/**
* Data representing the test app and the tested application/library.
@@ -52,6 +53,9 @@
@NonNull
String getInstrumentationRunner();
+ @NonNull
+ Map<String, String> getInstrumentationRunnerArguments();
+
/**
* Returns whether the tested app is enabled for code coverage
*/
diff --git a/build-system/builder/src/test/java/com/android/builder/core/AaptPackageProcessBuilderTest.java b/build-system/builder/src/test/java/com/android/builder/core/AaptPackageProcessBuilderTest.java
index 1f68790..e4b8435 100644
--- a/build-system/builder/src/test/java/com/android/builder/core/AaptPackageProcessBuilderTest.java
+++ b/build-system/builder/src/test/java/com/android/builder/core/AaptPackageProcessBuilderTest.java
@@ -291,7 +291,7 @@
List<String> command = processInfo.getArgs();
- assertTrue("res1,res2,xhdpi,nodpi,anydpi".equals(command.get(command.indexOf("-c") + 1)));
+ assertTrue("res1,res2,xhdpi,nodpi".equals(command.get(command.indexOf("-c") + 1)));
assertTrue(command.indexOf("--preferred-density") == -1);
}
diff --git a/build-system/builder/src/test/java/com/android/builder/internal/compiler/PreDexCacheTest.java b/build-system/builder/src/test/java/com/android/builder/internal/compiler/PreDexCacheTest.java
index 53974de..98794cc 100644
--- a/build-system/builder/src/test/java/com/android/builder/internal/compiler/PreDexCacheTest.java
+++ b/build-system/builder/src/test/java/com/android/builder/internal/compiler/PreDexCacheTest.java
@@ -36,17 +36,21 @@
import com.android.sdklib.BuildToolInfo;
import com.android.sdklib.repository.FullRevision;
import com.google.common.base.Charsets;
-import com.google.common.base.Joiner;
-import com.google.common.collect.Lists;
import com.google.common.io.Files;
import junit.framework.TestCase;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.zip.ZipEntry;
public class PreDexCacheTest extends TestCase {
@@ -92,19 +96,19 @@
}
// read the source content
- List<String> lines = Files.readLines(input, Charsets.UTF_8);
+ JarFile jarFile = new JarFile(input);
+ JarEntry jarEntry = jarFile.getJarEntry("content.class");
+ assert jarEntry != null;
+ InputStream contentStream = jarFile.getInputStream(jarEntry);
+ byte[] content = new byte[256];
+ int read = contentStream.read(content);
+ contentStream.close();
+ jarFile.close();
- // modify the lines
- List<String> dexedLines = Lists.newArrayListWithCapacity(lines.size());
- for (String line : lines) {
- dexedLines.add(DEX_DATA + line + DEX_DATA);
- }
-
- // combine the lines
- String content = Joiner.on('\n').join(dexedLines);
+ String line = new String(content, 0, read, Charsets.UTF_8);
// write it
- Files.write(content, new File(output), Charsets.UTF_8);
+ Files.write(DEX_DATA + line + DEX_DATA, new File(output), Charsets.UTF_8);
} catch (Exception e) {
//noinspection ThrowableInstanceNeverThrown
processException = new ProcessException(null, e);
@@ -412,7 +416,15 @@
File input = File.createTempFile("predex", ".jar");
input.deleteOnExit();
- Files.write(content, input, Charsets.UTF_8);
+ JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(input));
+ try {
+ jarOutputStream.putNextEntry(new ZipEntry("content.class"));
+ jarOutputStream.write(content.getBytes(Charsets.UTF_8));
+ jarOutputStream.closeEntry();
+ } finally {
+ jarOutputStream.close();
+ }
+
return input;
}
diff --git a/build-system/builder/src/test/java/com/android/builder/png/NinePatchAaptProcessorTestUtils.java b/build-system/builder/src/test/java/com/android/builder/png/NinePatchAaptProcessorTestUtils.java
index c95ebc7..ddbe625 100644
--- a/build-system/builder/src/test/java/com/android/builder/png/NinePatchAaptProcessorTestUtils.java
+++ b/build-system/builder/src/test/java/com/android/builder/png/NinePatchAaptProcessorTestUtils.java
@@ -16,7 +16,7 @@
package com.android.builder.png;
-import static junit.framework.Assert.assertTrue;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import com.android.SdkConstants;
@@ -33,7 +33,7 @@
import com.google.common.collect.Maps;
import com.google.common.io.Files;
-import junit.framework.Assert;
+import org.junit.Assert;
import java.awt.image.BufferedImage;
import java.io.File;
@@ -62,12 +62,18 @@
public static final byte[] SIGNATURE = new byte[]{
(byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
+ /**
+ * Returns the lastest build tools that's at least the passed version.
+ * @param fullRevision the minimum required build tools version.
+ * @return the latest build tools.
+ * @throws RuntimeException if the latest build tools is older than fullRevision.
+ */
static File getAapt(FullRevision fullRevision) {
ILogger logger = new StdLogger(StdLogger.Level.VERBOSE);
SdkManager sdkManager = SdkManager.createManager(getSdkDir().getAbsolutePath(), logger);
assert sdkManager != null;
- BuildToolInfo buildToolInfo = sdkManager.getBuildTool(fullRevision);
- if (buildToolInfo == null) {
+ BuildToolInfo buildToolInfo = sdkManager.getLatestBuildTool();
+ if (buildToolInfo == null || buildToolInfo.getRevision().compareTo(fullRevision) < 0) {
throw new RuntimeException("Test requires build-tools " + fullRevision.toShortString());
}
return new File(buildToolInfo.getPath(BuildToolInfo.PathId.AAPT));
diff --git a/build-system/builder/src/test/java/com/android/builder/png/NinePatchAsyncAaptProcessTest.java b/build-system/builder/src/test/java/com/android/builder/png/NinePatchAsyncAaptProcessTest.java
index 607a516..ab5a3f1 100644
--- a/build-system/builder/src/test/java/com/android/builder/png/NinePatchAsyncAaptProcessTest.java
+++ b/build-system/builder/src/test/java/com/android/builder/png/NinePatchAsyncAaptProcessTest.java
@@ -26,7 +26,6 @@
import org.junit.AfterClass;
import org.junit.BeforeClass;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -43,7 +42,6 @@
/**
* Asynchronous version of the aapt cruncher test.
*/
-@Ignore // TODO: Re-enable when build tools 22 are available.
@RunWith(Parameterized.class)
public class NinePatchAsyncAaptProcessTest {
@@ -83,7 +81,7 @@
@NonNull
private static PngCruncher getCruncher() {
ILogger logger = new StdLogger(StdLogger.Level.VERBOSE);
- File aapt = NinePatchAaptProcessorTestUtils.getAapt(FullRevision.parseRevision("22.1.3"));
+ File aapt = NinePatchAaptProcessorTestUtils.getAapt(FullRevision.parseRevision("22.0.1"));
return QueuedCruncher.Builder.INSTANCE.newCruncher(aapt.getAbsolutePath(), logger);
}
diff --git a/build-system/changelog.txt b/build-system/changelog.txt
index f73b880..10ec3ec 100644
--- a/build-system/changelog.txt
+++ b/build-system/changelog.txt
@@ -1,12 +1,50 @@
1.3.0
- By default, "LICENSE" and "LICENSE.txt" are excluded when creating an APK.
This can be changed from the DSL:
- android {
- packagingOptions.excludes = []
- }
+
+ android {
+ packagingOptions.excludes = []
+ }
+
- New sourceSets task for inspecting the set of all available source sets.
- Unit tests recognize multi-flavor and per-variant source folders (e.g.
testDemoDebug). Android tests recognized multi-flavor source folders.
+- Unit testing improvements
+ * Run javac on main and test sources, even if useJack is true.
+ * Correctly recognize per-build-type dependencies.
+- It's now possible to specify instrumentation test runner arguments in
+ build.gradle (in defaultConfig or per flavor):
+
+ android {
+ defaultConfig {
+ testInstrumentationRunnerArguments size: "medium"
+ }
+
+ productFlavors {
+ foo {
+ testInstrumentationRunnerArguments foo: "bar"
+ }
+ }
+ }
+
+ or from the command line:
+
+ ./gradlew cC \
+ -Pandroid.testInstrumentationRunnerArguments.size=medium \
+ -Pandroid.testInstrumentationRunnerArguments.class=TestA,TestB
+
+- Arbitrary additional AAPT parameters can be set in build.gradle:
+ android {
+ aaptOptions {
+ additionalParameters "--custom_option", "value"
+ }
+ }
+- Resource names are validated before they are merged.
+- When building aar, do not provide automatic @{applicationId} placeholder
+ in manifest merger. Use a different placeholder like @{libApplicationId}
+ and provide a value for it if applicationIds should be baked in the library.
+- Introduce support for incremental compilation support with Jill and Jack. Change is purely
+ internal and does not require DSL change nor can it be disabled.
1.2.0
- Unit testing improvements
@@ -21,7 +59,7 @@
* DSL: new code block for configuring the test tasks:
android {
testOptions {
- unitTest.all {
+ unitTests.all {
jvmArgs '-XX:MaxPermSize=256m' // Or any other gradle option.
}
}
@@ -59,6 +97,8 @@
* New configurations for adding test-only dependencies, e.g.
testCompile 'junit:junit:4.11'
testMyFlavorCompile 'some:library:1.0'
+ * New option, android.testOptions.unitTests.returnDefaultValues to control
+ the behaviour of the "mockable" android.jar.
- Task names that used to contain 'Test', e.g. 'assembleDebugTest' now use
'AndroidTest', e.g. 'assembleDebugAndroidTest'. This is to distinguish them
from the unit test tasks, e.g. 'assembleDebugUnitTest'.
@@ -79,6 +119,7 @@
useNewCruncher false
}
}
+- Improved DSL reference. See http://developer.android.com/tools/building/plugin-for-gradle.html
1.0.0
- Final 1.0.0 version
diff --git a/build-system/docs/src/fromGradle/docs/css/base.css b/build-system/docs/src/fromGradle/docs/css/base.css
index ec53702..9a208b4 100644
--- a/build-system/docs/src/fromGradle/docs/css/base.css
+++ b/build-system/docs/src/fromGradle/docs/css/base.css
@@ -363,6 +363,10 @@
margin-right: 10px;
}
+div.chapter > p {
+ margin: 2em 0 2em 0;
+}
+
.text-container h1, .text-container h2, .text-container h3, .text-container h4, .text-container h5,
div.book h1, div.book h2, div.book h3, div.book h4, div.book h5,
div.chapter h1, div.chapter h2, div.chapter h3, div.chapter h4, div.chapter h5 {
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.BaseExtension.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.BaseExtension.xml
index 35c0e95..b182893 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.BaseExtension.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.BaseExtension.xml
@@ -47,6 +47,7 @@
<tr><td>jacoco</td></tr>
<tr><td>splits</td></tr>
<tr><td>adbOptions</td></tr>
+ <tr><td>useLibrary</td></tr>
</table>
</section>
</section>
\ No newline at end of file
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.AaptOptions.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.AaptOptions.xml
index f9dc302..293a81f 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.AaptOptions.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.AaptOptions.xml
@@ -8,7 +8,6 @@
<tr><td>ignoreAssets</td></tr>
<tr><td>noCompress</td></tr>
<tr><td>failOnMissingConfigEntry</td></tr>
- <tr><td>useNewCruncher</td></tr>
</table>
</section>
<section>
@@ -17,6 +16,7 @@
<thead>
<tr><td>Name</td></tr>
</thead>
+ <tr><td>noCompress</td></tr>
</table>
</section>
</section>
\ No newline at end of file
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.BuildType.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.BuildType.xml
index 126df1f..14afb53 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.BuildType.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.BuildType.xml
@@ -5,16 +5,22 @@
<thead>
<tr><td>Name</td></tr>
</thead>
- <tr><td>debuggable</td></tr>
- <tr><td>jniDebuggable</td></tr>
- <tr><td>renderscriptDebuggable</td></tr>
- <tr><td>renderscriptOptimLevel</td></tr>
<tr><td>applicationIdSuffix</td></tr>
- <tr><td>versionNameSuffix</td></tr>
- <tr><td>signingConfig</td></tr>
- <tr><td>zipAlignEnabled</td></tr>
+ <tr><td>debuggable</td></tr>
+ <tr><td>embedMicroApp</td></tr>
+ <tr><td>jniDebuggable</td></tr>
<tr><td>minifyEnabled</td></tr>
<tr><td>multiDexEnabled</td></tr>
+ <tr><td>name</td></tr>
+ <tr><td>pseudoLocalesEnabled</td></tr>
+ <tr><td>renderscriptDebuggable</td></tr>
+ <tr><td>renderscriptOptimLevel</td></tr>
+ <tr><td>shrinkResources</td></tr>
+ <tr><td>signingConfig</td></tr>
+ <tr><td>testCoverageEnabled</td></tr>
+ <tr><td>useJack</td></tr>
+ <tr><td>versionNameSuffix</td></tr>
+ <tr><td>zipAlignEnabled</td></tr>
</table>
</section>
<section>
@@ -23,9 +29,14 @@
<thead>
<tr><td>Name</td></tr>
</thead>
+ <tr><td>buildConfigField</td></tr>
+ <tr><td>resValue</td></tr>
<tr><td>proguardFile</td></tr>
<tr><td>proguardFiles</td></tr>
+ <tr><td>resValue</td></tr>
<tr><td>setProguardFiles</td></tr>
+ <tr><td>shrinkResources</td></tr>
+ <tr><td>useJack</td></tr>
</table>
</section>
</section>
\ No newline at end of file
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.DensitySplitOptions.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.DensitySplitOptions.xml
index 1e9ce24..da862a7 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.DensitySplitOptions.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.DensitySplitOptions.xml
@@ -5,7 +5,6 @@
<thead>
<tr><td>Name</td></tr>
</thead>
- <tr><td>strict</td></tr>
<tr><td>compatibleScreens</td></tr>
</table>
</section>
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.GroupableProductFlavor.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.GroupableProductFlavor.xml
deleted file mode 100644
index 36a19bb..0000000
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.GroupableProductFlavor.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<section>
- <section>
- <title>Properties</title>
- <table>
- <thead>
- <tr><td>Name</td></tr>
- </thead>
- <tr><td>flavorDimension</td></tr>
- </table>
- </section>
- <section>
- <title>Methods</title>
- <table>
- <thead>
- <tr><td>Name</td></tr>
- </thead>
- </table>
- </section>
-</section>
\ No newline at end of file
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.ProductFlavor.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.ProductFlavor.xml
index 1319e0b..72abcb7 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.ProductFlavor.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/com.android.build.gradle.internal.dsl.ProductFlavor.xml
@@ -5,16 +5,21 @@
<thead>
<tr><td>Name</td></tr>
</thead>
+ <tr><td>applicationId</td></tr>
<tr><td>dimension</td></tr>
+ <tr><td>flavorDimension</td></tr>
+ <tr><td>multiDexEnabled</td></tr>
+ <tr><td>signingConfig</td></tr>
+ <tr><td>testApplicationId</td></tr>
+ <tr><td>testFunctionalTest</td></tr>
+ <tr><td>testHandleProfiling</td></tr>
+ <tr><td>testInstrumentationRunner</td></tr>
+ <tr><td>testInstrumentationRunnerArguments</td></tr>
+ <tr><td>useJack</td></tr>
+ <tr><td>versionCode</td></tr>
<tr><td>versionCode</td></tr>
<tr><td>versionName</td></tr>
- <tr><td>applicationId</td></tr>
- <tr><td>testApplicationId</td></tr>
- <tr><td>testInstrumentationRunner</td></tr>
- <tr><td>signingConfig</td></tr>
- <tr><td>testHandleProfiling</td></tr>
- <tr><td>testFunctionalTest</td></tr>
- <tr><td>multiDexEnabled</td></tr>
+ <tr><td>versionName</td></tr>
</table>
</section>
<section>
@@ -23,11 +28,18 @@
<thead>
<tr><td>Name</td></tr>
</thead>
- <tr><td>targetSdkVersion</td></tr>
+ <tr><td>maxSdkVersion</td></tr>
<tr><td>minSdkVersion</td></tr>
<tr><td>proguardFile</td></tr>
<tr><td>proguardFiles</td></tr>
+ <tr><td>resConfig</td></tr>
+ <tr><td>resConfigs</td></tr>
+ <tr><td>resValue</td></tr>
<tr><td>setProguardFiles</td></tr>
+ <tr><td>targetSdkVersion</td></tr>
+ <tr><td>testInstrumentationRunnerArgument</td></tr>
+ <tr><td>testInstrumentationRunnerArguments</td></tr>
+ <tr><td>useJack</td></tr>
</table>
</section>
</section>
\ No newline at end of file
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.core.DefaultProductFlavor.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.core.DefaultProductFlavor.xml
index bb55ea8..5ec5789 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.core.DefaultProductFlavor.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.core.DefaultProductFlavor.xml
@@ -1,3 +1,19 @@
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
<section>
<section>
<title>Properties</title>
diff --git a/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.internal.BaseConfigImpl.xml b/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.internal.BaseConfigImpl.xml
index bb55ea8..43f7a19 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.internal.BaseConfigImpl.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/com.android.builder.internal.BaseConfigImpl.xml
@@ -1,3 +1,19 @@
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
<section>
<section>
<title>Properties</title>
@@ -5,6 +21,10 @@
<thead>
<tr><td>Name</td></tr>
</thead>
+ <tr><td>proguardFiles</td></tr>
+ <tr><td>consumerProguardFiles</td></tr>
+ <tr><td>manifestPlaceholders</td></tr>
+ <tr><td>multiDexEnabled</td></tr>
</table>
</section>
<section>
diff --git a/build-system/docs/src/fromGradle/docs/dsl/dsl.xml b/build-system/docs/src/fromGradle/docs/dsl/dsl.xml
index d78b148..edb4014 100644
--- a/build-system/docs/src/fromGradle/docs/dsl/dsl.xml
+++ b/build-system/docs/src/fromGradle/docs/dsl/dsl.xml
@@ -23,19 +23,19 @@
<title>Android configuration structure</title>
<table>
<title>Android configuration blocks</title>
- <tr><td>defaultConfig</td></tr>
- <tr><td>sourceSets</td></tr>
- <tr><td>buildTypes</td></tr>
- <tr><td>signingConfigs</td></tr>
- <tr><td>productFlavors</td></tr>
- <tr><td>testOptions</td></tr>
<tr><td>aaptOptions</td></tr>
- <tr><td>lintOptions</td></tr>
- <tr><td>dexOptions</td></tr>
+ <tr><td>buildTypes</td></tr>
<tr><td>compileOptions</td></tr>
- <tr><td>packagingOptions</td></tr>
+ <tr><td>defaultConfig</td></tr>
+ <tr><td>dexOptions</td></tr>
<tr><td>jacoco</td></tr>
+ <tr><td>lintOptions</td></tr>
+ <tr><td>packagingOptions</td></tr>
+ <tr><td>productFlavors</td></tr>
+ <tr><td>signingConfigs</td></tr>
+ <tr><td>sourceSets</td></tr>
<tr><td>splits</td></tr>
+ <tr><td>testOptions</td></tr>
</table>
</section>
@@ -44,25 +44,26 @@
<para>Listed below are some of the central types which are used in the Android plugin:</para>
<table>
<title>DSL types</title>
- <tr><td>com.android.build.gradle.BaseExtension</td></tr>
- <tr><td>com.android.build.gradle.TestedExtension</td></tr>
<tr><td>com.android.build.gradle.AppExtension</td></tr>
+ <tr><td>com.android.build.gradle.BaseExtension</td></tr>
<tr><td>com.android.build.gradle.LibraryExtension</td></tr>
<tr><td>com.android.build.gradle.TestExtension</td></tr>
+ <tr><td>com.android.build.gradle.api.AndroidSourceDirectorySet</td></tr>
+ <tr><td>com.android.build.gradle.api.AndroidSourceFile</td></tr>
<tr><td>com.android.build.gradle.api.AndroidSourceSet</td></tr>
- <tr><td>com.android.build.gradle.internal.dsl.ProductFlavor</td></tr>
- <tr><td>com.android.build.gradle.internal.dsl.GroupableProductFlavor</td></tr>
- <tr><td>com.android.build.gradle.internal.dsl.BuildType</td></tr>
- <tr><td>com.android.build.gradle.internal.dsl.SigningConfig</td></tr>
- <tr><td>com.android.build.gradle.internal.dsl.TestOptions</td></tr>
- <tr><td>com.android.build.gradle.internal.dsl.AaptOptions</td></tr>
- <tr><td>com.android.build.gradle.internal.dsl.LintOptions</td></tr>
- <tr><td>com.android.build.gradle.internal.dsl.DexOptions</td></tr>
<tr><td>com.android.build.gradle.internal.CompileOptions</td></tr>
- <tr><td>com.android.build.gradle.internal.dsl.PackagingOptions</td></tr>
- <tr><td>com.android.build.gradle.internal.coverage.JacocoExtension</td></tr>
- <tr><td>com.android.build.gradle.internal.dsl.Splits</td></tr>
<tr><td>com.android.build.gradle.internal.api.VariantFilter</td></tr>
+ <tr><td>com.android.build.gradle.internal.coverage.JacocoExtension</td></tr>
+ <tr><td>com.android.build.gradle.internal.dsl.AaptOptions</td></tr>
+ <tr><td>com.android.build.gradle.internal.dsl.BuildType</td></tr>
+ <tr><td>com.android.build.gradle.internal.dsl.DexOptions</td></tr>
+ <tr><td>com.android.build.gradle.internal.dsl.LintOptions</td></tr>
+ <tr><td>com.android.build.gradle.internal.dsl.PackagingOptions</td></tr>
+ <tr><td>com.android.build.gradle.internal.dsl.ProductFlavor</td></tr>
+ <tr><td>com.android.build.gradle.internal.dsl.SigningConfig</td></tr>
+ <tr><td>com.android.build.gradle.internal.dsl.Splits</td></tr>
+ <tr><td>com.android.build.gradle.internal.dsl.TestOptions.UnitTestOptions</td></tr>
+ <tr><td>com.android.build.gradle.internal.dsl.TestOptions</td></tr>
</table>
</section>
</book>
diff --git a/build-system/google-services/build.gradle b/build-system/google-services/build.gradle
new file mode 100644
index 0000000..e5afe75
--- /dev/null
+++ b/build-system/google-services/build.gradle
@@ -0,0 +1,48 @@
+apply plugin: 'groovy'
+apply plugin: 'clone-artifacts'
+apply plugin: 'idea'
+apply plugin: 'jacoco'
+
+dependencies {
+ compile gradleApi()
+ compile project(':base:gradle')
+
+ testCompile 'junit:junit:4.12'
+}
+
+ext {
+
+}
+
+group = 'com.google.gms'
+archivesBaseName = 'google-services'
+version = rootProject.ext.buildVersion
+
+project.ext.pomName = 'Gradle Plug-in for Google Services'
+project.ext.pomDesc = 'Gradle plug-in for Google Services'
+
+apply from: "$rootDir/buildSrc/base/publish.gradle"
+apply from: "$rootDir/buildSrc/base/bintray.gradle"
+
+groovydoc {
+ exclude "**/internal/**"
+ includePrivate false
+
+ docTitle "Gradle Plugin for Google Services"
+ header ""
+ footer "Copyright (C) 2015 The Android Open Source Project"
+ overview ""
+}
+
+task javadocJar(type: Jar, dependsOn:groovydoc) {
+ classifier 'javadoc'
+ from groovydoc.destinationDir
+}
+
+// Only package JavaDoc if using --init-script=buildSrc/base/release.gradle
+if (project.has("release")) {
+ artifacts {
+ archives javadocJar
+ }
+}
+
diff --git a/build-system/google-services/google-services.iml b/build-system/google-services/google-services.iml
new file mode 100644
index 0000000..772aed8
--- /dev/null
+++ b/build-system/google-services/google-services.iml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/groovy" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/groovy" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
+ <orderEntry type="library" name="groovy" level="project" />
+ <orderEntry type="module" module-name="gradle" exported="" />
+ </component>
+</module>
\ No newline at end of file
diff --git a/build-system/google-services/src/main/groovy/com/google/gms/googleservices/GoogleServicesPlugin.groovy b/build-system/google-services/src/main/groovy/com/google/gms/googleservices/GoogleServicesPlugin.groovy
new file mode 100644
index 0000000..00b8c79
--- /dev/null
+++ b/build-system/google-services/src/main/groovy/com/google/gms/googleservices/GoogleServicesPlugin.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gms.googleservices
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+class GoogleServicesPlugin implements Plugin<Project> {
+
+ public final static String JSON_FILE_NAME = 'google-services.json'
+
+ @Override
+ void apply(Project project) {
+ // setup this plugin no matter the order.
+ if (!checkForKnownPlugins(project)) {
+ project.plugins.whenPluginAdded {
+ checkForKnownPlugins(project)
+ }
+ }
+ }
+
+ private void setupPlugin(Project project, boolean isLibrary) {
+ if (isLibrary) {
+ project.android.libraryVariants.all { variant ->
+ handleVariant(project, variant)
+ }
+ } else {
+ project.android.applicationVariants.all { variant ->
+ handleVariant(project, variant)
+ }
+ }
+ }
+
+ private static void handleVariant(Project project, def variant) {
+ File quickstartFile = project.file(JSON_FILE_NAME)
+ File outputDir = project.file("$project.buildDir/generated/res/google-services/$variant.dirName")
+
+ GoogleServicesTask task = project.tasks.create("process${variant.name.capitalize()}GoogleServices", GoogleServicesTask)
+
+ task.quickstartFile = quickstartFile
+ task.intermediateDir = outputDir
+ task.packageName = variant.applicationId
+
+ variant.registerResGeneratingTask(task, outputDir)
+ }
+
+ private boolean checkForKnownPlugins(Project project) {
+ if (project.plugins.hasPlugin("android") ||
+ project.plugins.hasPlugin("com.android.application")) {
+ // this is a bit fragile but since this is internal usage this is ok
+ // (another plugin could declare itself to be 'android')
+ setupPlugin(project, false)
+ return true
+ } else if (project.plugins.hasPlugin("android-library") ||
+ project.plugins.hasPlugin("com.android.library")) {
+ // this is a bit fragile but since this is internal usage this is ok
+ // (another plugin could declare itself to be 'android-library')
+ setupPlugin(project, true)
+ return true
+ }
+ return false
+ }
+}
diff --git a/build-system/google-services/src/main/groovy/com/google/gms/googleservices/GoogleServicesTask.java b/build-system/google-services/src/main/groovy/com/google/gms/googleservices/GoogleServicesTask.java
new file mode 100644
index 0000000..aadd5bb
--- /dev/null
+++ b/build-system/google-services/src/main/groovy/com/google/gms/googleservices/GoogleServicesTask.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gms.googleservices;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.GradleException;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ */
+public class GoogleServicesTask extends DefaultTask {
+
+ private static final String STATUS_DISABLED = "1";
+ private static final String STATUS_ENABLED = "2";
+
+ /**
+ * The input is not technically optional but we want to control the error message.
+ * Without @Optional, Gradle will complain itself the file is missing.
+ */
+ @InputFile @Optional
+ public File quickstartFile;
+
+ @OutputDirectory
+ public File intermediateDir;
+
+ @Input
+ public String packageName;
+
+ @TaskAction
+ public void action() throws IOException {
+ if (!quickstartFile.isFile()) {
+ getLogger().warn("File " + quickstartFile.getName() + " is missing from module root folder." +
+ " The Google Quickstart Plugin cannot function without it.");
+
+ // Skip the rest of the actions because it would not make sense if `quickstartFile` is missing.
+ return;
+ }
+
+ // delete content of outputdir.
+ deleteFolder(intermediateDir);
+ if (!intermediateDir.mkdirs()) {
+ throw new GradleException("Failed to create folder: " + intermediateDir);
+ }
+
+ JsonElement root = new JsonParser().parse(Files.newReader(quickstartFile, Charsets.UTF_8));
+
+ if (!root.isJsonObject()) {
+ throw new GradleException("Malformed root json");
+ }
+
+ JsonObject rootObject = root.getAsJsonObject();
+
+ Map<String, String> resValues = new TreeMap<String, String>();
+
+ handleProjectNumber(rootObject, resValues);
+
+ JsonObject clientObject = getClientForPackageName(rootObject);
+
+ if (clientObject != null) {
+ handleAnalytics(clientObject, resValues);
+ handleAdsService(clientObject, resValues);
+ } else {
+ getLogger().warn("No matching client found for package name '" + packageName + "'");
+ }
+
+ // write the values file.
+ File values = new File(intermediateDir, "values");
+ if (!values.exists() && !values.mkdirs()) {
+ throw new GradleException("Failed to create folder: " + values);
+ }
+
+ Files.write(getValuesContent(resValues), new File(values, "values.xml"), Charsets.UTF_8);
+ }
+
+ /**
+ * Handle project_info/project_number for @string/gcm_defaultSenderId, and fill the res map with the read value.
+ * @param rootObject the root Json object.
+ * @throws IOException
+ */
+ private void handleProjectNumber(JsonObject rootObject, Map<String, String> resValues)
+ throws IOException {
+ JsonObject projectInfo = rootObject.getAsJsonObject("project_info");
+ if (projectInfo == null) {
+ throw new GradleException("Missing project_info object");
+ }
+
+ JsonPrimitive projectNumber = projectInfo.getAsJsonPrimitive("project_number");
+ if (projectNumber == null) {
+ throw new GradleException("Missing project_info/project_number object");
+ }
+
+ resValues.put("gcm_defaultSenderId", projectNumber.getAsString());
+ }
+
+ /**
+ * Handle a client object for analytics (@xml/global_tracker)
+ * @param clientObject the client Json object.
+ * @throws IOException
+ */
+ private void handleAnalytics(JsonObject clientObject, Map<String, String> resValues)
+ throws IOException {
+ JsonObject analyticsService = getServiceByName(clientObject, "analytics_service");
+ if (analyticsService == null) return;
+
+ JsonObject analyticsProp = analyticsService.getAsJsonObject("analytics_property");
+ if (analyticsProp == null) return;
+
+ JsonPrimitive trackingId = analyticsProp.getAsJsonPrimitive("tracking_id");
+ if (trackingId == null) return;
+
+ resValues.put("ga_trackingId", trackingId.getAsString());
+
+ File xml = new File(intermediateDir, "xml");
+ if (!xml.exists() && !xml.mkdirs()) {
+ throw new GradleException("Failed to create folder: " + xml);
+ }
+
+ Files.write(getGlobalTrackerContent(
+ trackingId.getAsString()),
+ new File(xml, "global_tracker.xml"),
+ Charsets.UTF_8);
+ }
+
+ /**
+ * Handle a client object for analytics (@xml/global_tracker)
+ * @param clientObject the client Json object.
+ * @throws IOException
+ */
+ private void handleAdsService(JsonObject clientObject, Map<String, String> resValues)
+ throws IOException {
+ JsonObject adsService = getServiceByName(clientObject, "ads_service");
+ if (adsService == null) return;
+
+ findStringByName(adsService, "test_banner_ad_unit_id", resValues);
+ findStringByName(adsService, "test_interstitial_ad_unit_id", resValues);
+ }
+
+ private static void findStringByName(JsonObject jsonObject, String stringName,
+ Map<String, String> resValues) {
+ JsonPrimitive id = jsonObject.getAsJsonPrimitive(stringName);
+ if (id != null) {
+ resValues.put(stringName, id.getAsString());
+ }
+ }
+
+ /**
+ * find an item in the "client" array that match the package name of the app
+ * @param jsonObject the root json object.
+ * @return a JsonObject representing the client entry or null if no match is found.
+ */
+ private JsonObject getClientForPackageName(JsonObject jsonObject) {
+ JsonArray array = jsonObject.getAsJsonArray("client");
+ if (array != null) {
+ final int count = array.size();
+ for (int i = 0 ; i < count ; i++) {
+ JsonElement clientElement = array.get(i);
+ if (clientElement == null || !clientElement.isJsonObject()) {
+ continue;
+ }
+
+ JsonObject clientObject = clientElement.getAsJsonObject();
+
+ JsonObject clientInfo = clientObject.getAsJsonObject("client_info");
+ if (clientInfo == null) continue;
+
+ JsonObject androidClientInfo = clientInfo.getAsJsonObject("android_client_info");
+ if (androidClientInfo == null) continue;
+
+ JsonPrimitive clientPackageName = androidClientInfo.getAsJsonPrimitive("package_name");
+ if (clientPackageName == null) continue;
+
+ if (packageName.equals(clientPackageName.getAsString())) {
+ return clientObject;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Finds a service by name in the client object. Returns null if the service is not found
+ * or if the service is disabled.
+ *
+ * @param clientObject the json object that represents the client.
+ * @param serviceName the service name
+ * @return the service if found.
+ */
+ private JsonObject getServiceByName(JsonObject clientObject, String serviceName) {
+ JsonObject services = clientObject.getAsJsonObject("services");
+ if (services == null) return null;
+
+ JsonObject service = services.getAsJsonObject(serviceName);
+ if (service == null) return null;
+
+ JsonPrimitive status = service.getAsJsonPrimitive("status");
+ if (status == null) return null;
+
+ String statusStr = status.getAsString();
+
+ if (STATUS_DISABLED.equals(statusStr)) return null;
+ if (!STATUS_ENABLED.equals(statusStr)) {
+ getLogger().warn(String.format("Status with value '%1$s' for service '%2$s' is unknown",
+ statusStr,
+ serviceName));
+ return null;
+ }
+
+ return service;
+ }
+
+
+ private static String getGlobalTrackerContent(String ga_trackingId) {
+ return "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
+ "<resources>\n" +
+ " <string name=\"ga_trackingId\">" + ga_trackingId + "</string>\n" +
+ "</resources>\n";
+ }
+
+ private static String getValuesContent(Map<String, String> entries) {
+ StringBuilder sb = new StringBuilder(256);
+
+ sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
+ "<resources>\n");
+
+ for (Map.Entry<String, String> entry : entries.entrySet()) {
+ sb.append(" <string name=\"").append(entry.getKey()).append("\">")
+ .append(entry.getValue()).append("</string>\n");
+ }
+
+ sb.append("</resources>\n");
+
+ return sb.toString();
+ }
+
+ private static void deleteFolder(final File folder) {
+ if (!folder.exists()) {
+ return;
+ }
+ File[] files = folder.listFiles();
+ if (files != null) {
+ for (final File file : files) {
+ if (file.isDirectory()) {
+ deleteFolder(file);
+ } else {
+ if (!file.delete()) {
+ throw new GradleException("Failed to delete: " + file);
+ }
+ }
+ }
+ }
+ if (!folder.delete()) {
+ throw new GradleException("Failed to delete: " + folder);
+ }
+ }
+}
diff --git a/build-system/google-services/src/main/resources/META-INF/gradle-plugins/com.google.gms.google-services.properties b/build-system/google-services/src/main/resources/META-INF/gradle-plugins/com.google.gms.google-services.properties
new file mode 100644
index 0000000..592bed6
--- /dev/null
+++ b/build-system/google-services/src/main/resources/META-INF/gradle-plugins/com.google.gms.google-services.properties
@@ -0,0 +1 @@
+implementation-class=com.google.gms.googleservices.GoogleServicesPlugin
diff --git a/build-system/gradle-core/build.gradle b/build-system/gradle-core/build.gradle
index d0815d9..2505e6d 100644
--- a/build-system/gradle-core/build.gradle
+++ b/build-system/gradle-core/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'groovy'
+apply plugin: 'jacoco'
apply plugin: 'clone-artifacts'
configurations {
@@ -37,6 +29,7 @@
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-all:1.9.5'
testCompile project(':base:project-test-lib')
+ testCompile project(':base:testutils')
}
tasks.compileJava.dependsOn ":setupGradleInIde"
@@ -69,6 +62,8 @@
header ""
footer "Copyright (C) 2012 The Android Open Source Project"
overview ""
+
+ groovyClasspath = configurations.provided
}
task javadocJar(type: Jar, dependsOn:groovydoc) {
diff --git a/build-system/gradle-core/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/PackageTestResults.java b/build-system/gradle-core/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/PackageTestResults.java
index 4f410a8..9ea3e7e 100644
--- a/build-system/gradle-core/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/PackageTestResults.java
+++ b/build-system/gradle-core/src/fromGradle/groovy/com/android/build/gradle/internal/test/report/PackageTestResults.java
@@ -30,7 +30,7 @@
public PackageTestResults(String name, AllTestResults model) {
super(model);
- this.name = name.length() == 0 ? DEFAULT_PACKAGE : name;
+ this.name = name.isEmpty() ? DEFAULT_PACKAGE : name;
}
@Override
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidConfig.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidConfig.java
new file mode 100644
index 0000000..ec7373f
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidConfig.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.api.AndroidSourceSet;
+import com.android.build.gradle.api.VariantFilter;
+import com.android.build.gradle.internal.CompileOptions;
+import com.android.build.gradle.internal.coverage.JacocoExtension;
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.dsl.AdbOptions;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
+import com.android.build.gradle.internal.dsl.DexOptions;
+import com.android.build.gradle.internal.dsl.LintOptions;
+import com.android.build.gradle.internal.dsl.PackagingOptions;
+import com.android.build.gradle.internal.dsl.PreprocessingOptions;
+import com.android.build.gradle.internal.dsl.Splits;
+import com.android.build.gradle.internal.dsl.TestOptions;
+import com.android.builder.core.LibraryRequest;
+import com.android.builder.model.SigningConfig;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.builder.testing.api.TestServer;
+import com.android.sdklib.repository.FullRevision;
+
+import org.gradle.api.Action;
+import org.gradle.api.NamedDomainObjectContainer;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * User configuration settings for all android plugins.
+ */
+public interface AndroidConfig {
+
+ /** Build tool version */
+ String getBuildToolsVersion();
+
+ /** Compile SDK version */
+ String getCompileSdkVersion();
+
+ /** Build tool revisions */
+ FullRevision getBuildToolsRevision();
+
+ /** Name of the variant to publish */
+ String getDefaultPublishConfig();
+
+ /** Whether to also publish non-default variants */
+ boolean getPublishNonDefault();
+
+ /** Filter to determine which variants to build */
+ Action<VariantFilter> getVariantFilter();
+
+ /** Adb options */
+ AdbOptions getAdbOptions();
+
+ /** A prefix to be used when creating new resources. Used by Studio */
+ String getResourcePrefix();
+
+ /** List of flavor dimensions */
+ List<String> getFlavorDimensionList();
+
+ /** Whether to generate pure splits or multi apk */
+ boolean getGeneratePureSplits();
+
+ /** Preprocessing Options */
+ PreprocessingOptions getPreprocessingOptions();
+
+ @Deprecated
+ boolean getEnforceUniquePackageName();
+
+ /** Default config, shared by all flavors. */
+ CoreProductFlavor getDefaultConfig();
+
+ /** Options for aapt, tool for packaging resources. */
+ AaptOptions getAaptOptions();
+
+ /** Compile options */
+ CompileOptions getCompileOptions();
+
+ /** Dex options. */
+ DexOptions getDexOptions();
+
+ /** JaCoCo options. */
+ JacocoExtension getJacoco();
+
+ /** Lint options. */
+ LintOptions getLintOptions();
+
+ /** Packaging options. */
+ PackagingOptions getPackagingOptions();
+
+ /** APK splits */
+ Splits getSplits();
+
+ /** Options for running tests. */
+ TestOptions getTestOptions();
+
+ /** List of device providers */
+ @NonNull
+ List<DeviceProvider> getDeviceProviders();
+
+ /** List of remote CI servers */
+ @NonNull
+ List<TestServer> getTestServers();
+
+ /** All product flavors used by this project. */
+ Collection<? extends CoreProductFlavor> getProductFlavors();
+
+ /** Build types used by this project. */
+ Collection<? extends CoreBuildType> getBuildTypes();
+
+ /** Signing configs used by this project. */
+ Collection<? extends SigningConfig> getSigningConfigs();
+
+ /** Source sets for all variants */
+ NamedDomainObjectContainer<AndroidSourceSet> getSourceSets();
+
+ /** Whether to package build config class file */
+ Boolean getPackageBuildConfig();
+
+ Collection<LibraryRequest> getLibraryRequests();
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidGradleOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidGradleOptions.java
new file mode 100644
index 0000000..7d71dd3
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AndroidGradleOptions.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidProject;
+import com.google.common.collect.Maps;
+
+import org.gradle.api.Project;
+
+import java.util.Map;
+
+/**
+ * Determines if various options, triggered from the command line or environment, are set.
+ */
+public class AndroidGradleOptions {
+
+ private static final String PROPERTY_TEST_RUNNER_ARGS =
+ "android.testInstrumentationRunnerArguments.";
+
+ // TODO: Drop the "com." prefix, for consistency.
+ private static final String PROPERTY_BENCHMARK_NAME = "com.android.benchmark.name";
+ private static final String PROPERTY_BENCHMARK_MODE = "com.android.benchmark.mode";
+
+ @NonNull
+ public static Map<String, String> getExtraInstrumentationTestRunnerArgs(@NonNull Project project) {
+ Map<String, String> argsMap = Maps.newHashMap();
+ for (Map.Entry<String, ?> entry : project.getProperties().entrySet()) {
+ if (entry.getKey().startsWith(PROPERTY_TEST_RUNNER_ARGS)) {
+ String argName = entry.getKey().substring(PROPERTY_TEST_RUNNER_ARGS.length());
+ String argValue = entry.getValue().toString();
+
+ argsMap.put(argName, argValue);
+ }
+ }
+
+ return argsMap;
+ }
+
+ @Nullable
+ public static String getBenchmarkName(@NonNull Project project) {
+ return getString(project, PROPERTY_BENCHMARK_NAME);
+ }
+
+ @Nullable
+ public static String getBenchmarkMode(@NonNull Project project) {
+ return getString(project, PROPERTY_BENCHMARK_MODE);
+ }
+
+ public static boolean invokedFromIde(@NonNull Project project) {
+ return getBoolean(project, AndroidProject.PROPERTY_INVOKED_FROM_IDE);
+ }
+
+ public static boolean buildModelOnly(@NonNull Project project) {
+ return getBoolean(project, AndroidProject.PROPERTY_BUILD_MODEL_ONLY);
+ }
+
+ public static boolean buildModelOnlyAdvanced(@NonNull Project project) {
+ return getBoolean(project, AndroidProject.PROPERTY_BUILD_MODEL_ONLY_ADVANCED);
+ }
+
+ @Nullable
+ public static String getApkLocation(@NonNull Project project) {
+ return getString(project, AndroidProject.PROPERTY_APK_LOCATION);
+ }
+
+ @Nullable
+ public static SigningOptions getSigningOptions(@NonNull Project project) {
+ String signingStoreFile =
+ getString(project, AndroidProject.PROPERTY_SIGNING_STORE_FILE);
+ String signingStorePassword =
+ getString(project, AndroidProject.PROPERTY_SIGNING_STORE_PASSWORD);
+ String signingKeyAlias =
+ getString(project, AndroidProject.PROPERTY_SIGNING_KEY_ALIAS);
+ String signingKeyPassword =
+ getString(project, AndroidProject.PROPERTY_SIGNING_KEY_PASSWORD);
+
+ if (signingStoreFile != null
+ && signingStorePassword != null
+ && signingKeyAlias != null
+ && signingKeyPassword != null) {
+ String signingStoreType =
+ getString(project, AndroidProject.PROPERTY_SIGNING_STORE_TYPE);
+
+ return new SigningOptions(
+ signingStoreFile,
+ signingStorePassword,
+ signingKeyAlias,
+ signingKeyPassword,
+ signingStoreType);
+ }
+
+ return null;
+ }
+
+ @Nullable
+ private static String getString(@NonNull Project project, String propertyName) {
+ return (String) project.getProperties().get(propertyName);
+ }
+
+ private static boolean getBoolean(
+ @NonNull Project project,
+ @NonNull String propertyName) {
+ if (project.hasProperty(propertyName)) {
+ Object value = project.getProperties().get(propertyName);
+ if (value instanceof String) {
+ return Boolean.parseBoolean((String) value);
+ }
+ }
+
+ return false;
+ }
+
+ public static class SigningOptions {
+ @NonNull public final String storeFile;
+ @NonNull public final String storePassword;
+ @NonNull public final String keyAlias;
+ @NonNull public final String keyPassword;
+ @Nullable public final String storeType;
+
+ public SigningOptions(
+ @NonNull String storeFile,
+ @NonNull String storePassword,
+ @NonNull String keyAlias,
+ @NonNull String keyPassword,
+ @Nullable String storeType) {
+ this.storeFile = storeFile;
+ this.storeType = storeType;
+ this.storePassword = storePassword;
+ this.keyAlias = keyAlias;
+ this.keyPassword = keyPassword;
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AppExtension.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AppExtension.groovy
deleted file mode 100644
index ce05288..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/AppExtension.groovy
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle
-
-import com.android.annotations.NonNull
-import com.android.build.gradle.api.ApplicationVariant
-import com.android.build.gradle.api.BaseVariant
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.builder.core.AndroidBuilder
-import groovy.transform.CompileStatic
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.internal.DefaultDomainObjectSet
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.internal.reflect.Instantiator
-
-/**
- * 'android' extension for 'com.android.application' project.
- */
-@CompileStatic
-public class AppExtension extends TestedExtension {
-
- private final DefaultDomainObjectSet<ApplicationVariant> applicationVariantList =
- new DefaultDomainObjectSet<ApplicationVariant>(ApplicationVariant.class)
-
- AppExtension(
- @NonNull ProjectInternal project,
- @NonNull Instantiator instantiator,
- @NonNull AndroidBuilder androidBuilder,
- @NonNull SdkHandler sdkHandler,
- @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
- @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
- @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
- @NonNull ExtraModelInfo extraModelInfo,
- boolean isLibrary) {
- super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
- signingConfigs, extraModelInfo, isLibrary)
- }
-
- /**
- * Returns the list of Application variants. Since the collections is built after evaluation,
- * it should be used with Gradle's <code>all</code> iterator to process future items.
- */
- public DefaultDomainObjectSet<ApplicationVariant> getApplicationVariants() {
- return applicationVariantList
- }
-
- @Override
- void addVariant(BaseVariant variant) {
- applicationVariantList.add((ApplicationVariant) variant)
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/BaseExtension.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/BaseExtension.groovy
deleted file mode 100644
index 1a3d81e..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/BaseExtension.groovy
+++ /dev/null
@@ -1,623 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle
-
-import com.android.SdkConstants
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.gradle.api.AndroidSourceSet
-import com.android.build.gradle.api.BaseVariant
-import com.android.build.gradle.internal.CompileOptions
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.LoggingUtil
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.SourceSetSourceProviderWrapper
-import com.android.build.gradle.internal.coverage.JacocoExtension
-import com.android.build.gradle.internal.dsl.AaptOptions
-import com.android.build.gradle.internal.dsl.AdbOptions
-import com.android.build.gradle.internal.dsl.AndroidSourceSetFactory
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.DexOptions
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.LintOptions
-import com.android.build.gradle.internal.dsl.PackagingOptions
-import com.android.build.gradle.internal.dsl.ProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.build.gradle.internal.dsl.Splits
-import com.android.build.gradle.internal.dsl.TestOptions
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.BuilderConstants
-import com.android.builder.model.SourceProvider
-import com.android.builder.testing.api.DeviceProvider
-import com.android.builder.testing.api.TestServer
-import com.android.sdklib.repository.FullRevision
-import com.google.common.collect.Lists
-import groovy.transform.CompileStatic
-import org.gradle.api.Action
-import org.gradle.api.GradleException
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.Project
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.ConfigurationContainer
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.logging.Logger
-import org.gradle.api.logging.Logging
-import org.gradle.api.tasks.SourceSet
-import org.gradle.internal.reflect.Instantiator
-
-/**
- * Base 'android' extension for all android plugins.
- *
- * <p>This is never used directly. Instead,
- *<ul>
- * <li>Plugin 'com.android.application' uses {@link AppExtension}</li>
- * <li>Plugin 'com.android.library' uses {@link LibraryExtension}</li>
- * </ul>
- */
-public abstract class BaseExtension {
-
- private String target
- private FullRevision buildToolsRevision
-
- /** Default config, shared by all flavors. */
- final ProductFlavor defaultConfig
-
- /** Options for aapt, tool for packaging resources. */
- final AaptOptions aaptOptions
-
- /** Lint options. */
- final LintOptions lintOptions
-
- /** Dex options. */
- final DexOptions dexOptions
-
- /** Options for running tests. */
- final TestOptions testOptions
-
- /** Compile options */
- final CompileOptions compileOptions
-
- /** Packaging options. */
- final PackagingOptions packagingOptions
-
- /** JaCoCo options. */
- final JacocoExtension jacoco
-
- /** APK splits */
- final Splits splits
-
- /** All product flavors used by this project. */
- final NamedDomainObjectContainer<GroupableProductFlavor> productFlavors
-
- /** Build types used by this project. */
- final NamedDomainObjectContainer<BuildType> buildTypes
-
- /** Signing configs used by this project. */
- final NamedDomainObjectContainer<SigningConfig> signingConfigs
-
- private ExtraModelInfo extraModelInfo
-
- protected Project project
-
- /** Adb options */
- final AdbOptions adbOptions;
-
- /** A prefix to be used when creating new resources. Used by Studio */
- String resourcePrefix
-
- List<String> flavorDimensionList
-
- private String defaultPublishConfig = "release"
- private boolean publishNonDefault = false
-
- private Closure<Void> variantFilter
-
- private final List<DeviceProvider> deviceProviderList = Lists.newArrayList();
- private final List<TestServer> testServerList = Lists.newArrayList();
-
- private final AndroidBuilder androidBuilder
-
- private final SdkHandler sdkHandler
-
- protected Logger logger
-
- private boolean isWritable = true;
-
- /**
- * The source sets container.
- */
- final NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer
-
- BaseExtension(
- @NonNull ProjectInternal project,
- @NonNull Instantiator instantiator,
- @NonNull AndroidBuilder androidBuilder,
- @NonNull SdkHandler sdkHandler,
- @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
- @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
- @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
- @NonNull ExtraModelInfo extraModelInfo,
- boolean isLibrary) {
- this.androidBuilder = androidBuilder
- this.sdkHandler = sdkHandler
- this.buildTypes = buildTypes
- this.productFlavors = productFlavors
- this.signingConfigs = signingConfigs
- this.extraModelInfo = extraModelInfo
- this.project = project
-
- logger = Logging.getLogger(this.class)
-
- defaultConfig = instantiator.newInstance(ProductFlavor, BuilderConstants.MAIN,
- project, instantiator, project.getLogger())
-
- aaptOptions = instantiator.newInstance(AaptOptions)
- dexOptions = instantiator.newInstance(DexOptions)
- lintOptions = instantiator.newInstance(LintOptions)
- testOptions = instantiator.newInstance(TestOptions)
- compileOptions = instantiator.newInstance(CompileOptions)
- packagingOptions = instantiator.newInstance(PackagingOptions)
- jacoco = instantiator.newInstance(JacocoExtension)
- adbOptions = instantiator.newInstance(AdbOptions)
- splits = instantiator.newInstance(Splits, instantiator)
-
- sourceSetsContainer = project.container(AndroidSourceSet,
- new AndroidSourceSetFactory(instantiator, project, isLibrary))
-
- sourceSetsContainer.whenObjectAdded { AndroidSourceSet sourceSet ->
- ConfigurationContainer configurations = project.getConfigurations()
-
- createConfiguration(
- configurations,
- sourceSet.getCompileConfigurationName(),
- "Classpath for compiling the ${sourceSet.name} sources.")
-
- String packageConfigDescription
- if (isLibrary) {
- packageConfigDescription = "Classpath only used when publishing '${sourceSet.name}'."
- } else {
- packageConfigDescription = "Classpath packaged with the compiled '${sourceSet.name}' classes."
- }
- createConfiguration(
- configurations,
- sourceSet.getPackageConfigurationName(),
- packageConfigDescription)
-
- createConfiguration(
- configurations,
- sourceSet.getProvidedConfigurationName(),
- "Classpath for only compiling the ${sourceSet.name} sources.")
-
- createConfiguration(
- configurations,
- sourceSet.getWearAppConfigurationName(),
- "Link to a wear app to embed for object '${sourceSet.name}'.")
-
- sourceSet.setRoot(String.format("src/%s", sourceSet.getName()))
- }
-
- sourceSetsContainer.create(defaultConfig.name)
- }
-
- /**
- * Disallow further modification on the extension.
- */
- public void disableWrite() {
- isWritable = false
- }
-
- protected checkWritability() {
- if (!isWritable) {
- throw new GradleException(
- "Android tasks have already been created.\n" +
- "This happens when calling android.applicationVariants,\n" +
- "android.libraryVariants or android.testVariants.\n" +
- "Once these methods are called, it is not possible to\n" +
- "continue configuring the model.")
- }
- }
-
- protected void createConfiguration(
- @NonNull ConfigurationContainer configurations,
- @NonNull String configurationName,
- @NonNull String configurationDescription) {
- logger.info("Creating configuration ${configurationName}.")
-
- Configuration configuration = configurations.findByName(configurationName)
- if (configuration == null) {
- configuration = configurations.create(configurationName)
- }
- configuration.setVisible(false);
- configuration.setDescription(configurationDescription)
- }
-
- /**
- * Sets the compile SDK version, based on full SDK version string, e.g.
- * <code>android-21</code> for Lollipop.
- */
- void compileSdkVersion(String version) {
- checkWritability()
- this.target = version
- }
-
- /**
- * Sets the compile SDK version, based on API level, e.g. 21 for Lollipop.
- */
- void compileSdkVersion(int apiLevel) {
- compileSdkVersion("android-" + apiLevel)
- }
-
- void setCompileSdkVersion(int apiLevel) {
- compileSdkVersion(apiLevel)
- }
-
- void setCompileSdkVersion(String target) {
- compileSdkVersion(target)
- }
-
- void buildToolsVersion(String version) {
- checkWritability()
- buildToolsRevision = FullRevision.parseRevision(version)
- }
-
- /**
- * <strong>Required.</strong> Version of the build tools to use.
- *
- * <p>Value assigned to this property is parsed and stored in a normalized form, so reading it
- * back may give a slightly different string.
- */
- String getBuildToolsVersion() {
- return buildToolsRevision.toString()
- }
-
- void setBuildToolsVersion(String version) {
- buildToolsVersion(version)
- }
-
- /**
- * Configures the build types.
- */
- void buildTypes(Action<? super NamedDomainObjectContainer<BuildType>> action) {
- checkWritability()
- action.execute(buildTypes)
- }
-
- /**
- * Configures the product flavors.
- */
- void productFlavors(Action<? super NamedDomainObjectContainer<GroupableProductFlavor>> action) {
- checkWritability()
- action.execute(productFlavors)
- }
-
- /**
- * Configures the signing configs.
- */
- void signingConfigs(Action<? super NamedDomainObjectContainer<SigningConfig>> action) {
- checkWritability()
- action.execute(signingConfigs)
- }
-
- public void flavorDimensions(String... dimensions) {
- checkWritability()
- flavorDimensionList = Arrays.asList(dimensions)
- }
-
- /**
- * Configures the source sets. Note that the Android plugin uses its own implementation of
- * source sets, {@link AndroidSourceSet}.
- */
- void sourceSets(Action<NamedDomainObjectContainer<AndroidSourceSet>> action) {
- checkWritability()
- action.execute(sourceSetsContainer)
- }
-
- /**
- * All source sets. Note that the Android plugin uses its own implementation of
- * source sets, {@link AndroidSourceSet}.
- */
- NamedDomainObjectContainer<AndroidSourceSet> getSourceSets() {
- sourceSetsContainer
- }
-
- /**
- * The default configuration, inherited by all build flavors (if any are defined).
- */
- void defaultConfig(Action<ProductFlavor> action) {
- checkWritability()
- action.execute(defaultConfig)
- }
-
- /**
- * Configures aapt options.
- */
- void aaptOptions(Action<AaptOptions> action) {
- checkWritability()
- action.execute(aaptOptions)
- }
-
- /**
- * Configures dex options.
- * @param action
- */
- void dexOptions(Action<DexOptions> action) {
- checkWritability()
- action.execute(dexOptions)
- }
-
- /**
- * Configure lint options.
- */
- void lintOptions(Action<LintOptions> action) {
- checkWritability()
- action.execute(lintOptions)
- }
-
- /** Configures the test options. */
- void testOptions(Action<TestOptions> action) {
- checkWritability()
- action.execute(testOptions)
- }
-
- /**
- * Configures compile options.
- */
- void compileOptions(Action<CompileOptions> action) {
- checkWritability()
- action.execute(compileOptions)
- }
-
- /**
- * Configures packaging options.
- */
- void packagingOptions(Action<PackagingOptions> action) {
- checkWritability()
- action.execute(packagingOptions)
- }
-
- /**
- * Configures JaCoCo options.
- */
- void jacoco(Action<JacocoExtension> action) {
- checkWritability()
- action.execute(jacoco)
- }
-
- /**
- * Configures adb options.
- */
- void adbOptions(Action<AdbOptions> action) {
- checkWritability()
- action.execute(adbOptions)
- }
-
- /**
- * Configures APK splits.
- */
- void splits(Action<Splits> action) {
- checkWritability()
- action.execute(splits)
- }
-
- void deviceProvider(DeviceProvider deviceProvider) {
- checkWritability()
- deviceProviderList.add(deviceProvider)
- }
-
- @NonNull
- List<DeviceProvider> getDeviceProviders() {
- return deviceProviderList
- }
-
- void testServer(TestServer testServer) {
- checkWritability()
- testServerList.add(testServer)
- }
-
- @NonNull
- List<TestServer> getTestServers() {
- return testServerList
- }
-
- public void defaultPublishConfig(String value) {
- setDefaultPublishConfig(value)
- }
-
- public void publishNonDefault(boolean value) {
- publishNonDefault = value
- }
-
- /**
- * Name of the configuration used to build the default artifact of this project.
- *
- * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Referencing-a-Library">
- * Referencing a Library</a>
- */
- public String getDefaultPublishConfig() {
- return defaultPublishConfig
- }
-
- public void setDefaultPublishConfig(String value) {
- defaultPublishConfig = value
- }
-
- /**
- * Whether to publish artifacts for all configurations, not just the default one.
- *
- * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Referencing-a-Library">
- * Referencing a Library</a>
- */
- public boolean getPublishNonDefault() {
- return publishNonDefault
- }
-
- /**
- * Sets a variant filter to control which variant are excluded. The closure is passed a single
- * object of type {@link com.android.build.gradle.internal.api.VariantFilter}
- * @param filter the filter as a closure
- */
- void variantFilter(Closure<Void> filter) {
- setVariantFilter(filter)
- }
-
- /**
- * Sets a variant filter to control which variant are excluded. The closure is passed a single
- * object of type {@link com.android.build.gradle.internal.api.VariantFilter}
- * @param filter the filter as a closure
- */
- void setVariantFilter(Closure<Void> filter) {
- variantFilter = filter
- }
-
- /**
- * A variant filter to control which variant are excluded. The filter is a closure which
- * is passed a single object of type {@link com.android.build.gradle.internal.api.VariantFilter}
- */
- public Closure<Void> getVariantFilter() {
- return variantFilter;
- }
-
- void resourcePrefix(String prefix) {
- resourcePrefix = prefix
- }
-
- abstract void addVariant(BaseVariant variant)
-
- public void registerArtifactType(@NonNull String name,
- boolean isTest,
- int artifactType) {
- extraModelInfo.registerArtifactType(name, isTest, artifactType)
- }
-
- public void registerBuildTypeSourceProvider(
- @NonNull String name,
- @NonNull BuildType buildType,
- @NonNull SourceProvider sourceProvider) {
- extraModelInfo.registerBuildTypeSourceProvider(name, buildType, sourceProvider)
- }
-
- public void registerProductFlavorSourceProvider(
- @NonNull String name,
- @NonNull ProductFlavor productFlavor,
- @NonNull SourceProvider sourceProvider) {
- extraModelInfo.registerProductFlavorSourceProvider(name, productFlavor, sourceProvider)
- }
-
- @CompileStatic
- public void registerJavaArtifact(
- @NonNull String name,
- @NonNull BaseVariant variant,
- @NonNull String assembleTaskName,
- @NonNull String javaCompileTaskName,
- @NonNull Collection<File> generatedSourceFolders,
- @NonNull Iterable<String> ideSetupTaskNames,
- @NonNull Configuration configuration,
- @NonNull File classesFolder,
- @NonNull File javaResourceFolder,
- @Nullable SourceProvider sourceProvider) {
- extraModelInfo.registerJavaArtifact(name, variant, assembleTaskName,
- javaCompileTaskName, generatedSourceFolders, ideSetupTaskNames,
- configuration, classesFolder, javaResourceFolder, sourceProvider)
- }
-
- public void registerMultiFlavorSourceProvider(
- @NonNull String name,
- @NonNull String flavorName,
- @NonNull SourceProvider sourceProvider) {
- extraModelInfo.registerMultiFlavorSourceProvider(name, flavorName, sourceProvider)
- }
-
- @NonNull
- public SourceProvider wrapJavaSourceSet(@NonNull SourceSet sourceSet) {
- return new SourceSetSourceProviderWrapper(sourceSet)
- }
-
- /**
- * <strong>Required.</strong> Compile SDK version.
- *
- * <p>Setter can be called with a string like "android-21" or a number.
- *
- * <p>Value assigned to this property is parsed and stored in a normalized form, so reading it
- * back may give a slightly different string.
- */
- public String getCompileSdkVersion() {
- return target
- }
-
- public FullRevision getBuildToolsRevision() {
- return buildToolsRevision
- }
-
- public File getSdkDirectory() {
- return sdkHandler.getSdkFolder()
- }
-
- public File getNdkDirectory() {
- return sdkHandler.getNdkFolder()
- }
-
- public List<File> getBootClasspath() {
- return androidBuilder.getBootClasspath()
- }
-
- public File getAdbExe() {
- return sdkHandler.getSdkInfo().adb
- }
-
- public File getDefaultProguardFile(String name) {
- File sdkDir = sdkHandler.getAndCheckSdkFolder()
- return new File(sdkDir,
- SdkConstants.FD_TOOLS + File.separatorChar
- + SdkConstants.FD_PROGUARD + File.separatorChar
- + name);
- }
-
- // ---------------
- // TEMP for compatibility
-
- /**
- * Whether to generate PNGs from vector drawables. This doesn't work yet, so it's disabled by
- * default.
- */
- boolean preprocessResources = false;
-
- // by default, we do not generate pure splits
- boolean generatePureSplits = false;
-
- void generatePureSplits(boolean flag) {
- if (flag) {
- logger.warn("Pure splits are not supported by PlayStore yet.")
- }
- this.generatePureSplits = flag;
- }
-
- private boolean enforceUniquePackageName = true
-
- public void enforceUniquePackageName(boolean value) {
- if (!value) {
- LoggingUtil.displayDeprecationWarning(logger, project, "Support for libraries with same package name is deprecated and will be removed in a future release.")
- }
- enforceUniquePackageName = value
- }
-
- public void setEnforceUniquePackageName(boolean value) {
- enforceUniquePackageName(value)
- }
-
- public getEnforceUniquePackageName() {
- return enforceUniquePackageName
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/LibraryExtension.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/LibraryExtension.groovy
deleted file mode 100644
index 44d090b..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/LibraryExtension.groovy
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle
-
-import com.android.annotations.NonNull
-import com.android.build.gradle.api.BaseVariant
-import com.android.build.gradle.api.LibraryVariant
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.LoggingUtil
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.builder.core.AndroidBuilder
-import groovy.transform.CompileStatic
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.internal.DefaultDomainObjectSet
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.internal.reflect.Instantiator
-
-/**
- * 'android' extension for 'com.android.library' project.
- */
-@CompileStatic
-public class LibraryExtension extends TestedExtension {
-
- private final DefaultDomainObjectSet<LibraryVariant> libraryVariantList =
- new DefaultDomainObjectSet<LibraryVariant>(LibraryVariant.class)
-
- LibraryExtension(
- @NonNull ProjectInternal project,
- @NonNull Instantiator instantiator,
- @NonNull AndroidBuilder androidBuilder,
- @NonNull SdkHandler sdkHandler,
- @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
- @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
- @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
- @NonNull ExtraModelInfo extraModelInfo,
- boolean isLibrary) {
- super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
- signingConfigs, extraModelInfo, isLibrary)
- }
-
- /**
- * Returns the list of library variants. Since the collections is built after evaluation,
- * it should be used with Gradle's <code>all</code> iterator to process future items.
- */
- public DefaultDomainObjectSet<LibraryVariant> getLibraryVariants() {
- return libraryVariantList
- }
-
- @Override
- void addVariant(BaseVariant variant) {
- libraryVariantList.add((LibraryVariant) variant)
- }
-
- // ---------------
- // TEMP for compatibility
- // STOPSHIP Remove in 1.0
-
- private boolean packageBuildConfig = true
-
- public void packageBuildConfig(boolean value) {
- if (!value) {
- LoggingUtil.displayDeprecationWarning(logger, project, "Support for not packaging BuildConfig is deprecated and will be removed in 1.0")
- }
-
- packageBuildConfig = value
- }
-
- public void setPackageBuildConfig(boolean value) {
- packageBuildConfig(value)
- }
-
- boolean getPackageBuildConfig() {
- return packageBuildConfig
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/ReportingPlugin.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/ReportingPlugin.groovy
index 4f834bd..9871cf7 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/ReportingPlugin.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/ReportingPlugin.groovy
@@ -49,6 +49,7 @@
mergeReportsTask.group = JavaBasePlugin.VERIFICATION_GROUP
mergeReportsTask.description = "Merges all the Android test reports from the sub projects."
mergeReportsTask.reportType = ReportType.MULTI_PROJECT
+ mergeReportsTask.setVariantName("")
mergeReportsTask.conventionMapping.resultsDir = {
String location = extension.resultsDir != null ?
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestAndroidConfig.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestAndroidConfig.java
new file mode 100644
index 0000000..3ccd01c
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestAndroidConfig.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle;
+
+/**
+ * User configuration settings for 'com.android.test' project.
+ */
+public interface TestAndroidConfig extends AndroidConfig {
+
+ /**
+ * Returns the Gradle path of the project that this test project tests.
+ */
+ String getTargetProjectPath();
+
+ /**
+ * Returns the variant of the tested project.
+ *
+ * Default is 'debug'
+ */
+ String getTargetVariant();
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestExtension.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestExtension.groovy
deleted file mode 100644
index 729b471..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestExtension.groovy
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.build.gradle
-
-import com.android.annotations.NonNull
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.api.ApplicationVariant
-import com.android.build.gradle.api.BaseVariant
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.builder.core.AndroidBuilder
-import groovy.transform.CompileStatic
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.internal.DefaultDomainObjectSet
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.internal.reflect.Instantiator
-
-/**
- * 'android' extension for 'com.android.test' project.
- */
-@CompileStatic
-public class TestExtension extends BaseExtension {
-
- private final DefaultDomainObjectSet<ApplicationVariant> applicationVariantList =
- new DefaultDomainObjectSet<ApplicationVariant>(ApplicationVariant.class)
-
- private String targetProjectPath = null
- private String targetVariant = "debug"
-
- TestExtension(
- @NonNull ProjectInternal project,
- @NonNull Instantiator instantiator,
- @NonNull AndroidBuilder androidBuilder,
- @NonNull SdkHandler sdkHandler,
- @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
- @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
- @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
- @NonNull ExtraModelInfo extraModelInfo,
- boolean isLibrary) {
- super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
- signingConfigs, extraModelInfo, isLibrary)
- }
-
- /**
- * Returns the list of Application variants. Since the collections is built after evaluation,
- * it should be used with Gradle's <code>all</code> iterator to process future items.
- */
- public DefaultDomainObjectSet<ApplicationVariant> getApplicationVariants() {
- return applicationVariantList
- }
-
- @Override
- void addVariant(BaseVariant variant) {
- applicationVariantList.add((ApplicationVariant) variant)
- }
-
- /**
- * Returns the Gradle path of the project that this test project tests.
- */
- String getTargetProjectPath() {
- return targetProjectPath
- }
-
- void setTargetProjectPath(String targetProjectPath) {
- checkWritability()
- this.targetProjectPath = targetProjectPath
- }
-
- void targetProjectPath(String targetProjectPath) {
- setTargetProjectPath(targetProjectPath)
- }
-
- /**
- * Returns the variant of the tested project.
- *
- * Default is 'debug'
- */
- String getTargetVariant() {
- return targetVariant
- }
-
- void setTargetVariant(String targetVariant) {
- checkWritability()
- this.targetVariant = targetVariant
- }
-
- void targetVariant(String targetVariant) {
- setTargetVariant(targetVariant)
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestedAndroidConfig.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestedAndroidConfig.java
new file mode 100644
index 0000000..000c956
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestedAndroidConfig.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.api.TestVariant;
+import com.android.build.gradle.api.UnitTestVariant;
+
+import org.gradle.api.DomainObjectSet;
+
+/**
+ * User configuration settings for android plugin with test component.
+ */
+public interface TestedAndroidConfig extends AndroidConfig {
+
+ /**
+ * Return the name of the BuildType for testing.
+ */
+ @NonNull
+ String getTestBuildType();
+
+ /**
+ * Returns the list of (Android) test variants. Since the collections is built after evaluation,
+ * it should be used with Gradle's <code>all</code> iterator to process future items.
+ */
+ @NonNull
+ DomainObjectSet<TestVariant> getTestVariants();
+
+ /**
+ * Returns the list of (Android) test variants. Since the collections is built after evaluation,
+ * it should be used with Gradle's <code>all</code> iterator to process future items.
+ */
+ @NonNull
+ DomainObjectSet<UnitTestVariant> getUnitTestVariants();
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestedExtension.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestedExtension.groovy
deleted file mode 100644
index 5892e3d..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/TestedExtension.groovy
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.build.gradle
-import com.android.annotations.NonNull
-import com.android.build.gradle.api.TestVariant
-import com.android.build.gradle.api.UnitTestVariant
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.builder.core.AndroidBuilder
-import groovy.transform.CompileStatic
-import org.gradle.api.DomainObjectSet
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.internal.DefaultDomainObjectSet
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.internal.reflect.Instantiator
-
-import static com.android.builder.core.VariantType.ANDROID_TEST
-import static com.android.builder.core.VariantType.UNIT_TEST
-/**
- * base 'android' extension for plugins that have a test component.
- */
-@CompileStatic
-public abstract class TestedExtension extends BaseExtension {
-
- private final DomainObjectSet<TestVariant> testVariantList =
- new DefaultDomainObjectSet<TestVariant>(TestVariant.class)
-
- private final DomainObjectSet<UnitTestVariant> unitTestVariantList =
- new DefaultDomainObjectSet<UnitTestVariant>(UnitTestVariant.class)
-
- String testBuildType = "debug"
-
- TestedExtension(
- @NonNull ProjectInternal project,
- @NonNull Instantiator instantiator,
- @NonNull AndroidBuilder androidBuilder,
- @NonNull SdkHandler sdkHandler,
- @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
- @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
- @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
- @NonNull ExtraModelInfo extraModelInfo,
- boolean isLibrary) {
- super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
- signingConfigs, extraModelInfo, isLibrary)
-
- sourceSetsContainer.create(ANDROID_TEST.prefix)
- sourceSetsContainer.create(UNIT_TEST.prefix)
- }
-
- /**
- * Returns the list of (Android) test variants. Since the collections is built after evaluation,
- * it should be used with Gradle's <code>all</code> iterator to process future items.
- */
- @NonNull
- public DomainObjectSet<TestVariant> getTestVariants() {
- return testVariantList
- }
-
- void addTestVariant(TestVariant testVariant) {
- testVariantList.add(testVariant)
- }
-
- /**
- * Returns the list of (Android) test variants. Since the collections is built after evaluation,
- * it should be used with Gradle's <code>all</code> iterator to process future items.
- */
- @NonNull
- public DomainObjectSet<UnitTestVariant> getUnitTestVariants() {
- return unitTestVariantList
- }
-
- void addUnitTestVariant(UnitTestVariant testVariant) {
- unitTestVariantList.add(testVariant)
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceFile.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceFile.java
index b6bc97a..76f161a 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceFile.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceFile.java
@@ -35,7 +35,7 @@
File getSrcFile();
/**
- * Sets the location of the file.
+ * Sets the location of the file. Returns this object.
*
* @param srcPath The source directory. This is evaluated as for
* {@link org.gradle.api.Project#file(Object)}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceSet.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceSet.groovy
deleted file mode 100644
index 0e00ccc..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceSet.groovy
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.api
-import com.android.annotations.NonNull
-/**
- * A {@code AndroidSourceSet} represents a logical group of Java, aidl, renderscript source
- * as well as Android and non-Android resources.
- */
-public interface AndroidSourceSet {
-
- /**
- * Returns the name of this source set.
- *
- * @return The name. Never returns null.
- */
- @NonNull
- String getName();
-
- /**
- * Returns the Java resources which are to be copied into the javaResources output directory.
- *
- * @return the java resources. Never returns null.
- */
- @NonNull
- AndroidSourceDirectorySet getResources();
-
- /**
- * Configures the Java resources for this set.
- *
- * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet} which
- * contains the java resources.
- *
- * @param configureClosure The closure to use to configure the javaResources.
- * @return this
- */
- @NonNull
- AndroidSourceSet resources(Closure configureClosure);
-
- /**
- * Returns the Java source which is to be compiled by the Java compiler into the class output
- * directory.
- *
- * @return the Java source. Never returns null.
- */
- @NonNull
- AndroidSourceDirectorySet getJava();
-
- /**
- * Configures the Java source for this set.
- *
- * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet} which
- * contains the Java source.
- *
- * @param configureClosure The closure to use to configure the Java source.
- * @return this
- */
- @NonNull
- AndroidSourceSet java(Closure configureClosure);
-
- /**
- * Returns the name of the compile configuration for this source set.
- * @return The configuration name
- */
- @NonNull
- String getCompileConfigurationName();
-
- /**
- * Returns the name of the runtime configuration for this source set.
- * @return The runtime configuration name
- */
- @NonNull
- String getPackageConfigurationName();
-
- /**
- * Returns the name of the compiled-only configuration for this source set.
- * @return The provided configuration name
- */
- @NonNull
- String getProvidedConfigurationName();
-
- /**
- * Returns the name of the wearApp configuration for this source set.
- * @return The configuration name
- */
- @NonNull
- String getWearAppConfigurationName();
-
- /**
- * The Android Manifest file for this source set.
- *
- * @return the manifest. Never returns null.
- */
- @NonNull
- AndroidSourceFile getManifest();
-
- /**
- * Configures the location of the Android Manifest for this set.
- *
- * <p>The given closure is used to configure the {@link AndroidSourceFile} which contains the
- * manifest.
- *
- * @param configureClosure The closure to use to configure the Android Manifest.
- * @return this
- */
- @NonNull
- AndroidSourceSet manifest(Closure configureClosure);
-
- /**
- * The Android Resources directory for this source set.
- *
- * @return the resources. Never returns null.
- */
- @NonNull
- AndroidSourceDirectorySet getRes();
-
- /**
- * Configures the location of the Android Resources for this set.
- *
- * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
- * which contains the resources.
- *
- * @param configureClosure The closure to use to configure the Resources.
- * @return this
- */
- @NonNull
- AndroidSourceSet res(Closure configureClosure);
-
- /**
- * The Android Assets directory for this source set.
- *
- * @return the assets. Never returns null.
- */
- @NonNull
- AndroidSourceDirectorySet getAssets();
-
- /**
- * Configures the location of the Android Assets for this set.
- *
- * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
- * which contains the assets.
- *
- * @param configureClosure The closure to use to configure the Assets.
- * @return this
- */
- @NonNull
- AndroidSourceSet assets(Closure configureClosure);
-
- /**
- * The Android AIDL source directory for this source set.
- *
- * @return the source. Never returns null.
- */
- @NonNull
- AndroidSourceDirectorySet getAidl();
-
- /**
- * Configures the location of the Android AIDL source for this set.
- *
- * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
- * which contains the AIDL source.
- *
- * @param configureClosure The closure to use to configure the AIDL source.
- * @return this
- */
- @NonNull
- AndroidSourceSet aidl(Closure configureClosure);
-
- /**
- * The Android Renderscript source directory for this source set.
- *
- * @return the source. Never returns null.
- */
- @NonNull
- AndroidSourceDirectorySet getRenderscript();
-
- /**
- * Configures the location of the Android Renderscript source for this set.
- *
- * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
- * which contains the Renderscript source.
- *
- * @param configureClosure The closure to use to configure the Renderscript source.
- * @return this
- */
- @NonNull
- AndroidSourceSet renderscript(Closure configureClosure);
-
- /**
- * The Android JNI source directory for this source set.
- *
- * @return the source. Never returns null.
- */
- @NonNull
- AndroidSourceDirectorySet getJni();
-
- /**
- * Configures the location of the Android JNI source for this set.
- *
- * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
- * which contains the JNI source.
- *
- * @param configureClosure The closure to use to configure the JNI source.
- * @return this
- */
- @NonNull
- AndroidSourceSet jni(Closure configureClosure);
-
- /**
- * The Android JNI libs directory for this source set.
- *
- * @return the libs. Never returns null.
- */
- @NonNull
- AndroidSourceDirectorySet getJniLibs();
-
- /**
- * Configures the location of the Android JNI libs for this set.
- *
- * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
- * which contains the JNI libs.
- *
- * @param configureClosure The closure to use to configure the JNI libs.
- * @return this
- */
- @NonNull
- AndroidSourceSet jniLibs(Closure configureClosure);
-
- /**
- * Sets the root of the source sets to a given path.
- *
- * All entries of the source set are located under this root directory.
- *
- * @param path the root directory.
- * @return this
- */
- @NonNull
- AndroidSourceSet setRoot(String path);
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceSet.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceSet.java
new file mode 100644
index 0000000..26079e3
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/AndroidSourceSet.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle.api;
+import com.android.annotations.NonNull;
+
+import groovy.lang.Closure;
+
+/**
+ * An AndroidSourceSet represents a logical group of Java, aidl and RenderScript sources
+ * as well as Android and non-Android (Java-style) resources.
+ */
+public interface AndroidSourceSet {
+
+ /**
+ * Returns the name of this source set.
+ *
+ * @return The name. Never returns null.
+ */
+ @NonNull
+ String getName();
+
+ /**
+ * Returns the Java resources which are to be copied into the javaResources output directory.
+ *
+ * @return the java resources. Never returns null.
+ */
+ @NonNull
+ AndroidSourceDirectorySet getResources();
+
+ /**
+ * Configures the Java resources for this set.
+ *
+ * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet} which
+ * contains the java resources.
+ *
+ * @param configureClosure The closure to use to configure the javaResources.
+ * @return this
+ */
+ @NonNull
+ AndroidSourceSet resources(Closure configureClosure);
+
+ /**
+ * Returns the Java source which is to be compiled by the Java compiler into the class output
+ * directory.
+ *
+ * @return the Java source. Never returns null.
+ */
+ @NonNull
+ AndroidSourceDirectorySet getJava();
+
+ /**
+ * Configures the Java source for this set.
+ *
+ * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet} which
+ * contains the Java source.
+ *
+ * @param configureClosure The closure to use to configure the Java source.
+ * @return this
+ */
+ @NonNull
+ AndroidSourceSet java(Closure configureClosure);
+
+ /**
+ * Returns the name of the compile configuration for this source set.
+ * @return The configuration name
+ */
+ @NonNull
+ String getCompileConfigurationName();
+
+ /**
+ * Returns the name of the runtime configuration for this source set.
+ * @return The runtime configuration name
+ */
+ @NonNull
+ String getPackageConfigurationName();
+
+ /**
+ * Returns the name of the compiled-only configuration for this source set.
+ * @return The provided configuration name
+ */
+ @NonNull
+ String getProvidedConfigurationName();
+
+ /**
+ * Returns the name of the wearApp configuration for this source set.
+ * @return The configuration name
+ */
+ @NonNull
+ String getWearAppConfigurationName();
+
+ /**
+ * The Android Manifest file for this source set.
+ *
+ * @return the manifest. Never returns null.
+ */
+ @NonNull
+ AndroidSourceFile getManifest();
+
+ /**
+ * Configures the location of the Android Manifest for this set.
+ *
+ * <p>The given closure is used to configure the {@link AndroidSourceFile} which contains the
+ * manifest.
+ *
+ * @param configureClosure The closure to use to configure the Android Manifest.
+ * @return this
+ */
+ @NonNull
+ AndroidSourceSet manifest(Closure configureClosure);
+
+ /**
+ * The Android Resources directory for this source set.
+ *
+ * @return the resources. Never returns null.
+ */
+ @NonNull
+ AndroidSourceDirectorySet getRes();
+
+ /**
+ * Configures the location of the Android Resources for this set.
+ *
+ * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
+ * which contains the resources.
+ *
+ * @param configureClosure The closure to use to configure the Resources.
+ * @return this
+ */
+ @NonNull
+ AndroidSourceSet res(Closure configureClosure);
+
+ /**
+ * The Android Assets directory for this source set.
+ *
+ * @return the assets. Never returns null.
+ */
+ @NonNull
+ AndroidSourceDirectorySet getAssets();
+
+ /**
+ * Configures the location of the Android Assets for this set.
+ *
+ * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
+ * which contains the assets.
+ *
+ * @param configureClosure The closure to use to configure the Assets.
+ * @return this
+ */
+ @NonNull
+ AndroidSourceSet assets(Closure configureClosure);
+
+ /**
+ * The Android AIDL source directory for this source set.
+ *
+ * @return the source. Never returns null.
+ */
+ @NonNull
+ AndroidSourceDirectorySet getAidl();
+
+ /**
+ * Configures the location of the Android AIDL source for this set.
+ *
+ * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
+ * which contains the AIDL source.
+ *
+ * @param configureClosure The closure to use to configure the AIDL source.
+ * @return this
+ */
+ @NonNull
+ AndroidSourceSet aidl(Closure configureClosure);
+
+ /**
+ * The Android RenderScript source directory for this source set.
+ *
+ * @return the source. Never returns null.
+ */
+ @NonNull
+ AndroidSourceDirectorySet getRenderscript();
+
+ /**
+ * Configures the location of the Android RenderScript source for this set.
+ *
+ * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
+ * which contains the Renderscript source.
+ *
+ * @param configureClosure The closure to use to configure the Renderscript source.
+ * @return this
+ */
+ @NonNull
+ AndroidSourceSet renderscript(Closure configureClosure);
+
+ /**
+ * The Android JNI source directory for this source set.
+ *
+ * @return the source. Never returns null.
+ */
+ @NonNull
+ AndroidSourceDirectorySet getJni();
+
+ /**
+ * Configures the location of the Android JNI source for this set.
+ *
+ * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
+ * which contains the JNI source.
+ *
+ * @param configureClosure The closure to use to configure the JNI source.
+ * @return this
+ */
+ @NonNull
+ AndroidSourceSet jni(Closure configureClosure);
+
+ /**
+ * The Android JNI libs directory for this source set.
+ *
+ * @return the libs. Never returns null.
+ */
+ @NonNull
+ AndroidSourceDirectorySet getJniLibs();
+
+ /**
+ * Configures the location of the Android JNI libs for this set.
+ *
+ * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
+ * which contains the JNI libs.
+ *
+ * @param configureClosure The closure to use to configure the JNI libs.
+ * @return this
+ */
+ @NonNull
+ AndroidSourceSet jniLibs(Closure configureClosure);
+
+ /**
+ * Sets the root of the source sets to a given path.
+ *
+ * All entries of the source set are located under this root directory.
+ *
+ * @param path the root directory.
+ * @return this
+ */
+ @NonNull
+ AndroidSourceSet setRoot(String path);
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/BaseVariant.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/BaseVariant.java
index d61fa28..786c395 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/BaseVariant.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/BaseVariant.java
@@ -29,7 +29,7 @@
import com.android.builder.model.SourceProvider;
import org.gradle.api.Task;
-import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.AbstractCopyTask;
import org.gradle.api.tasks.compile.AbstractCompile;
import org.gradle.api.tasks.compile.JavaCompile;
@@ -108,7 +108,7 @@
* This is always non-null but could be empty.
*/
@NonNull
- List<GroupableProductFlavor> getProductFlavors();
+ List<ProductFlavor> getProductFlavors();
/**
* Returns a list of sorted SourceProvider in order of ascending order, meaning, the earlier
@@ -205,7 +205,7 @@
* Returns the Java resource processing task.
*/
@NonNull
- Copy getProcessJavaResources();
+ AbstractCopyTask getProcessJavaResources();
/**
* Returns the assemble task for all this variant's output
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/GroupableProductFlavor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/GroupableProductFlavor.java
deleted file mode 100644
index f8c9b82..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/GroupableProductFlavor.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.api;
-
-import com.android.annotations.Nullable;
-import com.android.builder.model.ProductFlavor;
-
-/**
- * A product flavor that is associated with a flavor dimension.
- */
-@Deprecated
-public interface GroupableProductFlavor extends ProductFlavor {
-
- // TODO: Remove interface now ProductFlavor has dimension.
-
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/VariantFilter.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/VariantFilter.java
index a28a6e6..c5aa6ef 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/VariantFilter.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/api/VariantFilter.java
@@ -50,5 +50,5 @@
* Returns the list of flavors, or an empty list.
*/
@NonNull
- List<GroupableProductFlavor> getFlavors();
+ List<ProductFlavor> getFlavors();
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.groovy
deleted file mode 100644
index 6f43460..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.groovy
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal
-
-import com.android.annotations.NonNull
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.internal.profile.SpanRecorders
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.variant.ApplicationVariantData
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.profile.ExecutionType
-import com.android.builder.profile.Recorder
-import com.android.builder.profile.ThreadRecorder
-import org.gradle.api.Project
-import org.gradle.api.artifacts.Configuration
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-
-/**
- * TaskManager for creating tasks in an Android application project.
- */
-class ApplicationTaskManager extends TaskManager {
- public ApplicationTaskManager (
- Project project,
- AndroidBuilder androidBuilder,
- BaseExtension extension,
- SdkHandler sdkHandler,
- DependencyManager dependencyManager,
- ToolingModelBuilderRegistry toolingRegistry) {
- super(project, androidBuilder, extension, sdkHandler, dependencyManager, toolingRegistry)
- }
-
- @Override
- public void createTasksForVariantData(
- @NonNull TaskFactory tasks,
- @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
- assert variantData instanceof ApplicationVariantData;
- ApplicationVariantData appVariantData = (ApplicationVariantData) variantData;
-
- VariantScope variantScope = variantData.getScope()
-
- createAnchorTasks(tasks, variantScope);
- createCheckManifestTask(tasks, variantScope);
-
- handleMicroApp(tasks, variantScope);
-
- // Add a task to process the manifest(s)
- SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_MANIFEST_TASK) {
- createMergeAppManifestsTask(tasks, variantScope)
- }
-
- // Add a task to create the res values
- SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_GENERATE_RES_VALUES_TASK) {
- createGenerateResValuesTask(tasks, variantScope);
- }
-
- // Add a task to compile renderscript files.
- SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_CREATE_RENDERSCRIPT_TASK) {
- createRenderscriptTask(tasks, variantScope);
- }
-
- // Add a task to merge the resource folders
- SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_RESOURCES_TASK) {
- createMergeResourcesTask(tasks, variantScope);
- }
-
- // Add a task to merge the asset folders
- SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_ASSETS_TASK) {
- createMergeAssetsTask(tasks, variantScope);
- }
-
- // Add a task to create the BuildConfig class
- SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_BUILD_CONFIG_TASK) {
- createBuildConfigTask(tasks, variantScope);
- }
-
- SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_PREPROCESS_RESOURCES_TASK) {
- createPreprocessResourcesTask(tasks, variantScope)
- }
-
- SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_PROCESS_RES_TASK) {
- // Add a task to process the Android Resources and generate source files
- createProcessResTask(tasks, variantScope, true /*generateResourcePackage*/);
-
- // Add a task to process the java resources
- createProcessJavaResTask(tasks, variantScope);
- }
-
- SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_AIDL_TASK) {
- createAidlTask(tasks, variantScope);
- }
-
- // Add a compile task
- SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_COMPILE_TASK) {
- if (variantData.getVariantConfiguration().getUseJack()) {
- createJackTask(appVariantData, null /*testedVariant*/);
- } else {
- createJavaCompileTask(tasks, variantScope);
- createJarTask(tasks, variantScope);
- createPostCompilationTasks(tasks, variantScope);
- }
- }
-
- // Add NDK tasks
- if (isNdkTaskNeeded) {
- SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_NDK_TASK) {
- createNdkTasks(variantData);
- }
- }
- variantScope.setNdkBuildable(getNdkBuildable(variantData))
- variantScope.setNdkOutputDirectories(getNdkOutputDirectories(variantData))
-
- if (variantData.getSplitHandlingPolicy() ==
- BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY) {
- if (extension.getBuildToolsRevision().getMajor() < 21) {
- throw new RuntimeException("Pure splits can only be used with buildtools 21 and later")
- }
- SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_SPLIT_TASK) {
- createSplitResourcesTasks(variantScope);
- createSplitAbiTasks(variantScope);
- }
- }
- SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_PACKAGING_TASK) {
- createPackagingTask(tasks, variantScope, true /*publishApk*/);
- }
-
- // create the lint tasks.
- SpanRecorders.record(ExecutionType.APP_TASK_MANAGER_CREATE_LINT_TASK) {
- createLintTasks(tasks, variantScope);
- }
- }
-
- /**
- * Configure variantData to generate embedded wear application.
- */
- private void handleMicroApp(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope) {
- BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
- if (variantData.getVariantConfiguration().getBuildType().isEmbedMicroApp()) {
- // get all possible configurations for the variant. We'll take the highest priority
- // of them that have a file.
- List<String> wearConfigNames = variantData.getWearConfigNames();
-
- for (String configName : wearConfigNames) {
- Configuration config = project.getConfigurations().findByName(
- configName);
- // this shouldn't happen, but better safe.
- if (config == null) {
- continue;
- }
-
- Set<File> file = config.getFiles();
-
- int count = file.size();
- if (count == 1) {
- createGenerateMicroApkDataTask(tasks, scope, config);
- // found one, bail out.
- return;
- } else if (count > 1) {
- throw new RuntimeException(String.format(
- "Configuration '%s' resolves to more than one apk.", configName));
- }
- }
- }
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.java
new file mode 100644
index 0000000..649c43e
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ApplicationTaskManager.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.internal.scope.AndroidTask;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.variant.ApplicationVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.profile.ExecutionType;
+import com.android.builder.profile.Recorder;
+import com.android.builder.profile.ThreadRecorder;
+
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.tasks.compile.JavaCompile;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * TaskManager for creating tasks in an Android application project.
+ */
+public class ApplicationTaskManager extends TaskManager {
+
+ public ApplicationTaskManager(
+ Project project,
+ AndroidBuilder androidBuilder,
+ AndroidConfig extension,
+ SdkHandler sdkHandler,
+ DependencyManager dependencyManager,
+ ToolingModelBuilderRegistry toolingRegistry) {
+ super(project, androidBuilder, extension, sdkHandler, dependencyManager, toolingRegistry);
+ }
+
+ @Override
+ public void createTasksForVariantData(
+ @NonNull final TaskFactory tasks,
+ @NonNull final BaseVariantData<? extends BaseVariantOutputData> variantData) {
+ assert variantData instanceof ApplicationVariantData;
+ final ApplicationVariantData appVariantData = (ApplicationVariantData) variantData;
+
+ final VariantScope variantScope = variantData.getScope();
+
+ createAnchorTasks(tasks, variantScope);
+ createCheckManifestTask(tasks, variantScope);
+
+ handleMicroApp(tasks, variantScope);
+
+ // Add a task to process the manifest(s)
+ ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_MANIFEST_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+ createMergeAppManifestsTask(tasks, variantScope);
+ return null;
+ }
+ });
+
+ // Add a task to create the res values
+ ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_GENERATE_RES_VALUES_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+ createGenerateResValuesTask(tasks, variantScope);
+ return null;
+ }
+ });
+
+ // Add a task to compile renderscript files.
+ ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_CREATE_RENDERSCRIPT_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+ createRenderscriptTask(tasks, variantScope);
+ return null;
+ }
+ });
+
+ // Add a task to merge the resource folders
+ ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_RESOURCES_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+ createMergeResourcesTask(tasks, variantScope);
+ return null;
+ }
+ });
+
+ // Add a task to merge the asset folders
+ ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_MERGE_ASSETS_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+ createMergeAssetsTask(tasks, variantScope);
+ return null;
+ }
+ });
+
+ // Add a task to create the BuildConfig class
+ ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_BUILD_CONFIG_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+ createBuildConfigTask(tasks, variantScope);
+ return null;
+ }
+ });
+
+ ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_PROCESS_RES_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+ // Add a task to process the Android Resources and generate source files
+ createProcessResTask(tasks, variantScope, true /*generateResourcePackage*/);
+
+ // Add a task to process the java resources
+ createProcessJavaResTasks(tasks, variantScope);
+ return null;
+ }
+ });
+
+ ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_AIDL_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+ createAidlTask(tasks, variantScope);
+ return null;
+ }
+ });
+
+ // Add a compile task
+ ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_COMPILE_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+ AndroidTask<JavaCompile> javacTask = createJavacTask(tasks, variantScope);
+
+ if (variantData.getVariantConfiguration().getUseJack()) {
+ createJackTask(tasks, variantScope);
+ } else {
+ setJavaCompilerTask(javacTask, tasks, variantScope);
+ createJarTask(tasks, variantScope);
+ createPostCompilationTasks(tasks, variantScope);
+ }
+ return null;
+ }
+ });
+
+ // Add NDK tasks
+ if (isNdkTaskNeeded) {
+ ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_NDK_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+ createNdkTasks(variantScope);
+ return null;
+ }
+ });
+ } else {
+ if (variantData.compileTask != null) {
+ variantData.compileTask.dependsOn(getNdkBuildable(variantData));
+ } else {
+ variantScope.getCompileTask().dependsOn(tasks, getNdkBuildable(variantData));
+ }
+ }
+ variantScope.setNdkBuildable(getNdkBuildable(variantData));
+
+ if (variantData.getSplitHandlingPolicy().equals(
+ BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY)) {
+ if (getExtension().getBuildToolsRevision().getMajor() < 21) {
+ throw new RuntimeException("Pure splits can only be used with buildtools 21 and later");
+ }
+
+ ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_SPLIT_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+ createSplitResourcesTasks(variantScope);
+ createSplitAbiTasks(variantScope);
+ return null;
+ }
+ });
+ }
+
+ ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_PACKAGING_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+ createPackagingTask(tasks, variantScope, true /*publishApk*/);
+ return null;
+ }
+ });
+
+ // create the lint tasks.
+ ThreadRecorder.get().record(ExecutionType.APP_TASK_MANAGER_CREATE_LINT_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+ createLintTasks(tasks, variantScope);
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Configure variantData to generate embedded wear application.
+ */
+ private void handleMicroApp(
+ @NonNull TaskFactory tasks,
+ @NonNull VariantScope scope) {
+ BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+ if (variantData.getVariantConfiguration().getBuildType().isEmbedMicroApp()) {
+ // get all possible configurations for the variant. We'll take the highest priority
+ // of them that have a file.
+ List<String> wearConfigNames = variantData.getWearConfigNames();
+
+ for (String configName : wearConfigNames) {
+ Configuration config = project.getConfigurations().findByName(
+ configName);
+ // this shouldn't happen, but better safe.
+ if (config == null) {
+ continue;
+ }
+
+ Set<File> file = config.getFiles();
+
+ int count = file.size();
+ if (count == 1) {
+ createGenerateMicroApkDataTask(tasks, scope, config);
+ // found one, bail out.
+ return;
+ } else if (count > 1) {
+ throw new RuntimeException(String.format(
+ "Configuration '%s' resolves to more than one apk.", configName));
+ }
+ }
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/BuildTypeData.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/BuildTypeData.groovy
deleted file mode 100644
index 106392f..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/BuildTypeData.groovy
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.internal
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.gradle.internal.api.DefaultAndroidSourceSet
-import com.android.build.gradle.internal.dsl.BuildType
-import org.gradle.api.Project
-import org.gradle.api.Task
-/**
- * Class containing a BuildType and associated data (Sourceset for instance).
- */
-class BuildTypeData extends VariantDimensionData {
- final BuildType buildType
- final Task assembleTask
-
- BuildTypeData(
- @NonNull BuildType buildType,
- @NonNull Project project,
- @NonNull DefaultAndroidSourceSet sourceSet,
- @Nullable DefaultAndroidSourceSet unitTestSourceSet) {
- super(sourceSet, null, unitTestSourceSet, project)
-
- this.buildType = buildType
-
- assembleTask = project.tasks.create("assemble${buildType.name.capitalize()}")
- assembleTask.description = "Assembles all ${buildType.name.capitalize()} builds."
- assembleTask.group = "Build"
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/BuildTypeData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/BuildTypeData.java
new file mode 100644
index 0000000..fa0279f
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/BuildTypeData.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle.internal;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.api.DefaultAndroidSourceSet;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.utils.StringHelper;
+
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.plugins.BasePlugin;
+
+/**
+ * Class containing a BuildType and associated data (Sourceset for instance).
+ */
+public class BuildTypeData extends VariantDimensionData {
+ private final CoreBuildType buildType;
+ private final Task assembleTask;
+
+ BuildTypeData(
+ @NonNull CoreBuildType buildType,
+ @NonNull Project project,
+ @NonNull DefaultAndroidSourceSet sourceSet,
+ @Nullable DefaultAndroidSourceSet unitTestSourceSet) {
+ super(sourceSet, null, unitTestSourceSet, project);
+
+ this.buildType = buildType;
+
+ String sourceSetName = StringHelper.capitalize(buildType.getName());
+
+ assembleTask = project.getTasks().create("assemble" + sourceSetName);
+ assembleTask.setDescription("Assembles all " + sourceSetName + " builds.");
+ assembleTask.setGroup(BasePlugin.BUILD_GROUP);
+ }
+
+ public CoreBuildType getBuildType() {
+ return buildType;
+ }
+
+ public Task getAssembleTask() {
+ return assembleTask;
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/CompileOptions.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/CompileOptions.groovy
deleted file mode 100644
index a9b7fae..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/CompileOptions.groovy
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal
-
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.google.common.base.Charsets
-import groovy.transform.CompileStatic
-import org.gradle.api.JavaVersion
-
-/**
- * Compilation options.
- */
-@CompileStatic
-class CompileOptions {
- @Nullable
- private JavaVersion sourceCompatibility
-
- @Nullable
- private JavaVersion targetCompatibility
-
- String encoding = Charsets.UTF_8.name()
-
- /**
- * Default Java version that will be used if the source and target compatibility levels will
- * not be set explicitly.
- */
- JavaVersion defaultJavaVersion = JavaVersion.VERSION_1_6
-
- boolean ndkCygwinMode = false
-
- void setSourceCompatibility(@NonNull JavaVersion sourceCompatibility) {
- this.sourceCompatibility = sourceCompatibility
- }
-
- /**
- * Language level of the source code.
- *
- * <p>Similar to what <a href="http://www.gradle.org/docs/current/userguide/java_plugin.html">
- * Gradle Java plugin</a> uses.
- */
- @NonNull
- JavaVersion getSourceCompatibility() {
- sourceCompatibility?: defaultJavaVersion
- }
-
- void setTargetCompatibility(@NonNull JavaVersion targetCompatibility) {
- this.targetCompatibility = targetCompatibility
- }
-
- /**
- * Version of the generated Java bytecode.
- *
- * <p>Similar to what <a href="http://www.gradle.org/docs/current/userguide/java_plugin.html">
- * Gradle Java plugin</a> uses.
- */
- @NonNull
- JavaVersion getTargetCompatibility() {
- targetCompatibility?: defaultJavaVersion
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/CompileOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/CompileOptions.java
new file mode 100644
index 0000000..e8e2403
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/CompileOptions.java
@@ -0,0 +1,129 @@
+/*
+ * 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.build.gradle.internal;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.google.common.base.Charsets;
+import org.gradle.api.JavaVersion;
+
+import java.util.Locale;
+
+/**
+ * Compilation options.
+ */
+public class CompileOptions {
+ @Nullable
+ private JavaVersion sourceCompatibility;
+
+ @Nullable
+ private JavaVersion targetCompatibility;
+
+ private String encoding = Charsets.UTF_8.name();
+
+ /**
+ * Default Java version that will be used if the source and target compatibility levels will
+ * not be set explicitly.
+ */
+ private JavaVersion defaultJavaVersion = JavaVersion.VERSION_1_6;
+
+ private boolean ndkCygwinMode = false;
+
+ /**
+ * Language level of the source code.
+ *
+ * <p>Formats supported are :
+ * "1.6"
+ * 1.6
+ * JavaVersion.Version_1_6
+ * "Version_1_6"
+ */
+ public void setSourceCompatibility(@NonNull Object sourceCompatibility) {
+ this.sourceCompatibility = convert(sourceCompatibility);
+ }
+
+ /**
+ * Language level of the source code.
+ *
+ * <p>Similar to what <a href="http://www.gradle.org/docs/current/userguide/java_plugin.html">
+ * Gradle Java plugin</a> uses.
+ */
+ @NonNull
+ public JavaVersion getSourceCompatibility() {
+ return sourceCompatibility != null ? sourceCompatibility : defaultJavaVersion;
+ }
+
+ /**
+ * Language level of the target code.
+ *
+ * <p>Formats supported are :
+ * "1.6"
+ * 1.6
+ * JavaVersion.Version_1_6
+ * "Version_1_6"
+ */
+ public void setTargetCompatibility(@NonNull Object targetCompatibility) {
+ this.targetCompatibility = convert(targetCompatibility);
+ }
+
+ /**
+ * Version of the generated Java bytecode.
+ *
+ * <p>Similar to what <a href="http://www.gradle.org/docs/current/userguide/java_plugin.html">
+ * Gradle Java plugin</a> uses.
+ */
+ @NonNull
+ public JavaVersion getTargetCompatibility() {
+ return targetCompatibility != null ? targetCompatibility : defaultJavaVersion;
+ }
+
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ public void setDefaultJavaVersion(JavaVersion defaultJavaVersion) {
+ this.defaultJavaVersion = defaultJavaVersion;
+ }
+
+ public JavaVersion getDefaultJavaVersion() {
+ return defaultJavaVersion;
+ }
+
+
+ private static final String VERSION_PREFIX = "VERSION_";
+ /**
+ * Convert all possible supported way of specifying a Java version to {@link JavaVersion}
+ * @param version the user provided java version.
+ * @return {@link JavaVersion}
+ * @throws RuntimeException if it cannot be converted.
+ */
+ @NonNull
+ private static JavaVersion convert(@NonNull Object version) {
+ // for backward version reasons, we support setting strings like 'Version_1_6'
+ if (version instanceof String) {
+ final String versionString = (String) version;
+ if (versionString.toUpperCase(Locale.ENGLISH).startsWith(VERSION_PREFIX)) {
+ version = versionString.substring(VERSION_PREFIX.length()).replace('_', '.');
+ }
+ }
+ return JavaVersion.toVersion(version);
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/DependencyManager.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/DependencyManager.java
index 834a880..c8c6df9 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/DependencyManager.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/DependencyManager.java
@@ -16,6 +16,13 @@
package com.android.build.gradle.internal;
+import static com.android.SdkConstants.DOT_JAR;
+import static com.android.SdkConstants.EXT_ANDROID_PACKAGE;
+import static com.android.SdkConstants.EXT_JAR;
+import static com.android.builder.core.BuilderConstants.EXT_LIB_ARCHIVE;
+import static com.android.builder.core.ErrorReporter.EvaluationMode.STANDARD;
+import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
+
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.gradle.internal.dependency.JarInfo;
@@ -34,12 +41,14 @@
import com.android.builder.model.MavenCoordinates;
import com.android.builder.model.SyncIssue;
import com.android.utils.ILogger;
+import com.google.common.base.Joiner;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
+import org.gradle.api.CircularReferenceException;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.UnknownProjectException;
@@ -61,15 +70,9 @@
import org.gradle.api.specs.Specs;
import org.gradle.util.GUtil;
-import static com.android.SdkConstants.DOT_JAR;
-import static com.android.SdkConstants.EXT_ANDROID_PACKAGE;
-import static com.android.SdkConstants.EXT_JAR;
-import static com.android.build.gradle.internal.ExtraModelInfo.ModelQueryMode.STANDARD;
-import static com.android.builder.core.BuilderConstants.EXT_LIB_ARCHIVE;
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
-
import java.io.File;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -128,10 +131,11 @@
public void resolveDependencies(
@NonNull VariantDependencies variantDeps,
- @Nullable VariantDependencies testedVariantDeps) {
+ @Nullable VariantDependencies testedVariantDeps,
+ @Nullable String testedProjectPath) {
Multimap<LibraryDependency, VariantDependencies> reverseMap = ArrayListMultimap.create();
- resolveDependencyForConfig(variantDeps, testedVariantDeps, reverseMap);
+ resolveDependencyForConfig(variantDeps, testedVariantDeps, testedProjectPath, reverseMap);
processLibraries(variantDeps.getLibraries(), reverseMap);
}
@@ -214,6 +218,7 @@
prepareLibraryTask.setDescription("Prepare " + library.getName());
prepareLibraryTask.setBundle(library.getBundle());
prepareLibraryTask.setExplodedDir(library.getBundleFolder());
+ prepareLibraryTask.setVariantName("");
prepareTaskMap.put(key, prepareLibraryTask);
}
@@ -224,6 +229,7 @@
private void resolveDependencyForConfig(
@NonNull VariantDependencies variantDeps,
@Nullable VariantDependencies testedVariantDeps,
+ @Nullable String testedProjectPath,
@NonNull Multimap<LibraryDependency, VariantDependencies> reverseMap) {
Configuration compileClasspath = variantDeps.getCompileConfiguration();
@@ -276,6 +282,8 @@
artifacts,
reverseMap,
currentUnresolvedDependencies,
+ testedProjectPath,
+ Collections.<String>emptyList(),
0);
} else if (dependencyResult instanceof UnresolvedDependencyResult) {
ComponentSelector attempted = ((UnresolvedDependencyResult) dependencyResult).getAttempted();
@@ -304,6 +312,8 @@
artifacts,
reverseMap,
currentUnresolvedDependencies,
+ testedProjectPath,
+ Collections.<String>emptyList(),
0);
} else if (dependencyResult instanceof UnresolvedDependencyResult) {
ComponentSelector attempted = ((UnresolvedDependencyResult) dependencyResult)
@@ -315,22 +325,35 @@
}
// now look through both results.
- // 1. All Android libraries must be in both lists.
- // since we reuse the same instance of LibInfo for identical modules
+ // 1. Handle the compile and package list of Libraries.
+ // For Libraries:
+ // Only library projects can support provided aar.
+ // However, package(publish)-only are still not supported (they don't make sense).
+ // For now, provided only dependencies will be kept normally in the compile-graph.
+ // However we'll want to not include them in the resource merging.
+ // For Applications:
+ // All Android libraries must be in both lists.
+ // ---
+ // Since we reuse the same instance of LibInfo for identical modules
// we can simply run through each list and look for libs that are in only one.
// While the list of library is actually a graph, it's fine to look only at the
- // top level ones since they transitive ones are in the same scope as the direct libraries.
+ // top level ones since the transitive ones are in the same scope as the direct libraries.
List<LibInfo> copyOfPackagedLibs = Lists.newArrayList(packagedAndroidLibraries);
+ boolean isLibrary = extraModelInfo.isLibrary();
for (LibInfo lib : compiledAndroidLibraries) {
if (!copyOfPackagedLibs.contains(lib)) {
- //noinspection ConstantConditions
- variantDeps.getChecker().addSyncIssue(extraModelInfo.handleSyncError(
- lib.getResolvedCoordinates().toString(),
- SyncIssue.TYPE_NON_JAR_PROVIDED_DEP,
- String.format(
- "Project %s: provided dependencies can only be jars. %s is an Android Library.",
- project.getName(), lib.getResolvedCoordinates())));
+ if (isLibrary || lib.isOptional()) {
+ lib.setIsOptional(true);
+ } else {
+ //noinspection ConstantConditions
+ variantDeps.getChecker().addSyncIssue(extraModelInfo.handleSyncError(
+ lib.getResolvedCoordinates().toString(),
+ SyncIssue.TYPE_NON_JAR_PROVIDED_DEP,
+ String.format(
+ "Project %s: provided dependencies can only be jars. %s is an Android Library.",
+ project.getName(), lib.getResolvedCoordinates())));
+ }
} else {
copyOfPackagedLibs.remove(lib);
}
@@ -359,7 +382,7 @@
gatherJarDependencies(jarInfoSet, packagedJars, false /*compiled*/, true /*packaged*/);
// at this step, we know that libraries have been checked and libraries can only
// be in both compiled and packaged scope.
- gatherJarDependenciesFromLibraries(jarInfoSet, compiledAndroidLibraries, true, true);
+ gatherJarDependenciesFromLibraries(jarInfoSet, compiledAndroidLibraries);
// the final list of JarDependency, created from the list of JarInfo.
List<JarDependency> jars = Lists.newArrayListWithCapacity(jarInfoSet.size());
@@ -376,9 +399,11 @@
for (JarDependency jar : jarDependencies) {
if (jar.isPackaged()) {
- MavenCoordinates coord = jar.getResolvedCoordinates();
+ MavenCoordinates coordinates = jar.getResolvedCoordinates();
//noinspection ConstantConditions
- testedDeps.put(computeVersionLessCoordinateKey(coord), coord.getVersion());
+ testedDeps.put(
+ computeVersionLessCoordinateKey(coordinates),
+ coordinates.getVersion());
}
}
@@ -387,28 +412,32 @@
// to the final immutable instance
for (JarInfo jar : jarInfoSet) {
if (jar.isPackaged()) {
- MavenCoordinates coord = jar.getResolvedCoordinates();
+ MavenCoordinates coordinates = jar.getResolvedCoordinates();
- String testedVersion = testedDeps.get(computeVersionLessCoordinateKey(coord));
+ String testedVersion = testedDeps.get(
+ computeVersionLessCoordinateKey(coordinates));
if (testedVersion != null) {
// same artifact, skip packaging of the dependency in the test app,
// whether the version is a match or not.
// if the dependency is present in both tested and test artifact,
// verify that they are the same version
- if (!testedVersion.equals(coord.getVersion())) {
- String artifactInfo = coord.getGroupId() + ":" + coord.getArtifactId();
+ if (!testedVersion.equals(coordinates.getVersion())) {
+ String artifactInfo = coordinates.getGroupId() + ":" + coordinates.getArtifactId();
variantDeps.getChecker().addSyncIssue(extraModelInfo.handleSyncError(
artifactInfo,
SyncIssue.TYPE_MISMATCH_DEP,
String.format(
"Conflict with dependency '%s'. Resolved versions for app (%s) and test app (%s) differ.",
- artifactInfo, testedVersion, coord.getVersion())));
+ artifactInfo,
+ testedVersion,
+ coordinates.getVersion())));
} else {
logger.info(String.format(
"Removed '%s' from packaging of %s: Already in tested package.",
- coord, variantDeps.getName()));
+ coordinates,
+ variantDeps.getName()));
}
} else {
// new artifact, convert it.
@@ -498,7 +527,7 @@
}
}
- if (extraModelInfo.getModelQueryMode() != STANDARD &&
+ if (extraModelInfo.getMode() != STANDARD &&
compileClasspath.getResolvedConfiguration().hasError()) {
for (String dependency : currentUnresolvedDependencies) {
extraModelInfo.handleSyncError(
@@ -581,7 +610,8 @@
libInfo.getProjectVariant(),
libInfo.getProject(),
libInfo.getRequestedCoordinates(),
- libInfo.getResolvedCoordinates());
+ libInfo.getResolvedCoordinates(),
+ libInfo.isOptional());
// add it to the map
convertedMap.put(libInfo, convertedLib);
@@ -620,18 +650,14 @@
private static void gatherJarDependenciesFromLibraries(
Set<JarInfo> outJarInfos,
- Collection<LibInfo> inLibraryDependencies,
- boolean compiled,
- boolean packaged) {
+ Collection<LibInfo> inLibraryDependencies) {
for (LibInfo libInfo : inLibraryDependencies) {
- gatherJarDependencies(outJarInfos, libInfo.getJarDependencies(), compiled, packaged);
+ gatherJarDependencies(outJarInfos, libInfo.getJarDependencies(),
+ true, !libInfo.isOptional());
- //noinspection unchecked
gatherJarDependenciesFromLibraries(
outJarInfos,
- (Collection<LibInfo>) (Collection<?>) libInfo.getDependencies(),
- compiled,
- packaged);
+ libInfo.getLibInfoDependencies());
}
}
@@ -658,7 +684,7 @@
List<ResolvedArtifact>> artifacts) {
Set<ResolvedArtifact> allArtifacts;
- if (extraModelInfo.getModelQueryMode() != STANDARD) {
+ if (extraModelInfo.getMode() != STANDARD) {
allArtifacts = configuration.getResolvedConfiguration().getLenientConfiguration().getArtifacts(
Specs.satisfyAll());
} else {
@@ -689,15 +715,17 @@
}
private void addDependency(
- ResolvedComponentResult resolvedComponentResult,
- VariantDependencies configDependencies,
- Collection<LibInfo> outLibraries,
- List<JarInfo> outJars,
- Map<ModuleVersionIdentifier, List<LibInfo>> alreadyFoundLibraries,
- Map<ModuleVersionIdentifier, List<JarInfo>> alreadyFoundJars,
- Map<ModuleVersionIdentifier, List<ResolvedArtifact>> artifacts,
- Multimap<LibraryDependency, VariantDependencies> reverseMap,
- Set<String> currentUnresolvedDependencies,
+ @NonNull ResolvedComponentResult resolvedComponentResult,
+ @NonNull VariantDependencies configDependencies,
+ @NonNull Collection<LibInfo> outLibraries,
+ @NonNull List<JarInfo> outJars,
+ @NonNull Map<ModuleVersionIdentifier, List<LibInfo>> alreadyFoundLibraries,
+ @NonNull Map<ModuleVersionIdentifier, List<JarInfo>> alreadyFoundJars,
+ @NonNull Map<ModuleVersionIdentifier, List<ResolvedArtifact>> artifacts,
+ @NonNull Multimap<LibraryDependency, VariantDependencies> reverseMap,
+ @NonNull Set<String> currentUnresolvedDependencies,
+ @Nullable String testedProjectPath,
+ @NonNull List<String> projectChain,
int indent) {
ModuleVersionIdentifier moduleVersion = resolvedComponentResult.getModuleVersion();
@@ -742,8 +770,34 @@
Set<? extends DependencyResult> dependencies = resolvedComponentResult.getDependencies();
for (DependencyResult dependencyResult : dependencies) {
if (dependencyResult instanceof ResolvedDependencyResult) {
+ ResolvedComponentResult selected =
+ ((ResolvedDependencyResult) dependencyResult).getSelected();
+
+ List<String> newProjectChain = projectChain;
+
+ ComponentIdentifier identifier = selected.getId();
+ if (identifier instanceof ProjectComponentIdentifier) {
+ String projectPath =
+ ((ProjectComponentIdentifier) identifier).getProjectPath();
+
+ int index = projectChain.indexOf(projectPath);
+ if (index != -1) {
+ projectChain.add(projectPath);
+ String path = Joiner
+ .on(" -> ")
+ .join(projectChain.subList(index, projectChain.size()));
+
+ throw new CircularReferenceException(
+ "Circular reference between projects: " + path);
+ }
+
+ newProjectChain = Lists.newArrayList();
+ newProjectChain.addAll(projectChain);
+ newProjectChain.add(projectPath);
+ }
+
addDependency(
- ((ResolvedDependencyResult) dependencyResult).getSelected(),
+ selected,
configDependencies,
nestedLibraries,
nestedJars,
@@ -752,7 +806,9 @@
artifacts,
reverseMap,
currentUnresolvedDependencies,
- indent+1);
+ testedProjectPath,
+ newProjectChain,
+ indent + 1);
} else if (dependencyResult instanceof UnresolvedDependencyResult) {
ComponentSelector attempted = ((UnresolvedDependencyResult) dependencyResult).getAttempted();
if (attempted != null) {
@@ -817,11 +873,23 @@
}
// check this jar does not have a dependency on an library, as this would not work.
if (!nestedLibraries.isEmpty()) {
- configDependencies.getChecker().addSyncIssue(extraModelInfo.handleSyncError(
- new MavenCoordinatesImpl(artifact).toString(),
- SyncIssue.TYPE_JAR_DEPEND_ON_AAR,
- String.format(
- "Module version %s depends on libraries but is a jar", moduleVersion)));
+ if (testedProjectPath != null && testedProjectPath.equals(gradlePath)) {
+ // TODO: make sure this is a direct dependency and not a transitive one.
+ // add nested libs as optional somehow...
+ for (LibInfo lib : nestedLibraries) {
+ lib.setIsOptional(true);
+ }
+ outLibraries.addAll(nestedLibraries);
+
+ } else {
+ configDependencies.getChecker()
+ .addSyncIssue(extraModelInfo.handleSyncError(
+ new MavenCoordinatesImpl(artifact).toString(),
+ SyncIssue.TYPE_JAR_DEPEND_ON_AAR,
+ String.format(
+ "Module '%s' depends on one or more Android Libraries but is a jar",
+ moduleVersion)));
+ }
}
if (jarsForThisModule == null) {
@@ -1012,16 +1080,16 @@
}
/**
- * Compute a version-less key represening the given coordinate.
- * @param coord the coordinate
+ * Compute a version-less key representing the given coordinates.
+ * @param coordinates the coordinate
* @return the key.
*/
@NonNull
- private static String computeVersionLessCoordinateKey(@NonNull MavenCoordinates coord) {
- StringBuilder sb = new StringBuilder(coord.getGroupId());
- sb.append(':').append(coord.getArtifactId());
- if (coord.getClassifier() != null) {
- sb.append(':').append(coord.getClassifier());
+ private static String computeVersionLessCoordinateKey(@NonNull MavenCoordinates coordinates) {
+ StringBuilder sb = new StringBuilder(coordinates.getGroupId());
+ sb.append(':').append(coordinates.getArtifactId());
+ if (coordinates.getClassifier() != null) {
+ sb.append(':').append(coordinates.getClassifier());
}
return sb.toString();
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ExecutionConfigurationUtil.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ExecutionConfigurationUtil.java
new file mode 100644
index 0000000..e9ddda3
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ExecutionConfigurationUtil.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal;
+
+import com.android.ide.common.internal.ExecutorSingleton;
+
+import org.gradle.api.Project;
+
+public class ExecutionConfigurationUtil {
+
+ private static final String THREAD_POOL_SIZE_PROPERTY = "com.android.build.threadPoolSize";
+
+ public static void setThreadPoolSize(Project project) {
+ if (!project.hasProperty(THREAD_POOL_SIZE_PROPERTY)) {
+ return;
+ }
+
+ String threadPoolSizeProperty = project.property(THREAD_POOL_SIZE_PROPERTY).toString();
+
+ try {
+ ExecutorSingleton.setThreadPoolSize(Integer.parseInt(threadPoolSizeProperty));
+ } catch (NumberFormatException e) {
+ project.getLogger().error("com.android.threadPoolSize should be an integer.");
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ExtraModelInfo.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ExtraModelInfo.java
index 1a7c470..af28ddf 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ExtraModelInfo.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ExtraModelInfo.java
@@ -19,27 +19,34 @@
import static com.android.builder.model.AndroidProject.PROPERTY_BUILD_MODEL_ONLY;
import static com.android.builder.model.AndroidProject.PROPERTY_BUILD_MODEL_ONLY_ADVANCED;
import static com.android.builder.model.AndroidProject.PROPERTY_INVOKED_FROM_IDE;
-import static com.android.ide.common.blame.output.GradleMessageRewriter.ErrorFormatMode;
+import static com.android.ide.common.blame.parser.JsonEncodedGradleMessageParser.STDOUT_ERROR_TAG;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.build.gradle.AndroidGradleOptions;
import com.android.build.gradle.api.BaseVariant;
-import com.android.build.gradle.internal.dsl.BuildType;
-import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
import com.android.build.gradle.internal.model.ArtifactMetaDataImpl;
import com.android.build.gradle.internal.model.JavaArtifactImpl;
import com.android.build.gradle.internal.model.SyncIssueImpl;
import com.android.build.gradle.internal.model.SyncIssueKey;
import com.android.build.gradle.internal.variant.DefaultSourceProviderContainer;
+import com.android.builder.core.ErrorReporter;
import com.android.builder.model.AndroidArtifact;
import com.android.builder.model.ArtifactMetaData;
import com.android.builder.model.JavaArtifact;
import com.android.builder.model.SourceProvider;
import com.android.builder.model.SourceProviderContainer;
import com.android.builder.model.SyncIssue;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.MessageJsonSerializer;
+import com.android.ide.common.blame.SourceFilePosition;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
@@ -52,16 +59,13 @@
/**
* For storing additional model information.
*/
-public class ExtraModelInfo {
-
- public enum ModelQueryMode {
- STANDARD, IDE, IDE_ADVANCED
- }
+public class ExtraModelInfo extends ErrorReporter {
@NonNull
private final Project project;
+ private final boolean isLibrary;
- private final ModelQueryMode modelQueryMode;
+ @NonNull
private final ErrorFormatMode errorFormatMode;
private final Map<SyncIssueKey, SyncIssue> syncIssues = Maps.newHashMap();
@@ -74,49 +78,63 @@
private final ListMultimap<String, SourceProviderContainer> extraProductFlavorSourceProviders = ArrayListMultimap.create();
private final ListMultimap<String, SourceProviderContainer> extraMultiFlavorSourceProviders = ArrayListMultimap.create();
- public ExtraModelInfo(@NonNull Project project) {
+ @Nullable
+ private final Gson mGson;
+
+ public ExtraModelInfo(@NonNull Project project, boolean isLibrary) {
+ super(computeModelQueryMode(project));
this.project = project;
- modelQueryMode = computeModelQueryMode(project);
+ this.isLibrary = isLibrary;
errorFormatMode = computeErrorFormatMode(project);
+ if (errorFormatMode == ErrorFormatMode.MACHINE_PARSABLE) {
+ GsonBuilder gsonBuilder = new GsonBuilder();
+ MessageJsonSerializer.registerTypeAdapters(gsonBuilder);
+ mGson = gsonBuilder.create();
+ } else {
+ mGson = null;
+ }
+ }
+
+ public boolean isLibrary() {
+ return isLibrary;
}
public Map<SyncIssueKey, SyncIssue> getSyncIssues() {
return syncIssues;
}
- public ModelQueryMode getModelQueryMode() {
- return modelQueryMode;
- }
-
- public ErrorFormatMode getErrorFormatMode() {
- return errorFormatMode;
- }
-
+ @Override
+ @NonNull
public SyncIssue handleSyncError(@NonNull String data, int type, @NonNull String msg) {
- switch (modelQueryMode) {
+ SyncIssue issue;
+ switch (getMode()) {
case STANDARD:
- if (isDependencyIssue(type)) {
- // if it's a dependency issue we don't throw right away. we'll
- // throw during build instead.
- // but we do log.
- project.getLogger().warn("WARNING: " + msg);
- return new SyncIssueImpl(type, SyncIssue.SEVERITY_ERROR, data, msg);
+ if (!isDependencyIssue(type)) {
+ throw new GradleException(msg);
}
- throw new GradleException(msg);
- case IDE:
+ // if it's a dependency issue we don't throw right away. we'll
+ // throw during build instead.
+ // but we do log.
+ project.getLogger().warn("WARNING: " + msg);
+ issue = new SyncIssueImpl(type, SyncIssue.SEVERITY_ERROR, data, msg);
+ break;
+ case IDE_LEGACY:
// compat mode for the only issue supported before the addition of SyncIssue
// in the model.
if (type != SyncIssue.TYPE_UNRESOLVED_DEPENDENCY) {
throw new GradleException(msg);
}
// intended fall-through
- case IDE_ADVANCED:
+ case IDE:
// new IDE, able to support SyncIssue.
- SyncIssue syncIssue = new SyncIssueImpl(type, SyncIssue.SEVERITY_ERROR, data, msg);
- syncIssues.put(SyncIssueKey.from(syncIssue), syncIssue);
+ issue = new SyncIssueImpl(type, SyncIssue.SEVERITY_ERROR, data, msg);
+ syncIssues.put(SyncIssueKey.from(issue), issue);
+ break;
+ default:
+ throw new RuntimeException("Unknown SyncIssue type");
}
- return null;
+ return issue;
}
private static boolean isDependencyIssue(int type) {
@@ -136,7 +154,50 @@
}
- public Collection<ArtifactMetaData> getExtraArtifacts() {
+ @Override
+ public void receiveMessage(@NonNull Message message) {
+ StringBuilder errorStringBuilder = new StringBuilder();
+ if (errorFormatMode == ErrorFormatMode.HUMAN_READABLE) {
+ for (SourceFilePosition pos : message.getSourceFilePositions()) {
+ errorStringBuilder.append(pos.toString());
+ errorStringBuilder.append(' ');
+ }
+ if (errorStringBuilder.length() > 0) {
+ errorStringBuilder.append(": ");
+ }
+ errorStringBuilder.append(message.getText()).append("\n");
+
+ } else {
+ //noinspection ConstantConditions mGson != null when errorFormatMode == MACHINE_PARSABLE
+ errorStringBuilder.append(STDOUT_ERROR_TAG)
+ .append(mGson.toJson(message)).append("\n");
+ }
+
+ String messageString = errorStringBuilder.toString();
+
+ switch (message.getKind()) {
+ case ERROR:
+ project.getLogger().error(messageString);
+ break;
+ case WARNING:
+ project.getLogger().warn(messageString);
+ break;
+ case INFO:
+ project.getLogger().info(messageString);
+ break;
+ case STATISTICS:
+ project.getLogger().trace(messageString);
+ break;
+ case UNKNOWN:
+ project.getLogger().debug(messageString);
+ break;
+ case SIMPLE:
+ project.getLogger().info(messageString);
+ break;
+ }
+ }
+
+ public Collection<ArtifactMetaData> getExtraArtifacts() {
return extraArtifactMap.values();
}
@@ -171,7 +232,7 @@
}
public void registerBuildTypeSourceProvider(@NonNull String name,
- @NonNull BuildType buildType,
+ @NonNull CoreBuildType buildType,
@NonNull SourceProvider sourceProvider) {
if (extraArtifactMap.get(name) == null) {
throw new IllegalArgumentException(String.format(
@@ -185,7 +246,7 @@
}
public void registerProductFlavorSourceProvider(@NonNull String name,
- @NonNull ProductFlavor productFlavor,
+ @NonNull CoreProductFlavor productFlavor,
@NonNull SourceProvider sourceProvider) {
if (extraArtifactMap.get(name) == null) {
throw new IllegalArgumentException(String.format(
@@ -235,7 +296,7 @@
JavaArtifact artifact = new JavaArtifactImpl(
name, assembleTaskName, javaCompileTaskName, ideSetupTaskNames,
- generatedSourceFolders, classesFolder, javaResourcesFolder,
+ generatedSourceFolders, classesFolder, javaResourcesFolder, null,
new ConfigurationDependencies(configuration), sourceProvider, null);
extraJavaArtifacts.put(variant.getName(), artifact);
@@ -246,36 +307,27 @@
* means we will attempt to resolve dependencies even if some are broken/unsupported to avoid
* failing the import in the IDE.
*/
- private static ModelQueryMode computeModelQueryMode(@NonNull Project project) {
- if (isPropertyTrue(project, PROPERTY_BUILD_MODEL_ONLY_ADVANCED)) {
- return ModelQueryMode.IDE_ADVANCED;
+ private static EvaluationMode computeModelQueryMode(@NonNull Project project) {
+ if (AndroidGradleOptions.buildModelOnlyAdvanced(project)) {
+ return EvaluationMode.IDE;
}
- if (isPropertyTrue(project, PROPERTY_BUILD_MODEL_ONLY)) {
- return ModelQueryMode.IDE;
+ if (AndroidGradleOptions.buildModelOnly(project)) {
+ return EvaluationMode.IDE_LEGACY;
}
- return ModelQueryMode.STANDARD;
+ return EvaluationMode.STANDARD;
}
private static ErrorFormatMode computeErrorFormatMode(@NonNull Project project) {
- if (isPropertyTrue(project, PROPERTY_INVOKED_FROM_IDE)) {
+ if (AndroidGradleOptions.invokedFromIde(project)) {
return ErrorFormatMode.MACHINE_PARSABLE;
} else {
return ErrorFormatMode.HUMAN_READABLE;
}
}
- private static boolean isPropertyTrue(
- @NonNull Project project,
- @NonNull String propertyName) {
- if (project.hasProperty(propertyName)) {
- Object value = project.getProperties().get(propertyName);
- if (value instanceof String) {
- return Boolean.parseBoolean((String) value);
- }
- }
-
- return false;
+ public enum ErrorFormatMode {
+ MACHINE_PARSABLE, HUMAN_READABLE
}
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.groovy
deleted file mode 100644
index 34664b9..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.groovy
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal
-import com.android.SdkConstants
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.LibraryExtension
-import com.android.build.gradle.internal.core.GradleVariantConfiguration
-import com.android.build.gradle.internal.profile.SpanRecorders
-import com.android.build.gradle.internal.scope.AndroidTask
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.tasks.MergeFileTask
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.build.gradle.internal.variant.LibVariantOutputData
-import com.android.build.gradle.internal.variant.LibraryVariantData
-import com.android.build.gradle.internal.variant.VariantHelper
-import com.android.build.gradle.tasks.ExtractAnnotations
-import com.android.build.gradle.tasks.MergeResources
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.BuilderConstants
-import com.android.builder.core.DefaultBuildType
-import com.android.builder.core.VariantType
-import com.android.builder.dependency.LibraryBundle
-import com.android.builder.dependency.LibraryDependency
-import com.android.builder.dependency.ManifestDependency
-import com.android.builder.model.AndroidLibrary
-import com.android.builder.model.MavenCoordinates
-import com.android.builder.profile.ExecutionType
-import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.api.internal.ConventionMapping
-import org.gradle.api.plugins.BasePlugin
-import org.gradle.api.tasks.Copy
-import org.gradle.api.tasks.Sync
-import org.gradle.api.tasks.bundling.Jar
-import org.gradle.api.tasks.bundling.Zip
-import org.gradle.tooling.BuildException
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-
-import static com.android.SdkConstants.FN_ANNOTATIONS_ZIP
-import static com.android.SdkConstants.LIBS_FOLDER
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-import static com.android.builder.model.AndroidProject.FD_OUTPUTS
-/**
- * TaskManager for creating tasks in an Android library project.
- */
-class LibraryTaskManager extends TaskManager {
-
- private static final String ANNOTATIONS = "annotations"
-
- private Task assembleDefault;
-
- public LibraryTaskManager (
- Project project,
- AndroidBuilder androidBuilder,
- BaseExtension extension,
- SdkHandler sdkHandler,
- DependencyManager dependencyManager,
- ToolingModelBuilderRegistry toolingRegistry) {
- super(project, androidBuilder, extension, sdkHandler, dependencyManager, toolingRegistry)
- }
-
- @Override
- public void createTasksForVariantData(
- @NonNull TaskFactory tasks,
- @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
- LibraryVariantData libVariantData = variantData as LibraryVariantData
- GradleVariantConfiguration variantConfig = variantData.variantConfiguration
- DefaultBuildType buildType = variantConfig.buildType
-
- VariantScope variantScope = variantData.getScope()
-
- String fullName = variantConfig.fullName
- String dirName = variantConfig.dirName
-
- createAnchorTasks(tasks, variantScope)
-
- createCheckManifestTask(tasks, variantScope)
-
- // Add a task to create the res values
- SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_GENERATE_RES_VALUES_TASK) {
- createGenerateResValuesTask(tasks, variantScope)
- }
-
- // Add a task to process the manifest(s)
- SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_MANIFEST_TASK) {
- createMergeLibManifestsTask(tasks, variantScope)
- }
-
- // Add a task to compile renderscript files.
- SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_CREATE_RENDERSCRIPT_TASK) {
- createRenderscriptTask(tasks, variantScope)
- }
-
- AndroidTask<MergeResources> packageRes = SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_RESOURCES_TASK) {
- // Create a merge task to only merge the resources from this library and not
- // the dependencies. This is what gets packaged in the aar.
- AndroidTask<MergeResources> mergeResourceTask = basicCreateMergeResourcesTask(
- tasks,
- variantScope,
- "package",
- new File(variantScope.getGlobalScope().getIntermediatesDir(),
- "$DIR_BUNDLES/$variantScope.variantConfiguration.dirName/res"),
- false /*includeDependencies*/,
- false /*process9Patch*/);
-
- if (!variantData.variantDependency.androidDependencies.isEmpty()) {
- // Add a task to merge the resource folders, including the libraries, in order to
- // generate the R.txt file with all the symbols, including the ones from
- // the dependencies.
- createMergeResourcesTask(tasks, variantScope)
- }
-
- mergeResourceTask.configure(tasks) { MergeResources task ->
- task.conventionMapping.publicFile = {
- new File(variantScope.globalScope.intermediatesDir,
- "$DIR_BUNDLES/${dirName}/${SdkConstants.FN_PUBLIC_TXT}")
- }
- }
-
- return mergeResourceTask;
- }
-
- // Add a task to merge the assets folders
- SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_ASSETS_TASK) {
- createMergeAssetsTask(tasks, variantScope)
- }
-
- // Add a task to create the BuildConfig class
- SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_BUILD_CONFIG_TASK) {
- createBuildConfigTask(tasks, variantScope)
- }
-
- SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_BACKPORT_RESOURCES_TASK) {
- createPreprocessResourcesTask(tasks, variantScope)
- }
-
- SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_PROCESS_RES_TASK) {
- // Add a task to generate resource source files, directing the location
- // of the r.txt file to be directly in the bundle.
- createProcessResTask(tasks, variantScope,
- new File("$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}"),
- false /*generateResourcePackage*/,
- )
-
- // process java resources
- createProcessJavaResTask(tasks, variantScope)
- }
-
- SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_AIDL_TASK) {
- createAidlTask(tasks, variantScope)
- }
-
- // Add a compile task
- SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_COMPILE_TASK) {
- createJavaCompileTask(tasks, variantScope);
- }
-
- // package the prebuilt native libs into the bundle folder
- Sync packageJniLibs = project.tasks.create(
- "package${fullName.capitalize()}JniLibs",
- Sync)
-
- // Add dependencies on NDK tasks if NDK plugin is applied.
- if (isNdkTaskNeeded) {
- // Add NDK tasks
- SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_NDK_TASK) {
- createNdkTasks(variantData);
- packageJniLibs.dependsOn variantData.ndkCompileTask
- packageJniLibs.from(variantData.ndkCompileTask.soFolder).include("**/*.so")
- }
- }
-
- Sync packageRenderscript = SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_PACKAGING_TASK) {
- // package from 2 sources.
- packageJniLibs.from(variantConfig.jniLibsList).include("**/*.so")
- packageJniLibs.into(project.file(
- "$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}/jni"))
-
- // package the renderscript header files files into the bundle folder
- Sync packageRenderscript = project.tasks.create(
- "package${fullName.capitalize()}Renderscript",
- Sync)
- // package from 3 sources. the order is important to make sure the override works well.
- packageRenderscript.from(variantConfig.renderscriptSourceList).include("**/*.rsh")
- packageRenderscript.into(project.file(
- "$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}/$SdkConstants.FD_RENDERSCRIPT"))
- return packageRenderscript;
- }
-
- // merge consumer proguard files from different build types and flavors
- MergeFileTask mergeProGuardFileTask = SpanRecorders.record(
- ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_PROGUARD_FILE_TASK) {
- MergeFileTask mergeProGuardFileTask = project.tasks.create(
- "merge${fullName.capitalize()}ProguardFiles",
- MergeFileTask)
- mergeProGuardFileTask.inputFiles =
- project.files(variantConfig.getConsumerProguardFiles()).files
- mergeProGuardFileTask.outputFile = project.file(
- "$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}/$LibraryBundle.FN_PROGUARD_TXT")
- return mergeProGuardFileTask
- }
-
- // copy lint.jar into the bundle folder
- Copy lintCopy = project.tasks.create(
- "copy${fullName.capitalize()}Lint",
- Copy)
- lintCopy.dependsOn LINT_COMPILE
- lintCopy.from("$project.buildDir/${FD_INTERMEDIATES}/lint/lint.jar")
- lintCopy.into("$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/$dirName")
-
- Zip bundle = project.tasks.create(
- "bundle${fullName.capitalize()}",
- Zip)
-
- libVariantData.generateAnnotationsTask = variantData.variantDependency.annotationsPresent ? createExtractAnnotations(
- fullName, project, variantData) : null
- if (libVariantData.generateAnnotationsTask != null) {
- bundle.dependsOn(libVariantData.generateAnnotationsTask)
- }
-
- final boolean instrumented = variantConfig.buildType.isTestCoverageEnabled()
-
-
- // data holding dependencies and input for the dex. This gets updated as new
- // post-compilation steps are inserted between the compilation and dx.
- PostCompilationData pcData = new PostCompilationData()
- SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_POST_COMPILATION_TASK) {
- pcData.classGeneratingTask = [variantScope.javaCompileTask.name]
- pcData.libraryGeneratingTask = Collections.singletonList(
- variantData.variantDependency.packageConfiguration.buildDependencies)
- pcData.inputFiles = {
- return variantData.javaCompileTask.outputs.files.files
- }
- pcData.inputDir = {
- return variantScope.javaOutputDir
- }
- pcData.inputLibraries = {
- return Collections.emptyList()
- }
-
- // if needed, instrument the code
- if (instrumented) {
- pcData = createJacocoTask(tasks, variantScope, pcData);
- }
- }
-
- if (buildType.isMinifyEnabled()) {
- // run proguard on output of compile task
- SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_PROGUARD_TASK) {
- File outFile = maybeCreateProguardTasks(tasks, variantScope, pcData);
- pcData.inputFiles = { [outFile] }
- pcData.inputDir = null
- pcData.inputLibraries = { [] }
- }
- } else {
- // package the local jar in libs/
- SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_PACKAGE_LOCAL_JAR) {
- Sync packageLocalJar = project.tasks.create(
- "package${fullName.capitalize()}LocalJar",
- Sync)
- packageLocalJar.from(
- DependencyManager.getPackagedLocalJarFileList(
- variantData.variantDependency).toArray())
- packageLocalJar.into(project.file(
- "$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}/$LIBS_FOLDER"))
-
- // add the input libraries. This is only going to be the agent jar if applicable
- // due to how inputLibraries is initialized.
- // TODO: clean this.
- packageLocalJar.from(pcData.inputLibraries)
- TaskManager.optionalDependsOn(packageLocalJar, pcData.libraryGeneratingTask)
- pcData.libraryGeneratingTask = Collections.singletonList(packageLocalJar)
-
- // jar the classes.
- Jar jar = project.tasks.create("package${fullName.capitalize()}Jar", Jar);
- jar.dependsOn variantScope.processJavaResourcesTask.name
-
- // add the class files (whether they are instrumented or not.
- jar.from(pcData.inputDir)
- TaskManager.optionalDependsOn(jar, pcData.classGeneratingTask)
- pcData.classGeneratingTask = Collections.singletonList(jar)
-
- jar.from(variantScope.getJavaResourcesDestinationDir())
-
- jar.destinationDir = project.file(
- "$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}")
- jar.archiveName = "classes.jar"
-
- String packageName = variantConfig.getPackageFromManifest()
- if (packageName == null) {
- throw new BuildException("Failed to read manifest", null)
- }
- packageName = packageName.replace('.', '/');
-
- jar.exclude(packageName + "/R.class")
- jar.exclude(packageName + "/R\$*.class")
- if (!((LibraryExtension) extension).packageBuildConfig) {
- jar.exclude(packageName + "/Manifest.class")
- jar.exclude(packageName + "/Manifest\$*.class")
- jar.exclude(packageName + "/BuildConfig.class")
- }
-
- if (libVariantData.generateAnnotationsTask != null) {
- // In case extract annotations strips out private typedef annotation classes
- jar.dependsOn libVariantData.generateAnnotationsTask
- }
- }
- }
-
- bundle.dependsOn packageRes.name, packageRenderscript, lintCopy, packageJniLibs, mergeProGuardFileTask
- TaskManager.optionalDependsOn(bundle, pcData.classGeneratingTask)
- TaskManager.optionalDependsOn(bundle, pcData.libraryGeneratingTask)
-
- bundle.setDescription("Assembles a bundle containing the library in ${fullName.capitalize()}.");
- bundle.destinationDir = project.file("$project.buildDir/${FD_OUTPUTS}/aar")
- bundle.setArchiveName("${project.name}-${variantConfig.baseName}.${BuilderConstants.EXT_LIB_ARCHIVE}")
- bundle.extension = BuilderConstants.EXT_LIB_ARCHIVE
- bundle.from(project.file("$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}"))
- bundle.from(project.file("$project.buildDir/${FD_INTERMEDIATES}/$ANNOTATIONS/${dirName}"))
-
- // get the single output for now, though that may always be the case for a library.
- LibVariantOutputData variantOutputData = libVariantData.outputs.get(0)
- variantOutputData.packageLibTask = bundle
-
- variantData.assembleVariantTask.dependsOn bundle
- variantOutputData.assembleTask = variantData.assembleVariantTask
-
- if (extension.defaultPublishConfig.equals(fullName)) {
- VariantHelper.setupDefaultConfig(project,
- variantData.variantDependency.packageConfiguration)
-
- // add the artifact that will be published
- project.artifacts.add("default", bundle)
-
- getAssembleDefault().dependsOn variantData.assembleVariantTask
- }
-
- // also publish the artifact with its full config name
- if (extension.publishNonDefault) {
- project.artifacts.add(variantData.variantDependency.publishConfiguration.name, bundle)
- bundle.classifier = variantData.variantDependency.publishConfiguration.name
- }
-
-
- // configure the variant to be testable.
- variantConfig.output = new LibraryBundle(
- bundle.archivePath,
- project.file("$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}"),
- variantData.getName(),
- project.getPath()) {
-
- @Override
- @Nullable
- String getProjectVariant() {
- return variantData.getName()
- }
-
- @NonNull
- @Override
- List<LibraryDependency> getDependencies() {
- return variantConfig.directLibraries
- }
-
- @NonNull
- @Override
- List<? extends AndroidLibrary> getLibraryDependencies() {
- return variantConfig.directLibraries
- }
-
- @NonNull
- @Override
- List<ManifestDependency> getManifestDependencies() {
- return variantConfig.directLibraries
- }
-
- @Override
- @Nullable
- MavenCoordinates getRequestedCoordinates() {
- return null
- }
-
- @Override
- @Nullable
- MavenCoordinates getResolvedCoordinates() {
- return null
- }
-
- @Override
- @NonNull
- protected File getJarsRootFolder() {
- return getFolder();
- }
- };
-
- SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_LINT_TASK) {
- createLintTasks(tasks, variantScope);
- }
- }
-
- public ExtractAnnotations createExtractAnnotations(
- String fullName, Project project, BaseVariantData variantData) {
- GradleVariantConfiguration config = variantData.variantConfiguration
- String dirName = config.dirName
-
- ExtractAnnotations task = project.tasks.create(
- "extract${fullName.capitalize()}Annotations",
- ExtractAnnotations)
- task.description =
- "Extracts Android annotations for the ${fullName} variant into the archive file"
- task.group = BasePlugin.BUILD_GROUP
- task.variant = variantData
- task.destinationDir = project.file("$project.buildDir/${FD_INTERMEDIATES}/$ANNOTATIONS/${dirName}")
- task.output = new File(task.destinationDir, FN_ANNOTATIONS_ZIP)
- task.classDir = project.file("$project.buildDir/${FD_INTERMEDIATES}/classes/${variantData.variantConfiguration.dirName}")
- task.source = variantData.getJavaSources()
- task.encoding = extension.compileOptions.encoding
- task.sourceCompatibility = extension.compileOptions.sourceCompatibility
- conventionMapping(task).map("classpath") {
- project.files(androidBuilder.getCompileClasspath(config))
- }
- task.dependsOn variantData.javaCompileTask
-
- // Setup the boot classpath just before the task actually runs since this will
- // force the sdk to be parsed. (Same as in compileTask)
- task.doFirst {
- task.bootClasspath = androidBuilder.getBootClasspathAsStrings()
- }
-
- return task
- }
-
- private Task getAssembleDefault() {
- if (assembleDefault == null) {
- assembleDefault = project.tasks.findByName("assembleDefault");
- }
- return assembleDefault
- }
-
- private static ConventionMapping conventionMapping(Task task) {
- task.conventionMapping
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.java
new file mode 100644
index 0000000..2f2c7a3
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/LibraryTaskManager.java
@@ -0,0 +1,613 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal;
+
+import static com.android.SdkConstants.FN_ANNOTATIONS_ZIP;
+import static com.android.SdkConstants.LIBS_FOLDER;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.internal.core.GradleVariantConfiguration;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.internal.scope.AndroidTask;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.tasks.MergeFileTask;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.build.gradle.internal.variant.LibVariantOutputData;
+import com.android.build.gradle.internal.variant.LibraryVariantData;
+import com.android.build.gradle.internal.variant.VariantHelper;
+import com.android.build.gradle.tasks.ExtractAnnotations;
+import com.android.build.gradle.tasks.MergeResources;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.dependency.LibraryBundle;
+import com.android.builder.dependency.LibraryDependency;
+import com.android.builder.dependency.ManifestDependency;
+import com.android.builder.model.AndroidLibrary;
+import com.android.builder.model.MavenCoordinates;
+import com.android.builder.profile.ExecutionType;
+import com.android.builder.profile.Recorder;
+import com.android.builder.profile.ThreadRecorder;
+
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.file.ConfigurableFileCollection;
+import org.gradle.api.plugins.BasePlugin;
+import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.Sync;
+import org.gradle.api.tasks.bundling.Jar;
+import org.gradle.api.tasks.bundling.Zip;
+import org.gradle.api.tasks.compile.JavaCompile;
+import org.gradle.tooling.BuildException;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * TaskManager for creating tasks in an Android library project.
+ */
+public class LibraryTaskManager extends TaskManager {
+
+ private static final String ANNOTATIONS = "annotations";
+
+ private Task assembleDefault;
+
+ public LibraryTaskManager (
+ Project project,
+ AndroidBuilder androidBuilder,
+ AndroidConfig extension,
+ SdkHandler sdkHandler,
+ DependencyManager dependencyManager,
+ ToolingModelBuilderRegistry toolingRegistry) {
+ super(project, androidBuilder, extension, sdkHandler, dependencyManager, toolingRegistry);
+ }
+
+ @Override
+ public void createTasksForVariantData(
+ @NonNull final TaskFactory tasks,
+ @NonNull final BaseVariantData<? extends BaseVariantOutputData> variantData) {
+ final LibraryVariantData libVariantData = (LibraryVariantData) variantData;
+ final GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration();
+ CoreBuildType buildType = variantConfig.getBuildType();
+
+ final VariantScope variantScope = variantData.getScope();
+
+ final String dirName = variantConfig.getDirName();
+
+ createAnchorTasks(tasks, variantScope);
+
+ createCheckManifestTask(tasks, variantScope);
+
+ // Add a task to create the res values
+ ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_GENERATE_RES_VALUES_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ createGenerateResValuesTask(tasks, variantScope);
+ return null;
+ }
+ });
+
+ // Add a task to process the manifest(s)
+ ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_MANIFEST_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ createMergeLibManifestsTask(tasks, variantScope);
+ return null;
+ }
+ });
+
+ // Add a task to compile renderscript files.
+ ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_CREATE_RENDERSCRIPT_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ createRenderscriptTask(tasks, variantScope);
+ return null;
+ }
+ });
+
+ AndroidTask<MergeResources> packageRes = ThreadRecorder.get().record(
+ ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_RESOURCES_TASK,
+ new Recorder.Block<AndroidTask<MergeResources>>() {
+ @Override
+ public AndroidTask<MergeResources> call() throws Exception {
+ // Create a merge task to only merge the resources from this library and not
+ // the dependencies. This is what gets packaged in the aar.
+ AndroidTask<MergeResources> mergeResourceTask =
+ basicCreateMergeResourcesTask(
+ tasks,
+ variantScope,
+ "package",
+ new File(
+ variantScope.getGlobalScope().getIntermediatesDir(),
+ DIR_BUNDLES + "/" + variantScope
+ .getVariantConfiguration().getDirName() +
+ "/res"),
+ false /*includeDependencies*/,
+ false /*process9Patch*/);
+
+ if (variantData.getVariantDependency().hasNonOptionalLibraries()) {
+ // Add a task to merge the resource folders, including the libraries, in order to
+ // generate the R.txt file with all the symbols, including the ones from
+ // the dependencies.
+ createMergeResourcesTask(tasks, variantScope);
+ }
+
+ mergeResourceTask.configure(tasks,
+ new Action<Task>() {
+ @Override
+ public void execute(Task task) {
+ MergeResources mergeResourcesTask = (MergeResources) task;
+ mergeResourcesTask.setPublicFile(new File(
+ variantScope.getGlobalScope().getIntermediatesDir(),
+ DIR_BUNDLES + "/" + dirName + "/" +
+ SdkConstants.FN_PUBLIC_TXT));
+ }
+ });
+
+ return mergeResourceTask;
+ }
+ });
+
+ // Add a task to merge the assets folders
+ ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_ASSETS_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() {
+ createMergeAssetsTask(tasks, variantScope);
+ return null;
+ }
+ });
+
+ // Add a task to create the BuildConfig class
+ ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_BUILD_CONFIG_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ createBuildConfigTask(tasks, variantScope);
+ return null;
+ }
+ });
+
+ ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_PROCESS_RES_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ // Add a task to generate resource source files, directing the location
+ // of the r.txt file to be directly in the bundle.
+ createProcessResTask(tasks, variantScope,
+ new File(variantScope.getGlobalScope().getIntermediatesDir(),
+ DIR_BUNDLES + "/" + dirName),
+ false /*generateResourcePackage*/);
+
+ // process java resources
+ createProcessJavaResTasks(tasks, variantScope);
+ return null;
+ }
+ });
+
+ ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_AIDL_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ createAidlTask(tasks, variantScope);
+ return null;
+ }
+ });
+
+ // Add a compile task
+ ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_COMPILE_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ AndroidTask<JavaCompile> javacTask = createJavacTask(tasks, variantScope);
+ TaskManager.setJavaCompilerTask(javacTask, tasks, variantScope);
+ return null;
+ }
+ });
+
+ // package the prebuilt native libs into the bundle folder
+ final Sync packageJniLibs = project.getTasks().create(
+ variantScope.getTaskName("package", "JniLibs"),
+ Sync.class);
+
+ // Add dependencies on NDK tasks if NDK plugin is applied.
+ if (isNdkTaskNeeded) {
+ // Add NDK tasks
+ ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_NDK_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ createNdkTasks(variantScope);
+ packageJniLibs.dependsOn(variantData.ndkCompileTask);
+ packageJniLibs.from(variantData.ndkCompileTask.getSoFolder())
+ .include("**/*.so");
+ return null;
+ }
+ });
+ } else {
+ if (variantData.compileTask != null) {
+ variantData.compileTask.dependsOn(getNdkBuildable(variantData));
+ } else {
+ variantScope.getCompileTask().dependsOn(tasks, getNdkBuildable(variantData));
+ }
+ }
+
+ Sync packageRenderscript = ThreadRecorder.get().record(
+ ExecutionType.LIB_TASK_MANAGER_CREATE_PACKAGING_TASK,
+ new Recorder.Block<Sync>() {
+ @Override
+ public Sync call() throws Exception {
+ // package from 2 sources.
+ packageJniLibs.from(variantConfig.getJniLibsList())
+ .include("**/*.so");
+ packageJniLibs.into(new File(
+ variantScope.getGlobalScope().getIntermediatesDir(),
+ DIR_BUNDLES + "/" + dirName + "/jni"));
+
+ // package the renderscript header files files into the bundle folder
+ Sync packageRenderscript = project.getTasks().create(
+ variantScope.getTaskName("package", "Renderscript"), Sync.class);
+ // package from 3 sources. the order is important to make sure the override works well.
+ packageRenderscript.from(variantConfig.getRenderscriptSourceList())
+ .include("**/*.rsh");
+ packageRenderscript.into(new File(
+ variantScope.getGlobalScope().getIntermediatesDir(),
+ DIR_BUNDLES + "/" + dirName + "/" + SdkConstants.FD_RENDERSCRIPT));
+ return packageRenderscript;
+ }
+ });
+
+ // merge consumer proguard files from different build types and flavors
+ MergeFileTask mergeProGuardFileTask = ThreadRecorder.get().record(
+ ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_PROGUARD_FILE_TASK,
+ new Recorder.Block<MergeFileTask>() {
+ @Override
+ public MergeFileTask call() throws Exception {
+ MergeFileTask mergeProGuardFileTask = project.getTasks().create(
+ variantScope.getTaskName("merge", "ProguardFiles"),
+ MergeFileTask.class);
+ mergeProGuardFileTask.setVariantName(variantConfig.getFullName());
+ mergeProGuardFileTask.setInputFiles(
+ project.files(variantConfig.getConsumerProguardFiles())
+ .getFiles());
+ mergeProGuardFileTask.setOutputFile(new File(
+ variantScope.getGlobalScope().getIntermediatesDir(),
+ DIR_BUNDLES + "/" + dirName + "/" + LibraryBundle.FN_PROGUARD_TXT));
+ return mergeProGuardFileTask;
+ }
+
+ });
+
+ // copy lint.jar into the bundle folder
+ Copy lintCopy = project.getTasks().create(
+ variantScope.getTaskName("copy", "Lint"), Copy.class);
+ lintCopy.dependsOn(LINT_COMPILE);
+ lintCopy.from(new File(
+ variantScope.getGlobalScope().getIntermediatesDir(),
+ "lint/lint.jar"));
+ lintCopy.into(new File(
+ variantScope.getGlobalScope().getIntermediatesDir(),
+ DIR_BUNDLES + "/" + dirName));
+
+ final Zip bundle = project.getTasks().create(variantScope.getTaskName("bundle"), Zip.class);
+
+ if (variantData.getVariantDependency().isAnnotationsPresent()) {
+ libVariantData.generateAnnotationsTask =
+ createExtractAnnotations(project, variantData);
+ }
+ if (libVariantData.generateAnnotationsTask != null) {
+ bundle.dependsOn(libVariantData.generateAnnotationsTask);
+ }
+
+ final boolean instrumented = variantConfig.getBuildType().isTestCoverageEnabled();
+
+ // data holding dependencies and input for the dex. This gets updated as new
+ // post-compilation steps are inserted between the compilation and dx.
+ final PostCompilationData pcDataTemp = new PostCompilationData();
+
+ final PostCompilationData pcData = ThreadRecorder.get().record(
+ ExecutionType.LIB_TASK_MANAGER_CREATE_POST_COMPILATION_TASK,
+ new Recorder.Block<PostCompilationData>() {
+ @Override
+ public PostCompilationData call() throws Exception {
+ pcDataTemp.setClassGeneratingTasks(Collections.singletonList(
+ variantScope.getJavacTask().getName()));
+ pcDataTemp.setLibraryGeneratingTasks(Collections.singletonList(
+ variantData.getVariantDependency().getPackageConfiguration()
+ .getBuildDependencies()));
+ pcDataTemp.setInputFilesCallable(new Callable<List<File>>() {
+ @Override
+ public List<File> call() throws Exception {
+ return new ArrayList<File>(
+ variantData.javacTask.getOutputs().getFiles().getFiles());
+ }
+
+ });
+ pcDataTemp.setInputDir(variantScope.getJavaOutputDir());
+ pcDataTemp.setInputLibraries(Collections.<File>emptyList());
+
+ // if needed, instrument the code
+ if (instrumented) {
+ return createJacocoTask(tasks, variantScope, pcDataTemp);
+ }
+ return pcDataTemp;
+ }
+ });
+ checkState(pcData != null);
+
+ if (buildType.isMinifyEnabled()) {
+ // run proguard on output of compile task
+ ThreadRecorder.get().record(
+ ExecutionType.LIB_TASK_MANAGER_CREATE_PROGUARD_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ File outFile = maybeCreateProguardTasks(tasks, variantScope,
+ pcData);
+ checkNotNull(outFile);
+ pcData.setInputFiles(Collections.singletonList(outFile));
+ pcData.setInputDirCallable(null);
+ pcData.setInputLibraries(Collections.<File>emptyList());
+ return null;
+ }
+ });
+ } else {
+ // package the local jar in libs/
+ ThreadRecorder.get().record(
+ ExecutionType.LIB_TASK_MANAGER_CREATE_PACKAGE_LOCAL_JAR,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ Sync packageLocalJar = project.getTasks().create(
+ variantScope.getTaskName("package", "LocalJar"), Sync.class);
+ packageLocalJar.from(
+ DependencyManager
+ .getPackagedLocalJarFileList(
+ variantData.getVariantDependency())
+ .toArray());
+ packageLocalJar.into(new File(
+ variantScope.getGlobalScope().getIntermediatesDir(),
+ DIR_BUNDLES + "/" + dirName + "/" + LIBS_FOLDER));
+
+ // add the input libraries. This is only going to be the agent jar if applicable
+ // due to how inputLibraries is initialized.
+ // TODO: clean this.
+ packageLocalJar.from(pcData.getInputLibrariesCallable());
+ TaskManager.optionalDependsOn(
+ packageLocalJar,
+ pcData.getLibraryGeneratingTasks());
+ pcData.setLibraryGeneratingTasks(
+ Collections.singletonList(packageLocalJar));
+
+ // jar the classes.
+ Jar jar = project.getTasks().create(
+ variantScope.getTaskName("package", "Jar"), Jar.class);
+ jar.dependsOn(variantScope.getMergeJavaResourcesTask().getName());
+
+ // add the class files (whether they are instrumented or not.
+ jar.from(pcData.getInputDirCallable());
+ TaskManager.optionalDependsOn(jar, pcData.getClassGeneratingTasks());
+ pcData.setClassGeneratingTasks(Collections.singletonList(jar));
+
+ jar.from(variantScope.getJavaResourcesDestinationDir());
+
+ jar.setDestinationDir(new File(
+ variantScope.getGlobalScope().getIntermediatesDir(),
+ DIR_BUNDLES + "/" + dirName));
+ jar.setArchiveName("classes.jar");
+
+ String packageName = variantConfig.getPackageFromManifest();
+ if (packageName == null) {
+ throw new BuildException("Failed to read manifest", null);
+ }
+
+ packageName = packageName.replace(".", "/");
+
+ jar.exclude(packageName + "/R.class");
+ jar.exclude(packageName + "/R$*.class");
+ if (!getExtension().getPackageBuildConfig()) {
+ jar.exclude(packageName + "/Manifest.class");
+ jar.exclude(packageName + "/Manifest$*.class");
+ jar.exclude(packageName + "/BuildConfig.class");
+ }
+
+ if (libVariantData.generateAnnotationsTask != null) {
+ // In case extract annotations strips out private typedef annotation classes
+ jar.dependsOn(libVariantData.generateAnnotationsTask);
+ }
+ return null;
+ }
+ });
+ }
+
+ bundle.dependsOn(packageRes.getName(), packageRenderscript, lintCopy, packageJniLibs,
+ mergeProGuardFileTask);
+ TaskManager.optionalDependsOn(bundle, pcData.getClassGeneratingTasks());
+ TaskManager.optionalDependsOn(bundle, pcData.getLibraryGeneratingTasks());
+
+ bundle.setDescription("Assembles a bundle containing the library in " +
+ variantConfig.getFullName() + ".");
+ bundle.setDestinationDir(new File(variantScope.getGlobalScope().getOutputsDir(), "aar"));
+ bundle.setArchiveName(project.getName() + "-" + variantConfig.getBaseName() + "."
+ + BuilderConstants.EXT_LIB_ARCHIVE);
+ bundle.setExtension(BuilderConstants.EXT_LIB_ARCHIVE);
+ bundle.from(new File(
+ variantScope.getGlobalScope().getIntermediatesDir(),
+ DIR_BUNDLES + "/" + dirName));
+ bundle.from(new File(
+ variantScope.getGlobalScope().getIntermediatesDir(),
+ ANNOTATIONS + "/" + dirName));
+
+ // get the single output for now, though that may always be the case for a library.
+ LibVariantOutputData variantOutputData = libVariantData.getOutputs().get(0);
+ variantOutputData.packageLibTask = bundle;
+
+ variantData.assembleVariantTask.dependsOn(bundle);
+ variantOutputData.assembleTask = variantData.assembleVariantTask;
+
+ if (getExtension().getDefaultPublishConfig().equals(variantConfig.getFullName())) {
+ VariantHelper.setupDefaultConfig(project,
+ variantData.getVariantDependency().getPackageConfiguration());
+
+ // add the artifact that will be published
+ project.getArtifacts().add("default", bundle);
+
+ getAssembleDefault().dependsOn(variantData.assembleVariantTask);
+ }
+
+ // also publish the artifact with its full config name
+ if (getExtension().getPublishNonDefault()) {
+ project.getArtifacts().add(
+ variantData.getVariantDependency().getPublishConfiguration().getName(), bundle);
+ bundle.setClassifier(
+ variantData.getVariantDependency().getPublishConfiguration().getName());
+ }
+
+ // configure the variant to be testable.
+ variantConfig.setOutput(new LibraryBundle(
+ bundle.getArchivePath(),
+ new File(variantScope.getGlobalScope().getIntermediatesDir(),
+ DIR_BUNDLES + "/" + dirName),
+ variantData.getName(),
+ project.getPath()) {
+ @Override
+ @Nullable
+ public String getProjectVariant() {
+ return variantData.getName();
+ }
+
+ @NonNull
+ @Override
+ public List<LibraryDependency> getDependencies() {
+ return variantConfig.getDirectLibraries();
+ }
+
+ @NonNull
+ @Override
+ public List<? extends AndroidLibrary> getLibraryDependencies() {
+ return variantConfig.getDirectLibraries();
+ }
+
+ @NonNull
+ @Override
+ public List<? extends ManifestDependency> getManifestDependencies() {
+ return variantConfig.getDirectLibraries();
+ }
+
+ @Override
+ @Nullable
+ public MavenCoordinates getRequestedCoordinates() {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ public MavenCoordinates getResolvedCoordinates() {
+ return null;
+ }
+
+ @Override
+ @NonNull
+ protected File getJarsRootFolder() {
+ return getFolder();
+ }
+
+ @Override
+ public boolean isOptional() {
+ return false;
+ }
+
+ });
+
+ ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_LINT_TASK,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ createLintTasks(tasks, variantScope);
+ return null;
+ }
+ });
+ }
+
+ public ExtractAnnotations createExtractAnnotations(
+ final Project project,
+ final BaseVariantData variantData) {
+ final GradleVariantConfiguration config = variantData.getVariantConfiguration();
+
+ final ExtractAnnotations task = project.getTasks().create(
+ variantData.getScope().getTaskName("extract", "Annotations"),
+ ExtractAnnotations.class);
+ task.setDescription(
+ "Extracts Android annotations for the " + variantData.getVariantConfiguration()
+ .getFullName()
+ + " variant into the archive file");
+ task.setGroup(BasePlugin.BUILD_GROUP);
+ task.variant = variantData;
+ task.setDestinationDir(new File(
+ variantData.getScope().getGlobalScope().getIntermediatesDir(),
+ ANNOTATIONS + "/" + config.getDirName()));
+ task.output = new File(task.getDestinationDir(), FN_ANNOTATIONS_ZIP);
+ task.classDir = new File(variantData.getScope().getGlobalScope().getIntermediatesDir(),
+ "classes/" + variantData.getVariantConfiguration().getDirName());
+ task.setSource(variantData.getJavaSources());
+ task.encoding = getExtension().getCompileOptions().getEncoding();
+ task.setSourceCompatibility(
+ getExtension().getCompileOptions().getSourceCompatibility().toString());
+ ConventionMappingHelper.map(task, "classpath", new Callable<ConfigurableFileCollection>() {
+ @Override
+ public ConfigurableFileCollection call() throws Exception {
+ return project.files(androidBuilder.getCompileClasspath(config));
+ }
+ });
+ task.dependsOn(variantData.getScope().getJavacTask().getName());
+
+ // Setup the boot classpath just before the task actually runs since this will
+ // force the sdk to be parsed. (Same as in compileTask)
+ task.doFirst(new Action<Task>() {
+ @Override
+ public void execute(Task task) {
+ if (task instanceof ExtractAnnotations) {
+ ExtractAnnotations extractAnnotations = (ExtractAnnotations) task;
+ extractAnnotations.bootClasspath = androidBuilder.getBootClasspathAsStrings();
+ }
+ }
+ });
+
+ return task;
+ }
+
+ private Task getAssembleDefault() {
+ if (assembleDefault == null) {
+ assembleDefault = project.getTasks().findByName("assembleDefault");
+ }
+ return assembleDefault;
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/NdkHandler.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/NdkHandler.java
new file mode 100644
index 0000000..dca6ee0
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/NdkHandler.java
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal;
+
+import static com.android.SdkConstants.FN_LOCAL_PROPERTIES;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.core.Toolchain;
+import com.android.sdklib.AndroidTargetHash;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.repository.PreciseRevision;
+import com.android.utils.Pair;
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.io.Closeables;
+
+import org.gradle.api.InvalidUserDataException;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Handles NDK related information.
+ */
+public class NdkHandler {
+
+ @Nullable
+ private String compileSdkVersion;
+ private boolean resolvedSdkVersion;
+ private final Toolchain toolchain;
+ private final String toolchainVersion;
+ private final File ndkDirectory;
+
+ private Map<Pair<Toolchain, Abi>, PreciseRevision> defaultToolchainVersions = Maps.newHashMap();
+
+
+ public NdkHandler(
+ @NonNull File projectDir,
+ @Nullable String compileSdkVersion,
+ @NonNull String toolchainName,
+ @NonNull String toolchainVersion) {
+ if (compileSdkVersion != null) {
+ setCompileSdkVersion(compileSdkVersion);
+ }
+ this.toolchain = Toolchain.getByName(toolchainName);
+ this.toolchainVersion = toolchainVersion;
+ ndkDirectory = findNdkDirectory(projectDir);
+ }
+
+ @Nullable
+ public String getCompileSdkVersion() {
+ if (!resolvedSdkVersion) {
+ resolveCompileSdkVersion();
+ }
+ return compileSdkVersion;
+ }
+
+ /**
+ * Retrieve the newest supported version if it is not the specified version is not supported.
+ *
+ * An older NDK may not support the specified compiledSdkVersion. In that case, determine what
+ * is the newest supported version and modify compileSdkVersion.
+ */
+ private void resolveCompileSdkVersion() {
+ if (compileSdkVersion == null) {
+ return;
+ }
+ File platformFolder = new File(ndkDirectory, "/platforms/" + compileSdkVersion);
+ if (!platformFolder.exists()) {
+ int targetVersion;
+ try {
+ targetVersion = Integer.parseInt(compileSdkVersion.substring("android-".length()));
+ } catch (NumberFormatException ignore) {
+ // If the targetVerison is not a number, most likely it is a preview version.
+ // In that case, assume we are using the highest available version.
+ targetVersion = Integer.MAX_VALUE;
+ }
+
+ File[] platformFolders = new File(ndkDirectory, "/platforms/").listFiles(
+ new FileFilter() {
+ @Override
+ public boolean accept(File file) {
+ return file.isDirectory();
+ }
+ });
+ int highestVersion = 0;
+ for(File platform :platformFolders) {
+ if (platform.getName().startsWith("android-")) {
+ try {
+ int version = Integer.parseInt(
+ platform.getName().substring("android-".length()));
+ if (version > highestVersion && version < targetVersion) {
+ highestVersion = version;
+ compileSdkVersion = "android-" + version;
+ }
+ } catch(NumberFormatException ignore) {
+ }
+ }
+ }
+ }
+ resolvedSdkVersion = true;
+ }
+
+ public void setCompileSdkVersion(@NonNull String compileSdkVersion) {
+ // Ensure compileSdkVersion is in platform hash string format (e.g. "android-21").
+ AndroidVersion androidVersion = AndroidTargetHash.getVersionFromHash(compileSdkVersion);
+ if (androidVersion == null) {
+ this.compileSdkVersion = null;
+ } else {
+ this.compileSdkVersion = AndroidTargetHash.getPlatformHashString(androidVersion);
+ }
+ resolvedSdkVersion = false;
+ }
+
+ public Toolchain getToolchain() {
+ return toolchain;
+ }
+
+ public String getToolchainVersion() {
+ return toolchainVersion;
+ }
+
+ /**
+ * Determine the location of the NDK directory.
+ *
+ * The NDK directory can be set in the local.properties file or using the ANDROID_NDK_HOME
+ * environment variable.
+ */
+ private static File findNdkDirectory(File projectDir) {
+ File localProperties = new File(projectDir, FN_LOCAL_PROPERTIES);
+
+ if (localProperties.isFile()) {
+
+ Properties properties = new Properties();
+ InputStreamReader reader = null;
+ try {
+ //noinspection IOResourceOpenedButNotSafelyClosed
+ FileInputStream fis = new FileInputStream(localProperties);
+ reader = new InputStreamReader(fis, Charsets.UTF_8);
+ properties.load(reader);
+ } catch (FileNotFoundException ignored) {
+ // ignore since we check up front and we don't want to fail on it anyway
+ // in case there's an env var.
+ } catch (IOException e) {
+ throw new RuntimeException(String.format("Unable to read %1$s.", localProperties), e);
+ } finally {
+ try {
+ Closeables.close(reader, true /* swallowIOException */);
+ } catch (IOException e) {
+ // ignore.
+ }
+ }
+
+ String ndkDirProp = properties.getProperty("ndk.dir");
+ if (ndkDirProp != null) {
+ return new File(ndkDirProp);
+ }
+
+ } else {
+ String envVar = System.getenv("ANDROID_NDK_HOME");
+ if (envVar != null) {
+ return new File(envVar);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the directory of the NDK.
+ */
+ @Nullable
+ public File getNdkDirectory() {
+ return ndkDirectory;
+ }
+
+ /**
+ * Return true if NDK directory is configured.
+ */
+ public boolean isNdkDirConfigured() {
+ return ndkDirectory != null;
+ }
+
+ private static String getToolchainPrefix(Toolchain toolchain, Abi abi) {
+ if (toolchain == Toolchain.GCC) {
+ return abi.getGccToolchainPrefix();
+ } else {
+ return "llvm";
+ }
+ }
+
+ /**
+ * Return the directory containing the toolchain.
+ *
+ * @param toolchain toolchain to use.
+ * @param toolchainVersion toolchain version to use.
+ * @param abi target ABI of the toolchaina
+ * @return a directory that contains the executables.
+ */
+ private File getToolchainPath(
+ Toolchain toolchain,
+ String toolchainVersion,
+ Abi abi) {
+ String version = toolchainVersion.isEmpty()
+ ? getDefaultToolchainVersion(toolchain, abi).toString()
+ : toolchainVersion;
+
+ File prebuiltFolder = new File(
+ ndkDirectory,
+ "toolchains/" + getToolchainPrefix(toolchain, abi) + "-" + version + "/prebuilt");
+
+ String osName = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
+ String hostOs;
+ if (osName.contains("windows")) {
+ hostOs = "windows";
+ } else if (osName.contains("mac")) {
+ hostOs = "darwin";
+ } else {
+ hostOs = "linux";
+ }
+
+ // There should only be one directory in the prebuilt folder. If there are more than one
+ // attempt to determine the right one based on the operating system.
+ File[] toolchainPaths = prebuiltFolder.listFiles(
+ new FileFilter() {
+ @Override
+ public boolean accept(File file) {
+ return file.isDirectory();
+ }
+ });
+
+ if (toolchainPaths == null) {
+ throw new InvalidUserDataException("Unable to find toolchain: " + prebuiltFolder);
+ }
+ if (toolchainPaths.length == 1) {
+ return toolchainPaths[0];
+ }
+
+ // Use 64-bit toolchain if available.
+ File toolchainPath = new File(prebuiltFolder, hostOs + "-x86_64");
+ if (toolchainPath.isDirectory()) {
+ return toolchainPath;
+ }
+
+ // Fallback to 32-bit if we can't find the 64-bit toolchain.
+ String osString = (osName.equals("windows")) ? hostOs : hostOs + "-x86";
+ toolchainPath = new File(prebuiltFolder, osString);
+ if (toolchainPath.isDirectory()) {
+ return toolchainPath;
+ } else {
+ throw new InvalidUserDataException("Unable to find toolchain prebuilt folder in: "
+ + prebuiltFolder);
+ }
+ }
+
+ /**
+ * Returns the sysroot directory for the toolchain.
+ */
+ public String getSysroot(Abi abi) {
+ if (getCompileSdkVersion() == null) {
+ return "";
+ } else {
+ return ndkDirectory + "/platforms/" + getCompileSdkVersion() + "/arch-"
+ + abi.getArchitecture();
+ }
+ }
+
+ /**
+ * Return the directory containing prebuilt binaries such as gdbserver.
+ */
+ public File getPrebuiltDirectory(Abi abi) {
+ return new File(ndkDirectory, "prebuilt/android-" + abi.getArchitecture());
+ }
+
+ /**
+ * Return true if compiledSdkVersion supports 64 bits ABI.
+ */
+ public boolean supports64Bits() {
+ if (getCompileSdkVersion() == null) {
+ return false;
+ }
+ String targetString = getCompileSdkVersion().replace("android-", "");
+ try {
+ return Integer.parseInt(targetString) >= 20;
+ } catch (NumberFormatException ignored) {
+ // "android-L" supports 64-bits.
+ return true;
+ }
+ }
+
+ /**
+ * Return the default version of the specified toolchain for a target abi.
+ *
+ * The default version is the highest version found in the NDK for the specified toolchain and
+ * ABI. The result is cached for performance.
+ */
+ private PreciseRevision getDefaultToolchainVersion(Toolchain toolchain, final Abi abi) {
+ PreciseRevision defaultVersion = defaultToolchainVersions.get(Pair.of(toolchain, abi));
+ if (defaultVersion != null) {
+ return defaultVersion;
+ }
+
+ final String toolchainPrefix = getToolchainPrefix(toolchain, abi);
+ File toolchains = new File(ndkDirectory, "toolchains");
+ File[] toolchainsForAbi = toolchains.listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.startsWith(toolchainPrefix);
+ }
+ });
+ if (toolchainsForAbi == null || toolchainsForAbi.length == 0) {
+ throw new RuntimeException(
+ "No toolchains found in the NDK toolchains folder for ABI with prefix: "
+ + toolchainPrefix);
+ }
+
+ // Once we have a list of toolchains, we look the highest version
+ PreciseRevision bestRevision = null;
+ for (File toolchainFolder : toolchainsForAbi) {
+ String folderName = toolchainFolder.getName();
+ String version = folderName.substring(toolchainPrefix.length() + 1);
+ try {
+ PreciseRevision revision = PreciseRevision.parseRevision(version);
+ if (bestRevision == null || revision.compareTo(bestRevision) > 0) {
+ bestRevision = revision;
+ }
+ } catch (NumberFormatException ignore) {
+ }
+ }
+ defaultToolchainVersions.put(Pair.of(toolchain, abi), bestRevision);
+ if (bestRevision == null) {
+ throw new RuntimeException("Unable to find a valid toolchain in " + toolchains);
+ }
+ return bestRevision;
+ }
+
+ /**
+ * Return the version of gcc that will be used by the NDK.
+ *
+ * Gcc is used by clang for linking. It also contains gnu-libstdc++.
+ *
+ * If the gcc toolchain is used, then it's simply the toolchain version requested by the user.
+ * If clang is used, then it depends the target abi.
+ */
+ public String getGccToolchainVersion(Abi abi) {
+ return (toolchain == Toolchain.GCC && !toolchainVersion.isEmpty())
+ ? toolchainVersion
+ : getDefaultToolchainVersion(Toolchain.GCC, abi).toString();
+ }
+
+ /**
+ * Return the folder containing gcc that will be used by the NDK.
+ */
+ public File getDefaultGccToolchainPath(Abi abi) {
+ return getToolchainPath(Toolchain.GCC, getGccToolchainVersion(abi), abi);
+ }
+
+ /**
+ * Returns a list of all ABI.
+ */
+ public static Collection<Abi> getAbiList() {
+ return ImmutableList.copyOf(Abi.values());
+ }
+
+ /**
+ * Returns a list of 32-bits ABI.
+ */
+ public static Collection<Abi> getAbiList32() {
+ ImmutableList.Builder<Abi> builder = ImmutableList.builder();
+ for (Abi abi : Abi.values()) {
+ if (!abi.supports64Bits()) {
+ builder.add(abi);
+ }
+ }
+ return builder.build();
+ }
+
+ /**
+ * Returns a list of supported ABI.
+ */
+ public Collection<Abi> getSupportedAbis() {
+ return supports64Bits() ? getAbiList() : getAbiList32();
+ }
+
+ /**
+ * Return the executable for compiling C code.
+ */
+ public File getCCompiler(Abi abi) {
+ String compiler = toolchain == Toolchain.CLANG ? "clang" : abi.getGccExecutablePrefix() + "-gcc";
+ return new File(getToolchainPath(toolchain, toolchainVersion, abi), "bin/" + compiler);
+ }
+
+ /**
+ * Return the executable for compiling C++ code.
+ */
+ public File getCppCompiler(Abi abi) {
+ String compiler = toolchain == Toolchain.CLANG ? "clang++" : abi.getGccExecutablePrefix() + "-g++";
+ return new File(getToolchainPath(toolchain, toolchainVersion, abi), "bin/" + compiler);
+ }
+
+ /**
+ * Return the executable for removing debug symbols from a shared object.
+ */
+ public File getStripCommand(Abi abi) {
+ String strip = toolchain == Toolchain.CLANG ? "ndk-strip" : abi.getGccExecutablePrefix() + "-strip";
+ return new File(getToolchainPath(toolchain, toolchainVersion, abi), "bin/" + strip);
+ }
+
+ /**
+ * Return a list of include directories for an STl.
+ */
+ public List<File> getStlIncludes(@Nullable String stlName, @NonNull Abi abi) {
+ File stlBaseDir = new File(ndkDirectory, "sources/cxx-stl/");
+ if (stlName == null || stlName.isEmpty()) {
+ stlName = "system";
+ } else if (stlName.contains("_")) {
+ stlName = stlName.substring(0, stlName.indexOf('_'));
+ }
+
+ List<File> includeDirs = Lists.newArrayList();
+ if (stlName.equals("system")) {
+ includeDirs.add(new File(stlBaseDir, "system/include"));
+ } else if (stlName.equals("stlport")) {
+ includeDirs.add(new File(stlBaseDir, "stlport/stlport"));
+ includeDirs.add(new File(stlBaseDir, "gabi++/include"));
+ } else if (stlName.equals("gnustl")) {
+ String gccToolchainVersion = getGccToolchainVersion(abi);
+ includeDirs.add(new File(stlBaseDir, "gnu-libstdc++/" + gccToolchainVersion + "/include"));
+ includeDirs.add(new File(stlBaseDir, "gnu-libstdc++/" + gccToolchainVersion +
+ "/libs/" + abi.getName() + "/include"));
+ includeDirs.add(new File(stlBaseDir, "gnu-libstdc++/" + gccToolchainVersion +
+ "/include/backward"));
+ } else if (stlName.equals("gabi++")) {
+ includeDirs.add(new File(stlBaseDir, "gabi++/include"));
+ } else if (stlName.equals("c++")) {
+ includeDirs.add(new File(stlBaseDir, "llvm-libc++/libcxx/include"));
+ includeDirs.add(new File(stlBaseDir, "gabi++/include"));
+ includeDirs.add(new File(stlBaseDir, "../android/support/include"));
+ }
+
+ return includeDirs;
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/PostCompilationData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/PostCompilationData.java
new file mode 100644
index 0000000..e914086
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/PostCompilationData.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.Callables;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Class to hold data to setup the many optional post-compilation steps.
+ */
+public class PostCompilationData {
+
+ @Nullable
+ private List<?> classGeneratingTasks;
+
+ @Nullable
+ private List<?> libraryGeneratingTasks;
+
+ @Nullable
+ private Callable<List<File>> inputFiles;
+
+ @Nullable
+ private Callable<File> inputDir;
+
+ @Nullable
+ private Callable<File> javaResourcesInputDir;
+
+ @Nullable
+ private Callable<List<File>> inputLibraries;
+
+ @NonNull
+ public List<?> getClassGeneratingTasks() {
+ Preconditions.checkState(classGeneratingTasks != null);
+ return classGeneratingTasks;
+ }
+
+ public void setClassGeneratingTasks(@NonNull List<?> classGeneratingTasks) {
+ this.classGeneratingTasks = classGeneratingTasks;
+ }
+
+ @NonNull
+ public List<?> getLibraryGeneratingTasks() {
+ Preconditions.checkState(libraryGeneratingTasks != null);
+ return libraryGeneratingTasks;
+ }
+
+ public void setLibraryGeneratingTasks(@NonNull List<?> libraryGeneratingTasks) {
+ this.libraryGeneratingTasks = libraryGeneratingTasks;
+ }
+
+ @Nullable
+ public Callable<List<File>> getInputFilesCallable() {
+ return inputFiles;
+ }
+
+ public void setInputFiles(@Nullable List<File> inputFiles) {
+ this.inputFiles = Callables.returning(inputFiles);
+ }
+
+ public void setInputFilesCallable(@Nullable Callable<List<File>> inputFiles) {
+ this.inputFiles = inputFiles;
+ }
+
+ @Nullable
+ public Callable<File> getInputDirCallable() {
+ return inputDir;
+ }
+
+ public void setInputDir(@NonNull File inputDir) {
+ this.inputDir = Callables.returning(inputDir);
+ }
+
+ public void setInputDirCallable(@Nullable Callable<File> inputDir) {
+ this.inputDir = inputDir;
+ }
+
+ @Nullable
+ public Callable<File> getJavaResourcesInputDirCallable() {
+ return javaResourcesInputDir;
+ }
+
+ public void setJavaResourcesInputDir(@NonNull File javaResourcesInputDir) {
+ this.javaResourcesInputDir = Callables.returning(javaResourcesInputDir);
+ }
+
+ public void setJavaResourcesInputDirCallable(@Nullable Callable<File> javaResourcesInputDir) {
+ this.javaResourcesInputDir = javaResourcesInputDir;
+ }
+
+ @Nullable
+ public Callable<List<File>> getInputLibrariesCallable() {
+ return inputLibraries;
+ }
+
+ public void setInputLibraries(@NonNull List<File> inputLibraries) {
+ this.inputLibraries = Callables.returning(inputLibraries);
+ }
+
+ public void setInputLibrariesCallable(@Nullable Callable<List<File>> inputLibraries) {
+ this.inputLibraries = inputLibraries;
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorCombo.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorCombo.java
index 1aa077a..561612f 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorCombo.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorCombo.java
@@ -18,34 +18,38 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.build.gradle.api.GroupableProductFlavor;
+import com.android.builder.model.DimensionAware;
import com.android.builder.model.ProductFlavor;
import com.android.utils.StringHelper;
+import com.google.common.base.Predicates;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
+import org.gradle.api.Named;
+
import java.util.List;
/**
* A combination of product flavors for a variant, each belonging to a different flavor dimension.
*/
-public class ProductFlavorCombo {
+public class ProductFlavorCombo<T extends DimensionAware & Named> {
private String name;
@NonNull
- private final List<GroupableProductFlavor> flavorList;
+ private final List<T> flavorList;
/**
* Create a ProductFlavorCombo.
* @param flavors Lists of ProductFlavor.
*/
- public ProductFlavorCombo(@NonNull GroupableProductFlavor... flavors) {
+ public ProductFlavorCombo(@NonNull T... flavors) {
flavorList = ImmutableList.copyOf(flavors);
}
- public ProductFlavorCombo(@NonNull Iterable<GroupableProductFlavor> flavors) {
+ public ProductFlavorCombo(@NonNull Iterable<T> flavors) {
flavorList = ImmutableList.copyOf(flavors);
}
@@ -54,7 +58,7 @@
if (name == null) {
boolean first = true;
StringBuilder sb = new StringBuilder();
- for (ProductFlavor flavor : flavorList) {
+ for (T flavor : flavorList) {
if (first) {
sb.append(flavor.getName());
first = false;
@@ -68,7 +72,7 @@
}
@NonNull
- public List<GroupableProductFlavor> getFlavorList() {
+ public List<T> getFlavorList() {
return flavorList;
}
@@ -79,20 +83,20 @@
* @return A list of ProductFlavorCombo representing all combinations of ProductFlavors.
*/
@NonNull
- public static List<ProductFlavorCombo> createCombinations(
+ public static <S extends DimensionAware & Named> List<ProductFlavorCombo<S>> createCombinations(
@Nullable List<String> flavorDimensions,
- @NonNull Iterable<? extends GroupableProductFlavor> productFlavors) {
+ @NonNull Iterable<S> productFlavors) {
- List <ProductFlavorCombo> result = Lists.newArrayList();
+ List <ProductFlavorCombo<S>> result = Lists.newArrayList();
if (flavorDimensions == null || flavorDimensions.isEmpty()) {
- for (GroupableProductFlavor flavor : productFlavors) {
- result.add(new ProductFlavorCombo(ImmutableList.of(flavor)));
+ for (S flavor : productFlavors) {
+ result.add(new ProductFlavorCombo<S>(ImmutableList.of(flavor)));
}
} else {
// need to group the flavor per dimension.
// First a map of dimension -> list(ProductFlavor)
- ArrayListMultimap<String, GroupableProductFlavor> map = ArrayListMultimap.create();
- for (GroupableProductFlavor flavor : productFlavors) {
+ ArrayListMultimap<String, S> map = ArrayListMultimap.create();
+ for (S flavor : productFlavors) {
String flavorDimension = flavor.getDimension();
if (flavorDimension == null) {
@@ -109,7 +113,7 @@
}
createProductFlavorCombinations(result,
- new GroupableProductFlavor[flavorDimensions.size()],
+ Lists.<S>newArrayListWithCapacity(flavorDimensions.size()),
0, flavorDimensions, map);
}
return result;
@@ -118,10 +122,9 @@
/**
* Remove all null reference from an array and create an ImmutableList it.
*/
- private static ImmutableList<GroupableProductFlavor> filterNullFromArray(
- GroupableProductFlavor[] flavors) {
- ImmutableList.Builder<GroupableProductFlavor> builder = ImmutableList.builder();
- for (GroupableProductFlavor flavor : flavors) {
+ private static ImmutableList<ProductFlavor> filterNullFromArray(ProductFlavor[] flavors) {
+ ImmutableList.Builder<ProductFlavor> builder = ImmutableList.builder();
+ for (ProductFlavor flavor : flavors) {
if (flavor != null) {
builder.add(flavor);
}
@@ -129,14 +132,14 @@
return builder.build();
}
- private static void createProductFlavorCombinations(
- List<ProductFlavorCombo> flavorGroups,
- GroupableProductFlavor[] group,
+ private static <S extends DimensionAware & Named> void createProductFlavorCombinations(
+ List<ProductFlavorCombo<S>> flavorGroups,
+ List<S> group,
int index,
List<String> flavorDimensionList,
- ListMultimap<String, GroupableProductFlavor> map) {
+ ListMultimap<String, S> map) {
if (index == flavorDimensionList.size()) {
- flavorGroups.add(new ProductFlavorCombo(filterNullFromArray(group)));
+ flavorGroups.add(new ProductFlavorCombo<S>(Iterables.filter(group, Predicates.notNull())));
return;
}
@@ -145,7 +148,7 @@
String dimension = flavorDimensionList.get(index);
// from our map, get all the possible flavors in that dimension.
- List<GroupableProductFlavor> flavorList = map.get(dimension);
+ List<S> flavorList = map.get(dimension);
// loop on all the flavors to add them to the current index and recursively fill the next
// indices.
@@ -153,10 +156,11 @@
throw new RuntimeException(String.format(
"No flavor is associated with flavor dimension '%1$s'.", dimension));
} else {
- for (GroupableProductFlavor flavor : flavorList) {
- group[index] = flavor;
+ for (S flavor : flavorList) {
+ group.add(flavor);
createProductFlavorCombinations(
flavorGroups, group, index + 1, flavorDimensionList, map);
+ group.remove(group.size() - 1);
}
}
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorData.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorData.groovy
deleted file mode 100644
index e8ad523..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorData.groovy
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.internal
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.gradle.internal.api.DefaultAndroidSourceSet
-import com.android.builder.core.BuilderConstants
-import com.android.builder.core.DefaultProductFlavor
-import groovy.transform.CompileStatic
-import org.gradle.api.Project
-import org.gradle.api.Task
-/**
- * Class containing a ProductFlavor and associated data (sourcesets)
- */
-@CompileStatic
-public class ProductFlavorData<T extends DefaultProductFlavor> extends VariantDimensionData {
- final T productFlavor
- final Task assembleTask
-
- ProductFlavorData(
- @NonNull T productFlavor,
- @NonNull DefaultAndroidSourceSet sourceSet,
- @Nullable DefaultAndroidSourceSet androidTestSourceSet,
- @Nullable DefaultAndroidSourceSet unitTestSourceSet,
- @NonNull Project project) {
- super(sourceSet, androidTestSourceSet, unitTestSourceSet, project)
-
- this.productFlavor = productFlavor
-
- if (!BuilderConstants.MAIN.equals(sourceSet.name)) {
- assembleTask = project.tasks.create("assemble${sourceSet.name.capitalize()}")
- assembleTask.description = "Assembles all ${sourceSet.name.capitalize()} builds."
- assembleTask.setGroup("Build")
- } else {
- assembleTask = null
- }
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorData.java
new file mode 100644
index 0000000..a7302bf
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/ProductFlavorData.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle.internal;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.api.DefaultAndroidSourceSet;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
+import com.android.builder.core.BuilderConstants;
+import com.android.utils.StringHelper;
+
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.plugins.BasePlugin;
+
+/**
+ * Class containing a ProductFlavor and associated data (sourcesets)
+ */
+public class ProductFlavorData<T extends CoreProductFlavor> extends VariantDimensionData {
+ private final T productFlavor;
+ private final Task assembleTask;
+
+ ProductFlavorData(
+ @NonNull T productFlavor,
+ @NonNull DefaultAndroidSourceSet sourceSet,
+ @Nullable DefaultAndroidSourceSet androidTestSourceSet,
+ @Nullable DefaultAndroidSourceSet unitTestSourceSet,
+ @NonNull Project project) {
+ super(sourceSet, androidTestSourceSet, unitTestSourceSet, project);
+
+ this.productFlavor = productFlavor;
+
+ if (!BuilderConstants.MAIN.equals(sourceSet.getName())) {
+ String sourceSetName = StringHelper.capitalize(sourceSet.getName());
+ assembleTask = project.getTasks().create("assemble" + sourceSetName);
+ assembleTask.setDescription("Assembles all " + sourceSetName + " builds.");
+ assembleTask.setGroup(BasePlugin.BUILD_GROUP);
+ } else {
+ assembleTask = null;
+ }
+ }
+
+ public T getProductFlavor() {
+ return productFlavor;
+ }
+
+ public Task getAssembleTask() {
+ return assembleTask;
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/SdkHandler.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/SdkHandler.java
index a50fb0b..f106613 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/SdkHandler.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/SdkHandler.java
@@ -21,6 +21,7 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.LibraryRequest;
import com.android.builder.sdk.DefaultSdkLoader;
import com.android.builder.sdk.PlatformLoader;
import com.android.builder.sdk.SdkInfo;
@@ -29,6 +30,7 @@
import com.android.sdklib.repository.FullRevision;
import com.android.utils.ILogger;
import com.google.common.base.Charsets;
+import com.google.common.base.Preconditions;
import com.google.common.io.Closeables;
import org.gradle.api.Project;
@@ -38,6 +40,7 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.util.Collection;
import java.util.Properties;
/**
@@ -73,23 +76,19 @@
}
public void initTarget(
- String targetHash,
- FullRevision buildToolRevision,
+ @NonNull String targetHash,
+ @NonNull FullRevision buildToolRevision,
+ @NonNull Collection<LibraryRequest> usedLibraries,
@NonNull AndroidBuilder androidBuilder) {
- if (targetHash == null) {
- throw new IllegalArgumentException("android.compileSdkVersion is missing!");
- }
-
- if (buildToolRevision == null) {
- throw new IllegalArgumentException("android.buildToolsVersion is missing!");
- }
+ Preconditions.checkNotNull(targetHash, "android.compileSdkVersion is missing!");
+ Preconditions.checkNotNull(buildToolRevision, "android.buildToolsVersion is missing!");
SdkLoader sdkLoader = getSdkLoader();
SdkInfo sdkInfo = sdkLoader.getSdkInfo(logger);
TargetInfo targetInfo = sdkLoader.getTargetInfo(targetHash, buildToolRevision, logger);
- androidBuilder.setTargetInfo(sdkInfo, targetInfo);
+ androidBuilder.setTargetInfo(sdkInfo, targetInfo, usedLibraries);
}
@Nullable
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.groovy
deleted file mode 100644
index 3d860a3..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.groovy
+++ /dev/null
@@ -1,2294 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal
-
-import com.android.SdkConstants
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.OutputFile
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.internal.core.GradleVariantConfiguration
-import com.android.build.gradle.internal.coverage.JacocoInstrumentTask
-import com.android.build.gradle.internal.coverage.JacocoPlugin
-import com.android.build.gradle.internal.coverage.JacocoReportTask
-import com.android.build.gradle.internal.dependency.LibraryDependencyImpl
-import com.android.build.gradle.internal.dependency.ManifestDependencyImpl
-import com.android.build.gradle.internal.dependency.VariantDependencies
-import com.android.build.gradle.internal.dsl.AbiSplitOptions
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.build.gradle.internal.publishing.ApkPublishArtifact
-import com.android.build.gradle.internal.publishing.MappingPublishArtifact
-import com.android.build.gradle.internal.publishing.MetadataPublishArtifact
-import com.android.build.gradle.internal.scope.AndroidTask
-import com.android.build.gradle.internal.scope.AndroidTaskRegistry
-import com.android.build.gradle.internal.scope.ConventionMappingHelper
-import com.android.build.gradle.internal.scope.GlobalScope
-import com.android.build.gradle.internal.scope.VariantOutputScope
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.tasks.AndroidReportTask
-import com.android.build.gradle.internal.tasks.CheckManifest
-import com.android.build.gradle.internal.tasks.DependencyReportTask
-import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestLibraryTask
-import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask
-import com.android.build.gradle.internal.tasks.FileSupplier
-import com.android.build.gradle.internal.tasks.GenerateApkDataTask
-import com.android.build.gradle.internal.tasks.InstallVariantTask
-import com.android.build.gradle.internal.tasks.MockableAndroidJarTask
-import com.android.build.gradle.internal.tasks.PrepareDependenciesTask
-import com.android.build.gradle.internal.tasks.SigningReportTask
-import com.android.build.gradle.internal.tasks.SourceSetsTask
-import com.android.build.gradle.internal.tasks.TestServerTask
-import com.android.build.gradle.internal.tasks.UninstallTask
-import com.android.build.gradle.internal.tasks.multidex.CreateMainDexList
-import com.android.build.gradle.internal.tasks.multidex.CreateManifestKeepList
-import com.android.build.gradle.internal.tasks.multidex.JarMergingTask
-import com.android.build.gradle.internal.tasks.multidex.RetraceMainDexList
-import com.android.build.gradle.internal.test.TestDataImpl
-import com.android.build.gradle.internal.test.report.ReportType
-import com.android.build.gradle.internal.variant.ApkVariantData
-import com.android.build.gradle.internal.variant.ApkVariantOutputData
-import com.android.build.gradle.internal.variant.ApplicationVariantData
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.build.gradle.internal.variant.LibraryVariantData
-import com.android.build.gradle.internal.variant.TestVariantData
-import com.android.build.gradle.internal.variant.TestedVariantData
-import com.android.build.gradle.tasks.AidlCompile
-import com.android.build.gradle.tasks.AndroidJarTask
-import com.android.build.gradle.tasks.AndroidProGuardTask
-import com.android.build.gradle.tasks.CompatibleScreensManifest
-import com.android.build.gradle.tasks.Dex
-import com.android.build.gradle.tasks.GenerateBuildConfig
-import com.android.build.gradle.tasks.GenerateResValues
-import com.android.build.gradle.tasks.GenerateSplitAbiRes
-import com.android.build.gradle.tasks.JackTask
-import com.android.build.gradle.tasks.JillTask
-import com.android.build.gradle.tasks.Lint
-import com.android.build.gradle.tasks.MergeAssets
-import com.android.build.gradle.tasks.MergeManifests
-import com.android.build.gradle.tasks.MergeResources
-import com.android.build.gradle.tasks.NdkCompile
-import com.android.build.gradle.tasks.PackageApplication
-import com.android.build.gradle.tasks.PackageSplitAbi
-import com.android.build.gradle.tasks.PackageSplitRes
-import com.android.build.gradle.tasks.PreCompilationVerificationTask
-import com.android.build.gradle.tasks.PreDex
-import com.android.build.gradle.tasks.PreprocessResourcesTask
-import com.android.build.gradle.tasks.ProcessAndroidResources
-import com.android.build.gradle.tasks.ProcessManifest
-import com.android.build.gradle.tasks.ProcessTestManifest
-import com.android.build.gradle.tasks.RenderscriptCompile
-import com.android.build.gradle.tasks.ShrinkResources
-import com.android.build.gradle.tasks.SplitZipAlign
-import com.android.build.gradle.tasks.ZipAlign
-import com.android.build.gradle.tasks.factory.JavaCompileConfigAction
-import com.android.build.gradle.tasks.factory.ProGuardTaskConfigAction
-import com.android.build.gradle.tasks.factory.ProcessJavaResConfigAction
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.VariantConfiguration
-import com.android.builder.core.VariantType
-import com.android.builder.dependency.LibraryDependency
-import com.android.builder.internal.testing.SimpleTestCallable
-import com.android.builder.model.AndroidProject
-import com.android.builder.testing.ConnectedDeviceProvider
-import com.android.builder.testing.TestData
-import com.android.builder.testing.api.DeviceProvider
-import com.android.builder.testing.api.TestServer
-import com.android.resources.Density
-import com.android.sdklib.AndroidTargetHash
-import com.android.sdklib.BuildToolInfo
-import com.android.sdklib.IAndroidTarget
-import com.google.common.base.CharMatcher
-import com.google.common.collect.ImmutableList
-import com.google.common.collect.ImmutableSet
-import com.google.common.collect.Iterators
-import com.google.common.collect.Lists
-import com.google.common.collect.Sets
-import groovy.transform.CompileDynamic
-import groovy.transform.CompileStatic
-import org.gradle.api.DefaultTask
-import org.gradle.api.GradleException
-import org.gradle.api.JavaVersion
-import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.execution.TaskExecutionGraph
-import org.gradle.api.internal.ConventionMapping
-import org.gradle.api.logging.LogLevel
-import org.gradle.api.logging.Logger
-import org.gradle.api.logging.Logging
-import org.gradle.api.plugins.BasePlugin
-import org.gradle.api.plugins.JavaBasePlugin
-import org.gradle.api.plugins.JavaPlugin
-import org.gradle.api.reporting.ConfigurableReport
-import org.gradle.api.tasks.Copy
-import org.gradle.api.tasks.bundling.Jar
-import org.gradle.api.tasks.compile.AbstractCompile
-import org.gradle.api.tasks.compile.JavaCompile
-import org.gradle.api.tasks.testing.Test
-import org.gradle.api.tasks.testing.TestTaskReports
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-import proguard.gradle.ProGuardTask
-
-import static com.android.build.OutputFile.DENSITY
-import static com.android.builder.core.BuilderConstants.CONNECTED
-import static com.android.builder.core.BuilderConstants.DEVICE
-import static com.android.builder.core.BuilderConstants.FD_ANDROID_RESULTS
-import static com.android.builder.core.BuilderConstants.FD_ANDROID_TESTS
-import static com.android.builder.core.BuilderConstants.FD_FLAVORS
-import static com.android.builder.core.BuilderConstants.FD_FLAVORS_ALL
-import static com.android.builder.core.BuilderConstants.FD_REPORTS
-import static com.android.builder.core.VariantType.ANDROID_TEST
-import static com.android.builder.core.VariantType.DEFAULT
-import static com.android.builder.core.VariantType.UNIT_TEST
-import static com.android.builder.model.AndroidProject.FD_GENERATED
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-import static com.android.builder.model.AndroidProject.FD_OUTPUTS
-import static com.android.builder.model.AndroidProject.PROPERTY_APK_LOCATION
-import static com.android.sdklib.BuildToolInfo.PathId.ZIP_ALIGN
-
-/**
- * Manages tasks creation.
- */
-@CompileStatic
-abstract class TaskManager {
-
- public static final String FILE_JACOCO_AGENT = 'jacocoagent.jar'
-
- public static final String DEFAULT_PROGUARD_CONFIG_FILE = 'proguard-android.txt'
-
- public final static String DIR_BUNDLES = "bundles";
-
- public static final String INSTALL_GROUP = "Install"
-
- public static final String BUILD_GROUP = BasePlugin.BUILD_GROUP
-
- public static final String ANDROID_GROUP = "Android"
-
- protected Project project
-
- protected AndroidBuilder androidBuilder
-
- private DependencyManager dependencyManager
-
- protected SdkHandler sdkHandler
-
- protected BaseExtension extension
-
- protected ToolingModelBuilderRegistry toolingRegistry
-
- private final GlobalScope globalScope
-
- private AndroidTaskRegistry androidTasks = new AndroidTaskRegistry();
-
- private Logger logger
-
- protected boolean isNdkTaskNeeded = true
-
- // Task names
- // TODO: Convert to AndroidTask.
- private static final String MAIN_PREBUILD = "preBuild"
-
- private static final String UNINSTALL_ALL = "uninstallAll"
-
- private static final String DEVICE_CHECK = "deviceCheck"
-
- protected static final String CONNECTED_CHECK = "connectedCheck"
-
- private static final String ASSEMBLE_ANDROID_TEST = "assembleAndroidTest"
-
- private static final String SOURCE_SETS = "sourceSets"
-
- private static final String LINT = "lint"
-
- protected static final String LINT_COMPILE = "compileLint"
-
- // Tasks
- private Copy jacocoAgentTask
-
- public MockableAndroidJarTask createMockableJar
-
- public TaskManager(
- Project project,
- AndroidBuilder androidBuilder,
- BaseExtension extension,
- SdkHandler sdkHandler,
- DependencyManager dependencyManager,
- ToolingModelBuilderRegistry toolingRegistry) {
- this.project = project
- this.androidBuilder = androidBuilder
- this.sdkHandler = sdkHandler
- this.extension = extension
- this.toolingRegistry = toolingRegistry
- this.dependencyManager = dependencyManager
- logger = Logging.getLogger(this.class)
-
- globalScope = new GlobalScope(
- project,
- androidBuilder,
- getArchivesBaseName(project),
- extension,
- sdkHandler,
- toolingRegistry);
- }
-
- private boolean isVerbose() {
- return project.logger.isEnabled(LogLevel.INFO)
- }
-
- private boolean isDebugLog() {
- return project.logger.isEnabled(LogLevel.DEBUG)
- }
-
- /**
- * Creates the tasks for a given BaseVariantData.
- * @param variantData the non-null BaseVariantData.
- * @param assembleTask an optional assembleTask to be used. If null, a new one is created.
- */
- abstract public void createTasksForVariantData(
- @NonNull TaskFactory tasks,
- @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData)
-
- public GlobalScope getGlobalScope() {
- return globalScope
- }
-
- /**
- * Returns a collection of buildables that creates native object.
- *
- * A buildable is considered to be any object that can be used as the argument to
- * Task.dependsOn. This could be a Task or a BuildableModelElement (e.g. BinarySpec).
- */
- protected Collection<Object> getNdkBuildable(BaseVariantData variantData) {
- return Collections.singleton(variantData.ndkCompileTask)
- }
-
- /**
- * Returns the directories of the NDK buildables.
- */
- protected Collection<File> getNdkOutputDirectories(BaseVariantData variantData) {
- return Collections.singleton(variantData.ndkCompileTask.soFolder)
- }
-
- private BaseExtension getExtension() {
- return extension
- }
-
- public void resolveDependencies(
- @NonNull VariantDependencies variantDeps,
- @Nullable VariantDependencies testedVariantDeps) {
- dependencyManager.resolveDependencies(variantDeps, testedVariantDeps)
- }
-
- /**
- * Create tasks before the evaluation (on plugin apply). This is useful for tasks that
- * could be referenced by custom build logic.
- */
- public void createTasksBeforeEvaluate(TaskFactory tasks) {
- tasks.create(UNINSTALL_ALL) {
- it.description = "Uninstall all applications."
- it.group = INSTALL_GROUP
- }
-
- tasks.create(DEVICE_CHECK) {
- it.description = "Runs all device checks using Device Providers and Test Servers."
- it.group = JavaBasePlugin.VERIFICATION_GROUP
- }
-
- tasks.create(CONNECTED_CHECK) {
- it.description = "Runs all device checks on currently connected devices."
- it.group = JavaBasePlugin.VERIFICATION_GROUP
- }
-
- tasks.create(MAIN_PREBUILD)
-
- tasks.create(SOURCE_SETS, SourceSetsTask) {
- it.description = "Prints out all the source sets defined in this project."
- it.group = ANDROID_GROUP
- }
-
- tasks.create(ASSEMBLE_ANDROID_TEST) {
- it.setGroup(BasePlugin.BUILD_GROUP);
- it.setDescription("Assembles all the Test applications.");
- }
-
- tasks.create(LINT, Lint) {
- it.description = "Runs lint on all variants."
- it.group = JavaBasePlugin.VERIFICATION_GROUP
- it.setLintOptions(getExtension().lintOptions)
- it.setSdkHome(sdkHandler.getSdkFolder())
- it.setToolingRegistry(toolingRegistry)
- }
- tasks.named(JavaBasePlugin.CHECK_TASK_NAME) {
- it.dependsOn LINT
- }
- createLintCompileTask(tasks)
- }
-
- public void createMockableJarTask() {
- createMockableJar = project.tasks.create("mockableAndroidJar", MockableAndroidJarTask)
- createMockableJar.group = BUILD_GROUP
- createMockableJar.description = "Creates a version of android.jar that's suitable for unit tests."
-
- conventionMapping(createMockableJar).map("androidJar") {
- new File(androidBuilder.target.getPath(IAndroidTarget.ANDROID_JAR))
- }
-
- CharMatcher safeCharacters = CharMatcher.JAVA_LETTER_OR_DIGIT.or(CharMatcher.anyOf('-.'))
- String sdkName = safeCharacters.negate().replaceFrom(extension.compileSdkVersion, '-')
-
- conventionMapping(createMockableJar).map("outputFile") {
- project.file(
- "$project.buildDir/${FD_INTERMEDIATES}/mockable-${sdkName}.jar")
- }
-
- conventionMapping(createMockableJar).map("returnDefaultValues") {
- extension.testOptions.unitTests.returnDefaultValues
- }
- }
-
- public void createMergeAppManifestsTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope variantScope) {
-
- ApplicationVariantData appVariantData = variantScope.variantData as ApplicationVariantData
- Set<String> screenSizes = appVariantData.getCompatibleScreens()
-
- // loop on all outputs. The only difference will be the name of the task, and location
- // of the generated manifest
- for (BaseVariantOutputData vod : appVariantData.outputs) {
- // create final var inside the loop to ensure the closures will work.
- final BaseVariantOutputData variantOutputData = vod
-
- VariantOutputScope scope = variantOutputData.getScope()
-
- AndroidTask<CompatibleScreensManifest> csmTask = null;
- if (vod.getMainOutputFile().getFilter(DENSITY) != null) {
- csmTask = androidTasks.create(tasks,
- new CompatibleScreensManifest.ConfigAction(scope, screenSizes));
- scope.compatibleScreensManifestTask = csmTask
- }
-
- scope.manifestProcessorTask = androidTasks.create(tasks,
- new MergeManifests.ConfigAction(scope))
-
- if (csmTask != null) {
- scope.manifestProcessorTask.dependsOn(tasks, csmTask)
- }
- }
- }
-
- @CompileDynamic
- private static ConventionMapping conventionMapping(Task task) {
- task.conventionMapping
- }
-
- /**
- * Using the BaseConventionPlugin does not work for archivesBaseName dynamic attribute,
- * revert to a dynamic property invocation.
- */
- @CompileDynamic
- private static String getArchivesBaseName(Project project){
- project.archivesBaseName
- }
-
- public void createMergeLibManifestsTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope) {
-
- AndroidTask<ProcessManifest> processManifest = androidTasks.create(tasks,
- new ProcessManifest.ConfigAction(scope));
-
- processManifest.dependsOn(tasks, scope.variantData.prepareDependenciesTask)
-
- BaseVariantOutputData variantOutputData = scope.variantData.outputs.get(0)
- variantOutputData.scope.manifestProcessorTask = processManifest
- }
-
- protected void createProcessTestManifestTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope) {
-
- AndroidTask<ProcessTestManifest> processTestManifestTask = androidTasks.create(tasks,
- new ProcessTestManifest.ConfigAction(scope));
-
- processTestManifestTask.dependsOn(tasks, scope.variantData.prepareDependenciesTask)
-
- BaseVariantOutputData variantOutputData = scope.variantData.outputs.get(0)
- variantOutputData.scope.manifestProcessorTask = processTestManifestTask
- }
-
- public void createRenderscriptTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope) {
- scope.renderscriptCompileTask = androidTasks.create(tasks,
- new RenderscriptCompile.ConfigAction(scope))
-
- BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
- GradleVariantConfiguration config = variantData.variantConfiguration
- // get single output for now.
- BaseVariantOutputData variantOutputData = variantData.outputs.get(0)
-
- scope.renderscriptCompileTask.dependsOn(tasks, variantData.prepareDependenciesTask)
- if (config.type.isForTesting()) {
- scope.renderscriptCompileTask.dependsOn(tasks, variantOutputData.scope.manifestProcessorTask)
- } else {
- scope.renderscriptCompileTask.dependsOn(tasks, scope.checkManifestTask)
- }
-
-
- scope.resourceGenTask.dependsOn(tasks, scope.renderscriptCompileTask)
- // only put this dependency if rs will generate Java code
- if (!config.renderscriptNdkModeEnabled) {
- scope.sourceGenTask.dependsOn(tasks, scope.renderscriptCompileTask)
- }
- }
-
- public AndroidTask<MergeResources> createMergeResourcesTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope) {
- return basicCreateMergeResourcesTask(
- tasks,
- scope,
- "merge",
- null /*outputLocation*/,
- true /*includeDependencies*/,
- true /*process9patch*/)
- }
-
- public AndroidTask<MergeResources> basicCreateMergeResourcesTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope,
- @NonNull String taskNamePrefix,
- @Nullable File outputLocation,
- final boolean includeDependencies,
- final boolean process9Patch) {
- scope.mergeResourcesTask = androidTasks.create(tasks,
- new MergeResources.ConfigAction(
- scope,
- taskNamePrefix,
- outputLocation,
- includeDependencies,
- process9Patch));
- scope.mergeResourcesTask.dependsOn(tasks,
- scope.variantData.prepareDependenciesTask,
- scope.resourceGenTask)
- return scope.mergeResourcesTask;
- }
-
-
- public void createMergeAssetsTask(TaskFactory tasks, VariantScope scope) {
- scope.mergeAssetsTask = androidTasks.create(tasks, new MergeAssets.ConfigAction(scope))
- scope.mergeAssetsTask.dependsOn(tasks,
- scope.variantData.prepareDependenciesTask,
- scope.assetGenTask)
- }
-
- public void createBuildConfigTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
- scope.generateBuildConfigTask = androidTasks.create(tasks, new GenerateBuildConfig.ConfigAction(scope))
-
- scope.sourceGenTask.dependsOn(tasks, scope.generateBuildConfigTask.name);
- if (scope.variantConfiguration.type.isForTesting()) {
- // in case of a test project, the manifest is generated so we need to depend
- // on its creation.
-
- // For test apps there should be a single output, so we get it.
- BaseVariantOutputData variantOutputData = scope.variantData.outputs.get(0)
-
- scope.generateBuildConfigTask.dependsOn(tasks, variantOutputData.scope.manifestProcessorTask)
- } else {
- scope.generateBuildConfigTask.dependsOn(tasks, scope.checkManifestTask)
- }
- }
-
- public void createGenerateResValuesTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope) {
- AndroidTask<GenerateResValues> generateResValuesTask = androidTasks.create(
- tasks, new GenerateResValues.ConfigAction(scope))
- scope.resourceGenTask.dependsOn(tasks, generateResValuesTask)
- }
-
- public void createPreprocessResourcesTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope) {
- BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
- String variantName = variantData.variantConfiguration.fullName.capitalize()
- int minSdk = variantData.variantConfiguration.minSdkVersion.getApiLevel()
-
- if (extension.preprocessResources && minSdk < PreprocessResourcesTask.MIN_SDK) {
- scope.preprocessResourcesTask = androidTasks.create(
- tasks,
- "preprocess${variantName}Resources",
- PreprocessResourcesTask) { PreprocessResourcesTask preprocessResourcesTask ->
- variantData.preprocessResourcesTask = preprocessResourcesTask
-
- preprocessResourcesTask.dependsOn variantData.mergeResourcesTask
-
- preprocessResourcesTask.mergedResDirectory =
- scope.getMergeResourcesOutputDir()
- preprocessResourcesTask.generatedResDirectory =
- project.file(
- "$project.buildDir/${FD_GENERATED}/res/pngs/${variantData.variantConfiguration.dirName}")
- preprocessResourcesTask.outputResDirectory =
- project.file(
- "$project.buildDir/${FD_INTERMEDIATES}/res/preprocessed/${variantData.variantConfiguration.dirName}")
- preprocessResourcesTask.incrementalFolder =
- project.file(
- "$project.buildDir/${FD_INTERMEDIATES}/incremental/preprocessResourcesTask/${variantData.variantConfiguration.dirName}")
-
- // TODO: configure this in the extension.
- preprocessResourcesTask.densitiesToGenerate = [Density.HIGH, Density.XHIGH]
- }
- }
- }
-
- public void createProcessResTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope,
- boolean generateResourcePackage) {
- createProcessResTask(
- tasks,
- scope,
- new File(globalScope.getBuildDir(), FD_INTERMEDIATES + "/symbols/" +
- scope.variantData.getVariantConfiguration().getDirName()),
- generateResourcePackage);
- }
-
- public void createProcessResTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope,
- @Nullable File symbolLocation,
- boolean generateResourcePackage) {
- BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData()
-
- variantData.calculateFilters(scope.getGlobalScope().getExtension().getSplits());
-
- // loop on all outputs. The only difference will be the name of the task, and location
- // of the generated data.
- for (BaseVariantOutputData vod : (List<? extends BaseVariantOutputData>)variantData.outputs) {
- // create final var inside the loop to ensure the closures will work.
- final VariantOutputScope variantOutputScope = vod.scope;
-
- variantOutputScope.processResourcesTask = androidTasks.create(tasks,
- new ProcessAndroidResources.ConfigAction(variantOutputScope, symbolLocation, generateResourcePackage));
- variantOutputScope.processResourcesTask.dependsOn(tasks,
- variantOutputScope.manifestProcessorTask,
- scope.mergeResourcesTask,
- scope.mergeAssetsTask)
-
- // TODO: Make it non-optional once this is not behind a flag.
- variantOutputScope.processResourcesTask.optionalDependsOn(tasks,
- scope.preprocessResourcesTask)
-
- if (vod.getMainOutputFile().getFilter(DENSITY) == null) {
- scope.generateRClassTask = variantOutputScope.processResourcesTask
- scope.sourceGenTask.optionalDependsOn(tasks, variantOutputScope.processResourcesTask)
- }
- }
- }
-
- /**
- * Creates the split resources packages task if necessary. AAPT will produce split packages
- * for all --split provided parameters. These split packages should be signed and moved
- * unchanged to the APK build output directory.
- * @param variantData the variant configuration.
- */
- public void createSplitResourcesTasks(@NonNull VariantScope scope) {
- BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
-
- assert variantData.getSplitHandlingPolicy() ==
- BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY;
-
- VariantConfiguration config = variantData.variantConfiguration
- Set<String> densityFilters = variantData.getFilters(OutputFile.FilterType.DENSITY)
- Set<String> abiFilters = variantData.getFilters(OutputFile.FilterType.ABI);
- Set<String> languageFilters = variantData.getFilters(OutputFile.FilterType.LANGUAGE);
-
- def outputs = variantData.outputs;
- if (outputs.size() != 1) {
- throw new RuntimeException("In release 21 and later, there can be only one main APK, " +
- "found " + outputs.size());
- }
-
- BaseVariantOutputData variantOutputData = outputs.get(0);
- VariantOutputScope variantOutputScope = variantOutputData.scope
- variantOutputData.packageSplitResourcesTask =
- project.tasks.create("package${config.fullName.capitalize()}SplitResources",
- PackageSplitRes);
- variantOutputData.packageSplitResourcesTask.inputDirectory =
- variantOutputScope.getProcessResourcePackageOutputFile().getParentFile()
- variantOutputData.packageSplitResourcesTask.densitySplits = densityFilters
- variantOutputData.packageSplitResourcesTask.languageSplits = languageFilters
- variantOutputData.packageSplitResourcesTask.outputBaseName = config.baseName
- variantOutputData.packageSplitResourcesTask.signingConfig =
- (SigningConfig) config.signingConfig
- variantOutputData.packageSplitResourcesTask.outputDirectory =
- new File("$project.buildDir/${FD_INTERMEDIATES}/splits/${config.dirName}")
- variantOutputData.packageSplitResourcesTask.androidBuilder = androidBuilder
- variantOutputData.packageSplitResourcesTask.dependsOn variantOutputScope.processResourcesTask.name
-
- SplitZipAlign zipAlign = project.tasks.
- create("zipAlign${config.fullName.capitalize()}SplitPackages", SplitZipAlign)
- conventionMapping(zipAlign).map("zipAlignExe") {
- String path = androidBuilder.targetInfo?.buildTools?.getPath(ZIP_ALIGN)
- if (path != null) {
- return new File(path)
- }
-
- return null
- }
-
- zipAlign.outputDirectory = new File("$project.buildDir/outputs/apk")
- conventionMapping(zipAlign).map("densityOrLanguageInputFiles") {
- return variantOutputData.packageSplitResourcesTask.getOutputFiles()
- }
- zipAlign.outputBaseName = config.baseName
- zipAlign.abiFilters = abiFilters
- zipAlign.languageFilters = languageFilters
- zipAlign.densityFilters = densityFilters
- File metadataDirectory = new File(zipAlign.outputDirectory.getParentFile(), "metadata")
- zipAlign.apkMetadataFile = new File(metadataDirectory, "${config.fullName}.mtd")
- ((ApkVariantOutputData) variantOutputData).splitZipAlign = zipAlign
- zipAlign.dependsOn(variantOutputData.packageSplitResourcesTask)
- }
-
- public void createSplitAbiTasks(@NonNull VariantScope scope) {
- ApplicationVariantData variantData = (ApplicationVariantData) scope.getVariantData()
-
- assert variantData.getSplitHandlingPolicy() ==
- BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY;
-
- VariantConfiguration config = variantData.variantConfiguration
- Set<String> filters = AbiSplitOptions.getAbiFilters(
- getExtension().getSplits().getAbiFilters())
- if (filters.isEmpty()) {
- return;
- }
- def outputs = variantData.outputs;
- if (outputs.size() != 1) {
- throw new RuntimeException("In release 21 and later, there can be only one main APK, " +
- "found " + outputs.size());
- }
-
- BaseVariantOutputData variantOutputData = outputs.get(0);
- // first create the split APK resources.
- GenerateSplitAbiRes generateSplitAbiRes = project.tasks.
- create("generate${config.fullName.capitalize()}SplitAbiRes",
- GenerateSplitAbiRes)
- generateSplitAbiRes.androidBuilder = androidBuilder
-
- generateSplitAbiRes.outputDirectory =
- new File("$project.buildDir/${FD_INTERMEDIATES}/abi/${config.dirName}")
- generateSplitAbiRes.splits = filters
- generateSplitAbiRes.outputBaseName = config.baseName
- generateSplitAbiRes.applicationId = config.getApplicationId()
- generateSplitAbiRes.versionCode = config.getVersionCode()
- generateSplitAbiRes.versionName = config.getVersionName()
- generateSplitAbiRes.debuggable = {
- config.buildType.debuggable
- }
- conventionMapping(generateSplitAbiRes).map("aaptOptions") {
- getExtension().aaptOptions
- }
- generateSplitAbiRes.dependsOn variantOutputData.scope.processResourcesTask.name
-
- // then package those resources with the appropriate JNI libraries.
- variantOutputData.packageSplitAbiTask =
- project.tasks.create("package${config.fullName.capitalize()}SplitAbi",
- PackageSplitAbi);
- variantOutputData.packageSplitAbiTask.inputFiles = generateSplitAbiRes.getOutputFiles()
- variantOutputData.packageSplitAbiTask.splits = filters
- variantOutputData.packageSplitAbiTask.outputBaseName = config.baseName
- variantOutputData.packageSplitAbiTask.signingConfig =
- (SigningConfig) config.signingConfig
- variantOutputData.packageSplitAbiTask.outputDirectory =
- new File("$project.buildDir/${FD_INTERMEDIATES}/splits/${config.dirName}")
- variantOutputData.packageSplitAbiTask.androidBuilder = androidBuilder
- variantOutputData.packageSplitAbiTask.dependsOn generateSplitAbiRes
- variantOutputData.packageSplitAbiTask.dependsOn getNdkBuildable(variantData)
-
- conventionMapping(variantOutputData.packageSplitAbiTask).map("jniFolders") {
- getJniFolders(variantData);
- }
- conventionMapping(variantOutputData.packageSplitAbiTask).
- map("jniDebuggable") { config.buildType.jniDebuggable }
- conventionMapping(variantOutputData.packageSplitAbiTask).
- map("packagingOptions") { getExtension().packagingOptions }
-
- ((ApkVariantOutputData) variantOutputData).splitZipAlign.abiInputFiles.addAll(
- variantOutputData.packageSplitAbiTask.getOutputFiles())
-
- ((ApkVariantOutputData) variantOutputData).splitZipAlign.dependsOn variantOutputData.packageSplitAbiTask
- }
-
- /**
- * Calculate the list of folders that can contain jni artifacts for this variant.
- * @param variantData the variant
- * @return a potentially empty list of directories that exist or not and that may contains
- * native resources.
- */
- @NonNull
- public Set<File> getJniFolders(@NonNull ApkVariantData variantData) {
- VariantConfiguration config = variantData.variantConfiguration
- // for now only the project's compilation output.
- Set<File> set = Sets.newHashSet()
- set.addAll(getNdkOutputDirectories(variantData))
- set.addAll(variantData.renderscriptCompileTask.libOutputDir)
- set.addAll(config.libraryJniFolders)
- set.addAll(config.jniLibsList)
-
- if (config.mergedFlavor.renderscriptSupportModeEnabled) {
- File rsLibs = androidBuilder.getSupportNativeLibFolder()
- if (rsLibs != null && rsLibs.isDirectory()) {
- set.add(rsLibs);
- }
- }
-
- return set
- }
-
- public void createProcessJavaResTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope) {
- scope.processJavaResourcesTask = androidTasks.create(
- tasks, new ProcessJavaResConfigAction(scope));
- }
-
- public void createAidlTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
- scope.aidlCompileTask = androidTasks.create(tasks,
- new AidlCompile.ConfigAction(scope));
- scope.sourceGenTask.dependsOn(tasks, scope.aidlCompileTask)
- scope.aidlCompileTask.dependsOn(tasks, scope.variantData.prepareDependenciesTask)
- }
-
- public void createJackAndUnitTestVerificationTask(
- @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
- @NonNull BaseVariantData<? extends BaseVariantOutputData> testedVariantData) {
-
- PreCompilationVerificationTask verificationTask = project.tasks.create(
- "preCompile${variantData.variantConfiguration.fullName.capitalize()}Java",
- PreCompilationVerificationTask)
- verificationTask.useJack = testedVariantData.getVariantConfiguration().getUseJack()
- verificationTask.testSourceFiles = variantData.getJavaSources()
- variantData.javaCompileTask.dependsOn verificationTask
- }
-
- public void createJavaCompileTask(
- @NonNull final TaskFactory tasks,
- @NonNull final VariantScope scope) {
- BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData;
- AndroidTask<JavaCompile> javaCompileTask = androidTasks.create(tasks,
- new JavaCompileConfigAction(scope));
- scope.javaCompileTask = javaCompileTask;
-
- scope.compileTask.dependsOn(tasks, javaCompileTask);
-
- javaCompileTask.optionalDependsOn(tasks, scope.sourceGenTask)
- javaCompileTask.dependsOn(tasks,
- scope.getVariantData().prepareDependenciesTask,
- scope.processJavaResourcesTask);
-
- // TODO - dependency information for the compile classpath is being lost.
- // Add a temporary approximation
- javaCompileTask.dependsOn(tasks,
- scope.getVariantData().getVariantDependency().getCompileConfiguration()
- .getBuildDependencies());
-
- if (variantData.getType().isForTesting()) {
- BaseVariantData testedVariantData =
- ((TestVariantData) variantData).getTestedVariantData() as BaseVariantData
- javaCompileTask.dependsOn(tasks,
- testedVariantData.javaCompileTask ?: testedVariantData.scope.javaCompileTask)
- }
-
-
- // Create jar task for uses by external modules.
- if (variantData.variantDependency.classesConfiguration != null) {
- tasks.create("package${variantData.variantConfiguration.fullName.capitalize()}JarArtifact", Jar) { Jar jar ->
- variantData.classesJarTask = jar
- jar.dependsOn javaCompileTask.name
-
- // add the class files (whether they are instrumented or not.
- jar.from({ scope.getJavaOutputDir() })
-
- jar.destinationDir = scope.getJavaOutputDir();
- jar.archiveName = "classes.jar"
- }
- }
- }
-
- public void createGenerateMicroApkDataTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope,
- @NonNull Configuration config) {
- AndroidTask<GenerateApkDataTask> generateMicroApkTask = androidTasks.create(tasks,
- new GenerateApkDataTask.ConfigAction(scope, config));
- generateMicroApkTask.dependsOn tasks, config
-
- // the merge res task will need to run after this one.
- scope.resourceGenTask.dependsOn(tasks, generateMicroApkTask);
- }
-
- public void createNdkTasks(
- @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
- NdkCompile ndkCompile = project.tasks.create(
- "compile${variantData.variantConfiguration.fullName.capitalize()}Ndk",
- NdkCompile)
-
- ndkCompile.dependsOn variantData.preBuildTask
-
- ndkCompile.androidBuilder = androidBuilder
- ndkCompile.ndkDirectory = sdkHandler.getNdkFolder()
- variantData.ndkCompileTask = ndkCompile
- variantData.compileTask.dependsOn variantData.ndkCompileTask
-
- GradleVariantConfiguration variantConfig = variantData.variantConfiguration
-
- if (variantConfig.mergedFlavor.renderscriptNdkModeEnabled) {
- ndkCompile.ndkRenderScriptMode = true
- ndkCompile.dependsOn variantData.renderscriptCompileTask
- } else {
- ndkCompile.ndkRenderScriptMode = false
- }
-
- conventionMapping(ndkCompile).map("sourceFolders") {
- List<File> sourceList = variantConfig.jniSourceList
- if (variantConfig.mergedFlavor.renderscriptNdkModeEnabled) {
- sourceList.add(variantData.renderscriptCompileTask.sourceOutputDir)
- }
-
- return sourceList
- }
-
- conventionMapping(ndkCompile).map("generatedMakefile") {
- project.file(
- "$project.buildDir/${FD_INTERMEDIATES}/ndk/${variantData.variantConfiguration.dirName}/Android.mk")
- }
-
- conventionMapping(ndkCompile).map("ndkConfig") { variantConfig.ndkConfig }
-
- conventionMapping(ndkCompile).map("debuggable") {
- variantConfig.buildType.jniDebuggable
- }
-
- conventionMapping(ndkCompile).map("objFolder") {
- project.file(
- "$project.buildDir/${FD_INTERMEDIATES}/ndk/${variantData.variantConfiguration.dirName}/obj")
- }
- conventionMapping(ndkCompile).map("soFolder") {
- project.file(
- "$project.buildDir/${FD_INTERMEDIATES}/ndk/${variantData.variantConfiguration.dirName}/lib")
- }
- }
-
- /**
- * Creates the tasks to build unit tests.
- *
- * @param variantData the test variant
- */
- void createUnitTestVariantTasks(
- @NonNull TaskFactory tasks,
- @NonNull TestVariantData variantData) {
- BaseVariantData testedVariantData = variantData.getTestedVariantData() as BaseVariantData
- variantData.assembleVariantTask.dependsOn createMockableJar
- VariantScope variantScope = variantData.getScope()
-
- createPreBuildTasks(tasks, variantScope)
- createProcessJavaResTask(tasks, variantScope)
- createCompileAnchorTask(tasks, variantScope)
- createJavaCompileTask(tasks, variantScope);
- createJackAndUnitTestVerificationTask(variantData, testedVariantData)
- createUnitTestTask(tasks, variantData)
-
- // This hides the assemble unit test task from the task list.
- variantData.assembleVariantTask.group = null
- }
-
- /**
- * Creates the tasks to build android tests.
- *
- * @param variantData the test variant
- */
- public void createAndroidTestVariantTasks(
- @NonNull TaskFactory tasks,
- @NonNull TestVariantData variantData) {
- VariantScope variantScope = variantData.getScope()
-
- BaseVariantData<? extends BaseVariantOutputData> testedVariantData =
- variantData.
- getTestedVariantData() as BaseVariantData<? extends BaseVariantOutputData>
-
- // get single output for now (though this may always be the case for tests).
- BaseVariantOutputData variantOutputData = variantData.outputs.get(0)
- BaseVariantOutputData testedVariantOutputData = testedVariantData.outputs.get(0)
-
- createAnchorTasks(tasks, variantScope)
-
- // Add a task to process the manifest
- createProcessTestManifestTask(tasks, variantScope)
-
- // Add a task to create the res values
- createGenerateResValuesTask(tasks, variantScope)
-
- // Add a task to compile renderscript files.
- createRenderscriptTask(tasks, variantScope)
-
- // Add a task to merge the resource folders
- createMergeResourcesTask(tasks, variantScope)
-
- // Add a task to merge the assets folders
- createMergeAssetsTask(tasks, variantScope)
-
- if (testedVariantData.variantConfiguration.type == VariantType.LIBRARY) {
- // in this case the tested library must be fully built before test can be built!
- if (testedVariantOutputData.assembleTask != null) {
- variantOutputData.scope.manifestProcessorTask.dependsOn(
- tasks, testedVariantOutputData.assembleTask)
- variantScope.mergeResourcesTask.dependsOn(tasks, testedVariantOutputData.assembleTask)
- }
- }
-
- // Add a task to create the BuildConfig class
- createBuildConfigTask(tasks, variantScope)
-
- createPreprocessResourcesTask(tasks, variantScope)
-
- // Add a task to generate resource source files
- createProcessResTask(tasks, variantScope, true /*generateResourcePackage*/)
-
- // process java resources
- createProcessJavaResTask(tasks, variantScope)
-
- createAidlTask(tasks, variantScope)
-
- // Add NDK tasks
- if (isNdkTaskNeeded) {
- createNdkTasks(variantData)
- }
- variantScope.setNdkBuildable(getNdkBuildable(variantData))
- variantScope.setNdkOutputDirectories(getNdkOutputDirectories(variantData))
-
- // Add a task to compile the test application
- if (variantData.getVariantConfiguration().useJack) {
- createJackTask(variantData, testedVariantData);
- } else {
- //createJavaCompileTask(variantData, testedVariantData)
- createJavaCompileTask(tasks, variantScope)
- createPostCompilationTasks(tasks, variantScope)
- }
-
- createPackagingTask(tasks, variantScope, false /*publishApk*/)
-
- tasks.named(ASSEMBLE_ANDROID_TEST) {
- it.dependsOn variantOutputData.assembleTask
- }
-
- createConnectedTestForVariantData(tasks, variantData, TestType.APPLICATION)
- }
-
- // TODO - should compile src/lint/java from src/lint/java and jar it into build/lint/lint.jar
- private void createLintCompileTask(TaskFactory tasks) {
- tasks.create(LINT_COMPILE, Task) { Task lintCompile ->
- File outputDir = new File("$project.buildDir/${FD_INTERMEDIATES}/lint")
-
- lintCompile.doFirst {
- // create the directory for lint output if it does not exist.
- if (!outputDir.exists()) {
- boolean mkdirs = outputDir.mkdirs();
- if (!mkdirs) {
- throw new GradleException("Unable to create lint output directory.")
- }
- }
- }
- }
- }
-
- /** Is the given variant relevant for lint? */
- private static boolean isLintVariant(
- @NonNull BaseVariantData<? extends BaseVariantOutputData> baseVariantData) {
- // Only create lint targets for variants like debug and release, not debugTest
- VariantConfiguration config = baseVariantData.variantConfiguration
- // TODO: re-enable with Jack when possible
- return !config.getType().isForTesting() && !config.useJack;
- }
-
- // Add tasks for running lint on individual variants. We've already added a
- // lint task earlier which runs on all variants.
- public void createLintTasks(TaskFactory tasks, VariantScope scope) {
- final BaseVariantData<? extends BaseVariantOutputData> baseVariantData = scope.variantData
- if (!isLintVariant(baseVariantData)) {
- return;
- }
-
- // wire the main lint task dependency.
- tasks.named(LINT) {
- it.dependsOn(LINT_COMPILE)
- if (baseVariantData.javaCompileTask != null) {
- it.dependsOn(baseVariantData.javaCompileTask)
- }
- if (scope.javaCompileTask != null) {
- it.dependsOn(scope.javaCompileTask.name)
- }
- }
-
- AndroidTask<Lint> variantLintCheck = androidTasks.create(
- tasks, new Lint.ConfigAction(scope))
- variantLintCheck.dependsOn(tasks, LINT_COMPILE)
- variantLintCheck.optionalDependsOn(tasks,
- baseVariantData.javaCompileTask,
- scope.javaCompileTask)
- }
-
- private void createLintVitalTask(@NonNull ApkVariantData variantData) {
- assert getExtension().lintOptions.checkReleaseBuilds
- // TODO: re-enable with Jack when possible
- if (!variantData.variantConfiguration.buildType.debuggable &&
- !variantData.variantConfiguration.useJack) {
- String variantName = variantData.variantConfiguration.fullName
- def capitalizedVariantName = variantName.capitalize()
- def taskName = "lintVital" + capitalizedVariantName
- Lint lintReleaseCheck = project.tasks.create(taskName, Lint)
- // TODO: Make this task depend on lintCompile too (resolve initialization order first)
- optionalDependsOn(lintReleaseCheck, variantData.javaCompileTask)
- lintReleaseCheck.setLintOptions(getExtension().lintOptions)
- lintReleaseCheck.setSdkHome(sdkHandler.getSdkFolder())
- lintReleaseCheck.setVariantName(variantName)
- lintReleaseCheck.setToolingRegistry(toolingRegistry)
- lintReleaseCheck.setFatalOnly(true)
- lintReleaseCheck.description = "Runs lint on just the fatal issues in the " +
- capitalizedVariantName + " build."
- //variantData.assembleVariantTask.dependsOn lintReleaseCheck
-
- // If lint is being run, we do not need to run lint vital.
- project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
- if (taskGraph.hasTask(LINT)) {
- lintReleaseCheck.setEnabled(false)
- }
- }
- }
- }
-
- private void createUnitTestTask(@NonNull TaskFactory tasks, @NonNull TestVariantData variantData) {
- BaseVariantData testedVariantData = variantData.testedVariantData as BaseVariantData
-
- Test runTestsTask = project.tasks.create(
- UNIT_TEST.prefix +
- testedVariantData.variantConfiguration.fullName.capitalize(),
- Test)
- runTestsTask.group = JavaBasePlugin.VERIFICATION_GROUP
- runTestsTask.description = "Run unit tests for the " +
- "$testedVariantData.variantConfiguration.fullName build."
-
- fixTestTaskSources(runTestsTask)
-
- runTestsTask.dependsOn variantData.assembleVariantTask
-
- AbstractCompile testCompileTask = variantData.javaCompileTask
- runTestsTask.testClassesDir = testCompileTask.destinationDir
-
- conventionMapping(runTestsTask).map("classpath") {
- project.files(
- testCompileTask.classpath,
- testCompileTask.outputs.files,
- variantData.processJavaResourcesTask.outputs,
- testedVariantData.processJavaResourcesTask.outputs,
- androidBuilder.bootClasspath.findAll {
- it.name != SdkConstants.FN_FRAMEWORK_LIBRARY
- },
- // Mockable JAR is last, to make sure you can shadow the classes with
- // dependencies.
- createMockableJar.outputFile)
- }
-
- // Put the variant name in the report path, so that different testing tasks don't
- // overwrite each other's reports.
- TestTaskReports testTaskReports = runTestsTask.reports
- for (ConfigurableReport report in [testTaskReports.junitXml, testTaskReports.html]) {
- report.destination = new File(report.destination, testedVariantData.name)
- }
-
- tasks.named(JavaPlugin.TEST_TASK_NAME) { Task test ->
- test.dependsOn runTestsTask
- }
-
- extension.testOptions.unitTests.applyConfiguration(runTestsTask)
- }
-
- @CompileDynamic
- private static void fixTestTaskSources(@NonNull Test testTask) {
- // We are running in afterEvaluate, so the JavaBasePlugin has already added a
- // callback to add test classes to the list of source files of the newly created task.
- // The problem is that we haven't configured the test classes yet (JavaBasePlugin
- // assumes all Test tasks are fully configured at this point), so we have to remove the
- // "directory null" entry from source files and add the right value.
- //
- // This is an ugly hack, since we assume sourceFiles is an instance of
- // DefaultConfigurableFileCollection.
- testTask.inputs.sourceFiles.from.clear()
- }
-
- public void createTopLevelTestTasks(TaskFactory tasks, boolean hasFlavors) {
- List<String> reportTasks = Lists.newArrayListWithExpectedSize(2)
-
- List<DeviceProvider> providers = getExtension().deviceProviders
-
- String connectedRootName = "${CONNECTED}${ANDROID_TEST.suffix}"
- String defaultReportsDir = "$project.buildDir/$FD_REPORTS/$FD_ANDROID_TESTS"
- String defaultResultsDir = "$project.buildDir/${FD_OUTPUTS}/$FD_ANDROID_RESULTS"
-
- // If more than one flavor, create a report aggregator task and make this the parent
- // task for all new connected tasks. Otherwise, create a top level connectedAndroidTest
- // DefaultTask.
- if (hasFlavors) {
- tasks.create(connectedRootName, AndroidReportTask) { AndroidReportTask mainConnectedTask ->
- mainConnectedTask.group = JavaBasePlugin.VERIFICATION_GROUP
- mainConnectedTask.description =
- "Installs and runs instrumentation tests for all flavors on connected devices."
- mainConnectedTask.reportType = ReportType.MULTI_FLAVOR
- conventionMapping(mainConnectedTask).map("resultsDir") {
- String rootLocation = extension.testOptions.resultsDir ?: defaultResultsDir
- project.file("$rootLocation/connected/$FD_FLAVORS_ALL")
- }
- conventionMapping(mainConnectedTask).map("reportsDir") {
- String rootLocation = extension.testOptions.reportDir ?: defaultReportsDir
- project.file("$rootLocation/connected/$FD_FLAVORS_ALL")
- }
-
- }
- reportTasks.add(connectedRootName)
- } else {
- tasks.create(connectedRootName) { Task connectedTask ->
- connectedTask.group = JavaBasePlugin.VERIFICATION_GROUP
- connectedTask.description =
- "Installs and runs instrumentation tests for all flavors on connected devices."
- }
- }
- tasks.named(CONNECTED_CHECK) {
- it.dependsOn connectedRootName
- }
-
- String mainProviderTaskName = "${DEVICE}${ANDROID_TEST.suffix}"
- // if more than one provider tasks, either because of several flavors, or because of
- // more than one providers, then create an aggregate report tasks for all of them.
- if (providers.size() > 1 || hasFlavors) {
- tasks.create(mainProviderTaskName, AndroidReportTask) { AndroidReportTask mainProviderTask ->
- mainProviderTask.group = JavaBasePlugin.VERIFICATION_GROUP
- mainProviderTask.description =
- "Installs and runs instrumentation tests using all Device Providers."
- mainProviderTask.reportType = ReportType.MULTI_FLAVOR
-
- conventionMapping(mainProviderTask).map("resultsDir") {
- String rootLocation = extension.testOptions.resultsDir ?: defaultResultsDir
-
- project.file("$rootLocation/devices/$FD_FLAVORS_ALL")
- }
- conventionMapping(mainProviderTask).map("reportsDir") {
- String rootLocation = extension.testOptions.reportDir ?: defaultReportsDir
- project.file("$rootLocation/devices/$FD_FLAVORS_ALL")
- }
- }
- reportTasks.add(mainProviderTaskName)
- } else {
- tasks.create(mainProviderTaskName) { Task providerTask ->
- providerTask.group = JavaBasePlugin.VERIFICATION_GROUP
- providerTask.description =
- "Installs and runs instrumentation tests using all Device Providers."
- }
- }
-
- tasks.named(DEVICE_CHECK) {
- it.dependsOn mainProviderTaskName
- }
-
- // Create top level unit test tasks.
- tasks.create(JavaPlugin.TEST_TASK_NAME) { Task unitTestTask ->
- unitTestTask.group = JavaBasePlugin.VERIFICATION_GROUP
- unitTestTask.description = "Run unit tests for all variants."
- }
- tasks.named(JavaBasePlugin.CHECK_TASK_NAME) { Task check ->
- check.dependsOn JavaPlugin.TEST_TASK_NAME
- }
-
- // If gradle is launched with --continue, we want to run all tests and generate an
- // aggregate report (to help with the fact that we may have several build variants, or
- // or several device providers).
- // To do that, the report tasks must run even if one of their dependent tasks (flavor
- // or specific provider tasks) fails, when --continue is used, and the report task is
- // meant to run (== is in the task graph).
- // To do this, we make the children tasks ignore their errors (ie they won't fail and
- // stop the build).
- if (!reportTasks.isEmpty() && project.gradle.startParameter.continueOnFailure) {
- project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
- for (String reportTask : reportTasks) {
- if (taskGraph.hasTask(reportTask)) {
- tasks.named(reportTask) { AndroidReportTask task ->
- task.setWillRun()
- }
- }
- }
- }
- }
- }
-
- public enum TestType {
-
- APPLICATION(DeviceProviderInstrumentTestTask.class),
- LIBRARY(DeviceProviderInstrumentTestLibraryTask.class),
-
- final private Class<? extends DeviceProviderInstrumentTestTask> taskType;
-
- TestType(Class<? extends DeviceProviderInstrumentTestTask> provider) {
- taskType = provider;
- }
- }
-
- protected void createConnectedTestForVariantData(
- @NonNull TaskFactory tasks,
- final TestVariantData testVariantData,
- TestType testType) {
- BaseVariantData<? extends BaseVariantOutputData> baseVariantData =
- testVariantData.testedVariantData as BaseVariantData
-
- // get single output for now
- BaseVariantOutputData variantOutputData = baseVariantData.outputs.get(0)
- BaseVariantOutputData testVariantOutputData = testVariantData.outputs.get(0)
-
- String connectedRootName = "${CONNECTED}${ANDROID_TEST.suffix}"
-
- String connectedTaskName =
- "${connectedRootName}${baseVariantData.variantConfiguration.fullName.capitalize()}"
-
- TestData testData = new TestDataImpl(testVariantData)
- BaseVariantData<? extends BaseVariantOutputData> testedVariantData =
- baseVariantData as BaseVariantData
- // create the check tasks for this test
- // first the connected one.
- ImmutableList<Task> artifactsTasks = ImmutableList.of(
- testVariantData.outputs.get(0).assembleTask,
- testedVariantData.assembleVariantTask)
-
- DeviceProviderInstrumentTestTask connectedTask =
- createDeviceProviderInstrumentTestTask(
- connectedTaskName,
- "Installs and runs the tests for ${baseVariantData.description} on connected devices.",
- testType.taskType,
- testData,
- artifactsTasks,
- new ConnectedDeviceProvider(
- sdkHandler.getSdkInfo().adb,
- new LoggerWrapper(logger)),
- CONNECTED
- )
-
- tasks.named(connectedRootName) {
- it.dependsOn connectedTask
- }
- testVariantData.connectedTestTask = connectedTask
-
- if (baseVariantData.variantConfiguration.buildType.isTestCoverageEnabled()) {
- def reportTask = project.tasks.create(
- "create${baseVariantData.variantConfiguration.fullName.capitalize()}CoverageReport",
- JacocoReportTask)
- reportTask.reportName = baseVariantData.variantConfiguration.fullName
- conventionMapping(reportTask).map("jacocoClasspath") {
- project.configurations[JacocoPlugin.ANT_CONFIGURATION_NAME]
- }
- conventionMapping(reportTask).map("coverageFile") {
- new File(connectedTask.getCoverageDir(),
- SimpleTestCallable.FILE_COVERAGE_EC)
- }
- conventionMapping(reportTask).map("classDir") {
- return baseVariantData.javaCompileTask.destinationDir
- }
- conventionMapping(reportTask).
- map("sourceDir") { baseVariantData.getJavaSourceFoldersForCoverage() }
-
- conventionMapping(reportTask).map("reportDir") {
- project.file(
- "$project.buildDir/$FD_REPORTS/coverage/${baseVariantData.variantConfiguration.dirName}")
- }
-
- reportTask.dependsOn connectedTask
- tasks.named(connectedRootName) {
- it.dependsOn reportTask
- }
- }
-
- String mainProviderTaskName = "${DEVICE}${ANDROID_TEST.suffix}"
-
- List<DeviceProvider> providers = getExtension().deviceProviders
-
- boolean hasFlavors = baseVariantData.variantConfiguration.hasFlavors()
-
- // now the providers.
- for (DeviceProvider deviceProvider : providers) {
- DeviceProviderInstrumentTestTask providerTask =
- createDeviceProviderInstrumentTestTask(
- hasFlavors ?
- "${deviceProvider.name}${ANDROID_TEST.suffix}${baseVariantData.variantConfiguration.fullName.capitalize()}" :
- "${deviceProvider.name}${ANDROID_TEST.suffix}",
- "Installs and runs the tests for Build '${baseVariantData.variantConfiguration.fullName}' using Provider '${deviceProvider.name.capitalize()}'.",
- testType.taskType,
- testData,
- artifactsTasks,
- deviceProvider,
- "$DEVICE/$deviceProvider.name"
- )
-
- tasks.named(mainProviderTaskName) {
- it.dependsOn providerTask
- }
- testVariantData.providerTestTaskList.add(providerTask)
-
- if (!deviceProvider.isConfigured()) {
- providerTask.enabled = false;
- }
- }
-
- // now the test servers
- // don't use an auto loop as it'll break the closure inside.
- List<TestServer> servers = getExtension().testServers
- for (TestServer testServer : servers) {
- def serverTask = project.tasks.create(
- hasFlavors ?
- "${testServer.name}${"upload".capitalize()}${baseVariantData.variantConfiguration.fullName}" :
- "${testServer.name}${"upload".capitalize()}",
- TestServerTask)
-
- serverTask.description =
- "Uploads APKs for Build '${baseVariantData.variantConfiguration.fullName}' to Test Server '${testServer.name.capitalize()}'."
- serverTask.group = JavaBasePlugin.VERIFICATION_GROUP
- serverTask.dependsOn testVariantOutputData.assembleTask,
- variantOutputData.assembleTask
-
- serverTask.testServer = testServer
-
- conventionMapping(serverTask).
- map("testApk") { testVariantOutputData.outputFile }
- if (!(baseVariantData instanceof LibraryVariantData)) {
- conventionMapping(serverTask).
- map("testedApk") { variantOutputData.outputFile }
- }
-
- conventionMapping(serverTask).
- map("variantName") { baseVariantData.variantConfiguration.fullName }
-
- tasks.named(DEVICE_CHECK) {
- it.dependsOn serverTask
- }
-
- if (!testServer.isConfigured()) {
- serverTask.enabled = false;
- }
- }
- }
-
- protected DeviceProviderInstrumentTestTask createDeviceProviderInstrumentTestTask(
- @NonNull String taskName,
- @NonNull String description,
- @NonNull Class<? extends DeviceProviderInstrumentTestTask> taskClass,
- @NonNull TestData testData,
- @NonNull List<Task> artifactsTasks,
- @NonNull DeviceProvider deviceProvider,
- @NonNull String subFolder) {
- DeviceProviderInstrumentTestTask testTask = project.tasks.create(
- taskName,
- taskClass as Class<DeviceProviderInstrumentTestTask>)
-
- testTask.description = description
- testTask.group = JavaBasePlugin.VERIFICATION_GROUP
-
- for (Task task : artifactsTasks) {
- testTask.dependsOn task
- }
-
- testTask.androidBuilder = androidBuilder
- testTask.testData = testData
- testTask.flavorName = testData.getFlavorName()
- testTask.deviceProvider = deviceProvider
- testTask.installOptions = getExtension().getAdbOptions().getInstallOptions();
-
- conventionMapping(testTask).map("resultsDir") {
- String rootLocation = getExtension().testOptions.resultsDir != null ?
- getExtension().testOptions.resultsDir :
- "$project.buildDir/${FD_OUTPUTS}/$FD_ANDROID_RESULTS"
-
- String flavorFolder = testData.getFlavorName()
- if (!flavorFolder.isEmpty()) {
- flavorFolder = "$FD_FLAVORS/" + flavorFolder
- }
-
- project.file("$rootLocation/$subFolder/$flavorFolder")
- }
-
- conventionMapping(testTask).map("adbExec") {
- return sdkHandler.getSdkInfo().getAdb()
- }
-
- conventionMapping(testTask).map("splitSelectExec") {
- String path = androidBuilder.targetInfo?.buildTools?.getPath(
- BuildToolInfo.PathId.SPLIT_SELECT)
- if (path != null) {
- File splitSelectExe = new File(path)
- return splitSelectExe.exists() ? splitSelectExe : null;
- } else {
- return null;
- }
- }
- testTask.processExecutor = androidBuilder.getProcessExecutor()
-
-
- conventionMapping(testTask).map("reportsDir") {
- String rootLocation = getExtension().testOptions.reportDir != null ?
- getExtension().testOptions.reportDir :
- "$project.buildDir/$FD_REPORTS/$FD_ANDROID_TESTS"
-
- String flavorFolder = testData.getFlavorName()
- if (!flavorFolder.isEmpty()) {
- flavorFolder = "$FD_FLAVORS/" + flavorFolder
- }
-
- project.file("$rootLocation/$subFolder/$flavorFolder")
- }
- conventionMapping(testTask).map("coverageDir") {
- String rootLocation = "$project.buildDir/${FD_OUTPUTS}/code-coverage"
-
- String flavorFolder = testData.getFlavorName()
- if (!flavorFolder.isEmpty()) {
- flavorFolder = "$FD_FLAVORS/" + flavorFolder
- }
-
- project.file("$rootLocation/$subFolder/$flavorFolder")
- }
-
- return testTask
- }
-
- /**
- * Class to hold data to setup the many optional
- * post-compilation steps.
- */
- public static class PostCompilationData {
-
- List<?> classGeneratingTask
-
- List<?> libraryGeneratingTask
-
- Closure<List<File>> inputFiles
-
- Closure<File> inputDir
-
- Closure<List<File>> inputLibraries
- }
-
- public void createJarTask(@NonNull TaskFactory tasks, @NonNull final VariantScope scope) {
- BaseVariantData variantData = scope.variantData;
-
- GradleVariantConfiguration config = variantData.variantConfiguration
- tasks.create("jar${config.fullName.capitalize()}Classes", AndroidJarTask) { AndroidJarTask jarTask ->
- // AndroidJarTask jarTask = project.tasks.create(
- // "jar${config.fullName.capitalize()}Classes",
- // AndroidJarTask)
-
- jarTask.setArchiveName("classes.jar");
- jarTask.setDestinationDir(new File(
- "$scope.globalScope.buildDir/${FD_INTERMEDIATES}/packaged/${config.dirName}/"))
- jarTask.from(scope.javaOutputDir);
- jarTask.dependsOn scope.javaCompileTask.name
- variantData.binayFileProviderTask = jarTask
- }
- }
-
- /**
- * Creates the post-compilation tasks for the given Variant.
- *
- * These tasks create the dex file from the .class files, plus optional intermediary steps
- * like proguard and jacoco
- *
- * @param variantData the variant data.
- */
- public void createPostCompilationTasks(TaskFactory tasks, @NonNull final VariantScope scope) {
- ApkVariantData variantData = (ApkVariantData) scope.variantData;
- GradleVariantConfiguration config = variantData.variantConfiguration
-
- // data holding dependencies and input for the dex. This gets updated as new
- // post-compilation steps are inserted between the compilation and dx.
- PostCompilationData pcData = new PostCompilationData()
- pcData.classGeneratingTask = [scope.javaCompileTask.name]
- pcData.libraryGeneratingTask =
- [variantData.variantDependency.packageConfiguration.buildDependencies]
- pcData.inputFiles = {
- variantData.javaCompileTask.outputs.files.files as List
- }
- pcData.inputDir = {
- scope.javaOutputDir
- }
- pcData.inputLibraries = {
- scope.globalScope.androidBuilder.getPackagedJars(config) as List
- }
-
- // ---- Code Coverage first -----
- boolean isTestCoverageEnabled = config.buildType.isTestCoverageEnabled() &&
- !config.type.isForTesting()
- if (isTestCoverageEnabled) {
- pcData = createJacocoTask(tasks, scope, pcData);
- }
-
- boolean isTestForApp = config.type.isForTesting() &&
- (variantData as TestVariantData).testedVariantData.variantConfiguration.type ==
- DEFAULT
- boolean isMinifyEnabled = config.isMinifyEnabled()
- boolean isMultiDexEnabled = config.isMultiDexEnabled() && !isTestForApp
- boolean isLegacyMultiDexMode = config.isLegacyMultiDexMode()
-
- // ----- Minify next ----
- File outFile = maybeCreateProguardTasks(tasks, scope, pcData);
- if (outFile != null) {
- pcData.inputFiles = { [outFile] }
- pcData.inputLibraries = { [] }
- } else if ((getExtension().dexOptions.preDexLibraries && !isMultiDexEnabled) ||
- (isMultiDexEnabled && !isLegacyMultiDexMode)) {
-
- AndroidTask<PreDex> preDexTask =
- androidTasks.create(tasks, new PreDex.ConfigAction(scope, pcData))
-
- // update dependency.
- preDexTask.dependsOn(tasks, pcData.libraryGeneratingTask)
- pcData.libraryGeneratingTask = [preDexTask.name] as List<Object>
-
- // update inputs
- if (isMultiDexEnabled) {
- pcData.inputLibraries = { [] }
-
- } else {
- pcData.inputLibraries = {
- project.fileTree(scope.getPreDexOutputDir()).files as List
- }
- }
- }
-
- AndroidTask<CreateMainDexList> createMainDexListTask = null;
- AndroidTask<RetraceMainDexList> retraceTask = null;
-
- // ----- Multi-Dex support
- if (isMultiDexEnabled && isLegacyMultiDexMode) {
- if (!isMinifyEnabled) {
- // create a task that will convert the output of the compilation
- // into a jar. This is needed by the multi-dex input.
- AndroidTask<JarMergingTask> jarMergingTask = androidTasks.create(tasks,
- new JarMergingTask.ConfigAction(scope, pcData));
-
- // update dependencies
- jarMergingTask.optionalDependsOn(
- tasks,
- pcData.classGeneratingTask,
- pcData.libraryGeneratingTask)
- pcData.libraryGeneratingTask = [jarMergingTask.name]
- pcData.classGeneratingTask = [jarMergingTask.name]
-
- // Update the inputs
- pcData.inputFiles = { [scope.getJarMergingOutputFile()] }
- pcData.inputDir = null
- pcData.inputLibraries = { [] }
- }
-
- // ----------
- // Create a task to collect the list of manifest entry points which are
- // needed in the primary dex
- AndroidTask<CreateManifestKeepList> manifestKeepListTask = androidTasks.create(tasks,
- new CreateManifestKeepList.ConfigAction(scope, pcData))
- manifestKeepListTask.dependsOn(tasks, variantData.outputs.get(0).scope.manifestProcessorTask)
-
- // ----------
- // Create a proguard task to shrink the classes to manifest components
- AndroidTask<ProGuardTask> proguardComponentsTask =
- androidTasks.create(tasks, new ProGuardTaskConfigAction(scope, pcData))
-
- // update dependencies
- proguardComponentsTask.dependsOn tasks, manifestKeepListTask
- proguardComponentsTask.optionalDependsOn(tasks,
- pcData.classGeneratingTask,
- pcData.libraryGeneratingTask)
-
- // ----------
- // Compute the full list of classes for the main dex file
- createMainDexListTask = androidTasks.create(tasks, new CreateMainDexList.ConfigAction(scope, pcData))
- createMainDexListTask.dependsOn(tasks, proguardComponentsTask)
- //createMainDexListTask.dependsOn { proguardMainDexTask }
-
- // ----------
- // If proguard is enabled, create a de-obfuscated list to aid debugging.
- if (isMinifyEnabled) {
- retraceTask = androidTasks.create(tasks,
- new RetraceMainDexList.ConfigAction(scope, pcData))
- retraceTask.dependsOn(tasks, scope.obfuscationTask, createMainDexListTask)
- }
- }
-
- AndroidTask<Dex> dexTask = androidTasks.create(tasks, new Dex.ConfigAction(scope, pcData));
- scope.setDexTask(dexTask);
-
- // dependencies, some of these could be null
- dexTask.optionalDependsOn(tasks,
- pcData.classGeneratingTask,
- pcData.libraryGeneratingTask,
- createMainDexListTask,
- retraceTask)
- }
-
- public PostCompilationData createJacocoTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope,
- @NonNull final PostCompilationData pcData) {
- AndroidTask<JacocoInstrumentTask> jacocoTask = androidTasks.create(tasks,
- new JacocoInstrumentTask.ConfigAction(scope, pcData));
-
- jacocoTask.optionalDependsOn(tasks, pcData.classGeneratingTask)
-
- Copy agentTask = getJacocoAgentTask()
- jacocoTask.dependsOn(tasks, agentTask)
-
- // update dependency.
- PostCompilationData pcData2 = new PostCompilationData()
- pcData2.classGeneratingTask = [jacocoTask.name]
- pcData2.libraryGeneratingTask = [pcData.libraryGeneratingTask, agentTask]
-
- // update inputs
- pcData2.inputFiles = {
- project.files(scope.variantData.jacocoInstrumentTask.getOutputDir()).files as List
- }
- pcData2.inputDir = {
- scope.variantData.jacocoInstrumentTask.getOutputDir()
- }
- pcData2.inputLibraries = {
- [pcData.inputLibraries.call(), [new File(agentTask.destinationDir, FILE_JACOCO_AGENT)]].
- flatten() as List
- }
-
- return pcData2
- }
-
- public void createJackTask(
- @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
- @Nullable BaseVariantData<? extends BaseVariantOutputData> testedVariantData) {
-
- GradleVariantConfiguration config = variantData.variantConfiguration
-
- // ----- Create Jill tasks -----
- JillTask jillRuntimeTask = project.tasks.create(
- "jill${config.fullName.capitalize()}RuntimeLibraries",
- JillTask)
-
- jillRuntimeTask.androidBuilder = androidBuilder
- jillRuntimeTask.dexOptions = getExtension().dexOptions
-
- conventionMapping(jillRuntimeTask).map("inputLibs") {
- androidBuilder.getBootClasspath()
- }
- conventionMapping(jillRuntimeTask).map("outputFolder") {
- project.file(
- "${project.buildDir}/${FD_INTERMEDIATES}/jill/${config.dirName}/runtime")
- }
-
- // ----
-
- JillTask jillPackagedTask = project.tasks.create(
- "jill${config.fullName.capitalize()}PackagedLibraries",
- JillTask)
-
- jillPackagedTask.dependsOn variantData.variantDependency.packageConfiguration.buildDependencies
- jillPackagedTask.androidBuilder = androidBuilder
- jillPackagedTask.dexOptions = getExtension().dexOptions
-
- conventionMapping(jillPackagedTask).map("inputLibs") {
- androidBuilder.getPackagedJars(config)
- }
- conventionMapping(jillPackagedTask).map("outputFolder") {
- project.file(
- "${project.buildDir}/${FD_INTERMEDIATES}/jill/${config.dirName}/packaged")
- }
-
- // ----- Create Jack Task -----
- JackTask compileTask = project.tasks.create(
- "compile${config.fullName.capitalize()}JavaWithJack",
- JackTask)
- compileTask.isVerbose = isVerbose()
- compileTask.isDebugLog = isDebugLog()
-
- // Jack is compiling and also providing the binary and mapping files.
- variantData.javaCompileTask = compileTask
- variantData.mappingFileProviderTask = compileTask
- variantData.binayFileProviderTask = compileTask
-
- variantData.javaCompileTask.dependsOn variantData.sourceGenTask, jillRuntimeTask, jillPackagedTask
- variantData.compileTask.dependsOn variantData.javaCompileTask
- // TODO - dependency information for the compile classpath is being lost.
- // Add a temporary approximation
- compileTask.dependsOn variantData.variantDependency.compileConfiguration.buildDependencies
-
- compileTask.androidBuilder = androidBuilder
- conventionMapping(compileTask).
- map("javaMaxHeapSize") { getExtension().dexOptions.getJavaMaxHeapSize() }
-
- compileTask.source = variantData.getJavaSources()
-
- compileTask.multiDexEnabled = config.isMultiDexEnabled()
- compileTask.minSdkVersion = config.minSdkVersion.apiLevel
-
- // if the tested variant is an app, add its classpath. For the libraries,
- // it's done automatically since the classpath includes the library output as a normal
- // dependency.
- if (testedVariantData instanceof ApplicationVariantData) {
- JackTask jackTask = (JackTask) testedVariantData.javaCompileTask
- conventionMapping(compileTask).map("classpath") {
- project.fileTree(jillRuntimeTask.outputFolder) +
- jackTask.classpath +
- project.fileTree(jackTask.jackFile)
- }
- } else {
- conventionMapping(compileTask).map("classpath") {
- project.fileTree(jillRuntimeTask.outputFolder)
- }
- }
-
- conventionMapping(compileTask).map("packagedLibraries") {
- project.fileTree(jillPackagedTask.outputFolder).files
- }
-
- conventionMapping(compileTask).map("destinationDir") {
- project.file("$project.buildDir/${FD_INTERMEDIATES}/dex/${config.dirName}")
- }
-
- conventionMapping(compileTask).map("jackFile") {
- project.file(
- "$project.buildDir/${FD_INTERMEDIATES}/packaged/${config.dirName}/classes.zip")
- }
-
- conventionMapping(compileTask).map("tempFolder") {
- project.file("$project.buildDir/${FD_INTERMEDIATES}/tmp/jack/${config.dirName}")
- }
- if (config.isMinifyEnabled()) {
- conventionMapping(compileTask).map("proguardFiles") {
- // since all the output use the same resources, we can use the first output
- // to query for a proguard file.
- BaseVariantOutputData variantOutputData = variantData.outputs.get(0)
-
- List<File> proguardFiles = config.getProguardFiles(true /*includeLibs*/,
- [getExtension().getDefaultProguardFile(DEFAULT_PROGUARD_CONFIG_FILE)])
- File proguardResFile = variantOutputData.processResourcesTask.proguardOutputFile
- if (proguardResFile != null) {
- proguardFiles.add(proguardResFile)
- }
- // for tested app, we only care about their aapt config since the base
- // configs are the same files anyway.
- if (testedVariantData != null) {
- // use single output for now.
- proguardResFile =
- testedVariantData.outputs.get(0).processResourcesTask.proguardOutputFile
- if (proguardResFile != null) {
- proguardFiles.add(proguardResFile)
- }
- }
-
- return proguardFiles
- }
-
- compileTask.mappingFile = project.file(
- "${project.buildDir}/${FD_OUTPUTS}/mapping/${variantData.variantConfiguration.dirName}/mapping.txt")
- }
-
- configureLanguageLevel(compileTask)
- }
-
- /**
- * Configures the source and target language level of a compile task. If the user has set it
- * explicitly, we obey the setting. Otherwise we change the default language level based on the
- * compile SDK version.
- *
- * <p>This method modifies getExtension().compileOptions, to propagate the language level to Studio.
- */
- private void configureLanguageLevel(AbstractCompile compileTask) {
- def compileOptions = getExtension().compileOptions
- JavaVersion javaVersionToUse
-
- Integer compileSdkLevel =
- AndroidTargetHash.getVersionFromHash(getExtension().compileSdkVersion)?.apiLevel
- switch (compileSdkLevel) {
- case null: // Default to 1.6 if we fail to parse compile SDK version.
- case 0..20:
- javaVersionToUse = JavaVersion.VERSION_1_6
- break
- default:
- javaVersionToUse = JavaVersion.VERSION_1_7
- break
- }
-
- def jdkVersion = JavaVersion.toVersion(System.getProperty("java.specification.version"))
- if (jdkVersion < javaVersionToUse) {
- logger.info(
- "Default language level for 'compileSdkVersion $compileSdkLevel' is " +
- "$javaVersionToUse, but the JDK used is $jdkVersion, so the JDK " +
- "language level will be used.")
- javaVersionToUse = jdkVersion
- }
-
- compileOptions.defaultJavaVersion = javaVersionToUse
-
- conventionMapping(compileTask).map("sourceCompatibility") {
- compileOptions.sourceCompatibility.toString()
- }
- conventionMapping(compileTask).map("targetCompatibility") {
- compileOptions.targetCompatibility.toString()
- }
- }
-
- /**
- * Creates the final packaging task, and optionally the zipalign task (if the variant is signed)
- * @param variantData
- * @param assembleTask an optional assembleTask to be used. If null a new one is created. The
- * assembleTask is always set in the Variant.
- * @param publishApk if true the generated APK gets published.
- */
- public void createPackagingTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope variantScope,
- boolean publishApk) {
- ApkVariantData variantData = (ApkVariantData) variantScope.getVariantData()
-
- GradleVariantConfiguration config = variantData.variantConfiguration
- boolean signedApk = variantData.isSigned()
- // use a dynamic property invocation due to gradle issue.
- String defaultLocation = "$project.buildDir/${FD_OUTPUTS}/apk"
- String apkLocation = defaultLocation
- if (project.hasProperty(PROPERTY_APK_LOCATION)) {
- apkLocation = project.getProperties().get(PROPERTY_APK_LOCATION)
- }
- SigningConfig sc = (SigningConfig) config.signingConfig
-
- boolean multiOutput = variantData.outputs.size() > 1
-
- // loop on all outputs. The only difference will be the name of the task, and location
- // of the generated data.
- for (ApkVariantOutputData vod : variantData.outputs) {
- VariantOutputScope variantOutputScope = vod.scope;
-
- // create final var inside the loop to ensure the closures will work.
- final ApkVariantOutputData variantOutputData = vod
-
- String outputName = variantOutputData.fullName
-
- // When shrinking resources, rather than having the packaging task
- // directly map to the packageOutputFile of ProcessAndroidResources,
- // we insert the ShrinkResources task into the chain, such that its
- // input is the ProcessAndroidResources packageOutputFile, and its
- // output is what the PackageApplication task reads.
- AndroidTask<ShrinkResources> shrinkTask = null;
-
- if (config.isMinifyEnabled() && config.getBuildType().isShrinkResources() && !config
- .getUseJack()) {
- shrinkTask = androidTasks.create(tasks,
- new ShrinkResources.ConfigAction(variantOutputScope));
- shrinkTask.dependsOn(tasks,
- variantScope.obfuscationTask,
- variantOutputScope.manifestProcessorTask,
- variantOutputScope.processResourcesTask);
- }
-
- AndroidTask<PackageApplication> packageApp = androidTasks.create(tasks,
- new PackageApplication.ConfigAction(variantOutputScope));
-
- packageApp.dependsOn(tasks,
- variantOutputScope.processResourcesTask,
- variantOutputScope.variantScope.processJavaResourcesTask,
- variantOutputScope.variantScope.getNdkBuildable());
-
- packageApp.optionalDependsOn(
- tasks,
- shrinkTask,
- variantOutputScope.variantScope.dexTask,
- variantOutputScope.variantScope.javaCompileTask,
- variantData.javaCompileTask, // TODO: Remove when Jack is converted to AndroidTask.
- variantOutputData.packageSplitResourcesTask,
- variantOutputData.packageSplitAbiTask);
-
- AndroidTask appTask = packageApp
-
- if (signedApk) {
- if (variantData.zipAlignEnabled) {
- AndroidTask<ZipAlign> zipAlignTask = androidTasks.create(
- tasks,
- new ZipAlign.ConfigAction(variantOutputScope));
- zipAlignTask.dependsOn(tasks, packageApp);
- if (variantOutputData.splitZipAlign != null) {
- zipAlignTask.dependsOn(tasks, variantOutputData.splitZipAlign)
- }
- appTask = zipAlignTask
- }
- }
-
- assert variantData.assembleVariantTask != null
-
- // Add an assemble task
- if (multiOutput) {
- // create a task for this output
- variantOutputData.assembleTask = createAssembleTask(variantOutputData)
-
- // variant assemble task depends on each output assemble task.
- variantData.assembleVariantTask.dependsOn variantOutputData.assembleTask
- } else {
- // single output
- variantOutputData.assembleTask = variantData.assembleVariantTask
- }
-
- if (!signedApk && variantOutputData.packageSplitResourcesTask != null) {
- // in case we are not signing the resulting APKs and we have some pure splits
- // we should manually copy them from the intermediate location to the final
- // apk location unmodified.
- Copy copyTask = project.tasks.create(
- "copySplit${outputName.capitalize()}",
- Copy)
- copyTask.destinationDir = new File(apkLocation as String);
- copyTask.from(variantOutputData.packageSplitResourcesTask.getOutputDirectory())
- variantOutputData.assembleTask.dependsOn(copyTask)
- copyTask.mustRunAfter(appTask.name)
- }
-
- variantOutputData.assembleTask.dependsOn appTask.name
-
- if (publishApk) {
- String projectBaseName = globalScope.projectBaseName;
-
- // if this variant is the default publish config or we also should publish non
- // defaults, proceed with declaring our artifacts.
- if (getExtension().defaultPublishConfig.equals(outputName)) {
- appTask.configure(tasks) { FileSupplier packageTask ->
- project.artifacts.add("default", new ApkPublishArtifact(
- projectBaseName,
- null,
- packageTask))
- }
-
- for (FileSupplier outputFileProvider :
- variantOutputData.getSplitOutputFileSuppliers()) {
- project.artifacts.add("default", new ApkPublishArtifact(
- projectBaseName,
- null,
- outputFileProvider))
- }
-
- if (variantOutputData.getMetadataFile() != null) {
- project.artifacts.add("default-metadata",
- new MetadataPublishArtifact(projectBaseName, null,
- variantOutputData.getMetadataFile()));
- }
-
- if (variantData.getMappingFileProvider() != null) {
- project.artifacts.add("default-mapping",
- new MappingPublishArtifact(projectBaseName,
- null,
- variantData.getMappingFileProvider()));
- }
- }
-
- if (getExtension().publishNonDefault) {
- appTask.configure(tasks) { FileSupplier packageTask ->
- project.artifacts.add(
- variantData.variantDependency.publishConfiguration.name,
- new ApkPublishArtifact(
- projectBaseName,
- null,
- packageTask))
- }
-
- for (FileSupplier outputFileProvider :
- variantOutputData.getSplitOutputFileSuppliers()) {
- project.artifacts.add(
- variantData.variantDependency.publishConfiguration.name,
- new ApkPublishArtifact(
- projectBaseName,
- null,
- outputFileProvider))
- }
-
- if (variantOutputData.getMetadataFile() != null) {
- project.artifacts.add(
- variantData.variantDependency.metadataConfiguration.name,
- new MetadataPublishArtifact(projectBaseName, null,
- variantOutputData.getMetadataFile()));
- }
-
- if (variantData.getMappingFileProvider() != null) {
- project.artifacts.add(
- variantData.variantDependency.mappingConfiguration.name,
- new MappingPublishArtifact(projectBaseName,
- null,
- variantData.getMappingFileProvider()));
- }
-
- if (variantData.classesJarTask != null) {
- project.artifacts.add(
- variantData.variantDependency.classesConfiguration.name,
- variantData.classesJarTask)
- }
- }
- }
- }
-
- // create install task for the variant Data. This will deal with finding the
- // right output if there are more than one.
- // Add a task to install the application package
- if (signedApk) {
- AndroidTask<InstallVariantTask> installTask = androidTasks.create(
- tasks,
- new InstallVariantTask.ConfigAction(variantScope));
- installTask.dependsOn(tasks, variantData.assembleVariantTask)
- }
-
- if (getExtension().lintOptions.checkReleaseBuilds) {
- createLintVitalTask(variantData)
- }
-
- // add an uninstall task
- AndroidTask<UninstallTask> uninstallTask = androidTasks.create(
- tasks,
- new UninstallTask.ConfigAction(variantScope));
-
- tasks.named(UNINSTALL_ALL) {
- it.dependsOn uninstallTask.name
- }
- }
-
- public Task createAssembleTask(
- @NonNull BaseVariantOutputData variantOutputData) {
- Task assembleTask = project.tasks.
- create("assemble${variantOutputData.fullName.capitalize()}")
- return assembleTask
- }
-
- public Task createAssembleTask(
- TaskFactory tasks,
- @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
- Task assembleTask = project.tasks.
- create("assemble${variantData.variantConfiguration.fullName.capitalize()}")
- return assembleTask;
- }
-
- public Copy getJacocoAgentTask() {
- if (jacocoAgentTask == null) {
- jacocoAgentTask = project.tasks.create("unzipJacocoAgent", Copy)
- jacocoAgentTask.from {
- project.configurations.getByName(JacocoPlugin.AGENT_CONFIGURATION_NAME).
- collect { project.zipTree(it) }
- }
- jacocoAgentTask.include FILE_JACOCO_AGENT
- jacocoAgentTask.into "$project.buildDir/${FD_INTERMEDIATES}/jacoco"
- }
-
- return jacocoAgentTask
- }
-
- /**
- * creates a zip align. This does not use convention mapping,
- * and is meant to let other plugin create zip align tasks.
- *
- * @param name the name of the task
- * @param inputFile the input file
- * @param outputFile the output file
- *
- * @return the task
- */
- @NonNull
- ZipAlign createZipAlignTask(
- @NonNull String name,
- @NonNull File inputFile,
- @NonNull File outputFile) {
- // Add a task to zip align application package
- def zipAlignTask = project.tasks.create(name, ZipAlign)
-
- zipAlignTask.inputFile = inputFile
- zipAlignTask.outputFile = outputFile
- conventionMapping(zipAlignTask).map("zipAlignExe") {
- String path = androidBuilder.targetInfo?.buildTools?.getPath(ZIP_ALIGN)
- if (path != null) {
- return new File(path)
- }
-
- return null
- }
-
- return zipAlignTask
- }
-
- /**
- * Creates the proguarding task for the given Variant if necessary.
- * @param variantData the variant data.
- * @param testedVariantData optional. variant data representing the tested variant, null if the
- * variant is not a test variant
- * @return null if the proguard task was not created, otherwise the expected outputFile.
- */
- @Nullable
- public File maybeCreateProguardTasks(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope,
- @NonNull final PostCompilationData pcData) {
- if (!scope.variantData.getVariantConfiguration().isMinifyEnabled()) {
- return null;
- }
-
- AndroidTask<AndroidProGuardTask> proguardTask =
- androidTasks.create(tasks, new AndroidProGuardTask.ConfigAction(scope, pcData));
- scope.obfuscationTask = proguardTask
-
- // update dependency.
- proguardTask.optionalDependsOn(tasks, pcData.classGeneratingTask, pcData.libraryGeneratingTask)
- pcData.libraryGeneratingTask = [proguardTask.name]
- pcData.classGeneratingTask = [proguardTask.name]
-
- // Return output file.
- return scope.getProguardOutputFile();
- }
-
- public void createReportTasks(
- List<BaseVariantData<? extends BaseVariantOutputData>> variantDataList) {
- def dependencyReportTask = project.tasks.create("androidDependencies", DependencyReportTask)
- dependencyReportTask.setDescription("Displays the Android dependencies of the project.")
- dependencyReportTask.setVariants(variantDataList)
- dependencyReportTask.setGroup(ANDROID_GROUP)
-
- def signingReportTask = project.tasks.create("signingReport", SigningReportTask)
- signingReportTask.setDescription("Displays the signing info for each variant.")
- signingReportTask.setVariants(variantDataList)
- signingReportTask.setGroup(ANDROID_GROUP)
- }
-
- public void createAnchorTasks(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope) {
- createPreBuildTasks(tasks, scope)
-
- // also create sourceGenTask
- final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
- scope.sourceGenTask = androidTasks.create(tasks,
- "generate${variantData.variantConfiguration.fullName.capitalize()}Sources") { Task task ->
- variantData.sourceGenTask = task
- }
- // and resGenTask
- scope.resourceGenTask = androidTasks.create(tasks,
- "generate${variantData.variantConfiguration.fullName.capitalize()}Resources") { Task task ->
- variantData.resourceGenTask = task
- }
-
- scope.assetGenTask = androidTasks.create(tasks,
- "generate${variantData.variantConfiguration.fullName.capitalize()}Assets") { Task task ->
- variantData.assetGenTask = task
- }
-
- // and compile task
- createCompileAnchorTask(tasks, scope)
- }
-
- private void createPreBuildTasks(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
- BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
- variantData.preBuildTask = project.tasks.create(
- "pre${variantData.variantConfiguration.fullName.capitalize()}Build")
-
- def prepareDependenciesTask = project.tasks.create(
- "prepare${variantData.variantConfiguration.fullName.capitalize()}Dependencies",
- PrepareDependenciesTask)
-
- variantData.prepareDependenciesTask = prepareDependenciesTask
- prepareDependenciesTask.dependsOn variantData.preBuildTask
-
- prepareDependenciesTask.androidBuilder = androidBuilder
- prepareDependenciesTask.variant = variantData
-
- // for all libraries required by the configurations of this variant, make this task
- // depend on all the tasks preparing these libraries.
- VariantDependencies configurationDependencies = variantData.variantDependency
- prepareDependenciesTask.addChecker(configurationDependencies.checker)
-
- for (LibraryDependencyImpl lib : configurationDependencies.libraries) {
- dependencyManager.
- addDependencyToPrepareTask(variantData, prepareDependenciesTask, lib)
- }
- }
-
- private void createCompileAnchorTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
- BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
- scope.compileTask = androidTasks.create(tasks,
- "compile${variantData.variantConfiguration.fullName.capitalize()}Sources") { DefaultTask task ->
- variantData.compileTask = task
- variantData.compileTask.group = BUILD_GROUP
- }
- variantData.assembleVariantTask.dependsOn scope.compileTask.name
- }
-
- public void createCheckManifestTask(
- @NonNull TaskFactory tasks,
- @NonNull VariantScope scope) {
- BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
- String name = variantData.variantConfiguration.fullName
- scope.checkManifestTask = androidTasks.create(tasks,
- "check${name.capitalize()}Manifest",
- CheckManifest) { CheckManifest checkManifestTask ->
- variantData.checkManifestTask = checkManifestTask
- checkManifestTask.variantName = name
- ConventionMappingHelper.map(variantData.checkManifestTask, "manifest") {
- variantData.variantConfiguration.getDefaultSourceSet().manifestFile
- }
- }
- scope.checkManifestTask.dependsOn(tasks, variantData.preBuildTask)
- variantData.prepareDependenciesTask.dependsOn(scope.checkManifestTask.name)
- }
-
- public static void optionalDependsOn(@NonNull Task main, Task... dependencies) {
- for (Task dependency : dependencies) {
- if (dependency != null) {
- main.dependsOn dependency
- }
- }
- }
-
- public static void optionalDependsOn(@NonNull Task main, @NonNull List<?> dependencies) {
- for (Object dependency : dependencies) {
- if (dependency != null) {
- main.dependsOn dependency
- }
- }
- }
-
- @NonNull
- private List<ManifestDependencyImpl> getManifestDependencies(
- List<LibraryDependency> libraries) {
-
- List<ManifestDependencyImpl> list = Lists.newArrayListWithCapacity(libraries.size())
-
- for (LibraryDependency lib : libraries) {
- // get the dependencies
- List<ManifestDependencyImpl> children = getManifestDependencies(lib.dependencies)
- list.add(new ManifestDependencyImpl(lib.getName(), lib.manifest, children))
- }
-
- return list
- }
-
- @NonNull
- protected Logger getLogger() {
- return logger
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.java
new file mode 100644
index 0000000..4c646b5
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TaskManager.java
@@ -0,0 +1,2459 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal;
+
+import static com.android.build.OutputFile.DENSITY;
+import static com.android.builder.core.BuilderConstants.CONNECTED;
+import static com.android.builder.core.BuilderConstants.DEVICE;
+import static com.android.builder.core.BuilderConstants.FD_ANDROID_RESULTS;
+import static com.android.builder.core.BuilderConstants.FD_ANDROID_TESTS;
+import static com.android.builder.core.BuilderConstants.FD_FLAVORS_ALL;
+import static com.android.builder.core.VariantType.ANDROID_TEST;
+import static com.android.builder.core.VariantType.DEFAULT;
+import static com.android.builder.core.VariantType.UNIT_TEST;
+import static com.android.sdklib.BuildToolInfo.PathId.ZIP_ALIGN;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.OutputFile;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.AndroidGradleOptions;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.core.GradleVariantConfiguration;
+import com.android.build.gradle.internal.coverage.JacocoInstrumentTask;
+import com.android.build.gradle.internal.coverage.JacocoPlugin;
+import com.android.build.gradle.internal.coverage.JacocoReportTask;
+import com.android.build.gradle.internal.dependency.LibraryDependencyImpl;
+import com.android.build.gradle.internal.dependency.ManifestDependencyImpl;
+import com.android.build.gradle.internal.dependency.VariantDependencies;
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.dsl.AbiSplitOptions;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.internal.dsl.PackagingOptions;
+import com.android.build.gradle.internal.publishing.ApkPublishArtifact;
+import com.android.build.gradle.internal.publishing.MappingPublishArtifact;
+import com.android.build.gradle.internal.publishing.MetadataPublishArtifact;
+import com.android.build.gradle.internal.scope.AndroidTask;
+import com.android.build.gradle.internal.scope.AndroidTaskRegistry;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.GlobalScope;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantOutputScope;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.tasks.AndroidReportTask;
+import com.android.build.gradle.internal.tasks.CheckManifest;
+import com.android.build.gradle.internal.tasks.DependencyReportTask;
+import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask;
+import com.android.build.gradle.internal.tasks.ExtractJavaResourcesTask;
+import com.android.build.gradle.internal.tasks.FileSupplier;
+import com.android.build.gradle.internal.tasks.GenerateApkDataTask;
+import com.android.build.gradle.internal.tasks.InstallVariantTask;
+import com.android.build.gradle.internal.tasks.MergeJavaResourcesTask;
+import com.android.build.gradle.internal.tasks.MockableAndroidJarTask;
+import com.android.build.gradle.internal.tasks.PrepareDependenciesTask;
+import com.android.build.gradle.internal.tasks.SigningReportTask;
+import com.android.build.gradle.internal.tasks.SourceSetsTask;
+import com.android.build.gradle.internal.tasks.TestServerTask;
+import com.android.build.gradle.internal.tasks.UninstallTask;
+import com.android.build.gradle.internal.tasks.multidex.CreateMainDexList;
+import com.android.build.gradle.internal.tasks.multidex.CreateManifestKeepList;
+import com.android.build.gradle.internal.tasks.multidex.JarMergingTask;
+import com.android.build.gradle.internal.tasks.multidex.RetraceMainDexList;
+import com.android.build.gradle.internal.test.TestDataImpl;
+import com.android.build.gradle.internal.test.report.ReportType;
+import com.android.build.gradle.internal.variant.ApkVariantData;
+import com.android.build.gradle.internal.variant.ApkVariantOutputData;
+import com.android.build.gradle.internal.variant.ApplicationVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.build.gradle.internal.variant.LibraryVariantData;
+import com.android.build.gradle.internal.variant.TestVariantData;
+import com.android.build.gradle.tasks.AidlCompile;
+import com.android.build.gradle.tasks.AndroidJarTask;
+import com.android.build.gradle.tasks.AndroidProGuardTask;
+import com.android.build.gradle.tasks.CompatibleScreensManifest;
+import com.android.build.gradle.tasks.Dex;
+import com.android.build.gradle.tasks.GenerateBuildConfig;
+import com.android.build.gradle.tasks.GenerateResValues;
+import com.android.build.gradle.tasks.GenerateSplitAbiRes;
+import com.android.build.gradle.tasks.JackTask;
+import com.android.build.gradle.tasks.JavaResourcesProvider;
+import com.android.build.gradle.tasks.JillTask;
+import com.android.build.gradle.tasks.Lint;
+import com.android.build.gradle.tasks.MergeAssets;
+import com.android.build.gradle.tasks.MergeManifests;
+import com.android.build.gradle.tasks.MergeResources;
+import com.android.build.gradle.tasks.NdkCompile;
+import com.android.build.gradle.tasks.PackageApplication;
+import com.android.build.gradle.tasks.PackageSplitAbi;
+import com.android.build.gradle.tasks.PackageSplitRes;
+import com.android.build.gradle.tasks.PreDex;
+import com.android.build.gradle.tasks.ProcessAndroidResources;
+import com.android.build.gradle.tasks.ProcessManifest;
+import com.android.build.gradle.tasks.ProcessTestManifest;
+import com.android.build.gradle.tasks.RenderscriptCompile;
+import com.android.build.gradle.tasks.ShrinkResources;
+import com.android.build.gradle.tasks.SplitZipAlign;
+import com.android.build.gradle.tasks.ZipAlign;
+import com.android.build.gradle.tasks.factory.JavaCompileConfigAction;
+import com.android.build.gradle.tasks.factory.ProGuardTaskConfigAction;
+import com.android.build.gradle.tasks.factory.ProcessJavaResConfigAction;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.VariantConfiguration;
+import com.android.builder.core.VariantType;
+import com.android.builder.dependency.LibraryDependency;
+import com.android.builder.internal.testing.SimpleTestCallable;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.sdk.TargetInfo;
+import com.android.builder.signing.SignedJarBuilder;
+import com.android.builder.testing.ConnectedDeviceProvider;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.builder.testing.api.TestServer;
+import com.android.sdklib.IAndroidTarget;
+import com.android.utils.StringHelper;
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.execution.TaskExecutionGraph;
+import org.gradle.api.file.ConfigurableFileCollection;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.file.FileTree;
+import org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.plugins.BasePlugin;
+import org.gradle.api.plugins.JavaBasePlugin;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.reporting.ConfigurableReport;
+import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.bundling.Jar;
+import org.gradle.api.tasks.compile.AbstractCompile;
+import org.gradle.api.tasks.compile.JavaCompile;
+import org.gradle.api.tasks.testing.Test;
+import org.gradle.api.tasks.testing.TestTaskReports;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+import groovy.lang.Closure;
+import proguard.gradle.ProGuardTask;
+
+/**
+ * Manages tasks creation.
+ */
+public abstract class TaskManager {
+
+ public static final String FILE_JACOCO_AGENT = "jacocoagent.jar";
+
+ public static final String DEFAULT_PROGUARD_CONFIG_FILE = "proguard-android.txt";
+
+ public static final String DIR_BUNDLES = "bundles";
+
+ public static final String INSTALL_GROUP = "Install";
+
+ public static final String BUILD_GROUP = BasePlugin.BUILD_GROUP;
+
+ public static final String ANDROID_GROUP = "Android";
+
+ protected Project project;
+
+ protected AndroidBuilder androidBuilder;
+
+ private DependencyManager dependencyManager;
+
+ protected SdkHandler sdkHandler;
+
+ protected AndroidConfig extension;
+
+ protected ToolingModelBuilderRegistry toolingRegistry;
+
+ private final GlobalScope globalScope;
+
+ private AndroidTaskRegistry androidTasks = new AndroidTaskRegistry();
+
+ private Logger logger;
+
+ protected boolean isNdkTaskNeeded = true;
+
+ // Task names
+ // TODO: Convert to AndroidTask.
+ private static final String MAIN_PREBUILD = "preBuild";
+
+ private static final String UNINSTALL_ALL = "uninstallAll";
+
+ private static final String DEVICE_CHECK = "deviceCheck";
+
+ protected static final String CONNECTED_CHECK = "connectedCheck";
+
+ private static final String ASSEMBLE_ANDROID_TEST = "assembleAndroidTest";
+
+ private static final String SOURCE_SETS = "sourceSets";
+
+ private static final String LINT = "lint";
+
+ protected static final String LINT_COMPILE = "compileLint";
+
+ // Tasks
+ private Copy jacocoAgentTask;
+
+ public MockableAndroidJarTask createMockableJar;
+
+ public TaskManager(
+ Project project,
+ AndroidBuilder androidBuilder,
+ AndroidConfig extension,
+ SdkHandler sdkHandler,
+ DependencyManager dependencyManager,
+ ToolingModelBuilderRegistry toolingRegistry) {
+ this.project = project;
+ this.androidBuilder = androidBuilder;
+ this.sdkHandler = sdkHandler;
+ this.extension = extension;
+ this.toolingRegistry = toolingRegistry;
+ this.dependencyManager = dependencyManager;
+ logger = Logging.getLogger(this.getClass());
+
+ globalScope = new GlobalScope(
+ project,
+ androidBuilder,
+ checkNotNull((String) project.getProperties().get("archivesBaseName")),
+ extension,
+ sdkHandler,
+ toolingRegistry);
+ }
+
+ private boolean isVerbose() {
+ return project.getLogger().isEnabled(LogLevel.INFO);
+ }
+
+ private boolean isDebugLog() {
+ return project.getLogger().isEnabled(LogLevel.DEBUG);
+ }
+
+ /**
+ * Creates the tasks for a given BaseVariantData.
+ */
+ public abstract void createTasksForVariantData(@NonNull TaskFactory tasks,
+ @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData);
+
+ public GlobalScope getGlobalScope() {
+ return globalScope;
+ }
+
+ /**
+ * Returns a collection of buildables that creates native object.
+ *
+ * A buildable is considered to be any object that can be used as the argument to
+ * Task.dependsOn. This could be a Task or a BuildableModelElement (e.g. BinarySpec).
+ */
+ protected Collection<Object> getNdkBuildable(BaseVariantData variantData) {
+ return Collections.<Object>singleton(variantData.ndkCompileTask);
+ }
+
+ /**
+ * Override to configure NDK data in the scope.
+ */
+ public void configureScopeForNdk(@NonNull VariantScope scope) {
+ final BaseVariantData variantData = scope.getVariantData();
+ scope.setNdkSoFolder(Collections.singleton(new File(
+ scope.getGlobalScope().getIntermediatesDir(),
+ "ndk/" + variantData.getVariantConfiguration().getDirName() + "/lib")));
+ File objFolder = new File(scope.getGlobalScope().getIntermediatesDir(),
+ "ndk/" + variantData.getVariantConfiguration().getDirName() + "/obj");
+ scope.setNdkObjFolder(objFolder);
+ for (Abi abi : NdkHandler.getAbiList()) {
+ scope.addNdkDebuggableLibraryFolders(abi,
+ new File(objFolder, "local/" + abi.getName()));
+ }
+
+ }
+
+ protected AndroidConfig getExtension() {
+ return extension;
+ }
+
+ public void resolveDependencies(
+ @NonNull VariantDependencies variantDeps,
+ @Nullable VariantDependencies testedVariantDeps,
+ @Nullable String testedProjectPath) {
+ dependencyManager.resolveDependencies(variantDeps, testedVariantDeps, testedProjectPath);
+ }
+
+ /**
+ * Create tasks before the evaluation (on plugin apply). This is useful for tasks that
+ * could be referenced by custom build logic.
+ */
+ public void createTasksBeforeEvaluate(@NonNull TaskFactory tasks) {
+ tasks.create(UNINSTALL_ALL, new Action<Task>() {
+ @Override
+ public void execute(Task uninstallAllTask) {
+ uninstallAllTask.setDescription("Uninstall all applications.");
+ uninstallAllTask.setGroup(INSTALL_GROUP);
+ }
+ });
+
+ tasks.create(DEVICE_CHECK, new Action<Task>() {
+ @Override
+ public void execute(Task deviceCheckTask) {
+ deviceCheckTask.setDescription(
+ "Runs all device checks using Device Providers and Test Servers.");
+ deviceCheckTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+ }
+ });
+
+ tasks.create(CONNECTED_CHECK, new Action<Task>() {
+ @Override
+ public void execute(Task connectedCheckTask) {
+ connectedCheckTask.setDescription(
+ "Runs all device checks on currently connected devices.");
+ connectedCheckTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+ }
+ });
+
+ tasks.create(MAIN_PREBUILD);
+
+ tasks.create(SOURCE_SETS, SourceSetsTask.class, new Action<SourceSetsTask>() {
+ @Override
+ public void execute(SourceSetsTask sourceSetsTask) {
+ sourceSetsTask.setConfig(extension);
+ sourceSetsTask.setDescription(
+ "Prints out all the source sets defined in this project.");
+ sourceSetsTask.setGroup(ANDROID_GROUP);
+ }
+ });
+
+ tasks.create(ASSEMBLE_ANDROID_TEST, new Action<Task>() {
+ @Override
+ public void execute(Task assembleAndroidTestTask) {
+ assembleAndroidTestTask.setGroup(BasePlugin.BUILD_GROUP);
+ assembleAndroidTestTask.setDescription("Assembles all the Test applications.");
+ }
+ });
+
+ tasks.create(LINT, Lint.class, new Action<Lint>() {
+ @Override
+ public void execute(Lint lintTask) {
+ lintTask.setDescription("Runs lint on all variants.");
+ lintTask.setVariantName("");
+ lintTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+ lintTask.setLintOptions(getExtension().getLintOptions());
+ lintTask.setSdkHome(sdkHandler.getSdkFolder());
+ lintTask.setToolingRegistry(toolingRegistry);
+ }
+ });
+ tasks.named(JavaBasePlugin.CHECK_TASK_NAME, new Action<Task>() {
+ @Override
+ public void execute(Task it) {
+ it.dependsOn(LINT);
+ }
+ });
+ createLintCompileTask(tasks);
+ }
+
+ public void createMockableJarTask() {
+ createMockableJar =
+ project.getTasks().create("mockableAndroidJar", MockableAndroidJarTask.class);
+ createMockableJar.setGroup(BUILD_GROUP);
+ createMockableJar.setDescription(
+ "Creates a version of android.jar that's suitable for unit tests.");
+ createMockableJar.setReturnDefaultValues(
+ extension.getTestOptions().getUnitTests().isReturnDefaultValues());
+
+ ConventionMappingHelper.map(createMockableJar, "androidJar", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ checkNotNull(androidBuilder.getTarget(), "ensureTargetSetup not called");
+ return new File(androidBuilder.getTarget().getPath(IAndroidTarget.ANDROID_JAR));
+ }
+ });
+
+ ConventionMappingHelper.map(createMockableJar, "outputFile", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ // Since the file ends up in $rootProject.buildDir, it will survive clean
+ // operations - projects generated by AS don't have a top-level clean task that
+ // would delete the top-level build directory. This means that the name has to
+ // encode all the necessary information, otherwise the task will be UP-TO-DATE
+ // even if the file should be regenerated. That's why we put the SDK version and
+ // "default-values" in there, so if one project uses the returnDefaultValues flag,
+ // it will just generate a new file and not change the semantics for other
+ // sub-projects. There's an implicit "v1" there as well, if we ever change the
+ // generator logic, the names will have to be changed.
+ String fileExt;
+ if (createMockableJar.getReturnDefaultValues()) {
+ fileExt = ".default-values.jar";
+ } else {
+ fileExt = ".jar";
+ }
+ File outDir = new File(
+ project.getRootProject().getBuildDir(),
+ AndroidProject.FD_GENERATED);
+
+ CharMatcher safeCharacters =
+ CharMatcher.JAVA_LETTER_OR_DIGIT.or(CharMatcher.anyOf("-."));
+ String sdkName =
+ safeCharacters.negate().replaceFrom(extension.getCompileSdkVersion(), '-');
+
+ return new File(outDir, "mockable-" + sdkName + fileExt);
+ }
+ });
+ }
+
+ public void createMergeAppManifestsTask(
+ @NonNull TaskFactory tasks,
+ @NonNull VariantScope variantScope) {
+
+ ApplicationVariantData appVariantData =
+ (ApplicationVariantData) variantScope.getVariantData();
+ Set<String> screenSizes = appVariantData.getCompatibleScreens();
+
+ // loop on all outputs. The only difference will be the name of the task, and location
+ // of the generated manifest
+ for (final BaseVariantOutputData vod : appVariantData.getOutputs()) {
+ VariantOutputScope scope = vod.getScope();
+
+ AndroidTask<CompatibleScreensManifest> csmTask = null;
+ if (vod.getMainOutputFile().getFilter(DENSITY) != null) {
+ csmTask = androidTasks.create(tasks,
+ new CompatibleScreensManifest.ConfigAction(scope, screenSizes));
+ scope.setCompatibleScreensManifestTask(csmTask);
+ }
+
+ scope.setManifestProcessorTask(androidTasks.create(tasks,
+ new MergeManifests.ConfigAction(scope)));
+
+ if (csmTask != null) {
+ scope.getManifestProcessorTask().dependsOn(tasks, csmTask);
+ }
+ }
+ }
+
+ public void createMergeLibManifestsTask(
+ @NonNull TaskFactory tasks,
+ @NonNull VariantScope scope) {
+
+ AndroidTask<ProcessManifest> processManifest = androidTasks.create(tasks,
+ new ProcessManifest.ConfigAction(scope));
+
+ processManifest.dependsOn(tasks, scope.getVariantData().prepareDependenciesTask);
+
+ BaseVariantOutputData variantOutputData = scope.getVariantData().getOutputs().get(0);
+ variantOutputData.getScope().setManifestProcessorTask(processManifest);
+ }
+
+ protected void createProcessTestManifestTask(
+ @NonNull TaskFactory tasks,
+ @NonNull VariantScope scope) {
+
+ AndroidTask<ProcessTestManifest> processTestManifestTask = androidTasks.create(tasks,
+ new ProcessTestManifest.ConfigAction(scope));
+
+ processTestManifestTask.dependsOn(tasks, scope.getVariantData().prepareDependenciesTask);
+
+ BaseVariantOutputData variantOutputData = scope.getVariantData().getOutputs().get(0);
+ variantOutputData.getScope().setManifestProcessorTask(processTestManifestTask);
+ }
+
+ public void createRenderscriptTask(
+ @NonNull TaskFactory tasks,
+ @NonNull VariantScope scope) {
+ scope.setRenderscriptCompileTask(
+ androidTasks.create(tasks, new RenderscriptCompile.ConfigAction(scope)));
+
+ BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+ GradleVariantConfiguration config = variantData.getVariantConfiguration();
+ // get single output for now.
+ BaseVariantOutputData variantOutputData = variantData.getOutputs().get(0);
+
+ scope.getRenderscriptCompileTask().dependsOn(tasks, variantData.prepareDependenciesTask);
+ if (config.getType().isForTesting()) {
+ scope.getRenderscriptCompileTask().dependsOn(tasks,
+ variantOutputData.getScope().getManifestProcessorTask());
+ } else {
+ scope.getRenderscriptCompileTask().dependsOn(tasks, scope.getCheckManifestTask());
+ }
+
+ scope.getResourceGenTask().dependsOn(tasks, scope.getRenderscriptCompileTask());
+ // only put this dependency if rs will generate Java code
+ if (!config.getRenderscriptNdkModeEnabled()) {
+ scope.getSourceGenTask().dependsOn(tasks, scope.getRenderscriptCompileTask());
+ }
+
+ }
+
+ public AndroidTask<MergeResources> createMergeResourcesTask(
+ @NonNull TaskFactory tasks,
+ @NonNull VariantScope scope) {
+ return basicCreateMergeResourcesTask(
+ tasks,
+ scope,
+ "merge",
+ null /*outputLocation*/,
+ true /*includeDependencies*/,
+ true /*process9patch*/);
+ }
+
+ public AndroidTask<MergeResources> basicCreateMergeResourcesTask(
+ @NonNull TaskFactory tasks,
+ @NonNull VariantScope scope,
+ @NonNull String taskNamePrefix,
+ @Nullable File outputLocation,
+ final boolean includeDependencies,
+ final boolean process9Patch) {
+ AndroidTask<MergeResources> mergeResourcesTask = androidTasks.create(tasks,
+ new MergeResources.ConfigAction(
+ scope,
+ taskNamePrefix,
+ outputLocation,
+ includeDependencies,
+ process9Patch));
+ mergeResourcesTask.dependsOn(tasks,
+ scope.getVariantData().prepareDependenciesTask,
+ scope.getResourceGenTask());
+ scope.setMergeResourcesTask(mergeResourcesTask);
+ scope.setResourceOutputDir(
+ Objects.firstNonNull(outputLocation, scope.getDefaultMergeResourcesOutputDir()));
+ return scope.getMergeResourcesTask();
+ }
+
+
+ public void createMergeAssetsTask(TaskFactory tasks, VariantScope scope) {
+ AndroidTask<MergeAssets> mergeAssetsTask = androidTasks.create(tasks, new MergeAssets.ConfigAction(scope));
+ mergeAssetsTask.dependsOn(tasks,
+ scope.getVariantData().prepareDependenciesTask,
+ scope.getAssetGenTask());
+ scope.setMergeAssetsTask(mergeAssetsTask);
+ }
+
+ public void createBuildConfigTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
+ AndroidTask<GenerateBuildConfig> generateBuildConfigTask =
+ androidTasks.create(tasks, new GenerateBuildConfig.ConfigAction(scope));
+ scope.setGenerateBuildConfigTask(generateBuildConfigTask);
+ scope.getSourceGenTask().dependsOn(tasks, generateBuildConfigTask.getName());
+ if (scope.getVariantConfiguration().getType().isForTesting()) {
+ // in case of a test project, the manifest is generated so we need to depend
+ // on its creation.
+
+ // For test apps there should be a single output, so we get it.
+ BaseVariantOutputData variantOutputData = scope.getVariantData().getOutputs().get(0);
+
+ generateBuildConfigTask.dependsOn(
+ tasks, variantOutputData.getScope().getManifestProcessorTask());
+ } else {
+ generateBuildConfigTask.dependsOn(tasks, scope.getCheckManifestTask());
+ }
+ }
+
+ public void createGenerateResValuesTask(
+ @NonNull TaskFactory tasks,
+ @NonNull VariantScope scope) {
+ AndroidTask<GenerateResValues> generateResValuesTask = androidTasks.create(
+ tasks, new GenerateResValues.ConfigAction(scope));
+ scope.getResourceGenTask().dependsOn(tasks, generateResValuesTask);
+ }
+
+ public void createProcessResTask(
+ @NonNull TaskFactory tasks,
+ @NonNull VariantScope scope,
+ boolean generateResourcePackage) {
+ createProcessResTask(
+ tasks,
+ scope,
+ new File(globalScope.getIntermediatesDir(),
+ "symbols/" + scope.getVariantData().getVariantConfiguration().getDirName()),
+ generateResourcePackage);
+ }
+
+ public void createProcessResTask(
+ @NonNull TaskFactory tasks,
+ @NonNull VariantScope scope,
+ @Nullable File symbolLocation,
+ boolean generateResourcePackage) {
+ BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+
+ variantData.calculateFilters(scope.getGlobalScope().getExtension().getSplits());
+
+ // loop on all outputs. The only difference will be the name of the task, and location
+ // of the generated data.
+ for (BaseVariantOutputData vod : variantData.getOutputs()) {
+ final VariantOutputScope variantOutputScope = vod.getScope();
+
+ variantOutputScope.setProcessResourcesTask(androidTasks.create(tasks,
+ new ProcessAndroidResources.ConfigAction(variantOutputScope, symbolLocation,
+ generateResourcePackage)));
+ variantOutputScope.getProcessResourcesTask().dependsOn(tasks,
+ variantOutputScope.getManifestProcessorTask(),
+ scope.getMergeResourcesTask(),
+ scope.getMergeAssetsTask());
+
+ if (vod.getMainOutputFile().getFilter(DENSITY) == null) {
+ scope.setGenerateRClassTask(variantOutputScope.getProcessResourcesTask());
+ scope.getSourceGenTask().optionalDependsOn(tasks,
+ variantOutputScope.getProcessResourcesTask());
+ }
+
+ }
+
+ }
+
+ /**
+ * Creates the split resources packages task if necessary. AAPT will produce split packages for
+ * all --split provided parameters. These split packages should be signed and moved unchanged to
+ * the APK build output directory.
+ */
+ public void createSplitResourcesTasks(@NonNull VariantScope scope) {
+ BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+
+ checkState(variantData.getSplitHandlingPolicy().equals(
+ BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY),
+ "Can only create split resources tasks for pure splits.");
+
+ final VariantConfiguration config = variantData.getVariantConfiguration();
+ Set<String> densityFilters = variantData.getFilters(OutputFile.FilterType.DENSITY);
+ Set<String> abiFilters = variantData.getFilters(OutputFile.FilterType.ABI);
+ Set<String> languageFilters = variantData.getFilters(OutputFile.FilterType.LANGUAGE);
+
+ List<? extends BaseVariantOutputData> outputs = variantData.getOutputs();
+ if (outputs.size() != 1) {
+ throw new RuntimeException(
+ "In release 21 and later, there can be only one main APK, " +
+ "found " + outputs.size());
+ }
+
+ final BaseVariantOutputData variantOutputData = outputs.get(0);
+ VariantOutputScope variantOutputScope = variantOutputData.getScope();
+ variantOutputData.packageSplitResourcesTask = project.getTasks().create(
+ scope.getTaskName("package", "SplitResources"),
+ PackageSplitRes.class);
+ variantOutputData.packageSplitResourcesTask.setInputDirectory(
+ variantOutputScope.getProcessResourcePackageOutputFile().getParentFile());
+ variantOutputData.packageSplitResourcesTask.setDensitySplits(densityFilters);
+ variantOutputData.packageSplitResourcesTask.setLanguageSplits(languageFilters);
+ variantOutputData.packageSplitResourcesTask.setOutputBaseName(config.getBaseName());
+ variantOutputData.packageSplitResourcesTask.setSigningConfig(config.getSigningConfig());
+ variantOutputData.packageSplitResourcesTask.setOutputDirectory(new File(
+ scope.getGlobalScope().getIntermediatesDir(), "splits/" + config.getDirName()));
+ variantOutputData.packageSplitResourcesTask.setAndroidBuilder(androidBuilder);
+ variantOutputData.packageSplitResourcesTask.setVariantName(config.getFullName());
+ variantOutputData.packageSplitResourcesTask.dependsOn(
+ variantOutputScope.getProcessResourcesTask().getName());
+
+ SplitZipAlign zipAlign = project.getTasks().create(
+ scope.getTaskName("zipAlign", "SplitPackages"),
+ SplitZipAlign.class);
+ zipAlign.setVariantName(config.getFullName());
+ ConventionMappingHelper.map(zipAlign, "zipAlignExe", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ final TargetInfo info = androidBuilder.getTargetInfo();
+ if (info == null) {
+ return null;
+ }
+ String path = info.getBuildTools().getPath(ZIP_ALIGN);
+ if (path == null) {
+ return null;
+ }
+ return new File(path);
+ }
+ });
+
+ zipAlign.setOutputDirectory(new File(scope.getGlobalScope().getBuildDir(), "outputs/apk"));
+ ConventionMappingHelper.map(zipAlign, "densityOrLanguageInputFiles",
+ new Callable<List<File>>() {
+ @Override
+ public List<File> call() {
+ return variantOutputData.packageSplitResourcesTask.getOutputFiles();
+ }
+ });
+ zipAlign.setOutputBaseName(config.getBaseName());
+ zipAlign.setAbiFilters(abiFilters);
+ zipAlign.setLanguageFilters(languageFilters);
+ zipAlign.setDensityFilters(densityFilters);
+ File metadataDirectory = new File(zipAlign.getOutputDirectory().getParentFile(),
+ "metadata");
+ zipAlign.setApkMetadataFile(new File(metadataDirectory, config.getFullName() + ".mtd"));
+ ((ApkVariantOutputData) variantOutputData).splitZipAlign = zipAlign;
+ zipAlign.dependsOn(variantOutputData.packageSplitResourcesTask);
+ }
+
+ public void createSplitAbiTasks(@NonNull final VariantScope scope) {
+ ApplicationVariantData variantData = (ApplicationVariantData) scope.getVariantData();
+
+ checkState(variantData.getSplitHandlingPolicy().equals(
+ BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY),
+ "split ABI tasks are only compatible with pure splits.");
+
+ final VariantConfiguration config = variantData.getVariantConfiguration();
+ Set<String> filters = AbiSplitOptions.getAbiFilters(
+ getExtension().getSplits().getAbiFilters());
+ if (filters.isEmpty()) {
+ return;
+ }
+
+ List<ApkVariantOutputData> outputs = variantData.getOutputs();
+ if (outputs.size() != 1) {
+ throw new RuntimeException(
+ "In release 21 and later, there can be only one main APK, " +
+ "found " + outputs.size());
+ }
+
+ BaseVariantOutputData variantOutputData = outputs.get(0);
+ // first create the split APK resources.
+ GenerateSplitAbiRes generateSplitAbiRes = project.getTasks().create(
+ scope.getTaskName("generate", "SplitAbiRes"),
+ GenerateSplitAbiRes.class);
+ generateSplitAbiRes.setAndroidBuilder(androidBuilder);
+ generateSplitAbiRes.setVariantName(config.getFullName());
+
+ generateSplitAbiRes.setOutputDirectory(new File(
+ scope.getGlobalScope().getIntermediatesDir(), "abi/" + config.getDirName()));
+ generateSplitAbiRes.setSplits(filters);
+ generateSplitAbiRes.setOutputBaseName(config.getBaseName());
+ generateSplitAbiRes.setApplicationId(config.getApplicationId());
+ generateSplitAbiRes.setVersionCode(config.getVersionCode());
+ generateSplitAbiRes.setVersionName(config.getVersionName());
+ ConventionMappingHelper.map(generateSplitAbiRes, "debuggable", new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ return config.getBuildType().isDebuggable();
+ }
+ });
+ ConventionMappingHelper.map(generateSplitAbiRes, "aaptOptions",
+ new Callable<AaptOptions>() {
+ @Override
+ public AaptOptions call() throws Exception {
+ return getExtension().getAaptOptions();
+ }
+ });
+ generateSplitAbiRes.dependsOn(
+ variantOutputData.getScope().getProcessResourcesTask().getName());
+
+ // then package those resources with the appropriate JNI libraries.
+ variantOutputData.packageSplitAbiTask = project.getTasks().create(
+ scope.getTaskName("package", "SplitAbi"), PackageSplitAbi.class);
+ variantOutputData.packageSplitAbiTask.setInputFiles(generateSplitAbiRes.getOutputFiles());
+ variantOutputData.packageSplitAbiTask.setSplits(filters);
+ variantOutputData.packageSplitAbiTask.setOutputBaseName(config.getBaseName());
+ variantOutputData.packageSplitAbiTask.setSigningConfig(config.getSigningConfig());
+ variantOutputData.packageSplitAbiTask.setOutputDirectory(new File(
+ scope.getGlobalScope().getIntermediatesDir(), "splits/" + config.getDirName()));
+ variantOutputData.packageSplitAbiTask.setMergingFolder(
+ new File(scope.getGlobalScope().getIntermediatesDir(),
+ "package-merge/" + variantOutputData.getDirName()));
+ variantOutputData.packageSplitAbiTask.setAndroidBuilder(androidBuilder);
+ variantOutputData.packageSplitAbiTask.setVariantName(config.getFullName());
+ variantOutputData.packageSplitAbiTask.dependsOn(generateSplitAbiRes);
+ variantOutputData.packageSplitAbiTask.dependsOn(scope.getNdkBuildable());
+
+ ConventionMappingHelper.map(variantOutputData.packageSplitAbiTask, "jniFolders",
+ new Callable<Set<File>>() {
+ @Override
+ public Set<File> call() throws Exception {
+ return getJniFolders(scope);
+ }
+
+ });
+ ConventionMappingHelper.map(variantOutputData.packageSplitAbiTask, "jniDebuggable",
+ new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ return config.getBuildType().isJniDebuggable();
+ }
+ });
+ ConventionMappingHelper.map(variantOutputData.packageSplitAbiTask, "packagingOptions",
+ new Callable<PackagingOptions>() {
+ @Override
+ public PackagingOptions call() throws Exception {
+ return getExtension().getPackagingOptions();
+ }
+ });
+ ConventionMappingHelper.map(variantOutputData.packageSplitAbiTask, "packagingOptionsFilter",
+ new Callable<SignedJarBuilder.IZipEntryFilter>() {
+ @Override
+ public SignedJarBuilder.IZipEntryFilter call() throws Exception {
+ return scope.getPackagingOptionsFilter();
+ }
+ });
+
+ ((ApkVariantOutputData) variantOutputData).splitZipAlign.getAbiInputFiles().addAll(
+ variantOutputData.packageSplitAbiTask.getOutputFiles());
+
+ ((ApkVariantOutputData) variantOutputData).splitZipAlign.dependsOn(
+ variantOutputData.packageSplitAbiTask);
+ }
+
+ /**
+ * Calculate the list of folders that can contain jni artifacts for this variant.
+ *
+ * @return a potentially empty list of directories that exist or not and that may contains
+ * native resources.
+ */
+ @NonNull
+ public Set<File> getJniFolders(@NonNull VariantScope scope) {
+
+ BaseVariantData variantData = scope.getVariantData();
+ VariantConfiguration config = variantData.getVariantConfiguration();
+ // for now only the project's compilation output.
+ Set<File> set = Sets.newHashSet();
+ addAllIfNotNull(set, scope.getNdkSoFolder());
+ set.add(variantData.renderscriptCompileTask.getLibOutputDir());
+ //noinspection unchecked
+ addAllIfNotNull(set, config.getLibraryJniFolders());
+ //noinspection unchecked
+ addAllIfNotNull(set, config.getJniLibsList());
+
+ if (Boolean.TRUE.equals(config.getMergedFlavor().getRenderscriptSupportModeEnabled())) {
+ File rsLibs = androidBuilder.getSupportNativeLibFolder();
+ if (rsLibs != null && rsLibs.isDirectory()) {
+ set.add(rsLibs);
+ }
+
+ }
+
+ return set;
+ }
+
+ private static <T> void addAllIfNotNull(@NonNull Collection<T> main, @Nullable Collection<T> toAdd) {
+ if (toAdd != null) {
+ main.addAll(toAdd);
+ }
+ }
+
+ /**
+ * Creates the java resources processing tasks.
+ *
+ * The java processing will happen in three steps :
+ * <ul>{@link ExtractJavaResourcesTask} will extract all java resources from packaged jar files
+ * dependencies. Each jar file will be extracted in a separate folder. Each folder will be
+ * located under {@link VariantScope#getPackagedJarsJavaResDestinationDir()}</ul>
+ * <ul>{@link ProcessJavaResConfigAction} will sync all source folders into a single folder
+ * identified by {@link VariantScope#getSourceFoldersJavaResDestinationDir()}</ul>
+ * <ul>{@link MergeJavaResourcesTask} will take all these folders and will create a single
+ * merged folder with the {@link PackagingOptions} settings applied. The folder is located at
+ * {@link VariantScope#getJavaResourcesDestinationDir()}</ul>
+ *
+ * the result of 3 is the final set of java resources to can be either directly embedded in
+ * the resulting APK or fed into the obfuscation tool to produce obfuscated resources.
+ *
+ * @param tasks tasks factory to create tasks.
+ * @param scope the variant scope we are operating under.
+ */
+ public void createProcessJavaResTasks(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
+ final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+
+ // first create the incremental task that will extract all libraries java resources
+ // in separate folders.
+ AndroidTask<ExtractJavaResourcesTask> extractJavaResourcesTask = androidTasks
+ .create(tasks, new ExtractJavaResourcesTask.Config(scope));
+
+ // now copy the source folders java resources into the temporary location, mainly to
+ // maintain the PluginDsl COPY semantics.
+ scope.setProcessJavaResourcesTask(
+ androidTasks.create(tasks, new ProcessJavaResConfigAction(scope)));
+
+ // and create the merge tasks that will merge everything.
+ AndroidTask<MergeJavaResourcesTask> mergeJavaResourcesTask = androidTasks
+ .create(tasks, new MergeJavaResourcesTask.Config(scope));
+ // the merge task is the official provider for merged java resources to be bundled in the
+ // final variant specific APK, this may change if obfuscation is turned on.
+ scope.setJavaResourcesProvider(
+ JavaResourcesProvider.Adapter.build(tasks, mergeJavaResourcesTask));
+
+ // set the dependencies.
+ extractJavaResourcesTask.dependsOn(tasks, variantData.prepareDependenciesTask);
+ scope.getProcessJavaResourcesTask().dependsOn(tasks, extractJavaResourcesTask);
+ mergeJavaResourcesTask.dependsOn(tasks, scope.getProcessJavaResourcesTask());
+
+ scope.setMergeJavaResourcesTask(mergeJavaResourcesTask);
+
+ }
+
+ public void createAidlTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
+ scope.setAidlCompileTask(androidTasks.create(tasks, new AidlCompile.ConfigAction(scope)));
+ scope.getSourceGenTask().dependsOn(tasks, scope.getAidlCompileTask());
+ scope.getAidlCompileTask().dependsOn(tasks, scope.getVariantData().prepareDependenciesTask);
+ }
+
+ /**
+ * Creates the task for creating *.class files using javac. These tasks are created regardless
+ * of whether Jack is used or not, but assemble will not depend on them if it is. They are
+ * always used when running unit tests.
+ */
+ public AndroidTask<JavaCompile> createJavacTask(
+ @NonNull final TaskFactory tasks,
+ @NonNull final VariantScope scope) {
+ final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+ final AndroidTask<JavaCompile> javacTask = androidTasks.create(tasks,
+ new JavaCompileConfigAction(scope));
+ scope.setJavacTask(javacTask);
+
+ javacTask.optionalDependsOn(tasks, scope.getSourceGenTask());
+ javacTask.dependsOn(tasks,
+ scope.getVariantData().prepareDependenciesTask,
+ scope.getMergeJavaResourcesTask());
+
+ // TODO - dependency information for the compile classpath is being lost.
+ // Add a temporary approximation
+ javacTask.dependsOn(tasks,
+ scope.getVariantData().getVariantDependency().getCompileConfiguration()
+ .getBuildDependencies());
+
+ if (variantData.getType().isForTesting()) {
+ BaseVariantData testedVariantData =
+ (BaseVariantData) ((TestVariantData) variantData).getTestedVariantData();
+ final JavaCompile testedJavacTask = testedVariantData.javacTask;
+ javacTask.dependsOn(tasks,
+ testedJavacTask != null ? testedJavacTask :
+ testedVariantData.getScope().getJavacTask());
+ }
+
+ // Create jar task for uses by external modules.
+ if (variantData.getVariantDependency().getClassesConfiguration() != null) {
+ tasks.create(scope.getTaskName("package", "JarArtifact"), Jar.class, new Action<Jar>() {
+ @Override
+ public void execute(Jar jar) {
+ variantData.classesJarTask = jar;
+ jar.dependsOn(javacTask.getName());
+
+ // add the class files (whether they are instrumented or not.
+ jar.from(scope.getJavaOutputDir());
+
+ jar.setDestinationDir(new File(
+ scope.getGlobalScope().getIntermediatesDir(),
+ "classes-jar/" +
+ variantData.getVariantConfiguration().getDirName()));
+ jar.setArchiveName("classes.jar");
+ }
+ });
+ }
+
+ return javacTask;
+ }
+
+ /**
+ * Makes the given task the one used by top-level "compile" task.
+ */
+ public static void setJavaCompilerTask(
+ @NonNull AndroidTask<? extends AbstractCompile> javaCompilerTask,
+ @NonNull TaskFactory tasks,
+ @NonNull VariantScope scope) {
+ scope.getCompileTask().dependsOn(tasks, javaCompilerTask);
+ scope.setJavaCompilerTask(javaCompilerTask);
+
+ // TODO: Get rid of it once we stop keeping tasks in variant data.
+ //noinspection VariableNotUsedInsideIf
+ if (scope.getVariantData().javacTask != null) {
+ // This is not the experimental plugin, let's update variant data, so Variants API
+ // keeps working.
+ scope.getVariantData().javaCompilerTask = (AbstractCompile) tasks.named(javaCompilerTask.getName());
+ }
+
+ }
+
+ public void createGenerateMicroApkDataTask(
+ @NonNull TaskFactory tasks,
+ @NonNull VariantScope scope,
+ @NonNull Configuration config) {
+ AndroidTask<GenerateApkDataTask> generateMicroApkTask = androidTasks.create(tasks,
+ new GenerateApkDataTask.ConfigAction(scope, config));
+ generateMicroApkTask.dependsOn(tasks, config);
+
+ // the merge res task will need to run after this one.
+ scope.getResourceGenTask().dependsOn(tasks, generateMicroApkTask);
+ }
+
+ public void createNdkTasks(@NonNull VariantScope scope) {
+ final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+ NdkCompile ndkCompile = project.getTasks().create(
+ scope.getTaskName("compile", "Ndk"),
+ NdkCompile.class);
+
+ ndkCompile.dependsOn(variantData.preBuildTask);
+
+ ndkCompile.setAndroidBuilder(androidBuilder);
+ ndkCompile.setVariantName(variantData.getName());
+ ndkCompile.setNdkDirectory(sdkHandler.getNdkFolder());
+ ndkCompile.setIsForTesting(variantData.getType().isForTesting());
+ variantData.ndkCompileTask = ndkCompile;
+ variantData.compileTask.dependsOn(variantData.ndkCompileTask);
+
+ final GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration();
+
+ if (Boolean.TRUE.equals(variantConfig.getMergedFlavor().getRenderscriptNdkModeEnabled())) {
+ ndkCompile.setNdkRenderScriptMode(true);
+ ndkCompile.dependsOn(variantData.renderscriptCompileTask);
+ } else {
+ ndkCompile.setNdkRenderScriptMode(false);
+ }
+
+ ConventionMappingHelper.map(ndkCompile, "sourceFolders", new Callable<List<File>>() {
+ @Override
+ public List<File> call() {
+ List<File> sourceList = variantConfig.getJniSourceList();
+ if (Boolean.TRUE.equals(
+ variantConfig.getMergedFlavor().getRenderscriptNdkModeEnabled())) {
+ sourceList.add(variantData.renderscriptCompileTask.getSourceOutputDir());
+ }
+
+ return sourceList;
+ }
+ });
+
+ ndkCompile.setGeneratedMakefile(new File(scope.getGlobalScope().getIntermediatesDir(),
+ "ndk/" + variantData.getVariantConfiguration().getDirName() + "/Android.mk"));
+
+ ConventionMappingHelper.map(ndkCompile, "ndkConfig", new Callable<CoreNdkOptions>() {
+ @Override
+ public CoreNdkOptions call() {
+ return variantConfig.getNdkConfig();
+ }
+ });
+
+ ConventionMappingHelper.map(ndkCompile, "debuggable", new Callable<Boolean>() {
+ @Override
+ public Boolean call() {
+ return variantConfig.getBuildType().isJniDebuggable();
+ }
+ });
+
+ ndkCompile.setObjFolder(new File(scope.getGlobalScope().getIntermediatesDir(),
+ "ndk/" + variantData.getVariantConfiguration().getDirName() + "/obj"));
+
+ Collection<File> ndkSoFolder = scope.getNdkSoFolder();
+ if (ndkSoFolder != null && !ndkSoFolder.isEmpty()) {
+ ndkCompile.setSoFolder(ndkSoFolder.iterator().next());
+ }
+ }
+
+ /**
+ * Creates the tasks to build unit tests.
+ */
+ public void createUnitTestVariantTasks(
+ @NonNull TaskFactory tasks,
+ @NonNull TestVariantData variantData) {
+ variantData.assembleVariantTask.dependsOn(createMockableJar);
+ VariantScope variantScope = variantData.getScope();
+
+ createPreBuildTasks(variantScope);
+ createProcessJavaResTasks(tasks, variantScope);
+ createCompileAnchorTask(tasks, variantScope);
+ AndroidTask<JavaCompile> javacTask = createJavacTask(tasks, variantScope);
+ setJavaCompilerTask(javacTask, tasks, variantScope);
+ createUnitTestTask(tasks, variantData);
+
+ // This hides the assemble unit test task from the task list.
+ variantData.assembleVariantTask.setGroup(null);
+ }
+
+ /**
+ * Creates the tasks to build android tests.
+ */
+ public void createAndroidTestVariantTasks(@NonNull TaskFactory tasks,
+ @NonNull TestVariantData variantData) {
+ VariantScope variantScope = variantData.getScope();
+
+ // get single output for now (though this may always be the case for tests).
+ final BaseVariantOutputData variantOutputData = variantData.getOutputs().get(0);
+
+ final BaseVariantData<BaseVariantOutputData> baseTestedVariantData =
+ (BaseVariantData<BaseVariantOutputData>) variantData.getTestedVariantData();
+ final BaseVariantOutputData testedVariantOutputData =
+ baseTestedVariantData.getOutputs().get(0);
+
+ createAnchorTasks(tasks, variantScope);
+
+ // Add a task to process the manifest
+ createProcessTestManifestTask(tasks, variantScope);
+
+ // Add a task to create the res values
+ createGenerateResValuesTask(tasks, variantScope);
+
+ // Add a task to compile renderscript files.
+ createRenderscriptTask(tasks, variantScope);
+
+ // Add a task to merge the resource folders
+ createMergeResourcesTask(tasks, variantScope);
+
+ // Add a task to merge the assets folders
+ createMergeAssetsTask(tasks, variantScope);
+
+ if (variantData.getTestedVariantData().getVariantConfiguration().getType().equals(
+ VariantType.LIBRARY)) {
+ // in this case the tested library must be fully built before test can be built!
+ if (testedVariantOutputData.assembleTask != null) {
+ variantOutputData.getScope().getManifestProcessorTask().dependsOn(
+ tasks, testedVariantOutputData.assembleTask);
+ variantScope.getMergeResourcesTask().dependsOn(
+ tasks, testedVariantOutputData.assembleTask);
+ }
+ }
+
+ // Add a task to create the BuildConfig class
+ createBuildConfigTask(tasks, variantScope);
+
+ // Add a task to generate resource source files
+ createProcessResTask(tasks, variantScope, true /*generateResourcePackage*/);
+
+ // process java resources
+ createProcessJavaResTasks(tasks, variantScope);
+
+ createAidlTask(tasks, variantScope);
+
+ // Add NDK tasks
+ if (isNdkTaskNeeded) {
+ createNdkTasks(variantScope);
+ }
+
+ variantScope.setNdkBuildable(getNdkBuildable(variantData));
+
+ // Add a task to compile the test application
+ if (variantData.getVariantConfiguration().getUseJack()) {
+ createJackTask(tasks, variantScope);
+ } else {
+ AndroidTask<JavaCompile> javacTask = createJavacTask(tasks, variantScope);
+ setJavaCompilerTask(javacTask, tasks, variantScope);
+ createPostCompilationTasks(tasks, variantScope);
+ }
+
+ createPackagingTask(tasks, variantScope, false /*publishApk*/);
+
+ tasks.named(ASSEMBLE_ANDROID_TEST, new Action<Task>() {
+ @Override
+ public void execute(Task it) {
+ it.dependsOn(variantOutputData.assembleTask);
+ }
+ });
+
+ createConnectedTestForVariant(tasks, variantScope);
+ }
+
+ // TODO - should compile src/lint/java from src/lint/java and jar it into build/lint/lint.jar
+ private void createLintCompileTask(TaskFactory tasks) {
+
+ // TODO: move doFirst into dedicated task class.
+ tasks.create(LINT_COMPILE, Task.class,
+ new Action<Task>() {
+ @Override
+ public void execute(Task lintCompile) {
+ final File outputDir =
+ new File(getGlobalScope().getIntermediatesDir(), "lint");
+
+ lintCompile.doFirst(new Action<Task>() {
+ @Override
+ public void execute(Task task) {
+ // create the directory for lint output if it does not exist.
+ if (!outputDir.exists()) {
+ boolean mkdirs = outputDir.mkdirs();
+ if (!mkdirs) {
+ throw new GradleException(
+ "Unable to create lint output directory.");
+ }
+ }
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Is the given variant relevant for lint?
+ */
+ private static boolean isLintVariant(
+ @NonNull BaseVariantData<? extends BaseVariantOutputData> baseVariantData) {
+ // Only create lint targets for variants like debug and release, not debugTest
+ VariantConfiguration config = baseVariantData.getVariantConfiguration();
+ return !config.getType().isForTesting();
+ }
+
+ /**
+ * Add tasks for running lint on individual variants. We've already added a
+ * lint task earlier which runs on all variants.
+ */
+ public void createLintTasks(TaskFactory tasks, final VariantScope scope) {
+ final BaseVariantData<? extends BaseVariantOutputData> baseVariantData =
+ scope.getVariantData();
+ if (!isLintVariant(baseVariantData)) {
+ return;
+ }
+
+ // wire the main lint task dependency.
+ tasks.named(LINT, new Action<Task>() {
+ @Override
+ public void execute(Task it) {
+ it.dependsOn(LINT_COMPILE);
+ it.dependsOn(scope.getJavacTask().getName());
+ }
+ });
+
+ AndroidTask<Lint> variantLintCheck = androidTasks.create(
+ tasks, new Lint.ConfigAction(scope));
+ variantLintCheck.dependsOn(tasks, LINT_COMPILE, scope.getJavacTask());
+ }
+
+ private void createLintVitalTask(@NonNull ApkVariantData variantData) {
+ checkState(getExtension().getLintOptions().isCheckReleaseBuilds());
+ // TODO: re-enable with Jack when possible
+ if (!variantData.getVariantConfiguration().getBuildType().isDebuggable() &&
+ !variantData.getVariantConfiguration().getUseJack()) {
+ String variantName = variantData.getVariantConfiguration().getFullName();
+ String capitalizedVariantName = StringHelper.capitalize(variantName);
+ String taskName = "lintVital" + capitalizedVariantName;
+ final Lint lintReleaseCheck = project.getTasks().create(taskName, Lint.class);
+ // TODO: Make this task depend on lintCompile too (resolve initialization order first)
+ optionalDependsOn(lintReleaseCheck, variantData.javacTask);
+ lintReleaseCheck.setLintOptions(getExtension().getLintOptions());
+ lintReleaseCheck.setSdkHome(sdkHandler.getSdkFolder());
+ lintReleaseCheck.setVariantName(variantName);
+ lintReleaseCheck.setToolingRegistry(toolingRegistry);
+ lintReleaseCheck.setFatalOnly(true);
+ lintReleaseCheck.setDescription(
+ "Runs lint on just the fatal issues in the " + capitalizedVariantName
+ + " build.");
+ //variantData.assembleVariantTask.dependsOn lintReleaseCheck
+
+ // If lint is being run, we do not need to run lint vital.
+ // TODO: Find a better way to do this.
+ project.getGradle().getTaskGraph().whenReady(new Closure<Void>(this, this) {
+ public void doCall(TaskExecutionGraph taskGraph) {
+ if (taskGraph.hasTask(LINT)) {
+ lintReleaseCheck.setEnabled(false);
+ }
+ }
+ });
+ }
+ }
+
+ private void createUnitTestTask(@NonNull TaskFactory tasks,
+ @NonNull final TestVariantData variantData) {
+ final BaseVariantData testedVariantData =
+ (BaseVariantData) variantData.getTestedVariantData();
+
+ final Test runTestsTask = project.getTasks().create(
+ variantData.getScope().getTaskName(UNIT_TEST.getPrefix()),
+ Test.class);
+ runTestsTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+ runTestsTask.setDescription(
+ "Run unit tests for the " +
+ testedVariantData.getVariantConfiguration().getFullName() + " build.");
+
+ fixTestTaskSources(runTestsTask);
+
+ runTestsTask.dependsOn(variantData.assembleVariantTask);
+
+ final AbstractCompile testCompileTask = variantData.javacTask;
+ runTestsTask.setTestClassesDir(testCompileTask.getDestinationDir());
+
+ ConventionMappingHelper.map(runTestsTask, "classpath",
+ new Callable<ConfigurableFileCollection>() {
+ @Override
+ public ConfigurableFileCollection call() throws Exception {
+ Iterable<File> filteredBootClasspath = Iterables.filter(
+ androidBuilder.getBootClasspath(),
+ new Predicate<File>() {
+ @Override
+ public boolean apply(@Nullable File file) {
+ return file != null &&
+ !SdkConstants.FN_FRAMEWORK_LIBRARY
+ .equals(file.getName());
+ }
+ });
+
+ return project.files(
+ testCompileTask.getClasspath(),
+ testCompileTask.getOutputs().getFiles(),
+ variantData.processJavaResourcesTask.getOutputs(),
+ testedVariantData.processJavaResourcesTask.getOutputs(),
+ filteredBootClasspath,
+ // Mockable JAR is last, to make sure you can shadow the classes
+ // withdependencies.
+ createMockableJar.getOutputFile());
+ }
+ });
+
+ // Put the variant name in the report path, so that different testing tasks don't
+ // overwrite each other's reports.
+ TestTaskReports testTaskReports = runTestsTask.getReports();
+
+ for (ConfigurableReport report : new ConfigurableReport[] {
+ testTaskReports.getJunitXml(), testTaskReports.getHtml()}) {
+ report.setDestination(new File(report.getDestination(), testedVariantData.getName()));
+ }
+
+ tasks.named(JavaPlugin.TEST_TASK_NAME, new Action<Task>() {
+ @Override
+ public void execute(Task test) {
+ test.dependsOn(runTestsTask);
+ }
+
+ });
+
+ extension.getTestOptions().getUnitTests().applyConfiguration(runTestsTask);
+ }
+
+ private static void fixTestTaskSources(@NonNull Test testTask) {
+ // We are running in afterEvaluate, so the JavaBasePlugin has already added a
+ // callback to add test classes to the list of source files of the newly created task.
+ // The problem is that we haven't configured the test classes yet (JavaBasePlugin
+ // assumes all Test tasks are fully configured at this point), so we have to remove the
+ // "directory null" entry from source files and add the right value.
+ //
+ // This is an ugly hack, since we assume sourceFiles is an instance of
+ // DefaultConfigurableFileCollection.
+ ((DefaultConfigurableFileCollection) testTask.getInputs().getSourceFiles()).getFrom().clear();
+ }
+
+ public void createTopLevelTestTasks(final TaskFactory tasks, boolean hasFlavors) {
+ final List<String> reportTasks = Lists.newArrayListWithExpectedSize(2);
+
+ List<DeviceProvider> providers = getExtension().getDeviceProviders();
+
+ final String connectedRootName = CONNECTED + ANDROID_TEST.getSuffix();
+ final String defaultReportsDir = getGlobalScope().getReportsDir().getAbsolutePath()
+ + "/" + FD_ANDROID_TESTS;
+ final String defaultResultsDir = getGlobalScope().getOutputsDir().getAbsolutePath()
+ + "/" + FD_ANDROID_RESULTS;
+
+ // If more than one flavor, create a report aggregator task and make this the parent
+ // task for all new connected tasks. Otherwise, create a top level connectedAndroidTest
+ // DefaultTask.
+ if (hasFlavors) {
+ tasks.create(connectedRootName, AndroidReportTask.class,
+ new Action<AndroidReportTask>() {
+ @Override
+ public void execute(AndroidReportTask mainConnectedTask) {
+ mainConnectedTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+ mainConnectedTask.setDescription("Installs and runs instrumentation "
+ + "tests for all flavors on connected devices.");
+ mainConnectedTask.setReportType(ReportType.MULTI_FLAVOR);
+ mainConnectedTask.setVariantName("");
+ ConventionMappingHelper.map(mainConnectedTask, "resultsDir",
+ new Callable<File>() {
+ @Override
+ public File call() {
+ final String dir =
+ extension.getTestOptions().getResultsDir();
+ String rootLocation = dir != null && !dir.isEmpty()
+ ? dir : defaultResultsDir;
+ return project.file(rootLocation + "/connected/"
+ + FD_FLAVORS_ALL);
+ }
+ });
+ ConventionMappingHelper.map(mainConnectedTask, "reportsDir",
+ new Callable<File>() {
+ @Override
+ public File call() {
+ final String dir =
+ extension.getTestOptions().getReportDir();
+ String rootLocation = dir != null && !dir.isEmpty()
+ ? dir : defaultReportsDir;
+ return project.file(rootLocation + "/connected/"
+ + FD_FLAVORS_ALL);
+ }
+ });
+
+ }
+
+ });
+ reportTasks.add(connectedRootName);
+ } else {
+ tasks.create(connectedRootName, new Action<Task>() {
+ @Override
+ public void execute(Task connectedTask) {
+ connectedTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+ connectedTask.setDescription(
+ "Installs and runs instrumentation tests for all flavors on connected devices.");
+ }
+
+ });
+ }
+
+ tasks.named(CONNECTED_CHECK, new Action<Task>() {
+ @Override
+ public void execute(Task it) {
+ it.dependsOn(connectedRootName);
+ }
+
+ });
+
+ final String mainProviderTaskName = DEVICE + ANDROID_TEST.getSuffix();
+ // if more than one provider tasks, either because of several flavors, or because of
+ // more than one providers, then create an aggregate report tasks for all of them.
+ if (providers.size() > 1 || hasFlavors) {
+ tasks.create(mainProviderTaskName, AndroidReportTask.class,
+ new Action<AndroidReportTask>() {
+ @Override
+ public void execute(AndroidReportTask mainProviderTask) {
+ mainProviderTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+ mainProviderTask.setDescription(
+ "Installs and runs instrumentation tests using all Device Providers.");
+ mainProviderTask.setReportType(ReportType.MULTI_FLAVOR);
+
+ ConventionMappingHelper.map(mainProviderTask, "resultsDir",
+ new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ final String dir =
+ extension.getTestOptions().getResultsDir();
+ String rootLocation = dir != null && !dir.isEmpty()
+ ? dir : defaultResultsDir;
+
+ return project.file(rootLocation + "/devices/"
+ + FD_FLAVORS_ALL);
+ }
+ });
+ ConventionMappingHelper.map(mainProviderTask, "reportsDir",
+ new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ final String dir =
+ extension.getTestOptions().getReportDir();
+ String rootLocation = dir != null && !dir.isEmpty()
+ ? dir : defaultReportsDir;
+ return project.file(rootLocation + "/devices/"
+ + FD_FLAVORS_ALL);
+ }
+ });
+ }
+
+ });
+ reportTasks.add(mainProviderTaskName);
+ } else {
+ tasks.create(mainProviderTaskName, new Action<Task>() {
+ @Override
+ public void execute(Task providerTask) {
+ providerTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+ providerTask.setDescription(
+ "Installs and runs instrumentation tests using all Device Providers.");
+ }
+ });
+ }
+
+ tasks.named(DEVICE_CHECK, new Action<Task>() {
+ @Override
+ public void execute(Task it) {
+ it.dependsOn(mainProviderTaskName);
+ }
+ });
+
+ // Create top level unit test tasks.
+ tasks.create(JavaPlugin.TEST_TASK_NAME, new Action<Task>() {
+ @Override
+ public void execute(Task unitTestTask) {
+ unitTestTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+ unitTestTask.setDescription("Run unit tests for all variants.");
+ }
+
+ });
+ tasks.named(JavaBasePlugin.CHECK_TASK_NAME,
+ new Action<Task>() {
+ @Override
+ public void execute(Task check) {
+ check.dependsOn(JavaPlugin.TEST_TASK_NAME);
+ }
+ });
+
+ // If gradle is launched with --continue, we want to run all tests and generate an
+ // aggregate report (to help with the fact that we may have several build variants, or
+ // or several device providers).
+ // To do that, the report tasks must run even if one of their dependent tasks (flavor
+ // or specific provider tasks) fails, when --continue is used, and the report task is
+ // meant to run (== is in the task graph).
+ // To do this, we make the children tasks ignore their errors (ie they won't fail and
+ // stop the build).
+ if (!reportTasks.isEmpty() && project.getGradle().getStartParameter()
+ .isContinueOnFailure()) {
+ project.getGradle().getTaskGraph().whenReady(new Closure<Void>(this, this) {
+ public void doCall(TaskExecutionGraph taskGraph) {
+ for (String reportTask : reportTasks) {
+ if (taskGraph.hasTask(reportTask)) {
+ tasks.named(reportTask, new Action<Task>() {
+ @Override
+ public void execute(Task task) {
+ ((AndroidReportTask) task).setWillRun();
+ }
+ });
+ }
+ }
+ }
+ });
+ }
+ }
+
+ protected void createConnectedTestForVariant(
+ @NonNull TaskFactory tasks,
+ @NonNull final VariantScope variantScope) {
+ final BaseVariantData<? extends BaseVariantOutputData> baseVariantData =
+ variantScope.getTestedVariantData();
+ final TestVariantData testVariantData = (TestVariantData) variantScope.getVariantData();
+
+ // get single output for now
+ final BaseVariantOutputData variantOutputData = baseVariantData.getOutputs().get(0);
+ final BaseVariantOutputData testVariantOutputData = testVariantData.getOutputs().get(0);
+
+ String connectedRootName = CONNECTED + ANDROID_TEST.getSuffix();
+
+ TestDataImpl testData = new TestDataImpl(testVariantData);
+ testData.setExtraInstrumentationTestRunnerArgs(
+ AndroidGradleOptions.getExtraInstrumentationTestRunnerArgs(project));
+
+ // create the check tasks for this test
+ // first the connected one.
+ ImmutableList<Task> artifactsTasks = ImmutableList.of(
+ testVariantData.getOutputs().get(0).assembleTask,
+ baseVariantData.assembleVariantTask);
+
+ final AndroidTask<DeviceProviderInstrumentTestTask> connectedTask = androidTasks.create(
+ tasks,
+ new DeviceProviderInstrumentTestTask.ConfigAction(
+ testVariantData.getScope(),
+ new ConnectedDeviceProvider(sdkHandler.getSdkInfo().getAdb(),
+ new LoggerWrapper(logger)), testData));
+
+ connectedTask.dependsOn(tasks, artifactsTasks);
+
+ tasks.named(connectedRootName, new Action<Task>() {
+ @Override
+ public void execute(Task it) {
+ it.dependsOn(connectedTask.getName());
+ }
+ });
+
+ if (baseVariantData.getVariantConfiguration().getBuildType().isTestCoverageEnabled()
+ && !baseVariantData.getVariantConfiguration().getUseJack()) {
+ final JacocoReportTask reportTask = project.getTasks().create(
+ variantScope.getTaskName("create", "CoverageReport"),
+ JacocoReportTask.class);
+ reportTask.setReportName(baseVariantData.getVariantConfiguration().getFullName());
+ ConventionMappingHelper.map(reportTask, "jacocoClasspath",
+ new Callable<FileCollection>() {
+ @Override
+ public FileCollection call() throws Exception {
+ return project.getConfigurations().getAt(
+ JacocoPlugin.ANT_CONFIGURATION_NAME);
+ }
+ });
+ ConventionMappingHelper.map(reportTask, "coverageFile", new Callable<File>() {
+ @Override
+ public File call() {
+ return new File(((TestVariantData) testVariantData.getScope()
+ .getVariantData()).connectedTestTask.getCoverageDir(),
+ SimpleTestCallable.FILE_COVERAGE_EC);
+ }
+ });
+ ConventionMappingHelper.map(reportTask, "classDir", new Callable<File>() {
+ @Override
+ public File call() {
+ return baseVariantData.javacTask.getDestinationDir();
+ }
+ });
+ ConventionMappingHelper.map(reportTask, "sourceDir", new Callable<List<File>>() {
+ @Override
+ public List<File> call() {
+ return baseVariantData.getJavaSourceFoldersForCoverage();
+ }
+ });
+
+ ConventionMappingHelper.map(reportTask, "reportDir", new Callable<File>() {
+ @Override
+ public File call() {
+ return new File(variantScope.getGlobalScope().getReportsDir(),
+ "/coverage/" + baseVariantData.getVariantConfiguration().getDirName());
+ }
+ });
+
+ reportTask.dependsOn(connectedTask.getName());
+ tasks.named(connectedRootName, new Action<Task>() {
+ @Override
+ public void execute(Task it) {
+ it.dependsOn(reportTask);
+ }
+ });
+ }
+
+ String mainProviderTaskName = DEVICE + ANDROID_TEST.getSuffix();
+
+ List<DeviceProvider> providers = getExtension().getDeviceProviders();
+
+ boolean hasFlavors = baseVariantData.getVariantConfiguration().hasFlavors();
+
+ // now the providers.
+ for (DeviceProvider deviceProvider : providers) {
+
+ final AndroidTask<DeviceProviderInstrumentTestTask> providerTask = androidTasks
+ .create(tasks, new DeviceProviderInstrumentTestTask.ConfigAction(
+ testVariantData.getScope(), deviceProvider, testData));
+
+ tasks.named(mainProviderTaskName, new Action<Task>() {
+ @Override
+ public void execute(Task it) {
+ it.dependsOn(providerTask.getName());
+ }
+ });
+ providerTask.dependsOn(tasks, artifactsTasks);
+ }
+
+ // now the test servers
+ List<TestServer> servers = getExtension().getTestServers();
+ for (TestServer testServer : servers) {
+ final TestServerTask serverTask = project.getTasks().create(
+ hasFlavors ?
+ baseVariantData.getScope().getTaskName(testServer.getName() + "Upload")
+ :
+ testServer.getName() + ("Upload"),
+ TestServerTask.class);
+
+ serverTask.setDescription(
+ "Uploads APKs for Build \'" + baseVariantData.getVariantConfiguration()
+ .getFullName() + "\' to Test Server \'" +
+ StringHelper.capitalize(testServer.getName()) + "\'.");
+ serverTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+ serverTask.setVariantName(
+ baseVariantData.getScope().getVariantConfiguration().getFullName());
+ serverTask.dependsOn(testVariantOutputData.assembleTask,
+ variantOutputData.assembleTask);
+
+ serverTask.setTestServer(testServer);
+
+ ConventionMappingHelper.map(serverTask, "testApk", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ return testVariantOutputData.getOutputFile();
+ }
+ });
+ if (!(baseVariantData instanceof LibraryVariantData)) {
+ ConventionMappingHelper.map(serverTask, "testedApk", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ return variantOutputData.getOutputFile();
+ }
+ });
+ }
+
+ ConventionMappingHelper.map(serverTask, "variantName", new Callable<String>() {
+ @Override
+ public String call() throws Exception {
+ return baseVariantData.getVariantConfiguration().getFullName();
+ }
+ });
+
+ tasks.named(DEVICE_CHECK, new Action<Task>() {
+ @Override
+ public void execute(Task it) {
+ it.dependsOn(serverTask);
+ }
+ });
+
+ if (!testServer.isConfigured()) {
+ serverTask.setEnabled(false);
+ }
+ }
+ }
+
+ public static void createJarTask(@NonNull TaskFactory tasks, @NonNull final VariantScope scope) {
+ final BaseVariantData variantData = scope.getVariantData();
+
+ final GradleVariantConfiguration config = variantData.getVariantConfiguration();
+ tasks.create(
+ scope.getTaskName("jar", "Classes"),
+ AndroidJarTask.class,
+ new Action<AndroidJarTask>() {
+ @Override
+ public void execute(AndroidJarTask jarTask) {
+ // AndroidJarTask jarTask = project.tasks.create(
+ // "jar${config.fullName.capitalize()}Classes",
+ // AndroidJarTask)
+
+ jarTask.setArchiveName("classes.jar");
+ jarTask.setDestinationDir(new File(
+ scope.getGlobalScope().getIntermediatesDir(),
+ "packaged/" + config.getDirName() + "/"));
+ jarTask.from(scope.getJavaOutputDir());
+ jarTask.dependsOn(scope.getJavacTask().getName());
+ variantData.binayFileProviderTask = jarTask;
+ }
+
+ });
+ }
+
+ /**
+ * Creates the post-compilation tasks for the given Variant.
+ *
+ * These tasks create the dex file from the .class files, plus optional intermediary steps like
+ * proguard and jacoco
+ *
+ */
+ public void createPostCompilationTasks(TaskFactory tasks, @NonNull final VariantScope scope) {
+ checkNotNull(scope.getJavacTask());
+
+ final ApkVariantData variantData = (ApkVariantData) scope.getVariantData();
+ final GradleVariantConfiguration config = variantData.getVariantConfiguration();
+
+ // data holding dependencies and input for the dex. This gets updated as new
+ // post-compilation steps are inserted between the compilation and dx.
+ PostCompilationData pcData = new PostCompilationData();
+ pcData.setClassGeneratingTasks(Collections.singletonList(scope.getJavacTask().getName()));
+ pcData.setLibraryGeneratingTasks(ImmutableList.of(variantData.prepareDependenciesTask,
+ variantData.getVariantDependency().getPackageConfiguration()
+ .getBuildDependencies()));
+ pcData.setInputFilesCallable(new Callable<List<File>>() {
+ @Override
+ public List<File> call() {
+ return new ArrayList<File>(
+ variantData.javacTask.getOutputs().getFiles().getFiles());
+ }
+
+ });
+ pcData.setInputDir(scope.getJavaOutputDir());
+
+ pcData.setJavaResourcesInputDir(scope.getJavaResourcesDestinationDir());
+
+ pcData.setInputLibrariesCallable(new Callable<List<File>>() {
+ @Override
+ public List<File> call() {
+ return new ArrayList<File>(
+ scope.getGlobalScope().getAndroidBuilder().getPackagedJars(config));
+ }
+
+ });
+
+ // ---- Code Coverage first -----
+ boolean isTestCoverageEnabled = config.getBuildType().isTestCoverageEnabled() && !config
+ .getType().isForTesting();
+ if (isTestCoverageEnabled) {
+ pcData = createJacocoTask(tasks, scope, pcData);
+ }
+
+ boolean isTestForApp = config.getType().isForTesting() &&
+ ((TestVariantData) variantData).getTestedVariantData().getVariantConfiguration()
+ .getType().equals(DEFAULT);
+ boolean isMinifyEnabled = config.isMinifyEnabled();
+ boolean isMultiDexEnabled = config.isMultiDexEnabled() && !isTestForApp;
+ boolean isLegacyMultiDexMode = config.isLegacyMultiDexMode();
+
+ // ----- Minify next ----
+ File outFile = maybeCreateProguardTasks(tasks, scope, pcData);
+ if (outFile != null) {
+ pcData.setInputFiles(Collections.singletonList(outFile));
+ pcData.setInputLibraries(Collections.<File>emptyList());
+ } else if ((getExtension().getDexOptions().getPreDexLibraries() && !isMultiDexEnabled) || (
+ isMultiDexEnabled && !isLegacyMultiDexMode)) {
+
+ AndroidTask<PreDex> preDexTask = androidTasks
+ .create(tasks, new PreDex.ConfigAction(scope, pcData));
+
+ // update dependency.
+ preDexTask.dependsOn(tasks, pcData.getLibraryGeneratingTasks());
+ pcData.setLibraryGeneratingTasks(Collections.singletonList(preDexTask.getName()));
+
+ // update inputs
+ if (isMultiDexEnabled) {
+ pcData.setInputLibraries(Collections.<File>emptyList());
+
+ } else {
+ pcData.setInputLibrariesCallable(new Callable<List<File>>() {
+ @Override
+ public List<File> call() {
+ return new ArrayList<File>(
+ project.fileTree(scope.getPreDexOutputDir()).getFiles());
+ }
+
+ });
+ }
+ }
+
+ AndroidTask<CreateMainDexList> createMainDexListTask = null;
+ AndroidTask<RetraceMainDexList> retraceTask = null;
+
+ // ----- Multi-Dex support
+ if (isMultiDexEnabled && isLegacyMultiDexMode) {
+ if (!isMinifyEnabled) {
+ // create a task that will convert the output of the compilation
+ // into a jar. This is needed by the multi-dex input.
+ AndroidTask<JarMergingTask> jarMergingTask = androidTasks.create(tasks,
+ new JarMergingTask.ConfigAction(scope, pcData));
+
+ // update dependencies
+ jarMergingTask.optionalDependsOn(tasks,
+ pcData.getClassGeneratingTasks(),
+ pcData.getLibraryGeneratingTasks());
+ pcData.setLibraryGeneratingTasks(
+ Collections.singletonList(jarMergingTask.getName()));
+ pcData.setClassGeneratingTasks(Collections.singletonList(jarMergingTask.getName()));
+
+ // Update the inputs
+ pcData.setInputFiles(Collections.singletonList(scope.getJarMergingOutputFile()));
+ pcData.setInputDirCallable(null);
+ pcData.setInputLibraries(Collections.<File>emptyList());
+ }
+
+ // ----------
+ // Create a task to collect the list of manifest entry points which are
+ // needed in the primary dex
+ AndroidTask<CreateManifestKeepList> manifestKeepListTask = androidTasks.create(tasks,
+ new CreateManifestKeepList.ConfigAction(scope, pcData));
+ manifestKeepListTask.dependsOn(tasks,
+ variantData.getOutputs().get(0).getScope().getManifestProcessorTask());
+
+ // ----------
+ // Create a proguard task to shrink the classes to manifest components
+ AndroidTask<ProGuardTask> proguardComponentsTask =
+ androidTasks.create(tasks, new ProGuardTaskConfigAction(scope, pcData));
+
+ // update dependencies
+ proguardComponentsTask.dependsOn(tasks, manifestKeepListTask);
+ proguardComponentsTask.optionalDependsOn(tasks,
+ pcData.getClassGeneratingTasks(),
+ pcData.getLibraryGeneratingTasks());
+
+ // ----------
+ // Compute the full list of classes for the main dex file
+ createMainDexListTask =
+ androidTasks.create(tasks, new CreateMainDexList.ConfigAction(scope, pcData));
+ createMainDexListTask.dependsOn(tasks, proguardComponentsTask);
+ //createMainDexListTask.dependsOn { proguardMainDexTask }
+
+ // ----------
+ // If proguard is enabled, create a de-obfuscated list to aid debugging.
+ if (isMinifyEnabled) {
+ retraceTask = androidTasks.create(tasks,
+ new RetraceMainDexList.ConfigAction(scope, pcData));
+ retraceTask.dependsOn(tasks, scope.getObfuscationTask(), createMainDexListTask);
+ }
+
+ }
+
+ AndroidTask<Dex> dexTask = androidTasks.create(tasks, new Dex.ConfigAction(scope, pcData));
+ scope.setDexTask(dexTask);
+
+ // dependencies, some of these could be null
+ dexTask.optionalDependsOn(tasks,
+ pcData.getClassGeneratingTasks(),
+ pcData.getLibraryGeneratingTasks(),
+ createMainDexListTask,
+ retraceTask);
+ }
+
+ public PostCompilationData createJacocoTask(
+ @NonNull TaskFactory tasks,
+ @NonNull final VariantScope scope,
+ @NonNull final PostCompilationData pcData) {
+ AndroidTask<JacocoInstrumentTask> jacocoTask =
+ androidTasks.create(tasks, new JacocoInstrumentTask.ConfigAction(scope, pcData));
+
+ jacocoTask.optionalDependsOn(tasks, pcData.getClassGeneratingTasks());
+
+ final Copy agentTask = getJacocoAgentTask();
+ jacocoTask.dependsOn(tasks, agentTask);
+
+ // update dependency.
+ PostCompilationData pcData2 = new PostCompilationData();
+ pcData2.setClassGeneratingTasks(Collections.singletonList(jacocoTask.getName()));
+ pcData2.setLibraryGeneratingTasks(
+ Arrays.asList(pcData.getLibraryGeneratingTasks(), agentTask));
+
+ // update inputs
+ pcData2.setInputFilesCallable(new Callable<List<File>>() {
+ @Override
+ public List<File> call() {
+ return new ArrayList<File>(
+ project.files(scope.getVariantData().jacocoInstrumentTask.getOutputDir())
+ .getFiles());
+ }
+
+ });
+ pcData2.setInputDirCallable(new Callable<File>() {
+ @Override
+ public File call() {
+ return scope.getVariantData().jacocoInstrumentTask.getOutputDir();
+ }
+
+ });
+ pcData2.setInputLibrariesCallable(new Callable<List<File>>() {
+ @Override
+ public List<File> call() throws Exception {
+ List<File> files = null;
+ files = new ArrayList<File>(pcData.getInputLibrariesCallable().call());
+ files.add(new File(agentTask.getDestinationDir(), FILE_JACOCO_AGENT));
+ return files;
+ }
+
+ });
+
+ return pcData2;
+ }
+
+ public void createJackTask(
+ @NonNull TaskFactory tasks,
+ @NonNull VariantScope scope) {
+
+ // ----- Create Jill tasks -----
+ final AndroidTask<JillTask> jillRuntimeTask = androidTasks.create(tasks,
+ new JillTask.RuntimeTaskConfigAction(scope));
+
+ final AndroidTask<JillTask> jillPackagedTask = androidTasks.create(tasks,
+ new JillTask.PackagedConfigAction(scope));
+
+ jillPackagedTask.dependsOn(tasks,
+ scope.getVariantData().getVariantDependency().getPackageConfiguration()
+ .getBuildDependencies());
+
+ // ----- Create Jack Task -----
+ AndroidTask<JackTask> jackTask = androidTasks.create(tasks,
+ new JackTask.ConfigAction(scope, isVerbose(), isDebugLog()));
+
+
+ // Jack is compiling and also providing the binary and mapping files.
+ setJavaCompilerTask(jackTask, tasks, scope);
+ jackTask.dependsOn(tasks, scope.getMergeJavaResourcesTask());
+ jackTask.dependsOn(tasks,
+ scope.getVariantData().sourceGenTask,
+ jillRuntimeTask,
+ jillPackagedTask,
+ // TODO - dependency information for the compile classpath is being lost.
+ // Add a temporary approximation
+ scope.getVariantData().getVariantDependency().getCompileConfiguration()
+ .getBuildDependencies());
+
+ }
+
+ /**
+ * Creates the final packaging task, and optionally the zipalign task (if the variant is signed)
+ *
+ * @param publishApk if true the generated APK gets published.
+ */
+ public void createPackagingTask(@NonNull TaskFactory tasks, @NonNull VariantScope variantScope,
+ boolean publishApk) {
+ final ApkVariantData variantData = (ApkVariantData) variantScope.getVariantData();
+
+ GradleVariantConfiguration config = variantData.getVariantConfiguration();
+ boolean signedApk = variantData.isSigned();
+ File apkLocation = new File(variantScope.getGlobalScope().getApkLocation());
+
+ boolean multiOutput = variantData.getOutputs().size() > 1;
+
+ // loop on all outputs. The only difference will be the name of the task, and location
+ // of the generated data.
+ for (final ApkVariantOutputData variantOutputData : variantData.getOutputs()) {
+ VariantOutputScope variantOutputScope = variantOutputData.getScope();
+
+ final String outputName = variantOutputData.getFullName();
+
+ // When shrinking resources, rather than having the packaging task
+ // directly map to the packageOutputFile of ProcessAndroidResources,
+ // we insert the ShrinkResources task into the chain, such that its
+ // input is the ProcessAndroidResources packageOutputFile, and its
+ // output is what the PackageApplication task reads.
+ AndroidTask<ShrinkResources> shrinkTask = null;
+
+ if (config.isMinifyEnabled() && config.getBuildType().isShrinkResources() &&
+ !config.getUseJack()) {
+ shrinkTask = androidTasks.create(
+ tasks, new ShrinkResources.ConfigAction(variantOutputScope));
+ shrinkTask.dependsOn(tasks, variantScope.getObfuscationTask(),
+ variantOutputScope.getManifestProcessorTask(),
+ variantOutputScope.getProcessResourcesTask());
+ }
+
+ AndroidTask<PackageApplication> packageApp = androidTasks.create(
+ tasks, new PackageApplication.ConfigAction(variantOutputScope));
+
+ packageApp.dependsOn(tasks, variantOutputScope.getProcessResourcesTask(),
+ variantOutputScope.getVariantScope().getMergeJavaResourcesTask(),
+ variantOutputScope.getVariantScope().getNdkBuildable());
+
+ packageApp.optionalDependsOn(
+ tasks,
+ shrinkTask,
+ // TODO: When Jack is converted, add activeDexTask to VariantScope.
+ variantOutputScope.getVariantScope().getDexTask(),
+ variantOutputScope.getVariantScope().getJavaCompilerTask(),
+ variantData.javaCompilerTask, // TODO: Remove when Jack is converted to AndroidTask.
+ variantOutputData.packageSplitResourcesTask,
+ variantOutputData.packageSplitAbiTask);
+
+ AndroidTask appTask = packageApp;
+
+ if (signedApk) {
+ if (variantData.getZipAlignEnabled()) {
+ AndroidTask<ZipAlign> zipAlignTask = androidTasks.create(
+ tasks, new ZipAlign.ConfigAction(variantOutputScope));
+ zipAlignTask.dependsOn(tasks, packageApp);
+ if (variantOutputData.splitZipAlign != null) {
+ zipAlignTask.dependsOn(tasks, variantOutputData.splitZipAlign);
+ }
+
+ appTask = zipAlignTask;
+ }
+
+ }
+
+ checkState(variantData.assembleVariantTask != null);
+
+ // Add an assemble task
+ if (multiOutput) {
+ // create a task for this output
+ variantOutputData.assembleTask = createAssembleTask(variantOutputData);
+
+ // variant assemble task depends on each output assemble task.
+ variantData.assembleVariantTask.dependsOn(variantOutputData.assembleTask);
+ } else {
+ // single output
+ variantOutputData.assembleTask = variantData.assembleVariantTask;
+ }
+
+ if (!signedApk && variantOutputData.packageSplitResourcesTask != null) {
+ // in case we are not signing the resulting APKs and we have some pure splits
+ // we should manually copy them from the intermediate location to the final
+ // apk location unmodified.
+ Copy copyTask = project.getTasks().create(
+ variantOutputScope.getTaskName("copySplit"), Copy.class);
+ copyTask.setDestinationDir(apkLocation);
+ copyTask.from(variantOutputData.packageSplitResourcesTask.getOutputDirectory());
+ variantOutputData.assembleTask.dependsOn(copyTask);
+ copyTask.mustRunAfter(appTask.getName());
+ }
+
+ variantOutputData.assembleTask.dependsOn(appTask.getName());
+
+ if (publishApk) {
+ final String projectBaseName = globalScope.getProjectBaseName();
+
+ // if this variant is the default publish config or we also should publish non
+ // defaults, proceed with declaring our artifacts.
+ if (getExtension().getDefaultPublishConfig().equals(outputName)) {
+ appTask.configure(tasks, new Action<Task>() {
+ @Override
+ public void execute(Task packageTask) {
+ project.getArtifacts().add("default",
+ new ApkPublishArtifact(projectBaseName,
+ null,
+ (FileSupplier) packageTask));
+ }
+
+ });
+
+ for (FileSupplier outputFileProvider :
+ variantOutputData.getSplitOutputFileSuppliers()) {
+ project.getArtifacts().add("default",
+ new ApkPublishArtifact(projectBaseName, null, outputFileProvider));
+ }
+
+ try {
+ if (variantOutputData.getMetadataFile() != null) {
+ project.getArtifacts().add("default-metadata",
+ new MetadataPublishArtifact(projectBaseName, null,
+ variantOutputData.getMetadataFile()));
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ if (variantData.getMappingFileProvider() != null) {
+ project.getArtifacts().add("default-mapping",
+ new MappingPublishArtifact(projectBaseName, null,
+ variantData.getMappingFileProvider()));
+ }
+ }
+
+ if (getExtension().getPublishNonDefault()) {
+ appTask.configure(tasks, new Action<Task>() {
+ @Override
+ public void execute(Task packageTask) {
+ project.getArtifacts().add(
+ variantData.getVariantDependency().getPublishConfiguration().getName(),
+ new ApkPublishArtifact(
+ projectBaseName,
+ null,
+ (FileSupplier) packageTask));
+ }
+
+ });
+
+ for (FileSupplier outputFileProvider :
+ variantOutputData.getSplitOutputFileSuppliers()) {
+ project.getArtifacts().add(
+ variantData.getVariantDependency().getPublishConfiguration().getName(),
+ new ApkPublishArtifact(
+ projectBaseName,
+ null,
+ outputFileProvider));
+ }
+
+ try {
+ if (variantOutputData.getMetadataFile() != null) {
+ project.getArtifacts().add(
+ variantData.getVariantDependency().getMetadataConfiguration().getName(),
+ new MetadataPublishArtifact(
+ projectBaseName,
+ null,
+ variantOutputData.getMetadataFile()));
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ if (variantData.getMappingFileProvider() != null) {
+ project.getArtifacts().add(
+ variantData.getVariantDependency().getMappingConfiguration().getName(),
+ new MappingPublishArtifact(
+ projectBaseName,
+ null,
+ variantData.getMappingFileProvider()));
+ }
+
+ if (variantData.classesJarTask != null) {
+ project.getArtifacts().add(
+ variantData.getVariantDependency().getClassesConfiguration().getName(),
+ variantData.classesJarTask);
+ }
+ }
+ }
+ }
+
+ // create install task for the variant Data. This will deal with finding the
+ // right output if there are more than one.
+ // Add a task to install the application package
+ if (signedApk) {
+ AndroidTask<InstallVariantTask> installTask = androidTasks.create(
+ tasks, new InstallVariantTask.ConfigAction(variantScope));
+ installTask.dependsOn(tasks, variantData.assembleVariantTask);
+ }
+
+ if (getExtension().getLintOptions().isCheckReleaseBuilds()) {
+ createLintVitalTask(variantData);
+ }
+
+ // add an uninstall task
+ final AndroidTask<UninstallTask> uninstallTask = androidTasks.create(
+ tasks, new UninstallTask.ConfigAction(variantScope));
+
+ tasks.named(UNINSTALL_ALL, new Action<Task>() {
+ @Override
+ public void execute(Task it) {
+ it.dependsOn(uninstallTask.getName());
+ }
+ });
+ }
+
+ public Task createAssembleTask(@NonNull final BaseVariantOutputData variantOutputData) {
+ Task assembleTask =
+ project.getTasks().create(variantOutputData.getScope().getTaskName("assemble"));
+ return assembleTask;
+ }
+
+ public Task createAssembleTask(TaskFactory tasks,
+ @NonNull final BaseVariantData<? extends BaseVariantOutputData> variantData) {
+ Task assembleTask =
+ project.getTasks().create(variantData.getScope().getTaskName("assemble"));
+ return assembleTask;
+ }
+
+ public Copy getJacocoAgentTask() {
+ if (jacocoAgentTask == null) {
+ jacocoAgentTask = project.getTasks().create("unzipJacocoAgent", Copy.class);
+ jacocoAgentTask.from(new Callable<List<FileTree>>() {
+ @Override
+ public List<FileTree> call() throws Exception {
+ return Lists.newArrayList(Iterables.transform(
+ project.getConfigurations().getByName(
+ JacocoPlugin.AGENT_CONFIGURATION_NAME),
+ new Function<Object, FileTree>() {
+ @Override
+ public FileTree apply(@Nullable Object it) {
+ return project.zipTree(it);
+ }
+ }));
+ }
+ });
+ jacocoAgentTask.include(FILE_JACOCO_AGENT);
+ jacocoAgentTask.into(new File(getGlobalScope().getIntermediatesDir(), "jacoco"));
+ }
+
+ return jacocoAgentTask;
+ }
+
+ /**
+ * creates a zip align. This does not use convention mapping, and is meant to let other plugin
+ * create zip align tasks.
+ *
+ * @param name the name of the task
+ * @param inputFile the input file
+ * @param outputFile the output file
+ * @return the task
+ */
+ @NonNull
+ public ZipAlign createZipAlignTask(
+ @NonNull String name,
+ @NonNull File inputFile,
+ @NonNull File outputFile) {
+ // Add a task to zip align application package
+ ZipAlign zipAlignTask = project.getTasks().create(name, ZipAlign.class);
+
+ zipAlignTask.setInputFile(inputFile);
+ zipAlignTask.setOutputFile(outputFile);
+ ConventionMappingHelper.map(zipAlignTask, "zipAlignExe", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ final TargetInfo info = androidBuilder.getTargetInfo();
+ if (info != null) {
+ String path = info.getBuildTools().getPath(ZIP_ALIGN);
+ if (path != null) {
+ return new File(path);
+ }
+ }
+
+ return null;
+ }
+ });
+
+ return zipAlignTask;
+ }
+
+ /**
+ * Creates the proguarding task for the given Variant if necessary.
+ *
+ * @return null if the proguard task was not created, otherwise the expected outputFile.
+ */
+ @Nullable
+ public File maybeCreateProguardTasks(
+ @NonNull final TaskFactory tasks,
+ @NonNull VariantScope scope,
+ @NonNull final PostCompilationData pcData) {
+ if (!scope.getVariantData().getVariantConfiguration().isMinifyEnabled()) {
+ return null;
+ }
+
+ final AndroidTask<AndroidProGuardTask> proguardTask = androidTasks.create(
+ tasks, new AndroidProGuardTask.ConfigAction(scope, pcData));
+ scope.setObfuscationTask(proguardTask);
+ scope.setJavaResourcesProvider(JavaResourcesProvider.Adapter.build(tasks, proguardTask));
+
+ // update dependency.
+ proguardTask.optionalDependsOn(tasks, pcData.getClassGeneratingTasks(),
+ pcData.getLibraryGeneratingTasks());
+ pcData.setLibraryGeneratingTasks(Collections.singletonList(proguardTask.getName()));
+ pcData.setClassGeneratingTasks(Collections.singletonList(proguardTask.getName()));
+
+ // Return output file.
+ return scope.getProguardOutputFile();
+ }
+
+ public void createReportTasks(
+ List<BaseVariantData<? extends BaseVariantOutputData>> variantDataList) {
+ DependencyReportTask dependencyReportTask =
+ project.getTasks().create("androidDependencies", DependencyReportTask.class);
+ dependencyReportTask.setDescription("Displays the Android dependencies of the project.");
+ dependencyReportTask.setVariants(variantDataList);
+ dependencyReportTask.setGroup(ANDROID_GROUP);
+
+ SigningReportTask signingReportTask =
+ project.getTasks().create("signingReport", SigningReportTask.class);
+ signingReportTask.setDescription("Displays the signing info for each variant.");
+ signingReportTask.setVariants(variantDataList);
+ signingReportTask.setGroup(ANDROID_GROUP);
+ }
+
+ public void createAnchorTasks(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
+ createPreBuildTasks(scope);
+
+ // also create sourceGenTask
+ final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+ scope.setSourceGenTask(androidTasks.create(tasks,
+ scope.getTaskName("generate", "Sources"),
+ Task.class,
+ new Action<Task>() {
+ @Override
+ public void execute(Task task) {
+ variantData.sourceGenTask = task;
+ }
+ }));
+ // and resGenTask
+ scope.setResourceGenTask(androidTasks.create(tasks,
+ scope.getTaskName("generate", "Resources"),
+ Task.class,
+ new Action<Task>() {
+ @Override
+ public void execute(Task task) {
+ variantData.resourceGenTask = task;
+ }
+
+ }));
+
+ scope.setAssetGenTask(androidTasks.create(tasks,
+ scope.getTaskName("generate", "Assets"),
+ Task.class,
+ new Action<Task>() {
+ @Override
+ public void execute(Task task) {
+ variantData.assetGenTask = task;
+ }
+ }));
+
+ // and compile task
+ createCompileAnchorTask(tasks, scope);
+ }
+
+ private void createPreBuildTasks(@NonNull VariantScope scope) {
+ final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+ variantData.preBuildTask = project.getTasks().create(scope.getTaskName("pre", "Build"));
+ variantData.preBuildTask.dependsOn(MAIN_PREBUILD);
+
+ PrepareDependenciesTask prepareDependenciesTask = project.getTasks().create(
+ scope.getTaskName("prepare", "Dependencies"), PrepareDependenciesTask.class);
+
+ variantData.prepareDependenciesTask = prepareDependenciesTask;
+ prepareDependenciesTask.dependsOn(variantData.preBuildTask);
+
+ prepareDependenciesTask.setAndroidBuilder(androidBuilder);
+ prepareDependenciesTask.setVariantName(scope.getVariantConfiguration().getFullName());
+ prepareDependenciesTask.setVariant(variantData);
+
+ // for all libraries required by the configurations of this variant, make this task
+ // depend on all the tasks preparing these libraries.
+ VariantDependencies configurationDependencies = variantData.getVariantDependency();
+ prepareDependenciesTask.addChecker(configurationDependencies.getChecker());
+
+ for (LibraryDependencyImpl lib : configurationDependencies.getLibraries()) {
+ dependencyManager.addDependencyToPrepareTask(variantData, prepareDependenciesTask, lib);
+ }
+ }
+
+ private void createCompileAnchorTask(@NonNull TaskFactory tasks, @NonNull final VariantScope scope) {
+ final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+ scope.setCompileTask(androidTasks.create(tasks, new TaskConfigAction<Task>() {
+ @Override
+ public String getName() {
+ return scope.getTaskName("compile", "Sources");
+ }
+
+ @Override
+ public Class<Task> getType() {
+ return Task.class;
+ }
+
+ @Override
+ public void execute(Task task) {
+ variantData.compileTask = task;
+ variantData.compileTask.setGroup(BUILD_GROUP);
+ }
+ }));
+ variantData.assembleVariantTask.dependsOn(scope.getCompileTask().getName());
+ }
+
+ public void createCheckManifestTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
+ final BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+ final String name = variantData.getVariantConfiguration().getFullName();
+ scope.setCheckManifestTask(androidTasks.create(tasks,
+ scope.getTaskName("check", "Manifest"),
+ CheckManifest.class,
+ new Action<CheckManifest>() {
+ @Override
+ public void execute(CheckManifest checkManifestTask) {
+ variantData.checkManifestTask = checkManifestTask;
+ checkManifestTask.setVariantName(name);
+ ConventionMappingHelper.map(checkManifestTask, "manifest",
+ new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ return variantData.getVariantConfiguration()
+ .getDefaultSourceSet().getManifestFile();
+ }
+ });
+ }
+
+ }));
+ scope.getCheckManifestTask().dependsOn(tasks, variantData.preBuildTask);
+ variantData.prepareDependenciesTask.dependsOn(scope.getCheckManifestTask().getName());
+ }
+
+ public static void optionalDependsOn(@NonNull Task main, Task... dependencies) {
+ for (Task dependency : dependencies) {
+ if (dependency != null) {
+ main.dependsOn(dependency);
+ }
+
+ }
+
+ }
+
+ public static void optionalDependsOn(@NonNull Task main, @NonNull List<?> dependencies) {
+ for (Object dependency : dependencies) {
+ if (dependency != null) {
+ main.dependsOn(dependency);
+ }
+
+ }
+
+ }
+
+ @NonNull
+ private static List<ManifestDependencyImpl> getManifestDependencies(
+ List<LibraryDependency> libraries) {
+
+ List<ManifestDependencyImpl> list = Lists.newArrayListWithCapacity(libraries.size());
+
+ for (LibraryDependency lib : libraries) {
+ // get the dependencies
+ List<ManifestDependencyImpl> children = getManifestDependencies(lib.getDependencies());
+ list.add(new ManifestDependencyImpl(lib.getName(), lib.getManifest(), children));
+ }
+
+ return list;
+ }
+
+ @NonNull
+ protected Logger getLogger() {
+ return logger;
+ }
+
+ @NonNull
+ protected AndroidTaskRegistry getAndroidTasks() {
+ return androidTasks;
+ }
+
+ private File getDefaultProguardFile(String name) {
+ File sdkDir = sdkHandler.getAndCheckSdkFolder();
+ return new File(sdkDir,
+ SdkConstants.FD_TOOLS + File.separatorChar + SdkConstants.FD_PROGUARD
+ + File.separatorChar + name);
+ }
+
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TestApplicationTaskManager.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TestApplicationTaskManager.java
index 1111641..ed65971 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TestApplicationTaskManager.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/TestApplicationTaskManager.java
@@ -16,12 +16,13 @@
package com.android.build.gradle.internal;
-import static com.android.builder.core.BuilderConstants.CONNECTED;
+import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.build.gradle.BaseExtension;
-import com.android.build.gradle.TestExtension;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.TestAndroidConfig;
+import com.android.build.gradle.internal.scope.AndroidTask;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask;
import com.android.build.gradle.internal.test.TestApplicationTestData;
@@ -43,11 +44,11 @@
import org.gradle.api.artifacts.dsl.DependencyHandler;
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
-import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
-
-
import java.io.File;
-import java.util.Locale;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
import proguard.ParseException;
@@ -60,7 +61,7 @@
public TestApplicationTaskManager(Project project,
AndroidBuilder androidBuilder,
- BaseExtension extension,
+ AndroidConfig extension,
SdkHandler sdkHandler,
DependencyManager dependencyManager,
ToolingModelBuilderRegistry toolingRegistry) {
@@ -68,16 +69,16 @@
}
@Override
- public void createTasksForVariantData(TaskFactory tasks,
+ public void createTasksForVariantData(@NonNull TaskFactory tasks,
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
super.createTasksForVariantData(tasks, variantData);
// create a new configuration with the target application coordinates.
- Configuration testTarget = project.getConfigurations().create("testTarget");
+ final Configuration testTarget = project.getConfigurations().create("testTarget");
DependencyHandler dependencyHandler = project.getDependencies();
- TestExtension testExtension = (TestExtension) extension;
+ TestAndroidConfig testExtension = (TestAndroidConfig) extension;
dependencyHandler.add("testTarget",
dependencyHandler.project(
ImmutableMap.of(
@@ -85,7 +86,7 @@
"configuration", testExtension.getTargetVariant())));
// and create the configuration for the project's metadata.
- Configuration testTargetMetadata = project.getConfigurations().create("testTargetMetadata");
+ final Configuration testTargetMetadata = project.getConfigurations().create("testTargetMetadata");
dependencyHandler.add("testTargetMetadata", dependencyHandler.project(
ImmutableMap.of(
@@ -97,77 +98,39 @@
variantData, testTarget, testTargetMetadata, androidBuilder);
// create the test connected check task.
- DeviceProviderInstrumentTestTask testConnectedCheck =
- createDeviceProviderInstrumentTestTask(
- project.getName() + "ConnectedCheck",
- "Installs and runs the tests for " + variantData.getDescription()
- + " on connected devices.",
- DeviceProviderInstrumentTestTask.class,
- testData,
- ImmutableList.of(variantData.assembleVariantTask),
- new ConnectedDeviceProvider(
- sdkHandler.getSdkInfo().getAdb(),
- new LoggerWrapper(getLogger())),
- CONNECTED
- );
+ AndroidTask<DeviceProviderInstrumentTestTask> testConnectedCheck =
+ getAndroidTasks().create(
+ tasks,
+ new DeviceProviderInstrumentTestTask.ConfigAction(
+ variantData.getScope(),
+ new ConnectedDeviceProvider(
+ sdkHandler.getSdkInfo().getAdb(),
+ new LoggerWrapper(getLogger())),
+ testData));
// make the test application connectedCheck depends on the configuration added above so
// we can retrieve its artifacts
- testConnectedCheck.dependsOn(testTarget);
- testConnectedCheck.dependsOn(testTargetMetadata);
+
+ testConnectedCheck.dependsOn(tasks,
+ testTarget,
+ testTargetMetadata,
+ variantData.assembleVariantTask);
// make the main ConnectedCheck task depends on this test connectedCheck
Task connectedCheck = tasks.named(CONNECTED_CHECK);
if (connectedCheck != null) {
- connectedCheck.dependsOn(testConnectedCheck);
+ connectedCheck.dependsOn(testConnectedCheck.getName());
}
}
@Override
@Nullable
public File maybeCreateProguardTasks(
- TaskFactory tasks,
- VariantScope scope,
+ @NonNull final TaskFactory tasks,
+ @NonNull final VariantScope scope,
@NonNull final PostCompilationData pcData) {
- BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
-
- final TestModuleProGuardTask proguardTask = project.getTasks().create(
- "proguard"+ variantData.getVariantConfiguration().getFullName().toUpperCase(
- Locale.getDefault()),
- TestModuleProGuardTask.class);
-
- variantData.obfuscationTask = proguardTask;
-
- // --- Output File ---
-
- final File outFile = variantData instanceof LibraryVariantData ?
- project.file(String.format("%s/%s/%s/%s/classes.jar",
- project.getBuildDir(),
- AndroidProject.FD_INTERMEDIATES,
- TaskManager.DIR_BUNDLES,
- variantData.getVariantConfiguration().getDirName())) :
- project.file(String.format("%s/%s/classes-proguard/%s/classes.jar",
- project.getBuildDir(),
- AndroidProject.FD_INTERMEDIATES,
- variantData.getVariantConfiguration().getDirName()));
- variantData.obfuscatedClassesJar = outFile;
-
DependencyHandler dependencyHandler = project.getDependencies();
- TestExtension testExtension = (TestExtension) extension;
-
- // and create the configuration for the project's classes.jar file.
- Configuration testClassesMapping = project.getConfigurations().create("testTargetClasses");
-
- dependencyHandler.add("testTargetClasses", dependencyHandler.project(
- ImmutableMap.of(
- "path", testExtension.getTargetProjectPath(),
- "configuration", testExtension.getTargetVariant() + "-classes"
- )));
-
- // Input the original .class files so the compiler can compile the test code correctly.
- proguardTask.setClassesConfiguration(testClassesMapping);
-
- // and create the configuration for the project's mapping file.
+ TestAndroidConfig testExtension = (TestAndroidConfig) extension;
Configuration testTargetMapping = project.getConfigurations().create("testTargetMapping");
dependencyHandler.add("testTargetMapping", dependencyHandler.project(
@@ -176,8 +139,24 @@
"configuration", testExtension.getTargetVariant() + "-mapping"
)));
+ if (testTargetMapping.getFiles().isEmpty()
+ || scope.getVariantConfiguration().getProvidedOnlyJars().isEmpty()) {
+ return null;
+ }
+
+ BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+
+ final TestModuleProGuardTask proguardTask = project.getTasks().create(
+ scope.getTaskName("proguard"),
+ TestModuleProGuardTask.class);
+
+ variantData.obfuscationTask = proguardTask;
+ proguardTask.setLogger(getLogger());
+
+ // and create the configuration for the project's mapping file.
// Input the mapping from the tested app so that we can deal with obfuscated code.
proguardTask.setMappingConfiguration(testTargetMapping);
+ proguardTask.setVariantConfiguration(scope.getVariantConfiguration());
// --- Proguard Config ---
@@ -210,17 +189,40 @@
});
try {
+
// injar: the compilation output
- proguardTask.injars(pcData.getInputDir());
+ proguardTask.injars(pcData.getInputDirCallable());
+ if (pcData.getJavaResourcesInputDirCallable() != null) {
+ proguardTask.injars(pcData.getJavaResourcesInputDirCallable());
+ }
+
+ // injar: the packaged dependencies
+ LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(1);
+ map.put("filter", "!META-INF/MANIFEST.MF");
+ proguardTask.injars(map, new Callable<Set<File>>() {
+ @Override
+ public Set<File> call() throws Exception {
+ return scope.getVariantConfiguration().getPackagedJars();
+ }
+ });
+
+ proguardTask.libraryjars(new Callable<List<File>>() {
+ @Override
+ public List<File> call() throws Exception {
+ return scope.getVariantConfiguration().getProvidedOnlyJars();
+ }
+ });
+
// All -dontwarn rules for test dependencies should go in here:
proguardTask.configuration(
variantData.getVariantConfiguration().getTestProguardFiles());
+
// --- Out files ---
- proguardTask.outjars(outFile);
+ proguardTask.outjars(scope.getProguardOutputFile());
final File proguardOut = project.file(
new File(project.getBuildDir(),
@@ -240,18 +242,21 @@
proguardTask.doFirst(new Action<Task>() {
@Override
public void execute(Task task) {
- proguardOut.mkdirs();
+ if (!proguardOut.mkdirs()) {
+ throw new RuntimeException(
+ "Cannot create proguard output folder " + proguardOut);
+ }
}
});
// update dependency.
- optionalDependsOn(proguardTask, pcData.getClassGeneratingTask());
- optionalDependsOn(proguardTask, pcData.getLibraryGeneratingTask());
- pcData.setLibraryGeneratingTask(ImmutableList.of(proguardTask));
- pcData.setClassGeneratingTask(ImmutableList.of(proguardTask));
+ optionalDependsOn(proguardTask, pcData.getClassGeneratingTasks());
+ optionalDependsOn(proguardTask, pcData.getLibraryGeneratingTasks());
+ pcData.setLibraryGeneratingTasks(ImmutableList.of(proguardTask));
+ pcData.setClassGeneratingTasks(ImmutableList.of(proguardTask));
// Update the inputs
- return outFile;
+ return scope.getProguardOutputFile();
} catch (Exception e) {
throw new RuntimeException(e);
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantManager.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantManager.java
index 873dfcd..fd2e2d1 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantManager.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantManager.java
@@ -16,38 +16,26 @@
package com.android.build.gradle.internal;
-import static com.android.build.OutputFile.NO_FILTER;
-import static com.android.builder.core.BuilderConstants.DEBUG;
import static com.android.builder.core.BuilderConstants.LINT;
import static com.android.builder.core.VariantType.ANDROID_TEST;
+import static com.android.builder.core.VariantType.LIBRARY;
import static com.android.builder.core.VariantType.UNIT_TEST;
-import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_KEY_ALIAS;
-import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_KEY_PASSWORD;
-import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_STORE_FILE;
-import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_STORE_PASSWORD;
-import static com.android.builder.model.AndroidProject.PROPERTY_SIGNING_STORE_TYPE;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.build.gradle.BaseExtension;
-import com.android.build.gradle.TestedExtension;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.AndroidGradleOptions;
+import com.android.build.gradle.TestAndroidConfig;
+import com.android.build.gradle.TestedAndroidConfig;
import com.android.build.gradle.api.AndroidSourceSet;
-import com.android.build.gradle.api.BaseVariant;
import com.android.build.gradle.internal.api.DefaultAndroidSourceSet;
import com.android.build.gradle.internal.api.ReadOnlyObjectProvider;
-import com.android.build.gradle.internal.api.TestVariantImpl;
-import com.android.build.gradle.internal.api.TestedVariant;
-import com.android.build.gradle.internal.api.UnitTestVariantImpl;
import com.android.build.gradle.internal.api.VariantFilter;
import com.android.build.gradle.internal.core.GradleVariantConfiguration;
import com.android.build.gradle.internal.dependency.VariantDependencies;
-import com.android.build.gradle.internal.dsl.BuildType;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
-import com.android.build.gradle.internal.dsl.ProductFlavor;
-import com.android.build.gradle.internal.dsl.SigningConfig;
-import com.android.build.gradle.internal.dsl.Splits;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
import com.android.build.gradle.internal.profile.SpanRecorders;
-import com.android.build.gradle.internal.variant.ApplicationVariantFactory;
import com.android.build.gradle.internal.variant.BaseVariantData;
import com.android.build.gradle.internal.variant.BaseVariantOutputData;
import com.android.build.gradle.internal.variant.TestVariantData;
@@ -55,6 +43,8 @@
import com.android.build.gradle.internal.variant.VariantFactory;
import com.android.builder.core.AndroidBuilder;
import com.android.builder.core.VariantType;
+import com.android.builder.model.ProductFlavor;
+import com.android.builder.model.SigningConfig;
import com.android.builder.profile.ExecutionType;
import com.android.builder.profile.Recorder;
import com.android.builder.profile.ThreadRecorder;
@@ -74,9 +64,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Set;
-
-import groovy.lang.Closure;
/**
* Class to create, manage variants.
@@ -91,7 +78,7 @@
@NonNull
private final AndroidBuilder androidBuilder;
@NonNull
- private final BaseExtension extension;
+ private final AndroidConfig extension;
@NonNull
private final VariantFactory variantFactory;
@NonNull
@@ -99,11 +86,11 @@
@NonNull
private final Instantiator instantiator;
@NonNull
- private ProductFlavorData<ProductFlavor> defaultConfigData;
+ private ProductFlavorData<CoreProductFlavor> defaultConfigData;
@NonNull
private final Map<String, BuildTypeData> buildTypes = Maps.newHashMap();
@NonNull
- private final Map<String, ProductFlavorData<GroupableProductFlavor>> productFlavors = Maps.newHashMap();
+ private final Map<String, ProductFlavorData<CoreProductFlavor>> productFlavors = Maps.newHashMap();
@NonNull
private final Map<String, SigningConfig> signingConfigs = Maps.newHashMap();
@@ -120,7 +107,7 @@
public VariantManager(
@NonNull Project project,
@NonNull AndroidBuilder androidBuilder,
- @NonNull BaseExtension extension,
+ @NonNull AndroidConfig extension,
@NonNull VariantFactory variantFactory,
@NonNull TaskManager taskManager,
@NonNull Instantiator instantiator) {
@@ -145,7 +132,7 @@
.getByName(UNIT_TEST.getPrefix());
}
- defaultConfigData = new ProductFlavorData<ProductFlavor>(
+ defaultConfigData = new ProductFlavorData<CoreProductFlavor>(
extension.getDefaultConfig(), mainSourceSet,
androidTestSourceSet, unitTestSourceSet, project);
signingOverride = createSigningOverride();
@@ -153,7 +140,7 @@
@NonNull
@Override
- public ProductFlavorData<ProductFlavor> getDefaultConfig() {
+ public ProductFlavorData<CoreProductFlavor> getDefaultConfig() {
return defaultConfigData;
}
@@ -165,7 +152,7 @@
@Override
@NonNull
- public Map<String, ProductFlavorData<GroupableProductFlavor>> getProductFlavors() {
+ public Map<String, ProductFlavorData<CoreProductFlavor>> getProductFlavors() {
return productFlavors;
}
@@ -184,9 +171,7 @@
* and adding it to the map.
* @param buildType the build type.
*/
- public void addBuildType(@NonNull BuildType buildType) {
- buildType.init(signingConfigs.get(DEBUG));
-
+ public void addBuildType(@NonNull CoreBuildType buildType) {
String name = buildType.getName();
checkName(name, "BuildType");
@@ -194,18 +179,17 @@
throw new RuntimeException("BuildType names cannot collide with ProductFlavor names");
}
- DefaultAndroidSourceSet mainSourceSet = (DefaultAndroidSourceSet) extension.getSourceSetsContainer().maybeCreate(name);
+ DefaultAndroidSourceSet mainSourceSet = (DefaultAndroidSourceSet) extension.getSourceSets().maybeCreate(name);
DefaultAndroidSourceSet unitTestSourceSet = null;
if (variantFactory.hasTestScope()) {
unitTestSourceSet = (DefaultAndroidSourceSet) extension
- .getSourceSetsContainer().maybeCreate(
+ .getSourceSets().maybeCreate(
computeSourceSetName(buildType.getName(), UNIT_TEST));
}
BuildTypeData buildTypeData = new BuildTypeData(
buildType, project, mainSourceSet, unitTestSourceSet);
- project.getTasks().getByName("assemble").dependsOn(buildTypeData.getAssembleTask());
buildTypes.put(name, buildTypeData);
}
@@ -216,7 +200,7 @@
*
* @param productFlavor the product flavor
*/
- public void addProductFlavor(@NonNull GroupableProductFlavor productFlavor) {
+ public void addProductFlavor(@NonNull CoreProductFlavor productFlavor) {
String name = productFlavor.getName();
checkName(name, "ProductFlavor");
@@ -224,22 +208,22 @@
throw new RuntimeException("ProductFlavor names cannot collide with BuildType names");
}
- DefaultAndroidSourceSet mainSourceSet = (DefaultAndroidSourceSet) extension.getSourceSetsContainer().maybeCreate(
+ DefaultAndroidSourceSet mainSourceSet = (DefaultAndroidSourceSet) extension.getSourceSets().maybeCreate(
productFlavor.getName());
DefaultAndroidSourceSet androidTestSourceSet = null;
DefaultAndroidSourceSet unitTestSourceSet = null;
if (variantFactory.hasTestScope()) {
androidTestSourceSet = (DefaultAndroidSourceSet) extension
- .getSourceSetsContainer().maybeCreate(
+ .getSourceSets().maybeCreate(
computeSourceSetName(productFlavor.getName(), ANDROID_TEST));
unitTestSourceSet = (DefaultAndroidSourceSet) extension
- .getSourceSetsContainer().maybeCreate(
+ .getSourceSets().maybeCreate(
computeSourceSetName(productFlavor.getName(), UNIT_TEST));
}
- ProductFlavorData<GroupableProductFlavor> productFlavorData =
- new ProductFlavorData<GroupableProductFlavor>(
+ ProductFlavorData<CoreProductFlavor> productFlavorData =
+ new ProductFlavorData<CoreProductFlavor>(
productFlavor,
mainSourceSet,
androidTestSourceSet,
@@ -266,7 +250,14 @@
final TaskFactory tasks = new TaskContainerAdaptor(project.getTasks());
if (variantDataList.isEmpty()) {
- populateVariantDataList();
+ ThreadRecorder.get().record(ExecutionType.VARIANT_MANAGER_CREATE_VARIANTS,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ populateVariantDataList();
+ return null;
+ }
+ });
}
// Create top level test tasks.
@@ -280,6 +271,7 @@
});
for (final BaseVariantData<? extends BaseVariantOutputData> variantData : variantDataList) {
+
SpanRecorders.record(project, ExecutionType.VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT,
new Recorder.Block<Void>() {
@Override
@@ -291,9 +283,6 @@
new Recorder.Property(SpanRecorders.VARIANT, variantData.getName()));
}
- // Create the variant API objects after the tasks have been created!
- createApiObjects();
-
taskManager.createReportTasks(variantDataList);
}
@@ -321,7 +310,7 @@
// each flavor
GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration();
- for (GroupableProductFlavor flavor : variantConfig.getProductFlavors()) {
+ for (CoreProductFlavor flavor : variantConfig.getProductFlavors()) {
productFlavors.get(flavor.getName()).getAssembleTask()
.dependsOn(variantData.assembleVariantTask);
}
@@ -357,8 +346,20 @@
* Create tasks for the specified variantData.
*/
public void createTasksForVariantData(
- TaskFactory tasks,
- BaseVariantData<? extends BaseVariantOutputData> variantData) {
+ final TaskFactory tasks,
+ final BaseVariantData<? extends BaseVariantOutputData> variantData) {
+
+ // Add dependency of assemble task on assemble build type task.
+ tasks.named("assemble", new Action<Task>() {
+ @Override
+ public void execute(Task task) {
+ BuildTypeData buildTypeData = buildTypes.get(
+ variantData.getVariantConfiguration().getBuildType().getName());
+ task.dependsOn(buildTypeData.getAssembleTask());
+ }
+ });
+
+
VariantType variantType = variantData.getType();
createAssembleTaskForVariantData(tasks, variantData);
@@ -379,9 +380,8 @@
testVariantProviders.add(buildTypeConfigurationProvider);
}
- for (com.android.build.gradle.api.GroupableProductFlavor productFlavor :
- testVariantConfig.getProductFlavors()) {
- ProductFlavorData<GroupableProductFlavor> data =
+ for (CoreProductFlavor productFlavor : testVariantConfig.getProductFlavors()) {
+ ProductFlavorData<CoreProductFlavor> data =
productFlavors.get(productFlavor.getName());
testVariantProviders.add(data.getTestConfigurationProvider(variantType));
}
@@ -390,8 +390,9 @@
testVariantProviders.add(defaultConfigData.getTestConfigurationProvider(variantType));
assert(testVariantConfig.getTestedConfig() != null);
+ VariantDependencies parentVariant = null;
if (testVariantConfig.getTestedConfig().getType() == VariantType.LIBRARY) {
- testVariantProviders.add(testedVariantData.getVariantDependency());
+ parentVariant = testedVariantData.getVariantDependency();
}
// If the variant being tested is a library variant, VariantDependencies must be
@@ -401,6 +402,7 @@
project, testVariantConfig.getFullName(),
false /*publishVariant*/,
variantType,
+ parentVariant,
testVariantProviders.toArray(
new ConfigurationProvider[testVariantProviders.size()]));
variantData.setVariantDependency(variantDep);
@@ -412,7 +414,8 @@
taskManager.resolveDependencies(variantDep,
testVariantConfig.getTestedConfig().getType() == VariantType.LIBRARY
? null
- : testedVariantData.getVariantDependency());
+ : testedVariantData.getVariantDependency(),
+ null /*testedProjectPath*/);
return null;
}
},
@@ -438,53 +441,44 @@
*/
public void populateVariantDataList() {
if (productFlavors.isEmpty()) {
- createVariantDataForProductFlavors(
- Collections.<com.android.build.gradle.api.GroupableProductFlavor>emptyList());
+ createVariantDataForProductFlavors(Collections.<ProductFlavor>emptyList());
} else {
List<String> flavorDimensionList = extension.getFlavorDimensionList();
- // Create iterable to get GroupableProductFlavor from ProductFlavorData.
- Iterable<GroupableProductFlavor> flavorDsl =
+ // Create iterable to get GradleProductFlavor from ProductFlavorData.
+ Iterable<CoreProductFlavor> flavorDsl =
Iterables.transform(
productFlavors.values(),
- new Function<ProductFlavorData<GroupableProductFlavor>, GroupableProductFlavor>() {
+ new Function<ProductFlavorData<CoreProductFlavor>, CoreProductFlavor>() {
@Override
- public GroupableProductFlavor apply(
- ProductFlavorData<GroupableProductFlavor> data) {
+ public CoreProductFlavor apply(
+ ProductFlavorData<CoreProductFlavor> data) {
return data.getProductFlavor();
}
});
// Get a list of all combinations of product flavors.
- List<ProductFlavorCombo> flavorComboList = ProductFlavorCombo.createCombinations(
- flavorDimensionList,
- flavorDsl);
+ List<ProductFlavorCombo<CoreProductFlavor>> flavorComboList =
+ ProductFlavorCombo.createCombinations(
+ flavorDimensionList,
+ flavorDsl);
- for (ProductFlavorCombo flavorCombo : flavorComboList) {
- createVariantDataForProductFlavors(flavorCombo.getFlavorList());
+ for (ProductFlavorCombo<CoreProductFlavor> flavorCombo : flavorComboList) {
+ //noinspection unchecked
+ createVariantDataForProductFlavors(
+ (List<ProductFlavor>) (List) flavorCombo.getFlavorList());
}
}
}
/**
- * Create a VariantData for a specific combination of BuildType and GroupableProductFlavor list.
+ * Create a VariantData for a specific combination of BuildType and ProductFlavor list.
*/
public BaseVariantData<? extends BaseVariantOutputData> createVariantData(
@NonNull com.android.builder.model.BuildType buildType,
- @NonNull List<? extends com.android.build.gradle.api.GroupableProductFlavor> productFlavorList) {
- Splits splits = extension.getSplits();
- Set<String> densities = splits.getDensityFilters();
- Set<String> abis = splits.getAbiFilters();
-
- // check against potentially empty lists. We always need to generate at least one output
- densities = densities.isEmpty() ? Collections.singleton(NO_FILTER) : densities;
- abis = abis.isEmpty() ? Collections.singleton(NO_FILTER) : abis;
-
+ @NonNull List<? extends ProductFlavor> productFlavorList) {
BuildTypeData buildTypeData = buildTypes.get(buildType.getName());
- Set<String> compatibleScreens = extension.getSplits().getDensity()
- .getCompatibleScreens();
-
GradleVariantConfiguration variantConfig = new GradleVariantConfiguration(
defaultConfigData.getProductFlavor(),
defaultConfigData.getSourceSet(),
@@ -493,14 +487,21 @@
variantFactory.getVariantConfigurationType(),
signingOverride);
+ if (variantConfig.getType() == LIBRARY && variantConfig.getUseJack()) {
+ project.getLogger().warn(
+ "{}, {}: Jack compiler is not supported in library projects, falling back to javac.",
+ project.getPath(),
+ variantConfig.getFullName());
+ }
+
// sourceSetContainer in case we are creating variant specific sourceSets.
NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer = extension
- .getSourceSetsContainer();
+ .getSourceSets();
// We must first add the flavors to the variant config, in order to get the proper
// variant-specific and multi-flavor name as we add/create the variant providers later.
- for (com.android.build.gradle.api.GroupableProductFlavor productFlavor : productFlavorList) {
- ProductFlavorData<GroupableProductFlavor> data = productFlavors.get(
+ for (ProductFlavor productFlavor : productFlavorList) {
+ ProductFlavorData<CoreProductFlavor> data = productFlavors.get(
productFlavor.getName());
String dimensionName = productFlavor.getDimension();
@@ -544,7 +545,7 @@
}
// 4. the flavors.
- for (com.android.build.gradle.api.GroupableProductFlavor productFlavor : productFlavorList) {
+ for (ProductFlavor productFlavor : productFlavorList) {
variantProviders.add(productFlavors.get(productFlavor.getName()).getMainProvider());
}
@@ -552,13 +553,14 @@
variantProviders.add(defaultConfigData.getMainProvider());
// Done. Create the variant and get its internal storage object.
- BaseVariantData<?> variantData = variantFactory.createVariantData(variantConfig,
- densities, abis, compatibleScreens, taskManager);
+ BaseVariantData<?> variantData =
+ variantFactory.createVariantData(variantConfig, taskManager);
final VariantDependencies variantDep = VariantDependencies.compute(
project, variantConfig.getFullName(),
isVariantPublished(),
variantData.getType(),
+ null,
variantProviders.toArray(new ConfigurationProvider[variantProviders.size()]));
variantData.setVariantDependency(variantDep);
@@ -569,11 +571,18 @@
variantDep.getPackageConfiguration().getName(), COM_ANDROID_SUPPORT_MULTIDEX);
}
+ final String testedProjectPath = extension instanceof TestAndroidConfig ?
+ ((TestAndroidConfig) extension).getTargetProjectPath() :
+ null;
+
SpanRecorders.record(project, ExecutionType.RESOLVE_DEPENDENCIES,
new Recorder.Block<Void>() {
@Override
public Void call() {
- taskManager.resolveDependencies(variantDep, null);
+ taskManager.resolveDependencies(
+ variantDep,
+ null /*testedVariantDeps*/,
+ testedProjectPath);
return null;
}
}, new Recorder.Property(SpanRecorders.VARIANT, variantConfig.getFullName()));
@@ -584,7 +593,7 @@
}
private static void createCompoundSourceSets(
- @NonNull List<? extends com.android.build.gradle.api.GroupableProductFlavor> productFlavorList,
+ @NonNull List<? extends ProductFlavor> productFlavorList,
GradleVariantConfiguration variantConfig,
NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer) {
if (!productFlavorList.isEmpty() && !variantConfig.getType().isSingleBuildType()) {
@@ -631,12 +640,12 @@
public TestVariantData createTestVariantData(
BaseVariantData testedVariantData,
VariantType type) {
- ProductFlavor defaultConfig = defaultConfigData.getProductFlavor();
- BuildType buildType = testedVariantData.getVariantConfiguration().getBuildType();
+ CoreProductFlavor defaultConfig = defaultConfigData.getProductFlavor();
+ CoreBuildType buildType = testedVariantData.getVariantConfiguration().getBuildType();
BuildTypeData buildTypeData = buildTypes.get(buildType.getName());
GradleVariantConfiguration testedConfig = testedVariantData.getVariantConfiguration();
- List<? extends com.android.build.gradle.api.GroupableProductFlavor> productFlavorList = testedConfig.getProductFlavors();
+ List<? extends CoreProductFlavor> productFlavorList = testedConfig.getProductFlavors();
// handle test variant
// need a suppress warning because ProductFlavor.getTestSourceSet(type) is annotated
@@ -653,8 +662,8 @@
type,
signingOverride);
- for (com.android.build.gradle.api.GroupableProductFlavor productFlavor : productFlavorList) {
- ProductFlavorData<GroupableProductFlavor> data = productFlavors
+ for (CoreProductFlavor productFlavor : productFlavorList) {
+ ProductFlavorData<CoreProductFlavor> data = productFlavors
.get(productFlavor.getName());
String dimensionName = productFlavor.getDimension();
@@ -672,7 +681,7 @@
createCompoundSourceSets(
productFlavorList,
testVariantConfig,
- extension.getSourceSetsContainer());
+ extension.getSourceSets());
// create the internal storage for this variant.
TestVariantData testVariantData = new TestVariantData(
@@ -691,11 +700,11 @@
* @param productFlavorList the flavor(s) to build.
*/
private void createVariantDataForProductFlavors(
- @NonNull List<com.android.build.gradle.api.GroupableProductFlavor> productFlavorList) {
+ @NonNull List<ProductFlavor> productFlavorList) {
BuildTypeData testBuildTypeData = null;
- if (extension instanceof TestedExtension) {
- TestedExtension testedExtension = (TestedExtension) extension;
+ if (extension instanceof TestedAndroidConfig) {
+ TestedAndroidConfig testedExtension = (TestedAndroidConfig) extension;
testBuildTypeData = buildTypes.get(testedExtension.getTestBuildType());
if (testBuildTypeData == null) {
@@ -706,15 +715,16 @@
BaseVariantData variantForAndroidTest = null;
- ProductFlavor defaultConfig = defaultConfigData.getProductFlavor();
+ CoreProductFlavor defaultConfig = defaultConfigData.getProductFlavor();
- Closure<Void> variantFilterClosure = extension.getVariantFilter();
+ Action<com.android.build.gradle.api.VariantFilter> variantFilterAction =
+ extension.getVariantFilter();
for (BuildTypeData buildTypeData : buildTypes.values()) {
boolean ignore = false;
- if (variantFilterClosure != null) {
+ if (variantFilterAction != null) {
variantFilter.reset(defaultConfig, buildTypeData.getBuildType(), productFlavorList);
- variantFilterClosure.call(variantFilter);
+ variantFilterAction.execute(variantFilter);
ignore = variantFilter.isIgnore();
}
@@ -724,6 +734,30 @@
productFlavorList);
variantDataList.add(variantData);
+ GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration();
+ ThreadRecorder.get().record(
+ ExecutionType.VARIANT_CONFIG,
+ Recorder.EmptyBlock,
+ new Recorder.Property(
+ "project",
+ project.getName()),
+ new Recorder.Property(
+ "variant",
+ variantData.getName()),
+ new Recorder.Property(
+ "use_jack",
+ Boolean.toString(variantConfig.getUseJack())),
+ new Recorder.Property(
+ "use_minify",
+ Boolean.toString(variantConfig.isMinifyEnabled())),
+ new Recorder.Property(
+ "use_multi_dex",
+ Boolean.toString(variantConfig.isMultiDexEnabled())),
+ new Recorder.Property(
+ "multi_dex_legacy",
+ Boolean.toString(variantConfig.isLegacyMultiDexMode())));
+
+
if (variantFactory.hasTestScope()) {
TestVariantData unitTestVariantData = createTestVariantData(
variantData,
@@ -731,8 +765,6 @@
variantDataList.add(unitTestVariantData);
if (buildTypeData == testBuildTypeData) {
- GradleVariantConfiguration variantConfig = variantData
- .getVariantConfiguration();
if (variantConfig.isMinifyEnabled() && variantConfig.getUseJack()) {
throw new RuntimeException(
"Cannot test obfuscated variants when compiling with jack.");
@@ -751,59 +783,6 @@
}
}
- public void createApiObjects() {
- for (BaseVariantData<?> variantData : variantDataList) {
- if (variantData.getType().isForTesting()) {
- // Testing variants are handled together with their "owners".
- continue;
- }
-
- BaseVariant variantApi =
- variantFactory.createVariantApi(variantData, readOnlyObjectProvider);
-
- if (variantFactory.hasTestScope()) {
- TestVariantData androidTestVariantData =
- ((TestedVariantData) variantData).getTestVariantData(ANDROID_TEST);
-
- if (androidTestVariantData != null) {
- TestVariantImpl androidTestVariant = instantiator.newInstance(
- TestVariantImpl.class,
- androidTestVariantData,
- variantApi,
- androidBuilder,
- readOnlyObjectProvider);
-
- // add the test output.
- ApplicationVariantFactory.createApkOutputApiObjects(
- instantiator,
- androidTestVariantData,
- androidTestVariant);
-
- ((TestedExtension) extension).addTestVariant(androidTestVariant);
- ((TestedVariant) variantApi).setTestVariant(androidTestVariant);
- }
-
- TestVariantData unitTestVariantData =
- ((TestedVariantData) variantData).getTestVariantData(UNIT_TEST);
- if (unitTestVariantData != null) {
- UnitTestVariantImpl unitTestVariant = instantiator.newInstance(
- UnitTestVariantImpl.class,
- unitTestVariantData,
- variantApi,
- androidBuilder,
- readOnlyObjectProvider);
-
- ((TestedExtension) extension).addUnitTestVariant(unitTestVariant);
- ((TestedVariant) variantApi).setUnitTestVariant(unitTestVariant);
- }
- }
-
- // Only add the variant API object to the domain object set once it's been fully
- // initialized.
- extension.addVariant(variantApi);
- }
- }
-
private boolean isVariantPublished() {
return extension.getPublishNonDefault();
}
@@ -826,21 +805,19 @@
}
private SigningConfig createSigningOverride() {
- if (project.hasProperty(PROPERTY_SIGNING_STORE_FILE) &&
- project.hasProperty(PROPERTY_SIGNING_STORE_PASSWORD) &&
- project.hasProperty(PROPERTY_SIGNING_KEY_ALIAS) &&
- project.hasProperty(PROPERTY_SIGNING_KEY_PASSWORD)) {
+ AndroidGradleOptions.SigningOptions signingOptions =
+ AndroidGradleOptions.getSigningOptions(project);
+ if (signingOptions != null) {
+ com.android.build.gradle.internal.dsl.SigningConfig signingConfigDsl =
+ new com.android.build.gradle.internal.dsl.SigningConfig("externalOverride");
- SigningConfig signingConfigDsl = new SigningConfig("externalOverride");
- Map<String, ?> props = project.getProperties();
+ signingConfigDsl.setStoreFile(new File(signingOptions.storeFile));
+ signingConfigDsl.setStorePassword(signingOptions.keyPassword);
+ signingConfigDsl.setKeyAlias(signingOptions.keyAlias);
+ signingConfigDsl.setKeyPassword(signingOptions.keyPassword);
- signingConfigDsl.setStoreFile(new File((String) props.get(PROPERTY_SIGNING_STORE_FILE)));
- signingConfigDsl.setStorePassword((String) props.get(PROPERTY_SIGNING_STORE_PASSWORD));
- signingConfigDsl.setKeyAlias((String) props.get(PROPERTY_SIGNING_KEY_ALIAS));
- signingConfigDsl.setKeyPassword((String) props.get(PROPERTY_SIGNING_KEY_PASSWORD));
-
- if (project.hasProperty(PROPERTY_SIGNING_STORE_TYPE)) {
- signingConfigDsl.setStoreType((String) props.get(PROPERTY_SIGNING_STORE_TYPE));
+ if (signingOptions.storeType != null) {
+ signingConfigDsl.setStoreType(signingOptions.storeType);
}
return signingConfigDsl;
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantModel.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantModel.java
index acfa596..b4e6ffd 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantModel.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/VariantModel.java
@@ -17,8 +17,7 @@
package com.android.build.gradle.internal;
import com.android.annotations.NonNull;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
-import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
import com.android.builder.model.SigningConfig;
import java.util.Map;
@@ -29,13 +28,13 @@
public interface VariantModel {
@NonNull
- ProductFlavorData<ProductFlavor> getDefaultConfig();
+ ProductFlavorData<CoreProductFlavor> getDefaultConfig();
@NonNull
Map<String, BuildTypeData> getBuildTypes();
@NonNull
- Map<String, ProductFlavorData<GroupableProductFlavor>> getProductFlavors();
+ Map<String, ProductFlavorData<CoreProductFlavor>> getProductFlavors();
@NonNull
Map<String, ? extends SigningConfig> getSigningConfigs();
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/BaseVariantImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/BaseVariantImpl.java
index 9c2e4aa..3355eb0 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/BaseVariantImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/BaseVariantImpl.java
@@ -20,7 +20,6 @@
import com.android.annotations.Nullable;
import com.android.build.gradle.api.BaseVariant;
import com.android.build.gradle.api.BaseVariantOutput;
-import com.android.build.gradle.api.GroupableProductFlavor;
import com.android.build.gradle.internal.variant.BaseVariantData;
import com.android.build.gradle.tasks.AidlCompile;
import com.android.build.gradle.tasks.GenerateBuildConfig;
@@ -35,7 +34,7 @@
import com.google.common.collect.Lists;
import org.gradle.api.Task;
-import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.Sync;
import org.gradle.api.tasks.compile.AbstractCompile;
import org.gradle.api.tasks.compile.JavaCompile;
@@ -118,7 +117,7 @@
@Override
@NonNull
- public List<GroupableProductFlavor> getProductFlavors() {
+ public List<ProductFlavor> getProductFlavors() {
return new ImmutableFlavorList(
getVariantData().getVariantConfiguration().getProductFlavors(),
readOnlyObjectProvider);
@@ -184,15 +183,13 @@
@Override
@Nullable
public JavaCompile getJavaCompile() {
- return getVariantData().javaCompileTask instanceof JavaCompile
- ? (JavaCompile) getVariantData().javaCompileTask
- : null;
+ return getVariantData().javacTask;
}
@NonNull
@Override
public AbstractCompile getJavaCompiler() {
- return getVariantData().javaCompileTask;
+ return getVariantData().javaCompilerTask;
}
@NonNull
@@ -215,7 +212,7 @@
@Override
@NonNull
- public Copy getProcessJavaResources() {
+ public Sync getProcessJavaResources() {
return getVariantData().processJavaResourcesTask;
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ImmutableFlavorList.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ImmutableFlavorList.java
index 5f43a96..38bdcc3 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ImmutableFlavorList.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ImmutableFlavorList.java
@@ -17,7 +17,7 @@
package com.android.build.gradle.internal.api;
import com.android.annotations.NonNull;
-import com.android.build.gradle.api.GroupableProductFlavor;
+import com.android.builder.model.ProductFlavor;
import java.lang.reflect.Array;
import java.util.Collection;
@@ -26,19 +26,19 @@
import java.util.ListIterator;
/**
- * Implementation of List that create read-only GroupableProductFlavor on the fly as they are
+ * Implementation of List that create read-only ProductFlavor on the fly as they are
* queried. The list itself is immutable.
*/
-public class ImmutableFlavorList implements List<GroupableProductFlavor> {
+public class ImmutableFlavorList implements List<ProductFlavor> {
@NonNull
- private final List<? extends GroupableProductFlavor> list;
+ private final List<? extends ProductFlavor> list;
@NonNull
private final ReadOnlyObjectProvider immutableObjectProvider;
ImmutableFlavorList(
- @NonNull List<? extends GroupableProductFlavor> list,
+ @NonNull List<? extends ProductFlavor> list,
@NonNull ReadOnlyObjectProvider immutableObjectProvider) {
this.list = list;
this.immutableObjectProvider = immutableObjectProvider;
@@ -61,16 +61,16 @@
@NonNull
@Override
- public Iterator<GroupableProductFlavor> iterator() {
- final Iterator<? extends GroupableProductFlavor> baseIterator = list.iterator();
- return new Iterator<GroupableProductFlavor>() {
+ public Iterator<ProductFlavor> iterator() {
+ final Iterator<? extends ProductFlavor> baseIterator = list.iterator();
+ return new Iterator<ProductFlavor>() {
@Override
public boolean hasNext() {
return baseIterator.hasNext();
}
@Override
- public GroupableProductFlavor next() {
+ public ProductFlavor next() {
return immutableObjectProvider.getProductFlavor(baseIterator.next());
}
@@ -116,7 +116,7 @@
}
@Override
- public boolean add(GroupableProductFlavor e) {
+ public boolean add(ProductFlavor e) {
throw new UnsupportedOperationException();
}
@@ -131,12 +131,12 @@
}
@Override
- public boolean addAll(@NonNull Collection<? extends GroupableProductFlavor> es) {
+ public boolean addAll(@NonNull Collection<? extends ProductFlavor> es) {
throw new UnsupportedOperationException();
}
@Override
- public boolean addAll(int i, @NonNull Collection<? extends GroupableProductFlavor> es) {
+ public boolean addAll(int i, @NonNull Collection<? extends ProductFlavor> es) {
throw new UnsupportedOperationException();
}
@@ -156,23 +156,23 @@
}
@Override
- public GroupableProductFlavor get(int i) {
- GroupableProductFlavor gpf = list.get(i);
+ public ProductFlavor get(int i) {
+ ProductFlavor gpf = list.get(i);
return immutableObjectProvider.getProductFlavor(gpf);
}
@Override
- public GroupableProductFlavor set(int i, GroupableProductFlavor e) {
+ public ProductFlavor set(int i, ProductFlavor e) {
throw new UnsupportedOperationException();
}
@Override
- public void add(int i, GroupableProductFlavor e) {
+ public void add(int i, ProductFlavor e) {
throw new UnsupportedOperationException();
}
@Override
- public GroupableProductFlavor remove(int i) {
+ public ProductFlavor remove(int i) {
throw new UnsupportedOperationException();
}
@@ -194,16 +194,16 @@
@NonNull
@Override
- public ListIterator<GroupableProductFlavor> listIterator() {
- final ListIterator<? extends GroupableProductFlavor> baseIterator = list.listIterator();
- return new ListIterator<GroupableProductFlavor>() {
+ public ListIterator<ProductFlavor> listIterator() {
+ final ListIterator<? extends ProductFlavor> baseIterator = list.listIterator();
+ return new ListIterator<ProductFlavor>() {
@Override
public boolean hasNext() {
return baseIterator.hasNext();
}
@Override
- public GroupableProductFlavor next() {
+ public ProductFlavor next() {
return immutableObjectProvider.getProductFlavor(baseIterator.next());
}
@@ -213,7 +213,7 @@
}
@Override
- public GroupableProductFlavor previous() {
+ public ProductFlavor previous() {
return immutableObjectProvider.getProductFlavor(baseIterator.previous());
}
@@ -233,12 +233,12 @@
}
@Override
- public void set(GroupableProductFlavor productFlavor) {
+ public void set(ProductFlavor productFlavor) {
throw new UnsupportedOperationException();
}
@Override
- public void add(GroupableProductFlavor productFlavor) {
+ public void add(ProductFlavor productFlavor) {
throw new UnsupportedOperationException();
}
};
@@ -246,16 +246,16 @@
@NonNull
@Override
- public ListIterator<GroupableProductFlavor> listIterator(int i) {
- final ListIterator<? extends GroupableProductFlavor> baseIterator = list.listIterator(i);
- return new ListIterator<GroupableProductFlavor>() {
+ public ListIterator<ProductFlavor> listIterator(int i) {
+ final ListIterator<? extends ProductFlavor> baseIterator = list.listIterator(i);
+ return new ListIterator<ProductFlavor>() {
@Override
public boolean hasNext() {
return baseIterator.hasNext();
}
@Override
- public GroupableProductFlavor next() {
+ public ProductFlavor next() {
return immutableObjectProvider.getProductFlavor(baseIterator.next());
}
@@ -265,7 +265,7 @@
}
@Override
- public GroupableProductFlavor previous() {
+ public ProductFlavor previous() {
return immutableObjectProvider.getProductFlavor(baseIterator.previous());
}
@@ -285,12 +285,12 @@
}
@Override
- public void set(GroupableProductFlavor productFlavor) {
+ public void set(ProductFlavor productFlavor) {
throw new UnsupportedOperationException();
}
@Override
- public void add(GroupableProductFlavor productFlavor) {
+ public void add(ProductFlavor productFlavor) {
throw new UnsupportedOperationException();
}
};
@@ -298,7 +298,7 @@
@NonNull
@Override
- public List<GroupableProductFlavor> subList(int i, int i2) {
+ public List<ProductFlavor> subList(int i, int i2) {
throw new UnsupportedOperationException();
}
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyBuildType.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyBuildType.java
index 8032cac..9399907 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyBuildType.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyBuildType.java
@@ -21,6 +21,9 @@
import com.android.builder.model.BuildType;
import com.android.builder.model.SigningConfig;
+import java.io.File;
+import java.util.List;
+
/**
* Read-only version of the BuildType wrapping another BuildType.
*
@@ -112,4 +115,10 @@
public SigningConfig getSigningConfig() {
return readOnlyObjectProvider.getSigningConfig(buildType.getSigningConfig());
}
+
+ @NonNull
+ @Override
+ public List<File> getJarJarRuleFiles() {
+ return buildType.getJarJarRuleFiles();
+ }
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyGroupableProductFlavor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyGroupableProductFlavor.java
deleted file mode 100644
index 1db306d..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyGroupableProductFlavor.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal.api;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.build.gradle.api.GroupableProductFlavor;
-
-/**
- * Read-only version of the GroupableProductFlavor wrapping another GroupableProductFlavor.
- *
- * In the variant API, it is important that the objects returned by the variants
- * are read-only.
- *
- * However, even though the API is defined to use the base interfaces as return
- * type (which all contain only getters), the dynamics of Groovy makes it easy to
- * actually use the setters of the implementation classes.
- *
- * This wrapper ensures that the returned instance is actually just a strict implementation
- * of the base interface and is read-only.
- */
-// TODO: Remove once GroupableProductFlavor interface is removed.
-@Deprecated
-public class ReadOnlyGroupableProductFlavor extends ReadOnlyProductFlavor implements
- GroupableProductFlavor {
-
- ReadOnlyGroupableProductFlavor(
- @NonNull GroupableProductFlavor productFlavor,
- @NonNull ReadOnlyObjectProvider readOnlyObjectProvider) {
- super(productFlavor, readOnlyObjectProvider);
- }
-
- @Deprecated
- public String getFlavorDimension() {
- return getDimension();
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyObjectProvider.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyObjectProvider.java
index 88138ec..953d3b6 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyObjectProvider.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyObjectProvider.java
@@ -18,7 +18,6 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.build.gradle.api.GroupableProductFlavor;
import com.android.builder.model.BuildType;
import com.android.builder.model.ProductFlavor;
import com.android.builder.model.SigningConfig;
@@ -27,7 +26,7 @@
import java.util.Map;
/**
- * Provides read-only versions of BuildType, (Groupable)ProductFlavor and SigningConfig instances
+ * Provides read-only versions of BuildType, ProductFlavor and SigningConfig instances
* so that they can safely be exposed through the variant API.
*
* The class creates them on the fly so that they are only created when a
@@ -47,7 +46,7 @@
* Map of read-only ProductFlavor. This maps the normal flavor to the read-only version.
*/
@NonNull
- private final Map<GroupableProductFlavor, GroupableProductFlavor> readOnlyFlavors = Maps.newIdentityHashMap();
+ private final Map<ProductFlavor, ProductFlavor> readOnlyFlavors = Maps.newIdentityHashMap();
/**
* Map of read-only SigningConfig. This maps the normal config to the read-only version.
@@ -94,11 +93,11 @@
* @return an read-only version.
*/
@NonNull
- public GroupableProductFlavor getProductFlavor(@NonNull GroupableProductFlavor productFlavor) {
- GroupableProductFlavor readOnlyProductFlavor = readOnlyFlavors.get(productFlavor);
+ public ProductFlavor getProductFlavor(@NonNull ProductFlavor productFlavor) {
+ ProductFlavor readOnlyProductFlavor = readOnlyFlavors.get(productFlavor);
if (readOnlyProductFlavor == null) {
readOnlyFlavors.put(productFlavor,
- readOnlyProductFlavor = new ReadOnlyGroupableProductFlavor(
+ readOnlyProductFlavor = new ReadOnlyProductFlavor(
productFlavor, this));
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyProductFlavor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyProductFlavor.java
index 5dcf746..2c0a489 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyProductFlavor.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/ReadOnlyProductFlavor.java
@@ -22,7 +22,10 @@
import com.android.builder.model.ProductFlavor;
import com.android.builder.model.SigningConfig;
+import java.io.File;
import java.util.Collection;
+import java.util.List;
+import java.util.Map;
/**
* read-only version of the ProductFlavor wrapping another ProductFlavor.
@@ -119,6 +122,12 @@
return productFlavor.getTestInstrumentationRunner();
}
+ @NonNull
+ @Override
+ public Map<String, String> getTestInstrumentationRunnerArguments() {
+ return productFlavor.getTestInstrumentationRunnerArguments();
+ }
+
@Nullable
@Override
public Boolean getTestHandleProfiling() {
@@ -148,4 +157,16 @@
public String getDimension() {
return productFlavor.getDimension();
}
+
+ @NonNull
+ @Override
+ public List<File> getJarJarRuleFiles() {
+ return productFlavor.getJarJarRuleFiles();
+ }
+
+ @Nullable
+ @Deprecated
+ public String getFlavorDimension() {
+ return productFlavor.getDimension();
+ }
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/TestedVariant.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/TestedVariant.java
index 6c60ced..c3b9e3f 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/TestedVariant.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/TestedVariant.java
@@ -17,7 +17,7 @@
package com.android.build.gradle.internal.api;
import com.android.annotations.Nullable;
-import com.android.build.gradle.TestedExtension;
+import com.android.build.gradle.TestedAndroidConfig;
import com.android.build.gradle.api.TestVariant;
import com.android.build.gradle.api.UnitTestVariant;
@@ -32,7 +32,7 @@
* Returns the build variant that will test this build variant.
*
* <p>The android test variant exists only for one build type, by default "debug". This is
- * controlled by {@link TestedExtension#testBuildType}.
+ * controlled by {@link TestedAndroidConfig#getTestBuildType}.
*/
@Nullable
TestVariant getTestVariant();
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/VariantFilter.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/VariantFilter.java
index a1e16de..015b0be 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/VariantFilter.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/api/VariantFilter.java
@@ -18,7 +18,6 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.build.gradle.api.GroupableProductFlavor;
import com.android.builder.model.BuildType;
import com.android.builder.model.ProductFlavor;
@@ -26,8 +25,8 @@
import java.util.List;
/**
- * An object representing a Variant (made of a build type and one or more flavors), and
- * a boolean controlling whether this variant is to be excluded.
+ * Exposes a read-only view of a variant as well as a flag that can be used to signal that the
+ * variant should be ignored.
*/
public class VariantFilter implements com.android.build.gradle.api.VariantFilter {
@@ -38,7 +37,7 @@
private ProductFlavor defaultConfig;
private BuildType buildType;
- private List<GroupableProductFlavor> flavors;
+ private List<ProductFlavor> flavors;
public VariantFilter(@NonNull ReadOnlyObjectProvider readOnlyObjectProvider) {
this.readOnlyObjectProvider = readOnlyObjectProvider;
@@ -47,7 +46,7 @@
public void reset(
@NonNull ProductFlavor defaultConfig,
@NonNull BuildType buildType,
- @Nullable List<GroupableProductFlavor> flavors) {
+ @Nullable List<ProductFlavor> flavors) {
ignore = false;
this.defaultConfig = defaultConfig;
this.buildType = buildType;
@@ -67,8 +66,10 @@
}
/**
- * The default config. This is a Read-Only version of the global
- * <code>android.defaultConfig</code> object.
+ * Returns a read-only ProductFlavor that represents the default config.
+ *
+ * <p>See {@link com.android.build.gradle.internal.dsl.ProductFlavor} for properties present
+ * on the returned object.
*/
@Override
@NonNull
@@ -77,8 +78,10 @@
}
/**
- * The Build Type used by the variant. This is Read-Only version, as changing this
- * object would have global impact.
+ * Returns a read-only Build Type.
+ *
+ * <p>See {@link com.android.build.gradle.internal.dsl.BuildType} for properties present
+ * on the returned object.
*/
@Override
@NonNull
@@ -87,14 +90,16 @@
}
/**
- * The list of Product Flavors for this variant. These are returned as Read-Only versions, as
- * changing these objects would have global impact.
+ * Returns the list of read-only flavors, or an empty list.
+ *
+ * <p>See {@link com.android.build.gradle.internal.dsl.ProductFlavor} for properties
+ * present on the returned objects.
*/
@NonNull
@Override
- public List<GroupableProductFlavor> getFlavors() {
+ public List<ProductFlavor> getFlavors() {
return flavors != null ?
new ImmutableFlavorList(flavors, readOnlyObjectProvider) :
- Collections.<GroupableProductFlavor>emptyList();
+ Collections.<ProductFlavor>emptyList();
}
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/Abi.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/Abi.java
new file mode 100644
index 0000000..01ca329
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/Abi.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.core;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+
+/**
+ * Enum of valid ABI you can specify for NDK.
+ */
+public enum Abi {
+ // name architecture gccToolchainPrefix gccExecutablePrefix supports64Bits
+ ARMEABI (SdkConstants.ABI_ARMEABI, SdkConstants.CPU_ARCH_ARM, "arm-linux-androideabi", "arm-linux-androideabi", false),
+ ARMEABI_V7A(SdkConstants.ABI_ARMEABI_V7A, SdkConstants.CPU_ARCH_ARM, "arm-linux-androideabi", "arm-linux-androideabi", false),
+ ARM64_V8A (SdkConstants.ABI_ARM64_V8A, SdkConstants.CPU_ARCH_ARM64, "aarch64-linux-android", "aarch64-linux-android", true),
+ X86 (SdkConstants.ABI_INTEL_ATOM, SdkConstants.CPU_ARCH_INTEL_ATOM, "x86", "i686-linux-android", false),
+ X86_64 (SdkConstants.ABI_INTEL_ATOM64, SdkConstants.CPU_ARCH_INTEL_ATOM64, "x86_64", "x86_64-linux-android", true),
+ MIPS (SdkConstants.ABI_MIPS, SdkConstants.CPU_ARCH_MIPS, "mipsel-linux-android", "mipsel-linux-android", false),
+ MIPS64 (SdkConstants.ABI_MIPS64, SdkConstants.CPU_ARCH_MIPS64, "mips64el-linux-android", "mips64el-linux-android", true);
+
+
+ @NonNull
+ private String name;
+ @NonNull
+ String architecture;
+ @NonNull
+ private String gccToolchainPrefix;
+ @NonNull
+ private String gccExecutablePrefix;
+ private boolean supports64Bits;
+
+ Abi(@NonNull String name, @NonNull String architecture,
+ @NonNull String gccToolchainPrefix, @NonNull String gccExecutablePrefix, boolean supports64Bits) {
+ this.name = name;
+ this.architecture = architecture;
+ this.gccToolchainPrefix = gccToolchainPrefix;
+ this.gccExecutablePrefix = gccExecutablePrefix;
+ this.supports64Bits = supports64Bits;
+ }
+
+ /**
+ * Returns the ABI Enum with the specified name.
+ */
+ @Nullable
+ public static Abi getByName(@NonNull String name) {
+ for (Abi abi : values()) {
+ if (abi.name.equals(name)) {
+ return abi;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns name of the ABI.
+ */
+ @NonNull
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the CPU architecture.
+ */
+ @NonNull
+ public String getArchitecture() {
+ return architecture;
+ }
+
+ /**
+ * Returns the platform string for locating the toolchains in the NDK.
+ */
+ @NonNull
+ public String getGccToolchainPrefix() {
+ return gccToolchainPrefix;
+ }
+
+ /**
+ * Returns the prefix of GCC tools for the ABI.
+ */
+ @NonNull
+ public String getGccExecutablePrefix() {
+ return gccExecutablePrefix;
+ }
+
+ /**
+ * Returns whether the ABI supports 64-bits.
+ */
+ public boolean supports64Bits() {
+ return supports64Bits;
+ }
+}
+
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/GradleVariantConfiguration.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/GradleVariantConfiguration.java
index 8b7187f..0aa6fe5 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/GradleVariantConfiguration.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/GradleVariantConfiguration.java
@@ -20,14 +20,13 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.build.gradle.internal.dsl.BuildType;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
-import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
import com.android.builder.core.VariantConfiguration;
import com.android.builder.core.VariantType;
import com.android.builder.model.SigningConfig;
import com.android.builder.model.SourceProvider;
-import com.google.common.base.Preconditions;
import java.util.List;
import java.util.Set;
@@ -38,7 +37,7 @@
*
* It also adds support for Ndk support that is not ready to go in the builder library.
*/
-public class GradleVariantConfiguration extends VariantConfiguration<BuildType, ProductFlavor, GroupableProductFlavor> {
+public class GradleVariantConfiguration extends VariantConfiguration<CoreBuildType, CoreProductFlavor, CoreProductFlavor> {
private final MergedNdkConfig mMergedNdkConfig = new MergedNdkConfig();
@@ -46,9 +45,9 @@
* Creates a {@link GradleVariantConfiguration} for a normal (non-test) variant.
*/
public GradleVariantConfiguration(
- @NonNull ProductFlavor defaultConfig,
+ @NonNull CoreProductFlavor defaultConfig,
@NonNull SourceProvider defaultSourceProvider,
- @NonNull BuildType buildType,
+ @NonNull CoreBuildType buildType,
@Nullable SourceProvider buildTypeSourceProvider,
@NonNull VariantType type,
@Nullable SigningConfig signingConfigOverride) {
@@ -62,9 +61,9 @@
*/
public GradleVariantConfiguration(
@Nullable VariantConfiguration testedConfig,
- @NonNull ProductFlavor defaultConfig,
+ @NonNull CoreProductFlavor defaultConfig,
@NonNull SourceProvider defaultSourceProvider,
- @NonNull BuildType buildType,
+ @NonNull CoreBuildType buildType,
@Nullable SourceProvider buildTypeSourceProvider,
@NonNull VariantType type,
@Nullable SigningConfig signingConfigOverride) {
@@ -76,7 +75,7 @@
@NonNull
@Override
public VariantConfiguration addProductFlavor(
- @NonNull GroupableProductFlavor productFlavor,
+ @NonNull CoreProductFlavor productFlavor,
@NonNull SourceProvider sourceProvider,
@NonNull String dimensionName) {
checkNotNull(productFlavor);
@@ -88,7 +87,7 @@
}
@NonNull
- public NdkConfig getNdkConfig() {
+ public CoreNdkOptions getNdkConfig() {
return mMergedNdkConfig;
}
@@ -121,7 +120,7 @@
}
// cant use merge flavor as useJack is not a prop on the base class.
- for (ProductFlavor productFlavor : getProductFlavors()) {
+ for (CoreProductFlavor productFlavor : getProductFlavors()) {
value = productFlavor.getUseJack();
if (value != null) {
return value;
@@ -143,9 +142,9 @@
mMergedNdkConfig.append(getDefaultConfig().getNdkConfig());
}
- final List<GroupableProductFlavor> flavors = getProductFlavors();
+ final List<CoreProductFlavor> flavors = getProductFlavors();
for (int i = flavors.size() - 1 ; i >= 0 ; i--) {
- NdkConfig ndkConfig = flavors.get(i).getNdkConfig();
+ CoreNdkOptions ndkConfig = flavors.get(i).getNdkConfig();
if (ndkConfig != null) {
mMergedNdkConfig.append(ndkConfig);
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/MergedNdkConfig.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/MergedNdkConfig.java
index c19e63e..7ced015 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/MergedNdkConfig.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/MergedNdkConfig.java
@@ -18,6 +18,7 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -27,7 +28,7 @@
/**
* Implementation of NdkConfig used to merge multiple configs together.
*/
-public class MergedNdkConfig implements NdkConfig {
+public class MergedNdkConfig implements CoreNdkOptions {
private String moduleName;
private String cFlags;
@@ -79,7 +80,7 @@
return jobs;
}
- public void append(@NonNull NdkConfig ndkConfig) {
+ public void append(@NonNull CoreNdkOptions ndkConfig) {
// override
if (ndkConfig.getModuleName() != null) {
moduleName = ndkConfig.getModuleName();
@@ -97,23 +98,19 @@
if (ndkConfig.getAbiFilters() != null) {
if (abiFilters == null) {
abiFilters = Sets.newHashSetWithExpectedSize(ndkConfig.getAbiFilters().size());
- } else {
- abiFilters.clear();
}
abiFilters.addAll(ndkConfig.getAbiFilters());
}
if (cFlags == null) {
cFlags = ndkConfig.getcFlags();
- } else if (ndkConfig.getcFlags() != null) {
+ } else if (ndkConfig.getcFlags() != null && !ndkConfig.getcFlags().isEmpty()) {
cFlags = cFlags + " " + ndkConfig.getcFlags();
}
if (ndkConfig.getLdLibs() != null) {
if (ldLibs == null) {
ldLibs = Lists.newArrayListWithCapacity(ndkConfig.getLdLibs().size());
- } else {
- ldLibs.clear();
}
ldLibs.addAll(ndkConfig.getLdLibs());
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/NdkConfig.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/NdkConfig.java
deleted file mode 100644
index 06e767c..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/NdkConfig.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal.core;
-
-import com.android.annotations.Nullable;
-
-import java.util.Collection;
-import java.util.Set;
-
-/**
- * Base class for NDK config file.
- */
-public interface NdkConfig {
-
- /**
- * The module name
- */
- @Nullable
- String getModuleName();
-
- /**
- * The C Flags
- */
- @Nullable
- String getcFlags();
-
- /**
- * The LD Libs
- */
- @Nullable
- Collection<String> getLdLibs();
-
- /**
- * The ABI Filters
- */
- @Nullable
- Set<String> getAbiFilters();
-
- /**
- * The APP_STL value
- */
- @Nullable
- String getStl();
-
- /**
- * Number of parallel threads to spawn.
- */
- @Nullable
- Integer getJobs();
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/Toolchain.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/Toolchain.java
new file mode 100644
index 0000000..6c81e9e
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/core/Toolchain.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.core;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+
+/**
+ * Enum of valid toolchains you can specify for NDK.
+ */
+public enum Toolchain {
+ GCC("gcc"),
+ CLANG("clang");
+
+ @NonNull
+ private final String name;
+
+ @NonNull
+ public static Toolchain getDefault() {
+ return GCC;
+ }
+
+ @Nullable
+ public static Toolchain getByName(@NonNull String toolchainName) {
+ for (Toolchain toolchain : values()) {
+ if (toolchain.name.equals(toolchainName)) {
+ return toolchain;
+ }
+ }
+ return null;
+ }
+
+ Toolchain(@NonNull String name) {
+ this.name = name;
+ }
+
+ @NonNull
+ public String getName() {
+ return name;
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/coverage/JacocoInstrumentTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/coverage/JacocoInstrumentTask.groovy
index cc37469..8a36055 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/coverage/JacocoInstrumentTask.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/coverage/JacocoInstrumentTask.groovy
@@ -16,10 +16,10 @@
package com.android.build.gradle.internal.coverage
-import com.android.build.gradle.internal.TaskManager
import com.android.build.gradle.internal.scope.ConventionMappingHelper
import com.android.build.gradle.internal.scope.TaskConfigAction
import com.android.build.gradle.internal.scope.VariantScope
+import com.android.build.gradle.internal.PostCompilationData
import org.gradle.api.DefaultTask
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.InputDirectory
@@ -64,9 +64,9 @@
VariantScope scope;
- TaskManager.PostCompilationData pcData;
+ PostCompilationData pcData;
- ConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
+ ConfigAction(VariantScope scope, PostCompilationData pcData) {
this.scope = scope
this.pcData = pcData
}
@@ -88,7 +88,7 @@
scope.globalScope.project.configurations[JacocoPlugin.ANT_CONFIGURATION_NAME]
}
// can't directly use the existing inputFiles closure as we need the dir instead :\
- ConventionMappingHelper.map(jacocoTask, "inputDir", pcData.inputDir)
+ ConventionMappingHelper.map(jacocoTask, "inputDir", pcData.inputDirCallable)
ConventionMappingHelper.map(jacocoTask, "outputDir") {
new File("${scope.globalScope.buildDir}/${FD_INTERMEDIATES}/coverage-instrumented-classes/${scope.variantConfiguration.dirName}")
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibInfo.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibInfo.java
index 2911e04..6244654 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibInfo.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibInfo.java
@@ -18,9 +18,9 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.annotations.concurrency.Immutable;
import com.android.builder.dependency.LibraryDependency;
import com.android.builder.model.MavenCoordinates;
+import com.google.common.collect.ImmutableList;
import java.io.File;
import java.util.Collection;
@@ -28,13 +28,19 @@
/**
* Version of LibraryDependency that includes transitive jar dependencies as JarInfo.
+ *
+ * This is used as a temporary object as the dependencies are processed. This object contains
+ * more information and can be mutated ({@link #setIsOptional(boolean)}) during the process.
+ *
+ * In the end the object will be converted into an immutable LibraryDependency instance.
*/
-@Immutable
public class LibInfo extends LibraryDependencyImpl {
@NonNull
private final Collection<JarInfo> jarDependencies;
+ private boolean mutableIsOptional = false;
+
public LibInfo(@NonNull File bundle,
@NonNull File explodedBundle,
@NonNull List<LibraryDependency> dependencies,
@@ -51,13 +57,41 @@
variantName,
projectPath,
requestedCoordinates,
- resolvedCoordinates);
+ resolvedCoordinates,
+ false);
this.jarDependencies = jarDependencies;
}
+ /**
+ * Mark the dependency as optional.
+ */
+ public void setIsOptional(boolean isOptional) {
+ this.mutableIsOptional = isOptional;
+ for (LibInfo libInfo : getLibInfoDependencies()) {
+ libInfo.setIsOptional(isOptional);
+ }
+ }
+
+ @Override
+ public boolean isOptional() {
+ return mutableIsOptional;
+ }
+
+ @NonNull
+ public List<LibInfo> getLibInfoDependencies() {
+ ImmutableList.Builder<LibInfo> libInfoBuilder = ImmutableList.builder();
+ for (LibraryDependency libraryDependency : getDependencies()) {
+ if (libraryDependency instanceof LibInfo) {
+ libInfoBuilder.add((LibInfo) libraryDependency);
+ } else {
+ throw new RuntimeException("Mixed LibInfo and LibraryDependencies instances !");
+ }
+ }
+ return libInfoBuilder.build();
+ }
+
@NonNull
public Collection<JarInfo> getJarDependencies() {
return jarDependencies;
}
-
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibraryDependencyImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibraryDependencyImpl.java
index 77cf021..4f46a86 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibraryDependencyImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/LibraryDependencyImpl.java
@@ -44,6 +44,8 @@
@Nullable
private final MavenCoordinates resolvedCoordinates;
+ private final boolean isOptional;
+
public LibraryDependencyImpl(
@NonNull File bundle,
@NonNull File explodedBundle,
@@ -52,12 +54,14 @@
@Nullable String variantName,
@Nullable String projectPath,
@Nullable MavenCoordinates requestedCoordinates,
- @Nullable MavenCoordinates resolvedCoordinates) {
+ @Nullable MavenCoordinates resolvedCoordinates,
+ boolean isOptional) {
super(bundle, explodedBundle, name, projectPath);
this.dependencies = dependencies;
this.variantName = variantName;
this.requestedCoordinates = requestedCoordinates;
this.resolvedCoordinates = resolvedCoordinates;
+ this.isOptional = isOptional;
}
@NonNull
@@ -96,6 +100,11 @@
return resolvedCoordinates;
}
+ @Override
+ public boolean isOptional() {
+ return isOptional;
+ }
+
/**
* Returns a version of the library dependency with the dependencies removed.
*/
@@ -109,7 +118,8 @@
variantName,
getProject(),
requestedCoordinates,
- resolvedCoordinates);
+ resolvedCoordinates,
+ isOptional);
}
@Override
@@ -126,12 +136,18 @@
LibraryDependencyImpl that = (LibraryDependencyImpl) o;
return Objects.equal(dependencies, that.dependencies) &&
Objects.equal(variantName, that.variantName) &&
- Objects.equal(resolvedCoordinates, that.resolvedCoordinates);
+ Objects.equal(resolvedCoordinates, that.resolvedCoordinates) &&
+ Objects.equal(isOptional, that.isOptional());
}
@Override
public int hashCode() {
- return Objects.hashCode(super.hashCode(), dependencies, variantName, resolvedCoordinates);
+ return Objects.hashCode(
+ super.hashCode(),
+ dependencies,
+ variantName,
+ resolvedCoordinates,
+ isOptional);
}
@Override
@@ -141,6 +157,7 @@
.add("variantName", variantName)
.add("requestedCoordinates", requestedCoordinates)
.add("resolvedCoordinates", resolvedCoordinates)
+ .add("isOptional", isOptional)
.add("super", super.toString())
.toString();
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/SymbolFileProviderImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/SymbolFileProviderImpl.java
index b28fce6..6554876 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/SymbolFileProviderImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/SymbolFileProviderImpl.java
@@ -17,6 +17,7 @@
package com.android.build.gradle.internal.dependency;
import com.android.annotations.NonNull;
+import com.android.builder.dependency.LibraryDependency;
import com.android.builder.dependency.SymbolFileProvider;
import org.gradle.api.tasks.InputFile;
@@ -30,10 +31,12 @@
private final File manifest;
private final File symbolFile;
+ private final boolean isOptional;
- public SymbolFileProviderImpl(@NonNull File manifest, @NonNull File symbolFile) {
- this.manifest = manifest;
- this.symbolFile = symbolFile;
+ public SymbolFileProviderImpl(@NonNull LibraryDependency library) {
+ manifest = library.getManifest();
+ symbolFile = library.getSymbolFile();
+ isOptional = library.isOptional();
}
@InputFile
@@ -49,4 +52,9 @@
public File getSymbolFile() {
return symbolFile;
}
+
+ @Override
+ public boolean isOptional() {
+ return isOptional;
+ }
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/VariantDependencies.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/VariantDependencies.groovy
index 888fbf5..68a653a 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/VariantDependencies.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dependency/VariantDependencies.groovy
@@ -36,7 +36,7 @@
*
* It optionally contains the dependencies for a test config for the given config.
*/
-public class VariantDependencies implements DependencyContainer, ConfigurationProvider {
+public class VariantDependencies implements DependencyContainer {
final String name
@@ -69,11 +69,13 @@
DependencyChecker checker
- static VariantDependencies compute(@NonNull Project project,
- @NonNull String name,
- boolean publishVariant,
- VariantType variantType,
- @NonNull ConfigurationProvider... providers) {
+ static VariantDependencies compute(
+ @NonNull Project project,
+ @NonNull String name,
+ boolean publishVariant,
+ @NonNull VariantType variantType,
+ @Nullable VariantDependencies parentVariant,
+ @NonNull ConfigurationProvider... providers) {
Set<Configuration> compileConfigs = Sets.newHashSetWithExpectedSize(providers.length * 2)
Set<Configuration> apkConfigs = Sets.newHashSetWithExpectedSize(providers.length)
@@ -89,6 +91,11 @@
}
}
+ if (parentVariant != null) {
+ compileConfigs.add(parentVariant.getCompileConfiguration())
+ apkConfigs.add(parentVariant.getPackageConfiguration())
+ }
+
Configuration compile = project.configurations.maybeCreate("_${name}Compile")
compile.visible = false
compile.description = "## Internal use, do not manually configure ##"
@@ -160,24 +167,16 @@
return name
}
- @Override
@NonNull
Configuration getCompileConfiguration() {
return compileConfiguration
}
- @Override
@NonNull
Configuration getPackageConfiguration() {
return packageConfiguration
}
- @Override
- @Nullable
- Configuration getProvidedConfiguration() {
- return null
- }
-
@Nullable
Configuration getPublishConfiguration() {
return publishConfiguration
@@ -233,6 +232,16 @@
return localJars
}
+ public boolean hasNonOptionalLibraries() {
+ for (LibraryDependency libraryDependency : libraries) {
+ if (!libraryDependency.isOptional()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
@Override
public String toString() {
return Objects.toStringHelper(this)
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AaptOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AaptOptions.java
index ea4de1c..3c3bdc4 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AaptOptions.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AaptOptions.java
@@ -73,6 +73,8 @@
/**
* Extensions of files that will not be stored compressed in the APK.
+ *
+ * <p>Equivalent of the -0 flag. See <code>aapt --help</code>
*/
@Override
@Optional
@@ -91,7 +93,6 @@
/**
* Enables or disables PNG crunching.
- * @param value true to enable, false to disable.
*/
public void setCruncherEnabled(boolean value) {
cruncherEnabled = value;
@@ -104,10 +105,9 @@
public boolean getCruncherEnabled() {
return cruncherEnabled;
}
+
/**
* Whether to use the new cruncher.
- *
- * <p>TODO: Document.
*/
@Input
public boolean getUseNewCruncher() {
@@ -124,6 +124,8 @@
/**
* Forces aapt to return an error if it fails to find an entry for a configuration.
+ *
+ * <p>See <code>aapt --help</code>
*/
@Override
@Input
@@ -131,10 +133,22 @@
return failOnMissingConfigEntry;
}
+ // -- DSL Methods. TODO remove once the instantiator does what I expect it to do.
+
+ /**
+ * Sets extensions of files that will not be stored compressed in the APK.
+ *
+ * <p>Equivalent of the -0 flag. See <code>aapt --help</code>
+ */
public void noCompress(String noCompress) {
noCompressList = Collections.singletonList(noCompress);
}
+ /**
+ * Sets extensions of files that will not be stored compressed in the APK.
+ *
+ * <p>Equivalent of the -0 flag. See <code>aapt --help</code>
+ */
public void noCompress(String... noCompress) {
noCompressList = Arrays.asList(noCompress);
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptions.java
index 6dbd7bb..c0f6eb3 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptions.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptions.java
@@ -16,34 +16,41 @@
package com.android.build.gradle.internal.dsl;
-import static com.android.build.OutputFile.NO_FILTER;
-
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.OutputFile;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Set;
/**
- * Data for per-ABI splits.
+ * DSL object for configuring per-abi splits options.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits">APK Splits</a>.
*/
public class AbiSplitOptions extends SplitOptions {
- private static final String[] ABI_LIST = new String[] {
- "armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64" };
-
private boolean universalApk = false;
@Override
protected Set<String> getDefaultValues() {
- return Sets.newHashSet(ABI_LIST);
+ Set<String> values = Sets.newHashSet();
+ for (Abi abi : NdkHandler.getAbiList()) {
+ values.add(abi.getName());
+ }
+ return values;
}
@Override
protected ImmutableSet<String> getAllowedValues() {
- return ImmutableSet.copyOf(ABI_LIST);
+ ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+ for (Abi abi : NdkHandler.getAbiList()) {
+ builder.add(abi.getName());
+ }
+ return builder.build();
}
/**
@@ -57,19 +64,6 @@
this.universalApk = universalApk;
}
- @NonNull
- @Override
- public Set<String> getApplicableFilters() {
- Set<String> list = super.getApplicableFilters();
-
- // if universal, and splitting enabled, then add an entry with no filter.
- if (isEnable() && universalApk) {
- list.add(NO_FILTER);
- }
-
- return list;
- }
-
/**
* Returns the list of actual abi filters, each value of the collection is guaranteed to be non
* null and of the possible API value.
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/BuildType.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/BuildType.groovy
deleted file mode 100644
index 80c7340..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/BuildType.groovy
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.internal.dsl
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.annotations.VisibleForTesting
-import com.android.build.gradle.internal.core.NdkConfig
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.BuilderConstants
-import com.android.builder.core.DefaultBuildType
-import com.android.builder.model.BaseConfig
-import com.android.builder.model.ClassField
-import org.gradle.api.Action
-import org.gradle.api.Project
-import org.gradle.api.logging.Logger
-import org.gradle.internal.reflect.Instantiator
-/**
- * DSL object to configure build types.
- */
-public class BuildType extends DefaultBuildType implements Serializable {
- private static final long serialVersionUID = 1L
-
- @NonNull
- private final Project project
- @NonNull
- private final Logger logger
-
- private final NdkOptions ndkConfig
-
- private Boolean useJack
-
- public BuildType(@NonNull String name,
- @NonNull Project project,
- @NonNull Instantiator instantiator,
- @NonNull Logger logger) {
- super(name)
- this.project = project
- this.logger = logger
- ndkConfig = instantiator.newInstance(NdkOptions.class)
- }
-
- @VisibleForTesting
- BuildType(@NonNull String name,
- @NonNull Project project,
- @NonNull Logger logger) {
- super(name)
- this.project = project
- this.logger = logger
- ndkConfig = null
- }
-
- @Nullable
- public NdkConfig getNdkConfig() {
- return ndkConfig;
- }
-
- public void init(SigningConfig debugSigningConfig) {
- if (BuilderConstants.DEBUG.equals(getName())) {
- setDebuggable(true)
- setEmbedMicroApp(false)
-
- assert debugSigningConfig != null
- setSigningConfig(debugSigningConfig)
- } else if (BuilderConstants.RELEASE.equals(getName())) {
- // no config needed for now.
- }
- }
-
- /** The signing configuration. */
- @Override
- SigningConfig getSigningConfig() {
- return (SigningConfig) super.signingConfig
- }
-
- @Override
- protected void _initWith(@NonNull BaseConfig that) {
- super._initWith(that)
- shrinkResources = that.shrinkResources
- useJack = that.useJack
- }
-
- int hashCode() {
- int result = super.hashCode()
- result = 31 * result + (useJack != null ? useJack.hashCode() : 0)
- result = 31 * result + (shrinkResources ? 1 : 0)
- return result
- }
-
- @Override
- boolean equals(o) {
- if (this.is(o)) return true
- if (getClass() != o.class) return false
- if (!super.equals(o)) return false
- if (useJack != o.useJack) return false
- if (shrinkResources != o.shrinkResources) return false
-
- return true
- }
-
- // -- DSL Methods. TODO remove once the instantiator does what I expect it to do.
-
- public void buildConfigField(
- @NonNull String type,
- @NonNull String name,
- @NonNull String value) {
- ClassField alreadyPresent = getBuildConfigFields().get(name);
- if (alreadyPresent != null) {
- logger.info(
- "BuildType(${getName()}): buildConfigField '$name' value is being replaced: ${alreadyPresent.value} -> $value");
- }
- addBuildConfigField(AndroidBuilder.createClassField(type, name, value));
- }
-
- public void resValue(
- @NonNull String type,
- @NonNull String name,
- @NonNull String value) {
- ClassField alreadyPresent = getResValues().get(name);
- if (alreadyPresent != null) {
- logger.info(
- "BuildType(${getName()}): resValue '$name' value is being replaced: ${alreadyPresent.value} -> $value");
- }
- addResValue(AndroidBuilder.createClassField(type, name, value));
- }
-
- /**
- * Adds a new ProGuard configuration file.
- *
- * <p><code>proguardFile getDefaultProguardFile('proguard-android.txt')</code></p>
- *
- * <p>There are 2 default rules files
- * <ul>
- * <li>proguard-android.txt
- * <li>proguard-android-optimize.txt
- * </ul>
- * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the
- * full path to the files. They are identical except for enabling optimizations.
- */
- @NonNull
- public BuildType proguardFile(Object proguardFile) {
- proguardFiles.add(project.file(proguardFile));
- return this;
- }
-
- /**
- * Adds new ProGuard configuration files.
- */
- @NonNull
- public BuildType proguardFiles(Object... proguardFileArray) {
- proguardFiles.addAll(project.files(proguardFileArray).files);
- return this;
- }
-
- /**
- * Sets the ProGuard configuration files.
- */
- @NonNull
- public BuildType setProguardFiles(Iterable<?> proguardFileIterable) {
- proguardFiles.clear();
- for (Object proguardFile : proguardFileIterable) {
- proguardFiles.add(project.file(proguardFile));
- }
- return this;
- }
-
- @NonNull
- public BuildType testProguardFile(Object proguardFile) {
- testProguardFiles.add(project.file(proguardFile));
- return this;
- }
-
- /**
- * Adds new ProGuard configuration files.
- */
- @NonNull
- public BuildType testProguardFiles(Object... proguardFileArray) {
- testProguardFiles.addAll(project.files(proguardFileArray).files);
- return this;
- }
-
- @NonNull
- public BuildType consumerProguardFiles(Object... proguardFileArray) {
- consumerProguardFiles.addAll(project.files(proguardFileArray).files);
- return this;
- }
-
- @NonNull
- public BuildType setConsumerProguardFiles(Iterable<?> proguardFileIterable) {
- consumerProguardFiles.clear();
- for (Object proguardFile : proguardFileIterable) {
- consumerProguardFiles.add(project.file(proguardFile));
- }
- return this;
- }
-
- void ndk(Action<NdkOptions> action) {
- action.execute(ndkConfig)
- }
-
- Boolean getUseJack() {
- return useJack
- }
-
- void setUseJack(Boolean useJack) {
- this.useJack = useJack
- }
-
- void useJack(Boolean useJack) {
- setUseJack(useJack)
- }
-
- boolean shrinkResources = false // opt-in for now until we've validated it in the field
-
- void shrinkResources(boolean flag) {
- this.shrinkResources = flag
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/BuildType.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/BuildType.java
new file mode 100644
index 0000000..9b7274d
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/BuildType.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle.internal.dsl;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.core.DefaultBuildType;
+import com.android.builder.model.BaseConfig;
+import com.android.builder.model.ClassField;
+
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.api.logging.Logger;
+import org.gradle.internal.reflect.Instantiator;
+
+import java.io.Serializable;
+
+/**
+ * DSL object to configure build types.
+ */
+public class BuildType extends DefaultBuildType implements CoreBuildType, Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @NonNull
+ private final Project project;
+
+ @NonNull
+ private final Logger logger;
+
+ @Nullable
+ private final NdkOptions ndkConfig;
+
+ @Nullable
+ private Boolean useJack;
+
+ private boolean shrinkResources = false; // opt-in for now until we've validated it in the field
+
+ public BuildType(@NonNull String name,
+ @NonNull Project project,
+ @NonNull Instantiator instantiator,
+ @NonNull Logger logger) {
+ super(name);
+ this.project = project;
+ this.logger = logger;
+ ndkConfig = instantiator.newInstance(NdkOptions.class);
+ }
+
+ @VisibleForTesting
+ BuildType(@NonNull String name,
+ @NonNull Project project,
+ @NonNull Logger logger) {
+ super(name);
+ this.project = project;
+ this.logger = logger;
+ ndkConfig = null;
+ }
+
+ @Override
+ @Nullable
+ public CoreNdkOptions getNdkConfig() {
+ return ndkConfig;
+ }
+
+ /**
+ * Initialize the DSL object. Not meant to be used from the build scripts.
+ */
+ public void init(SigningConfig debugSigningConfig) {
+ if (BuilderConstants.DEBUG.equals(getName())) {
+ setDebuggable(true);
+ setEmbedMicroApp(false);
+
+ assert debugSigningConfig != null;
+ setSigningConfig(debugSigningConfig);
+ } else if (BuilderConstants.RELEASE.equals(getName())) {
+ // no config needed for now.
+ }
+ }
+
+ /** The signing configuration. */
+ @Override
+ @Nullable
+ public SigningConfig getSigningConfig() {
+ return (SigningConfig) super.getSigningConfig();
+ }
+
+ @Override
+ protected void _initWith(@NonNull BaseConfig that) {
+ super._initWith(that);
+ BuildType thatBuildType = (BuildType) that;
+ shrinkResources = thatBuildType.isShrinkResources();
+ useJack = thatBuildType.getUseJack();
+ }
+
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (useJack != null ? useJack.hashCode() : 0);
+ result = 31 * result + (shrinkResources ? 1 : 0);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof BuildType)) return false;
+ if (!super.equals(o)) return false;
+ BuildType other = (BuildType) o;
+ if (useJack != other.getUseJack()) return false;
+ if (shrinkResources != other.isShrinkResources()) return false;
+
+ return true;
+ }
+
+ // -- DSL Methods. TODO remove once the instantiator does what I expect it to do.
+
+ /**
+ * Adds a new field to the generated BuildConfig class.
+ *
+ * <p>The field is generated as: <code><type> <name> = <value>;</code>
+ *
+ * <p>This means each of these must have valid Java content. If the type is a String, then the
+ * value should include quotes.
+ *
+ * @param type the type of the field
+ * @param name the name of the field
+ * @param value the value of the field
+ */
+ public void buildConfigField(
+ @NonNull String type,
+ @NonNull String name,
+ @NonNull String value) {
+ ClassField alreadyPresent = getBuildConfigFields().get(name);
+ if (alreadyPresent != null) {
+ logger.info("BuildType({}): buildConfigField '{}' value is being replaced: {} -> {}",
+ getName(), name, alreadyPresent.getValue(), value);
+ }
+ addBuildConfigField(AndroidBuilder.createClassField(type, name, value));
+ }
+
+ /**
+ * Adds a new generated resource.
+ *
+ * @param type the type of the resource
+ * @param name the name of the resource
+ * @param value the value of the resource
+ */
+ public void resValue(
+ @NonNull String type,
+ @NonNull String name,
+ @NonNull String value) {
+ ClassField alreadyPresent = getResValues().get(name);
+ if (alreadyPresent != null) {
+ logger.info("BuildType({}): resValue '{}' value is being replaced: {} -> {}",
+ getName(), name, alreadyPresent.getValue(), value);
+ }
+ addResValue(AndroidBuilder.createClassField(type, name, value));
+ }
+
+ /**
+ * Adds a new ProGuard configuration file.
+ *
+ * <p><code>proguardFile getDefaultProguardFile('proguard-android.txt')</code></p>
+ *
+ * <p>There are 2 default rules files
+ * <ul>
+ * <li>proguard-android.txt
+ * <li>proguard-android-optimize.txt
+ * </ul>
+ * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code>
+ * will return the full path to the files. They are identical except for enabling optimizations.
+ */
+ @NonNull
+ public BuildType proguardFile(@NonNull Object proguardFile) {
+ getProguardFiles().add(project.file(proguardFile));
+ return this;
+ }
+
+ /**
+ * Adds new ProGuard configuration files.
+ */
+ @NonNull
+ public BuildType proguardFiles(@NonNull Object... proguardFileArray) {
+ getProguardFiles().addAll(project.files(proguardFileArray).getFiles());
+ return this;
+ }
+
+ /**
+ * Sets the ProGuard configuration files.
+ */
+ @NonNull
+ public BuildType setProguardFiles(@NonNull Iterable<?> proguardFileIterable) {
+ getProguardFiles().clear();
+ for (Object proguardFile : proguardFileIterable) {
+ getProguardFiles().add(project.file(proguardFile));
+ }
+ return this;
+ }
+
+ /**
+ * Specifies a proguard rule file to be included in the published AAR.
+ *
+ * This proguard rule file will then be used by any application project that consume the AAR
+ * (if proguard is enabled).
+ *
+ * This allows AAR to specify shrinking or obfuscation exclude rules.
+ *
+ * This is only valid for Library project. This is ignored in Application project.
+ */
+ @NonNull
+ public BuildType testProguardFile(@NonNull Object proguardFile) {
+ getTestProguardFiles().add(project.file(proguardFile));
+ return this;
+ }
+
+ /**
+ * Adds new ProGuard configuration files.
+ */
+ @NonNull
+ public BuildType testProguardFiles(@NonNull Object... proguardFileArray) {
+ getTestProguardFiles().addAll(project.files(proguardFileArray).getFiles());
+ return this;
+ }
+
+ @NonNull
+ public BuildType consumerProguardFiles(@NonNull Object... proguardFileArray) {
+ getConsumerProguardFiles().addAll(project.files(proguardFileArray).getFiles());
+ return this;
+ }
+
+ /**
+ * Specifies a proguard rule file to be included in the published AAR.
+ *
+ * This proguard rule file will then be used by any application project that consume the AAR
+ * (if proguard is enabled).
+ *
+ * This allows AAR to specify shrinking or obfuscation exclude rules.
+ *
+ * This is only valid for Library project. This is ignored in Application project.
+ */
+ @NonNull
+ public BuildType setConsumerProguardFiles(@NonNull Iterable<?> proguardFileIterable) {
+ getConsumerProguardFiles().clear();
+ for (Object proguardFile : proguardFileIterable) {
+ getConsumerProguardFiles().add(project.file(proguardFile));
+ }
+ return this;
+ }
+
+ public void ndk(@NonNull Action<NdkOptions> action) {
+ action.execute(ndkConfig);
+ }
+
+ /**
+ * Whether the experimental Jack toolchain should be used.
+ */
+ @Override
+ @Nullable
+ public Boolean getUseJack() {
+ return useJack;
+ }
+
+ /**
+ * Whether the experimental Jack toolchain should be used.
+ */
+ public void setUseJack(@Nullable Boolean useJack) {
+ this.useJack = useJack;
+ }
+
+ /**
+ * Whether the experimental Jack toolchain should be used.
+ */
+ public void useJack(@Nullable Boolean useJack) {
+ setUseJack(useJack);
+ }
+
+ /**
+ * Whether shrinking of unused resources is enabled.
+ *
+ * Default is false;
+ */
+ @Override
+ public boolean isShrinkResources() {
+ return shrinkResources;
+ }
+
+ public void setShrinkResources(boolean shrinkResources) {
+ this.shrinkResources = shrinkResources;
+ }
+
+ /**
+ * Whether shrinking of unused resources is enabled.
+ *
+ * Default is false;
+ */
+ public void shrinkResources(boolean flag) {
+ this.shrinkResources = flag;
+ }
+
+ public void jarJarRuleFile(@NonNull Object file) {
+ getJarJarRuleFiles().add(project.file(file));
+ }
+
+ public void jarJarRuleFiles(@NonNull Object... files) {
+ getJarJarRuleFiles().clear();
+ for (Object file : files) {
+ getJarJarRuleFiles().add(project.file(file));
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreBuildType.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreBuildType.java
new file mode 100644
index 0000000..25ff6c5
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreBuildType.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.dsl;
+
+import com.android.annotations.Nullable;
+import com.android.builder.model.BuildType;
+
+/**
+ * A build type with addition properties for building with Gradle plugin.
+ */
+public interface CoreBuildType extends BuildType {
+
+ @Nullable
+ CoreNdkOptions getNdkConfig();
+
+ @Nullable
+ Boolean getUseJack();
+
+ boolean isShrinkResources();
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreNdkOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreNdkOptions.java
new file mode 100644
index 0000000..4302131
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreNdkOptions.java
@@ -0,0 +1,64 @@
+/*
+ * 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.build.gradle.internal.dsl;
+
+import com.android.annotations.Nullable;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Base class for NDK config file.
+ */
+public interface CoreNdkOptions {
+
+ /**
+ * The module name
+ */
+ @Nullable
+ String getModuleName();
+
+ /**
+ * The C Flags
+ */
+ @Nullable
+ String getcFlags();
+
+ /**
+ * The LD Libs
+ */
+ @Nullable
+ List<String> getLdLibs();
+
+ /**
+ * The ABI Filters
+ */
+ @Nullable
+ Set<String> getAbiFilters();
+
+ /**
+ * The APP_STL value
+ */
+ @Nullable
+ String getStl();
+
+ /**
+ * Number of parallel threads to spawn.
+ */
+ @Nullable
+ Integer getJobs();
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreProductFlavor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreProductFlavor.java
new file mode 100644
index 0000000..2909787
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreProductFlavor.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.dsl;
+
+import com.android.annotations.Nullable;
+import com.android.builder.model.ProductFlavor;
+
+import org.gradle.api.Named;
+
+/**
+ * A product flavor with addition properties for building with Gradle plugin.
+ */
+public interface CoreProductFlavor extends ProductFlavor, Named {
+
+ @Nullable
+ CoreNdkOptions getNdkConfig();
+
+ @Nullable
+ Boolean getUseJack();
+
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreSigningConfig.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreSigningConfig.java
new file mode 100644
index 0000000..7faa5db
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/CoreSigningConfig.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.dsl;
+
+import com.android.annotations.Nullable;
+
+import java.io.File;
+import com.android.builder.model.SigningConfig;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Optional;
+
+/**
+ * A SigningConfig with added annotations to be used with @Nested in a Gradle task.
+ */
+public interface CoreSigningConfig extends SigningConfig {
+
+ @Override
+ @Nullable
+ @InputFile @Optional
+ File getStoreFile();
+
+ @Override
+ @Nullable
+ @Input
+ String getStorePassword();
+
+ @Override
+ @Nullable
+ @Input
+ String getKeyAlias();
+
+ @Override
+ @Nullable
+ @Input
+ String getStoreType();
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptions.java
index e196bc1..fd14504 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptions.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptions.java
@@ -16,8 +16,6 @@
package com.android.build.gradle.internal.dsl;
-import static com.android.build.OutputFile.NO_FILTER;
-
import com.android.annotations.NonNull;
import com.android.resources.Density;
import com.google.common.collect.ImmutableSet;
@@ -25,12 +23,13 @@
import java.util.Arrays;
import java.util.Collections;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
- * Data for per-density splits.
+ * DSL object for configuring per-density splits options.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits">APK Splits</a>.
*/
public class DensitySplitOptions extends SplitOptions {
@@ -79,6 +78,11 @@
compatibleScreens = Sets.newHashSet(sizes);
}
+ /**
+ * Adds a new compatible screen.
+ *
+ * <p>See {@link #getCompatibleScreens()}.
+ */
public void compatibleScreens(@NonNull String... sizes) {
if (compatibleScreens == null) {
compatibleScreens = Sets.newHashSet(sizes);
@@ -89,7 +93,10 @@
}
/**
- * TODO: Document.
+ * A list of compatible screens.
+ *
+ * <p>This will inject a matching <code><compatible-screens><screen ...></code>
+ * node in the manifest. This is optional.
*/
@NonNull
public Set<String> getCompatibleScreens() {
@@ -99,22 +106,6 @@
return compatibleScreens;
}
- @NonNull
- @Override
- public Set<String> getApplicableFilters() {
- // use a LinkedHashSet so iteration order follows insertion order.
- LinkedHashSet<String> filters = new LinkedHashSet<String>();
-
- // if splitting enabled, then add an entry with no filter for universal
- // add it FIRST, since code assume that the first variant output will be the universal one.
- if (isEnable()) {
- filters.add(NO_FILTER);
- }
- filters.addAll(super.getApplicableFilters());
- return filters;
- }
-
-
/**
* Sets whether the build system should determine the splits based on the "language-*" folders
* in the resources. If the auto mode is set to true, the include list will be ignored.
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavor.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavor.groovy
deleted file mode 100644
index f69545b..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavor.groovy
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.internal.dsl
-import com.android.annotations.NonNull
-import org.gradle.api.Project
-import org.gradle.api.logging.Logger
-import org.gradle.internal.reflect.Instantiator
-
-import static com.android.build.gradle.internal.LoggingUtil.displayDeprecationWarning
-
-/**
- * A version of {@link ProductFlavor} that can receive a dimension name.
- */
-// TODO: Remove interface and implementors now ProductFlavor has dimension.
-@Deprecated
-public class GroupableProductFlavor
- extends ProductFlavor implements com.android.build.gradle.api.GroupableProductFlavor {
-
- public GroupableProductFlavor(
- @NonNull String name,
- @NonNull Project project,
- @NonNull Instantiator instantiator,
- @NonNull Logger logger) {
- super(name, project, instantiator, logger)
- }
-
- @Deprecated
- public void setFlavorDimension(String dimension) {
- displayDeprecationWarning(logger, project,
- "'flavorDimension' will be removed by Android Gradle Plugin 2.0, " +
- "it has been replaced by 'dimension'.")
- setDimension(dimension);
- }
-
- /**
- * Name of the dimension this product flavor belongs to. Has been replaced by
- * <code>dimension</code>
- */
- @Deprecated
- public String getFlavorDimension() {
- displayDeprecationWarning(logger, project,
- "'flavorDimension' will be removed by Android Gradle Plugin 2.0, " +
- "it has been replaced by 'dimension'.")
- return getDimension();
- }
-
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavorFactory.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavorFactory.java
deleted file mode 100644
index 92204e4..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/GroupableProductFlavorFactory.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.internal.dsl;
-
-import com.android.annotations.NonNull;
-
-import org.gradle.api.NamedDomainObjectFactory;
-import org.gradle.api.Project;
-import org.gradle.api.logging.Logger;
-import org.gradle.internal.reflect.Instantiator;
-
-/**
- * Factory to create GroupableProductFlavor object using an {@link Instantiator} to add
- * the DSL methods.
- */
-public class GroupableProductFlavorFactory implements NamedDomainObjectFactory<GroupableProductFlavor> {
-
- @NonNull
- private final Instantiator instantiator;
- @NonNull
- private final Project project;
- @NonNull
- private final Logger logger;
-
- public GroupableProductFlavorFactory(@NonNull Instantiator instantiator,
- @NonNull Project project,
- @NonNull Logger logger) {
- this.instantiator = instantiator;
- this.project = project;
- this.logger = logger;
- }
-
- @Override
- public GroupableProductFlavor create(String name) {
- return instantiator.newInstance(GroupableProductFlavor.class,
- name, project, instantiator, logger);
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LanguageSplitOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LanguageSplitOptions.java
index 25d1e08..ce3700a 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LanguageSplitOptions.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/LanguageSplitOptions.java
@@ -25,7 +25,9 @@
import java.util.Set;
/**
- * Data for per-language split.
+ * DSL object for configuring per-language splits options.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits">APK Splits</a>.
*/
public class LanguageSplitOptions {
@@ -44,6 +46,9 @@
include = Sets.newHashSet(list);
}
+ /**
+ * Adds an include pattern.
+ */
public void include(@NonNull String... includes) {
if (include == null) {
include = Sets.newHashSet(includes);
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/NdkOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/NdkOptions.java
index d5af4fb..fc10b78 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/NdkOptions.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/NdkOptions.java
@@ -18,7 +18,6 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.build.gradle.internal.core.NdkConfig;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -34,7 +33,7 @@
/**
* DSL object for NDK-related settings.
*/
-public class NdkOptions implements NdkConfig, Serializable {
+public class NdkOptions implements CoreNdkOptions, Serializable {
private static final long serialVersionUID = 1L;
private String moduleName;
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/PackagingOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/PackagingOptions.java
index 94ef560..2b86f65 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/PackagingOptions.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/PackagingOptions.java
@@ -30,6 +30,7 @@
private Set<String> excludes = Sets.newHashSet("LICENSE.txt", "LICENSE");
private Set<String> pickFirsts = Sets.newHashSet();
+ private Set<String> merges = Sets.newHashSet();
/**
* Returns the list of excluded paths.
@@ -78,4 +79,26 @@
public void setPickFirsts(Set<String> pickFirsts) {
this.pickFirsts = Sets.newHashSet(pickFirsts);
}
+
+ /**
+ * Returns the list of paths where all occurrences are concatenated and packaged in the APK.
+ */
+ @Override
+ @NonNull
+ @Input
+ public Set<String> getMerges() {
+ return Sets.newHashSet(merges);
+ }
+
+ public void setMerges(Set<String> merges) {
+ this.merges = Sets.newHashSet(merges);
+ }
+
+ /**
+ * Adds a merge path.
+ * @param path the path, as packaged in the APK
+ */
+ public void merge(String path) {
+ merges.add(path);
+ }
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/PreprocessingOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/PreprocessingOptions.java
new file mode 100644
index 0000000..72a9211
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/PreprocessingOptions.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.dsl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.android.resources.Density;
+import com.google.common.collect.Sets;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+/**
+ * Resource preprocessing options.
+ */
+public class PreprocessingOptions {
+ private EnumSet<Density> densities;
+ private boolean enabled;
+
+ public PreprocessingOptions() {
+ this.enabled = true;
+
+ // TODO: What are the right default values?
+ this.densities = EnumSet.of(
+ Density.MEDIUM,
+ Density.HIGH,
+ Density.XHIGH,
+ Density.XXHIGH);
+ }
+
+ /**
+ * Whether to enable resources pre-processing. This is disabled by default.
+ *
+ * <p>If resources pre-processing is enabled, the build process will create two copies of the
+ * resource tree: one with a merged view of resources for a given variant and one with the
+ * preprocessed files, ready to be packaged. This may slow down your clean builds.
+ */
+ public boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ /**
+ * Set of screen densities for which PNG files should be generated based on vector drawable
+ * resource files.
+ *
+ * <p>Default to {@code ["mdpi", "hdpi", "xhdpi", "xxhdpi"]}.
+ */
+ public Set<String> getDensities() {
+ Set<String> result = Sets.newHashSet();
+ for (Density density : densities) {
+ result.add(density.getResourceValue());
+ }
+ return result;
+ }
+
+ public void setDensities(Set<String> densities) {
+ EnumSet<Density> newValue = EnumSet.noneOf(Density.class);
+ for (String density : densities) {
+ Density typedValue = Density.getEnum(density);
+ checkArgument(typedValue != null, "Unrecognized density %s", density);
+ newValue.add(typedValue);
+ }
+ this.densities = newValue;
+ }
+
+ /**
+ * Returns the densities to generate as the underlying enum values.
+ *
+ * <p>Not meant to be used in build scripts.
+ */
+ public Set<Density> getTypedDensities() {
+ return densities;
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavor.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavor.groovy
deleted file mode 100644
index 72dbac4..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavor.groovy
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.internal.dsl
-
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.gradle.internal.LoggingUtil
-import com.android.build.gradle.internal.core.NdkConfig
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.BuilderConstants
-import com.android.builder.core.DefaultApiVersion
-import com.android.builder.core.DefaultProductFlavor
-import com.android.builder.model.ApiVersion
-import com.android.builder.model.ClassField
-import com.google.common.base.Strings
-import org.gradle.api.Action
-import org.gradle.api.Project
-import org.gradle.api.logging.Logger
-import org.gradle.internal.reflect.Instantiator
-
-/**
- * DSL object used to configure product flavors.
- */
-class ProductFlavor extends DefaultProductFlavor {
-
- @NonNull
- protected final Project project
-
- @NonNull
- protected final Logger logger
-
- private final NdkOptions ndkConfig
-
- private Boolean useJack
-
- ProductFlavor(@NonNull String name,
- @NonNull Project project,
- @NonNull Instantiator instantiator,
- @NonNull Logger logger) {
- super(name)
- this.project = project
- this.logger = logger
- ndkConfig = instantiator.newInstance(NdkOptions.class)
- }
-
- @Nullable
- public NdkConfig getNdkConfig() {
- return ndkConfig;
- }
-
- @NonNull
- public void setMinSdkVersion(int minSdkVersion) {
- setMinSdkVersion(new DefaultApiVersion(minSdkVersion));
- }
-
- /**
- * Sets minimum SDK version.
- *
- * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
- * uses-sdk element documentation</a>.
- */
- @NonNull
- public void minSdkVersion(int minSdkVersion) {
- setMinSdkVersion(minSdkVersion);
- }
-
- @NonNull
- public void setMinSdkVersion(String minSdkVersion) {
- setMinSdkVersion(getApiVersion(minSdkVersion))
- }
-
- /**
- * Sets minimum SDK version.
- *
- * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
- * uses-sdk element documentation</a>.
- */
- @NonNull
- public void minSdkVersion(String minSdkVersion) {
- setMinSdkVersion(minSdkVersion);
- }
-
- @NonNull
- public com.android.builder.model.ProductFlavor setTargetSdkVersion(int targetSdkVersion) {
- setTargetSdkVersion(new DefaultApiVersion(targetSdkVersion));
- return this;
- }
-
- /**
- * Sets the target SDK version to the given value.
- *
- * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
- * uses-sdk element documentation</a>.
- */
- @NonNull
- public void targetSdkVersion(int targetSdkVersion) {
- setTargetSdkVersion(targetSdkVersion);
- }
-
- @NonNull
- public void setTargetSdkVersion(String targetSdkVersion) {
- setTargetSdkVersion(getApiVersion(targetSdkVersion))
- }
-
- /**
- * Sets the target SDK version to the given value.
- *
- * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
- * uses-sdk element documentation</a>.
- */
- @NonNull
- public void targetSdkVersion(String targetSdkVersion) {
- setTargetSdkVersion(targetSdkVersion);
- }
-
- @NonNull
- public void maxSdkVersion(int targetSdkVersion) {
- setMaxSdkVersion(targetSdkVersion);
- }
-
- @Nullable
- private static ApiVersion getApiVersion(@Nullable String value) {
- if (!Strings.isNullOrEmpty(value)) {
- if (Character.isDigit(value.charAt(0))) {
- try {
- int apiLevel = Integer.valueOf(value)
- return new DefaultApiVersion(apiLevel)
- } catch (NumberFormatException e) {
- throw new RuntimeException("'${value}' is not a valid API level. ", e)
- }
- }
-
- return new DefaultApiVersion(value)
- }
-
- return null
- }
-
- /**
- * Signing config used by this product flavor.
- */
- @Override
- SigningConfig getSigningConfig() {
- return (SigningConfig) super.signingConfig
- }
-
-// -- DSL Methods. TODO remove once the instantiator does what I expect it to do.
-
- public void buildConfigField(
- @NonNull String type,
- @NonNull String name,
- @NonNull String value) {
- ClassField alreadyPresent = getBuildConfigFields().get(name);
- if (alreadyPresent != null) {
- String flavorName = getName();
- if (BuilderConstants.MAIN.equals(flavorName)) {
- logger.info(
- "DefaultConfig: buildConfigField '$name' value is being replaced: ${alreadyPresent.value} -> $value");
- } else {
- logger.info(
- "ProductFlavor($flavorName): buildConfigField '$name' value is being replaced: ${alreadyPresent.value} -> $value");
- }
- }
- addBuildConfigField(AndroidBuilder.createClassField(type, name, value));
- }
-
- public void resValue(
- @NonNull String type,
- @NonNull String name,
- @NonNull String value) {
- ClassField alreadyPresent = getResValues().get(name);
- if (alreadyPresent != null) {
- String flavorName = getName();
- if (BuilderConstants.MAIN.equals(flavorName)) {
- logger.info(
- "DefaultConfig: resValue '$name' value is being replaced: ${alreadyPresent.value} -> $value");
- } else {
- logger.info(
- "ProductFlavor($flavorName): resValue '$name' value is being replaced: ${alreadyPresent.value} -> $value");
- }
- }
- addResValue(AndroidBuilder.createClassField(type, name, value));
- }
-
- /**
- * Adds a new ProGuard configuration file.
- *
- * <p><code>proguardFile getDefaultProguardFile('proguard-android.txt')</code></p>
- *
- * <p>There are 2 default rules files
- * <ul>
- * <li>proguard-android.txt
- * <li>proguard-android-optimize.txt
- * </ul>
- * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the
- * full path to the files. They are identical except for enabling optimizations.
- */
- @NonNull
- public void proguardFile(Object proguardFile) {
- proguardFiles.add(project.file(proguardFile))
- }
-
- /**
- * Adds new ProGuard configuration files.
- */
- @NonNull
- public void proguardFiles(Object... proguardFileArray) {
- proguardFiles.addAll(project.files(proguardFileArray).files)
- }
-
- /**
- * Sets the ProGuard configuration files.
- */
- @NonNull
- public void setProguardFiles(Iterable<?> proguardFileIterable) {
- proguardFiles.clear()
- for (Object proguardFile : proguardFileIterable) {
- proguardFiles.add(project.file(proguardFile))
- }
- }
-
- @NonNull
- public void testProguardFile(Object proguardFile) {
- testProguardFiles.add(project.file(proguardFile));
- }
-
- /**
- * Adds new ProGuard configuration files.
- */
- @NonNull
- public void testProguardFiles(Object... proguardFileArray) {
- testProguardFiles.addAll(project.files(proguardFileArray).files);
- }
-
- @NonNull
- public void consumerProguardFiles(Object... proguardFileArray) {
- consumerProguardFiles.addAll(project.files(proguardFileArray).files)
- }
-
- @NonNull
- public void setConsumerProguardFiles(Iterable<?> proguardFileIterable) {
- consumerProguardFiles.clear()
- for (Object proguardFile : proguardFileIterable) {
- consumerProguardFiles.add(project.file(proguardFile))
- }
- }
-
- void ndk(Action<NdkOptions> action) {
- action.execute(ndkConfig)
- LoggingUtil.displayDeprecationWarning(
- logger,
- project,
- "Current NDK support is deprecated. Alternative will be provided in the future.");
- }
-
- void resConfig(@NonNull String config) {
- addResourceConfiguration(config);
- }
-
- void resConfigs(@NonNull String... config) {
- addResourceConfigurations(config);
- }
-
- void resConfigs(@NonNull Collection<String> config) {
- addResourceConfigurations(config);
- }
-
- Boolean getUseJack() {
- return useJack
- }
-
- void setUseJack(Boolean useJack) {
- this.useJack = useJack
- }
-
- void useJack(Boolean useJack) {
- setUseJack(useJack)
- }
-}
\ No newline at end of file
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavor.java
new file mode 100644
index 0000000..5733ced
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavor.java
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle.internal.dsl;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.LoggingUtil;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.core.DefaultApiVersion;
+import com.android.builder.core.DefaultProductFlavor;
+import com.android.builder.model.ApiVersion;
+import com.android.builder.model.ClassField;
+import com.google.common.base.Strings;
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.api.logging.Logger;
+import org.gradle.internal.reflect.Instantiator;
+
+import static com.android.build.gradle.tasks.NdkCompile.USE_DEPRECATED_NDK;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * DSL object used to configure product flavors.
+ */
+public class ProductFlavor extends DefaultProductFlavor implements CoreProductFlavor {
+
+ @NonNull
+ protected final Project project;
+
+ @NonNull
+ protected final Logger logger;
+
+ @NonNull
+ private final NdkOptions ndkConfig;
+
+ @Nullable
+ private Boolean useJack;
+
+ public ProductFlavor(@NonNull String name,
+ @NonNull Project project,
+ @NonNull Instantiator instantiator,
+ @NonNull Logger logger) {
+ super(name);
+ this.project = project;
+ this.logger = logger;
+ ndkConfig = instantiator.newInstance(NdkOptions.class);
+ }
+
+ @Override
+ @Nullable
+ public CoreNdkOptions getNdkConfig() {
+ return ndkConfig;
+ }
+
+ public void setMinSdkVersion(int minSdkVersion) {
+ setMinSdkVersion(new DefaultApiVersion(minSdkVersion));
+ }
+
+ /**
+ * Sets minimum SDK version.
+ *
+ * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
+ * uses-sdk element documentation</a>.
+ */
+ public void minSdkVersion(int minSdkVersion) {
+ setMinSdkVersion(minSdkVersion);
+ }
+
+ public void setMinSdkVersion(@Nullable String minSdkVersion) {
+ setMinSdkVersion(getApiVersion(minSdkVersion));
+ }
+
+ /**
+ * Sets minimum SDK version.
+ *
+ * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
+ * uses-sdk element documentation</a>.
+ */
+ public void minSdkVersion(@Nullable String minSdkVersion) {
+ setMinSdkVersion(minSdkVersion);
+ }
+
+ @NonNull
+ public com.android.builder.model.ProductFlavor setTargetSdkVersion(int targetSdkVersion) {
+ setTargetSdkVersion(new DefaultApiVersion(targetSdkVersion));
+ return this;
+ }
+
+ /**
+ * Sets the target SDK version to the given value.
+ *
+ * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
+ * uses-sdk element documentation</a>.
+ */
+ public void targetSdkVersion(int targetSdkVersion) {
+ setTargetSdkVersion(targetSdkVersion);
+ }
+
+ public void setTargetSdkVersion(@Nullable String targetSdkVersion) {
+ setTargetSdkVersion(getApiVersion(targetSdkVersion));
+ }
+
+ /**
+ * Sets the target SDK version to the given value.
+ *
+ * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
+ * uses-sdk element documentation</a>.
+ */
+ public void targetSdkVersion(@Nullable String targetSdkVersion) {
+ setTargetSdkVersion(targetSdkVersion);
+ }
+
+ /**
+ * Sets the maximum SDK version to the given value.
+ *
+ * <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
+ * uses-sdk element documentation</a>.
+ */
+ public void maxSdkVersion(int targetSdkVersion) {
+ setMaxSdkVersion(targetSdkVersion);
+ }
+
+ @Nullable
+ private static ApiVersion getApiVersion(@Nullable String value) {
+ if (!Strings.isNullOrEmpty(value)) {
+ if (Character.isDigit(value.charAt(0))) {
+ try {
+ int apiLevel = Integer.valueOf(value);
+ return new DefaultApiVersion(apiLevel);
+ } catch (NumberFormatException e) {
+ throw new RuntimeException("'" + value + "' is not a valid API level. ", e);
+ }
+ }
+
+ return new DefaultApiVersion(value);
+ }
+
+ return null;
+ }
+
+ /**
+ * Adds a custom argument to the test instrumentation runner, e.g:
+ *
+ * <p><pre>testInstrumentationRunnerArgument "size", "medium"</pre>
+ *
+ * <p>Test runner arguments can also be specified from the command line:
+ *
+ * <p><pre>
+ * INSTRUMENTATION_TEST_RUNNER_ARGS=size=medium,foo=bar ./gradlew connectedAndroidTest
+ * ./gradlew connectedAndroidTest -Pcom.android.tools.instrumentationTestRunnerArgs=size=medium,foo=bar
+ * </pre>
+ */
+ public void testInstrumentationRunnerArgument(@NonNull String key, @NonNull String value) {
+ getTestInstrumentationRunnerArguments().put(key, value);
+ }
+
+ /**
+ * Adds custom arguments to the test instrumentation runner, e.g:
+ *
+ * <p><pre>testInstrumentationRunnerArguments(size: "medium", foo: "bar")</pre>
+ *
+ * <p>Test runner arguments can also be specified from the command line:
+ *
+ * <p><pre>
+ * INSTRUMENTATION_TEST_RUNNER_ARGS=size=medium,foo=bar ./gradlew connectedAndroidTest
+ * ./gradlew connectedAndroidTest -Pcom.android.tools.instrumentationTestRunnerArgs=size=medium,foo=bar
+ * </pre>
+ */
+ public void testInstrumentationRunnerArguments(@NonNull Map<String, String> args) {
+ getTestInstrumentationRunnerArguments().putAll(args);
+ }
+
+ /**
+ * Signing config used by this product flavor.
+ */
+ @Override
+ @Nullable
+ public SigningConfig getSigningConfig() {
+ return (SigningConfig) super.getSigningConfig();
+ }
+
+// -- DSL Methods. TODO remove once the instantiator does what I expect it to do.
+
+ /**
+ * Adds a new field to the generated BuildConfig class.
+ *
+ * The field is generated as:
+ *
+ * <type> <name> = <value>;
+ *
+ * This means each of these must have valid Java content. If the type is a String, then the
+ * value should include quotes.
+ *
+ * @param type the type of the field
+ * @param name the name of the field
+ * @param value the value of the field
+ */
+ public void buildConfigField(
+ @NonNull String type,
+ @NonNull String name,
+ @NonNull String value) {
+ ClassField alreadyPresent = getBuildConfigFields().get(name);
+ if (alreadyPresent != null) {
+ String flavorName = getName();
+ if (BuilderConstants.MAIN.equals(flavorName)) {
+ logger.info(
+ "DefaultConfig: buildConfigField '{}' value is being replaced: {} -> {}",
+ name, alreadyPresent.getValue(), value);
+ } else {
+ logger.info(
+ "ProductFlavor({}): buildConfigField '{}' "
+ + "value is being replaced: {} -> {}",
+ flavorName, name, alreadyPresent.getValue(), value);
+ }
+ }
+ addBuildConfigField(AndroidBuilder.createClassField(type, name, value));
+ }
+
+ /**
+ * Adds a new generated resource.
+ *
+ * <p>This is equivalent to specifying a resource in res/values.
+ *
+ * <p>See <a href="http://developer.android.com/guide/topics/resources/available-resources.html">Resource Types</a>.
+ *
+ * @param type the type of the resource
+ * @param name the name of the resource
+ * @param value the value of the resource
+ */
+ public void resValue(
+ @NonNull String type,
+ @NonNull String name,
+ @NonNull String value) {
+ ClassField alreadyPresent = getResValues().get(name);
+ if (alreadyPresent != null) {
+ String flavorName = getName();
+ if (BuilderConstants.MAIN.equals(flavorName)) {
+ logger.info(
+ "DefaultConfig: resValue '{}' value is being replaced: {} -> {}",
+ name, alreadyPresent.getValue(), value);
+ } else {
+ logger.info(
+ "ProductFlavor({}): resValue '{}' value is being replaced: {} -> {}",
+ flavorName, name, alreadyPresent.getValue(), value);
+ }
+ }
+ addResValue(AndroidBuilder.createClassField(type, name, value));
+ }
+
+ /**
+ * Adds a new ProGuard configuration file.
+ *
+ * <p><code>proguardFile getDefaultProguardFile('proguard-android.txt')</code></p>
+ *
+ * <p>There are 2 default rules files
+ * <ul>
+ * <li>proguard-android.txt
+ * <li>proguard-android-optimize.txt
+ * </ul>
+ * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the
+ * full path to the files. They are identical except for enabling optimizations.
+ */
+ public void proguardFile(@NonNull Object proguardFile) {
+ getProguardFiles().add(project.file(proguardFile));
+ }
+
+ /**
+ * Adds new ProGuard configuration files.
+ *
+ * <p>There are 2 default rules files
+ * <ul>
+ * <li>proguard-android.txt
+ * <li>proguard-android-optimize.txt
+ * </ul>
+ * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the
+ * full path to the files. They are identical except for enabling optimizations.
+ */
+ public void proguardFiles(@NonNull Object... proguardFileArray) {
+ getProguardFiles().addAll(project.files(proguardFileArray).getFiles());
+ }
+
+ /**
+ * Sets the ProGuard configuration files.
+ *
+ * <p>There are 2 default rules files
+ * <ul>
+ * <li>proguard-android.txt
+ * <li>proguard-android-optimize.txt
+ * </ul>
+ * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the
+ * full path to the files. They are identical except for enabling optimizations.
+ */
+ public void setProguardFiles(@NonNull Iterable<?> proguardFileIterable) {
+ getProguardFiles().clear();
+ for (Object proguardFile : proguardFileIterable) {
+ getProguardFiles().add(project.file(proguardFile));
+ }
+ }
+
+ /**
+ * Specifies a proguard rule file to be included in the published AAR.
+ *
+ * <p>This proguard rule file will then be used by any application project that consume the AAR
+ * (if proguard is enabled).
+ *
+ * <p>This allows AAR to specify shrinking or obfuscation exclude rules.
+ *
+ * <p>This is only valid for Library project. This is ignored in Application project.
+ */
+ public void testProguardFile(@NonNull Object proguardFile) {
+ getTestProguardFiles().add(project.file(proguardFile));
+ }
+
+ /**
+ * Adds new ProGuard configuration files.
+ */
+ public void testProguardFiles(Object... proguardFileArray) {
+ getTestProguardFiles().addAll(project.files(proguardFileArray).getFiles());
+ }
+
+ public void consumerProguardFiles(Object... proguardFileArray) {
+ getConsumerProguardFiles().addAll(project.files(proguardFileArray).getFiles());
+ }
+
+ /**
+ * Specifies a proguard rule file to be included in the published AAR.
+ *
+ * <p>This proguard rule file will then be used by any application project that consume the AAR
+ * (if proguard is enabled).
+ *
+ * <p>This allows AAR to specify shrinking or obfuscation exclude rules.
+ *
+ * <p>This is only valid for Library project. This is ignored in Application project.
+ */
+ public void setConsumerProguardFiles(@NonNull Iterable<?> proguardFileIterable) {
+ getConsumerProguardFiles().clear();
+ for (Object proguardFile : proguardFileIterable) {
+ getConsumerProguardFiles().add(project.file(proguardFile));
+ }
+ }
+
+ public void ndk(Action<NdkOptions> action) {
+ action.execute(ndkConfig);
+ if (!project.hasProperty(USE_DEPRECATED_NDK)) {
+ throw new RuntimeException(
+ "Error: NDK integration is deprecated in the current plugin. Consider trying " +
+ "the new experimental plugin. For details, see " +
+ "http://tools.android.com/tech-docs/new-build-system/gradle-experimental. " +
+ "Set \"" + USE_DEPRECATED_NDK + "=true\" in gradle.properties to " +
+ "continue using the current NDK integration.");
+ }
+ }
+
+ /**
+ * Adds a resource configuration filter.
+ *
+ * <p>If a qualifier value is passed, then all other resources using a qualifier of the same type
+ * but of different value will be ignored from the final packaging of the APK.
+ *
+ * <p>For instance, specifying 'hdpi', will ignore all resources using mdpi, xhdpi, etc...
+ */
+ public void resConfig(@NonNull String config) {
+ addResourceConfiguration(config);
+ }
+
+ /**
+ * Adds several resource configuration filters.
+ *
+ * <p>If a qualifier value is passed, then all other resources using a qualifier of the same type
+ * but of different value will be ignored from the final packaging of the APK.
+ *
+ * <p>For instance, specifying 'hdpi', will ignore all resources using mdpi, xhdpi, etc...
+ */
+ public void resConfigs(@NonNull String... config) {
+ addResourceConfigurations(config);
+ }
+
+ /**
+ * Adds several resource configuration filters.
+ *
+ * <p>If a qualifier value is passed, then all other resources using a qualifier of the same type
+ * but of different value will be ignored from the final packaging of the APK.
+ *
+ * <p>For instance, specifying 'hdpi', will ignore all resources using mdpi, xhdpi, etc...
+ */
+ public void resConfigs(@NonNull Collection<String> config) {
+ addResourceConfigurations(config);
+ }
+
+ /**
+ * Whether the experimental Jack toolchain should be used.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/jackandjill">Jack and Jill</a>
+ */
+ @Override
+ @Nullable
+ public Boolean getUseJack() {
+ return useJack;
+ }
+
+ /**
+ * Whether the experimental Jack toolchain should be used.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/jackandjill">Jack and Jill</a>
+ */
+ public void setUseJack(Boolean useJack) {
+ this.useJack = useJack;
+ }
+
+ /**
+ * Whether the experimental Jack toolchain should be used.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/jackandjill">Jack and Jill</a>
+ */
+ public void useJack(Boolean useJack) {
+ setUseJack(useJack);
+ }
+
+ @Deprecated
+ public void setFlavorDimension(String dimension) {
+ LoggingUtil.displayDeprecationWarning(logger, project,
+ "'flavorDimension' will be removed by Android Gradle Plugin 2.0, " +
+ "it has been replaced by 'dimension'.");
+ setDimension(dimension);
+ }
+
+ /**
+ * Name of the dimension this product flavor belongs to. Has been replaced by
+ * <code>dimension</code>
+ */
+ @Deprecated
+ public String getFlavorDimension() {
+ LoggingUtil.displayDeprecationWarning(logger, project,
+ "'flavorDimension' will be removed by Android Gradle Plugin 2.0, " +
+ "it has been replaced by 'dimension'.");
+ return getDimension();
+ }
+
+ public void jarJarRuleFile(Object file) {
+ getJarJarRuleFiles().add(project.file(file));
+ }
+
+ public void jarJarRuleFiles(Object ...files) {
+ getJarJarRuleFiles().clear();
+ for (Object file : files) {
+ getJarJarRuleFiles().add(project.file(file));
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfig.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfig.java
index 4df150c..ad97b94 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfig.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfig.java
@@ -34,7 +34,8 @@
/**
* DSL object for configuring signing configs.
*/
-public class SigningConfig extends DefaultSigningConfig implements Serializable, Named {
+public class SigningConfig extends DefaultSigningConfig implements Serializable, Named,
+ CoreSigningConfig {
private static final long serialVersionUID = 1L;
/**
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfigFactory.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfigFactory.groovy
deleted file mode 100644
index 7d587b9..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfigFactory.groovy
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.internal.dsl
-
-import org.gradle.api.NamedDomainObjectFactory
-import org.gradle.internal.reflect.Instantiator
-/**
- * Factory to create SigningConfig object using an {@ling Instantiator} to add the DSL methods.
- */
-class SigningConfigFactory implements NamedDomainObjectFactory<SigningConfig> {
-
- final Instantiator instantiator
-
- public SigningConfigFactory(Instantiator instantiator) {
- this.instantiator = instantiator
- }
-
- @Override
- SigningConfig create(String name) {
- return instantiator.newInstance(SigningConfig.class, name)
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfigFactory.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfigFactory.java
new file mode 100644
index 0000000..49ce997
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SigningConfigFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle.internal.dsl;
+
+import com.android.annotations.NonNull;
+
+import org.gradle.api.NamedDomainObjectFactory;
+import org.gradle.internal.reflect.Instantiator;
+/**
+ * Factory to create SigningConfig object using an {@link Instantiator} to add the DSL methods.
+ */
+public class SigningConfigFactory implements NamedDomainObjectFactory<SigningConfig> {
+
+ private final Instantiator instantiator;
+
+ public SigningConfigFactory(Instantiator instantiator) {
+ this.instantiator = instantiator;
+ }
+
+ @Override
+ @NonNull
+ public SigningConfig create(@NonNull String name) {
+ return instantiator.newInstance(SigningConfig.class, name);
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SplitOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SplitOptions.java
index 11c8707..c9d356b 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SplitOptions.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/SplitOptions.java
@@ -22,7 +22,6 @@
import java.util.Arrays;
import java.util.Collections;
-import java.util.List;
import java.util.Set;
/**
@@ -74,7 +73,7 @@
/**
* Resets the list of included split configuration.
*
- * Use this before calling include, in order to manually configure the list of configuration
+ * <p>Use this before calling include, in order to manually configure the list of configuration
* to split on, rather than excluding from the default list.
*/
public void reset() {
@@ -84,14 +83,14 @@
/**
* Returns a list of all applicable filters for this dimension.
*
- * The list can return null, indicating that the no-filter option must also be used.
+ * <p>The list can return null, indicating that the no-filter option must also be used.
*
* @return the filters to use.
*/
@NonNull
public Set<String> getApplicableFilters() {
if (!enable) {
- return Collections.singleton(null);
+ return Collections.emptySet();
}
Set<String> results = Sets.newHashSetWithExpectedSize(values.size());
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/Splits.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/Splits.java
index 97c49b2..cd86795 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/Splits.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/Splits.java
@@ -17,9 +17,6 @@
package com.android.build.gradle.internal.dsl;
import com.android.annotations.NonNull;
-import com.android.resources.Density;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
import org.gradle.api.Action;
import org.gradle.internal.reflect.Instantiator;
@@ -27,7 +24,9 @@
import java.util.Set;
/**
- * Main entry point for all splits related information.
+ * DSL object for configuring APK Splits options.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits">APK Splits</a>.
*/
public class Splits {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/TestOptions.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/TestOptions.groovy
deleted file mode 100644
index 2753515..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/TestOptions.groovy
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal.dsl
-
-import groovy.transform.CompileStatic
-import org.gradle.api.DomainObjectSet
-import org.gradle.api.internal.DefaultDomainObjectSet
-import org.gradle.api.tasks.testing.Test
-import org.gradle.util.ConfigureUtil
-/**
- * Options for running tests.
- */
-@CompileStatic
-class TestOptions {
- /** Name of the results directory. */
- String resultsDir
-
- /** Name of the reports directory. */
- String reportDir
-
- /**
- * Options for controlling unit tests execution.
- *
- * @since 1.1
- */
- UnitTestOptions unitTests = new UnitTestOptions()
-
- /**
- * Configures unit test options.
- *
- * @since 1.2
- */
- def unitTests(Closure closure) {
- ConfigureUtil.configure(closure, unitTests);
- }
-
- /**
- * Options for controlling unit tests execution.
- */
- static class UnitTestOptions {
- private DomainObjectSet<Test> testTasks = new DefaultDomainObjectSet<Test>(Test)
-
- /**
- * Whether unmocked methods from android.jar should throw exceptions or return default
- * values (i.e. zero or null).
- *
- * <p>See <a href="http://tools.android.com/tech-docs/unit-testing-support">Unit testing support</a> for details.
- *
- * @since 1.1
- */
- boolean returnDefaultValues
-
- /**
- * Configures all unit testing tasks.
- *
- * <p>See {@link Test} for available options.
- *
- * <p>Inside the closure you can check the name of the task to configure only some test
- * tasks, e.g.
- *
- * <pre>
- * android {
- * testOptions {
- * unitTests.all {
- * if (it.name == 'testDebug') {
- * systemProperty 'debug', 'true'
- * }
- * }
- * }
- * }
- * </pre>
- *
- * @since 1.2
- */
- def all(Closure configClosure) {
- testTasks.all { Test testTask ->
- ConfigureUtil.configure(configClosure, testTask)
- }
- }
-
- /**
- * Configures a given test task. The configuration closures that were passed to
- * {@link #all(Closure)} will be applied to it.
- *
- * <p>Not meant to be called from build scripts. The reason it exists is that tasks
- * are created after the build scripts are evaluated, so users have to "register" their
- * configuration closures first and we can only apply them later.
- *
- * @since 1.2
- */
- public applyConfiguration(Test task) {
- this.testTasks.add(task)
- }
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/TestOptions.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/TestOptions.java
new file mode 100644
index 0000000..a1af3e3
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/dsl/TestOptions.java
@@ -0,0 +1,156 @@
+/*
+ * 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.build.gradle.internal.dsl;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+
+import org.gradle.api.Action;
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.tasks.testing.Test;
+import org.gradle.util.ConfigureUtil;
+
+import groovy.lang.Closure;
+
+/**
+ * Options for running tests.
+ */
+public class TestOptions {
+ @Nullable
+ private String resultsDir;
+
+ @Nullable
+ private String reportDir;
+
+ /**
+ * Options for controlling unit tests execution.
+ *
+ * @since 1.1
+ */
+ @NonNull
+ private final UnitTestOptions unitTests = new UnitTestOptions();
+
+ /**
+ * Configures unit test options.
+ *
+ * @since 1.2
+ */
+ public void unitTests(Closure closure) {
+ ConfigureUtil.configure(closure, unitTests);
+ }
+
+ /**
+ * Configures unit test options.
+ *
+ * @since 1.2
+ */
+ @NonNull
+ public UnitTestOptions getUnitTests() {
+ return unitTests;
+ }
+
+ /** Name of the results directory. */
+ @Nullable
+ public String getResultsDir() {
+ return resultsDir;
+ }
+
+ public void setResultsDir(@Nullable String resultsDir) {
+ this.resultsDir = resultsDir;
+ }
+
+ /** Name of the reports directory. */
+ @Nullable
+ public String getReportDir() {
+ return reportDir;
+ }
+
+ public void setReportDir(@Nullable String reportDir) {
+ this.reportDir = reportDir;
+ }
+
+ /**
+ * Options for controlling unit tests execution.
+ */
+ public static class UnitTestOptions {
+ private DomainObjectSet<Test> testTasks = new DefaultDomainObjectSet<Test>(Test.class);
+ private boolean returnDefaultValues;
+
+ /**
+ * Whether unmocked methods from android.jar should throw exceptions or return default
+ * values (i.e. zero or null).
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/unit-testing-support">Unit testing support</a> for details.
+ *
+ * @since 1.1
+ */
+ public boolean isReturnDefaultValues() {
+ return returnDefaultValues;
+ }
+
+ public void setReturnDefaultValues(boolean returnDefaultValues) {
+ this.returnDefaultValues = returnDefaultValues;
+ }
+
+ /**
+ * Configures all unit testing tasks.
+ *
+ * <p>See {@link Test} for available options.
+ *
+ * <p>Inside the closure you can check the name of the task to configure only some test
+ * tasks, e.g.
+ *
+ * <pre>
+ * android {
+ * testOptions {
+ * unitTests.all {
+ * if (it.name == 'testDebug') {
+ * systemProperty 'debug', 'true'
+ * }
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * @since 1.2
+ */
+ public void all(final Closure<Test> configClosure) {
+ testTasks.all(new Action<Test>() {
+ @Override
+ public void execute(Test testTask) {
+ ConfigureUtil.configure(configClosure, testTask);
+ }
+ });
+ }
+
+
+ /**
+ * Configures a given test task. The configuration closures that were passed to
+ * {@link #all(Closure)} will be applied to it.
+ *
+ * <p>Not meant to be called from build scripts. The reason it exists is that tasks
+ * are created after the build scripts are evaluated, so users have to "register" their
+ * configuration closures first and we can only apply them later.
+ *
+ * @since 1.2
+ */
+ public void applyConfiguration(@NonNull Test task) {
+ this.testTasks.add(task);
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidArtifactImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidArtifactImpl.java
index d13cc8a..e1ca92e 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidArtifactImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidArtifactImpl.java
@@ -22,6 +22,7 @@
import com.android.builder.model.AndroidArtifactOutput;
import com.android.builder.model.ClassField;
import com.android.builder.model.Dependencies;
+import com.android.builder.model.NativeLibrary;
import com.android.builder.model.SourceProvider;
import com.google.common.collect.Sets;
@@ -53,6 +54,8 @@
@Nullable
private final Set<String> abiFilters;
@NonNull
+ private final Collection<NativeLibrary> nativeLibraries;
+ @NonNull
private final Map<String, ClassField> buildConfigFields;
@NonNull
private final Map<String, ClassField> resValues;
@@ -74,6 +77,7 @@
@Nullable SourceProvider variantSourceProvider,
@Nullable SourceProvider multiFlavorSourceProviders,
@Nullable Set<String> abiFilters,
+ @NonNull Collection<NativeLibrary> nativeLibraries,
@NonNull Map<String,ClassField> buildConfigFields,
@NonNull Map<String,ClassField> resValues) {
super(name, assembleTaskName, compileTaskName, classesFolder, javaResourcesFolder,
@@ -87,6 +91,7 @@
this.sourceGenTaskName = sourceGenTaskName;
this.generatedResourceFolders = generatedResourceFolders;
this.abiFilters = abiFilters;
+ this.nativeLibraries = nativeLibraries;
this.buildConfigFields = buildConfigFields;
this.resValues = resValues;
}
@@ -140,6 +145,12 @@
@NonNull
@Override
+ public Collection<NativeLibrary> getNativeLibraries() {
+ return nativeLibraries;
+ }
+
+ @NonNull
+ @Override
public Map<String, ClassField> getBuildConfigFields() {
return buildConfigFields;
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidLibraryImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidLibraryImpl.java
index 62a1906..c928bf9 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidLibraryImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/AndroidLibraryImpl.java
@@ -65,6 +65,7 @@
private final File publicResources;
@NonNull
private final List<AndroidLibrary> dependencies;
+ private final boolean isOptional;
AndroidLibraryImpl(
@NonNull LibraryDependency libraryDependency,
@@ -90,6 +91,7 @@
lintJar = libraryDependency.getLintJar();
annotations = libraryDependency.getExternalAnnotations();
publicResources = libraryDependency.getPublicResources();
+ isOptional = libraryDependency.isOptional();
this.project = project;
this.variant = variant;
@@ -196,4 +198,9 @@
public File getPublicResources() {
return publicResources;
}
+
+ @Override
+ public boolean isOptional() {
+ return isOptional;
+ }
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/BaseConfigImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/BaseConfigImpl.java
index a5015ec..b57c225 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/BaseConfigImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/BaseConfigImpl.java
@@ -48,6 +48,8 @@
private File mMultiDexKeepFile;
@Nullable
private File mMultiDexKeepProguard;
+ @Nullable
+ private List<File> mJarJarRuleFiles;
protected BaseConfigImpl(@NonNull BaseConfig baseConfig) {
mManifestPlaceholders = ImmutableMap.copyOf(baseConfig.getManifestPlaceholders());
@@ -56,6 +58,7 @@
mMultiDexEnabled = baseConfig.getMultiDexEnabled();
mMultiDexKeepFile = baseConfig.getMultiDexKeepFile();
mMultiDexKeepProguard = baseConfig.getMultiDexKeepProguard();
+ mJarJarRuleFiles = baseConfig.getJarJarRuleFiles();
}
@NonNull
@@ -112,6 +115,12 @@
return mMultiDexKeepProguard;
}
+ @NonNull
+ @Override
+ public List<File> getJarJarRuleFiles() {
+ return mJarJarRuleFiles;
+ }
+
@Override
public String toString() {
return "BaseConfigImpl{" +
@@ -119,6 +128,7 @@
", mBuildConfigFields=" + mBuildConfigFields +
", mResValues=" + mResValues +
", mMultiDexEnabled=" + mMultiDexEnabled +
+ ", mJarJarRuleFiles=" + mJarJarRuleFiles +
'}';
}
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
index 3168120..4528821 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DefaultAndroidProject.java
@@ -28,6 +28,7 @@
import com.android.builder.model.ProductFlavorContainer;
import com.android.builder.model.SigningConfig;
import com.android.builder.model.SyncIssue;
+import com.android.builder.model.NativeToolchain;
import com.android.builder.model.Variant;
import com.google.common.collect.Lists;
@@ -69,6 +70,8 @@
private final File buildFolder;
@Nullable
private final String resourcePrefix;
+ @NonNull
+ private final Collection<NativeToolchain> nativeToolchains;
private final boolean isLibrary;
private final int apiVersion;
@@ -97,6 +100,7 @@
@NonNull LintOptions lintOptions,
@NonNull File buildFolder,
@Nullable String resourcePrefix,
+ @NonNull Collection<NativeToolchain> nativeToolchains,
boolean isLibrary,
int apiVersion) {
this.modelVersion = modelVersion;
@@ -116,6 +120,7 @@
this.resourcePrefix = resourcePrefix;
this.isLibrary = isLibrary;
this.apiVersion = apiVersion;
+ this.nativeToolchains = nativeToolchains;
}
@NonNull
@@ -267,4 +272,9 @@
return resourcePrefix;
}
+ @NonNull
+ @Override
+ public Collection<NativeToolchain> getNativeToolchains() {
+ return nativeToolchains;
+ }
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DependenciesImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DependenciesImpl.java
index f4cd045..f4c37df 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DependenciesImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/DependenciesImpl.java
@@ -17,6 +17,7 @@
package com.android.build.gradle.internal.model;
import static com.android.SdkConstants.DOT_JAR;
+import static com.android.SdkConstants.FD_JARS;
import com.android.annotations.NonNull;
import com.android.build.gradle.internal.core.GradleVariantConfiguration;
@@ -222,6 +223,8 @@
if (aarFile.isFile()) {
List<File> jarList = Lists.newArrayList();
+ File jarsFolder = new File(explodedFolder, FD_JARS);
+
ZipFile zipFile = null;
try {
//noinspection IOResourceOpenedButNotSafelyClosed
@@ -231,7 +234,7 @@
ZipEntry zipEntry = e.nextElement();
String name = zipEntry.getName();
if (name.startsWith("libs/") && name.endsWith(DOT_JAR)) {
- jarList.add(new File(explodedFolder, name.replace('/', File.separatorChar)));
+ jarList.add(new File(jarsFolder, name.replace('/', File.separatorChar)));
}
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaArtifactImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaArtifactImpl.java
index 7f05729..160b9a7 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaArtifactImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaArtifactImpl.java
@@ -36,6 +36,9 @@
private final Set<String> ideSetupTaskNames;
+ @Nullable
+ private final File mockablePlatformJar;
+
public static JavaArtifactImpl clone(@NonNull JavaArtifact javaArtifact) {
SourceProvider variantSP = javaArtifact.getVariantSourceProvider();
SourceProvider flavorsSP = javaArtifact.getMultiFlavorSourceProvider();
@@ -48,6 +51,7 @@
javaArtifact.getGeneratedSourceFolders(),
javaArtifact.getClassesFolder(),
javaArtifact.getJavaResourcesFolder(),
+ javaArtifact.getMockablePlatformJar(),
DependenciesImpl.cloneDependenciesForJavaArtifacts(javaArtifact.getDependencies()),
variantSP != null ? SourceProviderImpl.cloneProvider(variantSP) : null,
flavorsSP != null ? SourceProviderImpl.cloneProvider(flavorsSP) : null);
@@ -60,6 +64,7 @@
@NonNull Collection<File> generatedSourceFolders,
@NonNull File classesFolder,
@NonNull File javaResourcesFolder,
+ @Nullable File mockablePlatformJar,
@NonNull Dependencies dependencies,
@Nullable SourceProvider variantSourceProvider,
@Nullable SourceProvider multiFlavorSourceProviders) {
@@ -67,6 +72,7 @@
dependencies,
variantSourceProvider, multiFlavorSourceProviders, generatedSourceFolders);
this.ideSetupTaskNames = Sets.newHashSet(ideSetupTaskNames);
+ this.mockablePlatformJar = mockablePlatformJar;
}
@NonNull
@@ -74,4 +80,10 @@
public Set<String> getIdeSetupTaskNames() {
return ideSetupTaskNames;
}
+
+ @Override
+ @Nullable
+ public File getMockablePlatformJar() {
+ return mockablePlatformJar;
+ }
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaLibraryImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaLibraryImpl.java
index 6bb260d..d40955c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaLibraryImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/JavaLibraryImpl.java
@@ -48,4 +48,12 @@
public List<? extends JavaLibrary> getDependencies() {
return Collections.emptyList();
}
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("JavaLibraryImpl{");
+ sb.append("jarFile=").append(jarFile);
+ sb.append('}');
+ return sb.toString();
+ }
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java
index 838ad3e..b6e18ee 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ModelBuilder.java
@@ -15,20 +15,24 @@
*/
package com.android.build.gradle.internal.model;
+
import static com.android.builder.model.AndroidProject.ARTIFACT_MAIN;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.OutputFile;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
import com.android.build.gradle.api.ApkOutputFile;
import com.android.build.gradle.internal.BuildTypeData;
import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.NdkHandler;
import com.android.build.gradle.internal.ProductFlavorData;
import com.android.build.gradle.internal.TaskManager;
import com.android.build.gradle.internal.VariantManager;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
import com.android.build.gradle.internal.core.GradleVariantConfiguration;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.variant.ApkVariantOutputData;
import com.android.build.gradle.internal.variant.BaseVariantData;
@@ -37,7 +41,6 @@
import com.android.build.gradle.internal.variant.TestedVariantData;
import com.android.builder.Version;
import com.android.builder.core.AndroidBuilder;
-import com.android.builder.core.DefaultProductFlavor;
import com.android.builder.core.VariantType;
import com.android.builder.model.AaptOptions;
import com.android.builder.model.AndroidArtifact;
@@ -47,18 +50,23 @@
import com.android.builder.model.ArtifactMetaData;
import com.android.builder.model.JavaArtifact;
import com.android.builder.model.LintOptions;
+import com.android.builder.model.NativeLibrary;
+import com.android.builder.model.NativeToolchain;
+import com.android.builder.model.ProductFlavor;
import com.android.builder.model.SigningConfig;
import com.android.builder.model.SourceProvider;
import com.android.builder.model.SourceProviderContainer;
import com.android.builder.model.SyncIssue;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
+import com.google.common.base.Optional;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
-import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.Project;
import org.gradle.tooling.provider.model.ToolingModelBuilder;
@@ -66,6 +74,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
/**
* Builder for the custom Android model.
@@ -75,13 +84,19 @@
@NonNull
private final AndroidBuilder androidBuilder;
@NonNull
- private final BaseExtension extension;
+ private final AndroidConfig config;
@NonNull
private final ExtraModelInfo extraModelInfo;
@NonNull
private final VariantManager variantManager;
@NonNull
private final TaskManager taskManager;
+ @NonNull
+ private final NdkHandler ndkHandler;
+ @NonNull
+ private Map<Abi, NativeToolchain> toolchains;
+ @NonNull
+ private NativeLibraryFactory nativeLibFactory;
private final boolean isLibrary;
@@ -89,14 +104,18 @@
@NonNull AndroidBuilder androidBuilder,
@NonNull VariantManager variantManager,
@NonNull TaskManager taskManager,
- @NonNull BaseExtension extension,
+ @NonNull AndroidConfig config,
@NonNull ExtraModelInfo extraModelInfo,
+ @NonNull NdkHandler ndkHandler,
+ @NonNull NativeLibraryFactory nativeLibraryFactory,
boolean isLibrary) {
this.androidBuilder = androidBuilder;
- this.extension = extension;
+ this.config = config;
this.extraModelInfo = extraModelInfo;
this.variantManager = variantManager;
this.taskManager = taskManager;
+ this.ndkHandler = ndkHandler;
+ this.nativeLibFactory = nativeLibraryFactory;
this.isLibrary = isLibrary;
}
@@ -112,11 +131,7 @@
@Override
public Object buildAll(String modelName, Project project) {
- NamedDomainObjectContainer<SigningConfig> signingConfigs;
-
- // Cast is needed due to covariance issues.
- //noinspection unchecked
- signingConfigs = (NamedDomainObjectContainer<SigningConfig>) (NamedDomainObjectContainer<?>) extension.getSigningConfigs();
+ Collection<? extends SigningConfig> signingConfigs = config.getSigningConfigs();
// Get the boot classpath. This will ensure the target is configured.
List<String> bootClasspath = androidBuilder.getBootClasspathAsStrings();
@@ -135,14 +150,16 @@
}
LintOptions lintOptions = com.android.build.gradle.internal.dsl.LintOptions.create(
- extension.getLintOptions());
+ config.getLintOptions());
- AaptOptions aaptOptions = AaptOptionsImpl.create(extension.getAaptOptions());
+ AaptOptions aaptOptions = AaptOptionsImpl.create(config.getAaptOptions());
List<SyncIssue> syncIssues = Lists.newArrayList(extraModelInfo.getSyncIssues().values());
- List<String> flavorDimensionList = (extension.getFlavorDimensionList() != null ?
- extension.getFlavorDimensionList() : Lists.<String>newArrayList());
+ List<String> flavorDimensionList = (config.getFlavorDimensionList() != null ?
+ config.getFlavorDimensionList() : Lists.<String>newArrayList());
+
+ toolchains = createNativeToolchainModelMap(ndkHandler);
DefaultAndroidProject androidProject = new DefaultAndroidProject(
Version.ANDROID_GRADLE_PLUGIN_VERSION,
@@ -151,15 +168,16 @@
androidBuilder.getTarget() != null ? androidBuilder.getTarget().hashString() : "",
bootClasspath,
frameworkSource,
- cloneSigningConfigs(signingConfigs),
+ cloneSigningConfigs(config.getSigningConfigs()),
aaptOptions,
artifactMetaDataList,
findUnresolvedDependencies(syncIssues),
syncIssues,
- extension.getCompileOptions(),
+ config.getCompileOptions(),
lintOptions,
project.getBuildDir(),
- extension.getResourcePrefix(),
+ config.getResourcePrefix(),
+ ImmutableList.copyOf(toolchains.values()),
isLibrary,
Version.BUILDER_MODEL_API_VERSION);
@@ -188,6 +206,28 @@
return androidProject;
}
+ /**
+ * Create a map of ABI to NativeToolchain
+ */
+ public static Map<Abi, NativeToolchain> createNativeToolchainModelMap(
+ @NonNull NdkHandler ndkHandler) {
+ if (!ndkHandler.isNdkDirConfigured()) {
+ return ImmutableMap.of();
+ }
+
+ Map<Abi, NativeToolchain> toolchains = Maps.newHashMap();
+
+ for (Abi abi : ndkHandler.getSupportedAbis()) {
+ toolchains.put(
+ abi,
+ new NativeToolchainImpl(
+ ndkHandler.getToolchain().getName() + "-" + abi.getName(),
+ ndkHandler.getCCompiler(abi),
+ ndkHandler.getCppCompiler(abi)));
+ }
+ return toolchains;
+ }
+
@NonNull
private VariantImpl createVariant(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
@@ -200,8 +240,10 @@
List<AndroidArtifact> extraAndroidArtifacts = Lists.newArrayList(
extraModelInfo.getExtraAndroidArtifacts(variantName));
// Make sure all extra artifacts are serializable.
- Collection<JavaArtifact> extraJavaArtifacts = extraModelInfo.getExtraJavaArtifacts(variantName);
- List<JavaArtifact> clonedExtraJavaArtifacts = Lists.newArrayListWithCapacity(extraJavaArtifacts.size());
+ Collection<JavaArtifact> extraJavaArtifacts = extraModelInfo.getExtraJavaArtifacts(
+ variantName);
+ List<JavaArtifact> clonedExtraJavaArtifacts = Lists.newArrayListWithCapacity(
+ extraJavaArtifacts.size());
for (JavaArtifact javaArtifact : extraJavaArtifacts) {
clonedExtraJavaArtifacts.add(JavaArtifactImpl.clone(javaArtifact));
}
@@ -265,13 +307,6 @@
DependenciesImpl dependencies = DependenciesImpl.cloneDependencies(variantData,
androidBuilder);
- // Add the mockable JAR path. It will be created before tests are actually run from the IDE.
- dependencies.getJavaLibraries().add(
- new JavaLibraryImpl(
- taskManager.createMockableJar.getOutputFile(),
- null,
- null));
-
List<File> extraGeneratedSourceFolders = variantData.getExtraGeneratedSourceFolders();
return new JavaArtifactImpl(
variantType.getArtifactName(),
@@ -280,18 +315,40 @@
Sets.newHashSet(variantData.prepareDependenciesTask.getName(),
taskManager.createMockableJar.getName()),
extraGeneratedSourceFolders != null ? extraGeneratedSourceFolders : Collections.<File>emptyList(),
- (variantData.javaCompileTask != null) ?
- variantData.javaCompileTask.getDestinationDir() :
+ (variantData.javacTask != null) ?
+ variantData.javacTask.getDestinationDir() :
variantData.getScope().getJavaOutputDir(),
- variantData.processJavaResourcesTask.getDestinationDir(),
+ variantData.getScope().getJavaResourcesDestinationDir(),
+ taskManager.createMockableJar.getOutputFile(),
dependencies,
sourceProviders.variantSourceProvider,
sourceProviders.multiFlavorSourceProvider);
}
+ /**
+ * Create a NativeLibrary for each ABI.
+ */
+ private Collection<NativeLibrary> createNativeLibraries(
+ @NonNull Collection<Abi> abis,
+ @NonNull VariantScope scope) {
+ Collection<NativeLibrary> nativeLibraries = Lists.newArrayListWithCapacity(abis.size());
+ for (Abi abi : abis) {
+ NativeToolchain toolchain = toolchains.get(abi);
+ if (toolchain == null) {
+ continue;
+ }
+ Optional<NativeLibrary> lib = nativeLibFactory.create(scope, toolchain.getName(), abi);
+ if (lib.isPresent()) {
+ nativeLibraries.add(lib.get());
+ }
+ }
+ return nativeLibraries;
+ }
+
private AndroidArtifact createAndroidArtifact(
@NonNull String name,
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
+ VariantScope scope = variantData.getScope();
GradleVariantConfiguration variantConfiguration = variantData.getVariantConfiguration();
SigningConfig signingConfig = variantConfiguration.getSigningConfig();
@@ -306,6 +363,28 @@
List<? extends BaseVariantOutputData> variantOutputs = variantData.getOutputs();
List<AndroidArtifactOutput> outputs = Lists.newArrayListWithCapacity(variantOutputs.size());
+ CoreNdkOptions ndkConfig = variantData.getVariantConfiguration().getNdkConfig();
+ Collection<NativeLibrary> nativeLibraries = ImmutableList.of();
+ if (ndkHandler.getNdkDirectory() != null) {
+ if (config.getSplits().getAbi().isEnable()) {
+ nativeLibraries = createNativeLibraries(
+ config.getSplits().getAbi().isUniversalApk()
+ ? ndkHandler.getSupportedAbis()
+ : createAbiList(config.getSplits().getAbiFilters()),
+ scope);
+ } else {
+ if (ndkConfig.getAbiFilters() == null || ndkConfig.getAbiFilters().isEmpty()) {
+ nativeLibraries = createNativeLibraries(
+ ndkHandler.getSupportedAbis(),
+ scope);
+ } else {
+ nativeLibraries = createNativeLibraries(
+ createAbiList(ndkConfig.getAbiFilters()),
+ scope);
+ }
+ }
+ }
+
for (BaseVariantOutputData variantOutputData : variantOutputs) {
int intVersionCode;
if (variantOutputData instanceof ApkVariantOutputData) {
@@ -333,35 +412,45 @@
// add the main APK.
outputs.add(new AndroidArtifactOutputImpl(
outputFiles.build(),
- variantOutputData.assembleTask.getName(),
+ "assemble" + variantOutputData.getFullName(),
variantOutputData.getScope().getManifestOutputFile(),
intVersionCode));
}
- VariantScope scope = variantData.getScope();
return new AndroidArtifactImpl(
name,
outputs,
- variantData.assembleVariantTask.getName(),
+ variantData.assembleVariantTask == null ? scope.getTaskName("assemble") : variantData.assembleVariantTask.getName(),
variantConfiguration.isSigningReady() || variantData.outputsAreSigned,
signingConfigName,
variantConfiguration.getApplicationId(),
- scope.getSourceGenTask().getName(),
- scope.getCompileTask().getName(),
+ // TODO: Need to determine the tasks' name when the tasks may not be created
+ // in component plugin.
+ scope.getSourceGenTask() == null ? scope.getTaskName("generate", "Sources") : scope.getSourceGenTask().getName(),
+ scope.getCompileTask() == null ? scope.getTaskName("compile", "Sources") : scope.getCompileTask().getName(),
getGeneratedSourceFolders(variantData),
getGeneratedResourceFolders(variantData),
- (variantData.javaCompileTask != null) ?
- variantData.javaCompileTask.getDestinationDir() :
+ (variantData.javacTask != null) ?
+ variantData.javacTask.getDestinationDir() :
scope.getJavaOutputDir(),
scope.getJavaResourcesDestinationDir(),
DependenciesImpl.cloneDependencies(variantData, androidBuilder),
sourceProviders.variantSourceProvider,
sourceProviders.multiFlavorSourceProvider,
variantConfiguration.getSupportedAbis(),
+ nativeLibraries,
variantConfiguration.getMergedBuildConfigFields(),
variantConfiguration.getMergedResValues());
}
+ private static Collection<Abi> createAbiList(Collection<String> abiNames) {
+ ImmutableList.Builder<Abi> builder = ImmutableList.builder();
+ for (String abiName : abiNames) {
+ builder.add(Abi.getByName(abiName));
+ }
+ return builder.build();
+ }
+
private static SourceProviders determineSourceProviders(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
SourceProvider variantSourceProvider =
@@ -381,12 +470,12 @@
@NonNull
private static List<String> getProductFlavorNames(
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
- List<GroupableProductFlavor> productFlavors = variantData.getVariantConfiguration()
+ List<CoreProductFlavor> productFlavors = variantData.getVariantConfiguration()
.getProductFlavors();
List<String> flavorNames = Lists.newArrayListWithCapacity(productFlavors.size());
- for (DefaultProductFlavor flavor : productFlavors) {
+ for (ProductFlavor flavor : productFlavors) {
flavorNames.add(flavor.getName());
}
@@ -453,7 +542,7 @@
@NonNull
private static Collection<SigningConfig> cloneSigningConfigs(
- @NonNull Collection<SigningConfig> signingConfigs) {
+ @NonNull Collection<? extends SigningConfig> signingConfigs) {
Collection<SigningConfig> results = Lists.newArrayListWithCapacity(signingConfigs.size());
for (SigningConfig signingConfig : signingConfigs) {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeLibraryFactory.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeLibraryFactory.java
new file mode 100644
index 0000000..8879e6c
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeLibraryFactory.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.model;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.builder.model.NativeLibrary;
+import com.google.common.base.Optional;
+
+/**
+ * Factory for creating NativeLibrary.
+ */
+public interface NativeLibraryFactory {
+ @NonNull
+ Optional<NativeLibrary> create(
+ @NonNull VariantScope scope,
+ @NonNull String toolchainName,
+ @NonNull Abi abi);
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeLibraryImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeLibraryImpl.java
new file mode 100644
index 0000000..f40ae89
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeLibraryImpl.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.model;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.NativeLibrary;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Implementation of NativeLibrary that is serializable.
+ */
+public class NativeLibraryImpl implements NativeLibrary, Serializable{
+ private static final long serialVersionUID = 1L;
+
+ @NonNull
+ String name;
+ @NonNull
+ String toolchainName;
+ @NonNull
+ String abi;
+ @NonNull
+ List<File> cIncludeDirs;
+ @NonNull
+ List<File> cppIncludeDirs;
+ @NonNull
+ List<File> cSystemIncludeDirs;
+ @NonNull
+ List<File> cppSystemIncludeDirs;
+ @NonNull
+ List<String> cDefines;
+ @NonNull
+ List<String> cppDefines;
+ @NonNull
+ List<String> cCompilerFlags;
+ @NonNull
+ List<String> cppCompilerFlags;
+ @NonNull
+ List<File> debuggableLibraryFolders;
+
+ public NativeLibraryImpl(
+ @NonNull String name,
+ @NonNull String toolchainName,
+ @NonNull String abi,
+ @NonNull List<File> cIncludeDirs,
+ @NonNull List<File> cppIncludeDirs,
+ @NonNull List<File> cSystemIncludeDirs,
+ @NonNull List<File> cppSystemIncludeDirs,
+ @NonNull List<String> cDefines,
+ @NonNull List<String> cppDefines,
+ @NonNull List<String> cCompilerFlags,
+ @NonNull List<String> cppCompilerFlags,
+ @NonNull List<File> debuggableLibraryFolders) {
+ this.name = name;
+ this.toolchainName = toolchainName;
+ this.abi = abi;
+ this.cIncludeDirs = cIncludeDirs;
+ this.cppIncludeDirs = cppIncludeDirs;
+ this.cSystemIncludeDirs = cSystemIncludeDirs;
+ this.cppSystemIncludeDirs = cppSystemIncludeDirs;
+ this.cDefines = cDefines;
+ this.cppDefines = cppDefines;
+ this.cCompilerFlags = cCompilerFlags;
+ this.cppCompilerFlags = cppCompilerFlags;
+ this.debuggableLibraryFolders = debuggableLibraryFolders;
+ }
+
+ @NonNull
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @NonNull
+ @Override
+ public String getToolchainName() {
+ return toolchainName;
+ }
+
+ @NonNull
+ @Override
+ public String getAbi() {
+ return abi;
+ }
+
+ @NonNull
+ @Override
+ public List<File> getCIncludeDirs() {
+ return cIncludeDirs;
+ }
+
+ @NonNull
+ @Override
+ public List<File> getCppIncludeDirs() {
+ return cppIncludeDirs;
+ }
+
+ @NonNull
+ @Override
+ public List<File> getCSystemIncludeDirs() {
+ return cSystemIncludeDirs;
+ }
+
+ @NonNull
+ @Override
+ public List<File> getCppSystemIncludeDirs() {
+ return cppSystemIncludeDirs;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getCDefines() {
+ return cDefines;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getCppDefines() {
+ return cppDefines;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getCCompilerFlags() {
+ return cCompilerFlags;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getCppCompilerFlags() {
+ return cppCompilerFlags;
+ }
+
+ @NonNull
+ @Override
+ public List<File> getDebuggableLibraryFolders() {
+ return debuggableLibraryFolders;
+ }
+
+ @Override
+ public String toString() {
+ return "NativeLibraryImpl{" +
+ "name='" + name + '\'' +
+ ", toolchainName='" + toolchainName + '\'' +
+ ", cIncludeDirs=" + cIncludeDirs +
+ ", cppIncludeDirs=" + cppIncludeDirs +
+ ", cDefines=" + cDefines +
+ ", cppDefines=" + cppDefines +
+ ", cCompilerFlags=" + cCompilerFlags +
+ ", cppCompilerFlags=" + cppCompilerFlags +
+ ", solibSearchPaths=" + debuggableLibraryFolders +
+ '}';
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeToolchainImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeToolchainImpl.java
new file mode 100644
index 0000000..5e7be5e
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/NativeToolchainImpl.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.model;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.NativeToolchain;
+
+import java.io.File;
+import java.io.Serializable;
+
+/**
+ * Implementation of NativeToolchain that is serializable.
+ */
+public class NativeToolchainImpl implements NativeToolchain, Serializable {
+
+ @NonNull
+ String name;
+
+ @NonNull
+ File cCompilerExecutable;
+
+ @NonNull
+ File cppCompilerExecutable;
+
+ public NativeToolchainImpl(@NonNull String name, @NonNull File cCompilerExecutable,
+ @NonNull File cppCompilerExecutable) {
+ this.name = name;
+ this.cCompilerExecutable = cCompilerExecutable;
+ this.cppCompilerExecutable = cppCompilerExecutable;
+ }
+
+ @NonNull
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @NonNull
+ @Override
+ public File getCCompilerExecutable() {
+ return cCompilerExecutable;
+ }
+
+ @NonNull
+ @Override
+ public File getCppCompilerExecutable() {
+ return cppCompilerExecutable;
+ }
+
+ @Override
+ public String toString() {
+ return "ToolchainImpl{" +
+ "name='" + name + '\'' +
+ ", cCompilerExecutable=" + cCompilerExecutable +
+ ", cppCompilerExecutable=" + cppCompilerExecutable +
+ '}';
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java
index c7da888..da1648c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/model/ProductFlavorImpl.java
@@ -22,9 +22,11 @@
import com.android.builder.model.ProductFlavor;
import com.android.builder.model.SigningConfig;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
import java.io.Serializable;
import java.util.Collection;
+import java.util.Map;
import java.util.Set;
/**
@@ -47,6 +49,7 @@
private String mApplicationId = null;
private String mTestApplicationId = null;
private String mTestInstrumentationRunner = null;
+ private Map<String, String> mTestInstrumentationRunnerArguments = Maps.newHashMap();
private Boolean mTestHandleProfiling = null;
private Boolean mTestFunctionalTest = null;
private Set<String> mResourceConfigurations = null;
@@ -86,6 +89,9 @@
clonedFlavor.mResourceConfigurations = ImmutableSet.copyOf(
productFlavor.getResourceConfigurations());
+ clonedFlavor.mTestInstrumentationRunnerArguments = Maps.newHashMap(
+ productFlavor.getTestInstrumentationRunnerArguments());
+
return clonedFlavor;
}
@@ -163,6 +169,12 @@
return mTestInstrumentationRunner;
}
+ @NonNull
+ @Override
+ public Map<String, String> getTestInstrumentationRunnerArguments() {
+ return mTestInstrumentationRunnerArguments;
+ }
+
@Nullable
@Override
public Boolean getTestHandleProfiling() {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleJavaProcessExecutor.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleJavaProcessExecutor.groovy
deleted file mode 100644
index 51c84b9..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleJavaProcessExecutor.groovy
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal.process
-import com.android.annotations.NonNull
-import com.android.ide.common.process.JavaProcessExecutor
-import com.android.ide.common.process.JavaProcessInfo
-import com.android.ide.common.process.ProcessOutput
-import com.android.ide.common.process.ProcessOutputHandler
-import com.android.ide.common.process.ProcessResult
-import groovy.transform.CompileStatic
-import org.gradle.api.Project
-import org.gradle.process.ExecResult
-import org.gradle.process.JavaExecSpec
-
-/**
- * Implementation of JavaProcessExecutor that uses Gradle's mechanism to execute external java
- * processes.
- */
-@CompileStatic
-public class GradleJavaProcessExecutor implements JavaProcessExecutor {
-
- @NonNull
- private final Project project;
-
- public GradleJavaProcessExecutor(@NonNull Project project) {
- this.project = project
- }
-
- @NonNull
- @Override
- public ProcessResult execute(
- @NonNull JavaProcessInfo javaProcessInfo,
- @NonNull ProcessOutputHandler processOutputHandler) {
- ProcessOutput output = processOutputHandler.createOutput()
-
- ExecResult result = project.javaexec(getJavaExecClosure(javaProcessInfo, output))
-
- processOutputHandler.handleOutput(output);
-
- return new GradleProcessResult(result)
- }
-
- @NonNull
- private static Closure getJavaExecClosure(
- @NonNull final JavaProcessInfo javaProcessInfo,
- @NonNull final ProcessOutput processOutput) {
- return { JavaExecSpec javaExecSpec ->
- javaExecSpec.classpath(new File(javaProcessInfo.getClasspath()))
- javaExecSpec.setMain(javaProcessInfo.getMainClass())
- javaExecSpec.args(javaProcessInfo.getArgs())
- javaExecSpec.jvmArgs(javaProcessInfo.getJvmArgs())
- javaExecSpec.environment(javaProcessInfo.getEnvironment())
- javaExecSpec.setStandardOutput(processOutput.getStandardOutput())
- javaExecSpec.setErrorOutput(processOutput.getErrorOutput())
-
- // we want the caller to be able to do its own thing.
- javaExecSpec.setIgnoreExitValue(true)
- }
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleJavaProcessExecutor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleJavaProcessExecutor.java
new file mode 100644
index 0000000..fff68d8
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleJavaProcessExecutor.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.process;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.process.JavaProcessExecutor;
+import com.android.ide.common.process.JavaProcessInfo;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessOutput;
+import com.android.ide.common.process.ProcessOutputHandler;
+import com.android.ide.common.process.ProcessResult;
+
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.process.ExecResult;
+import org.gradle.process.JavaExecSpec;
+
+import java.io.File;
+
+/**
+ * Implementation of JavaProcessExecutor that uses Gradle's mechanism to execute external java
+ * processes.
+ */
+public class GradleJavaProcessExecutor implements JavaProcessExecutor {
+
+ @NonNull
+ private final Project project;
+
+ public GradleJavaProcessExecutor(@NonNull Project project) {
+ this.project = project;
+ }
+
+ @NonNull
+ @Override
+ public ProcessResult execute(
+ @NonNull JavaProcessInfo javaProcessInfo,
+ @NonNull ProcessOutputHandler processOutputHandler) {
+ ProcessOutput output = processOutputHandler.createOutput();
+
+ ExecResult result = project.javaexec(new ExecAction(javaProcessInfo, output));
+
+ try {
+ processOutputHandler.handleOutput(output);
+ } catch (ProcessException e) {
+ return new OutputHandlerFailedGradleProcessResult(e);
+ }
+
+ return new GradleProcessResult(result);
+ }
+
+ private static class ExecAction implements Action<JavaExecSpec> {
+
+ @NonNull
+ private final JavaProcessInfo javaProcessInfo;
+
+ @NonNull
+ private final ProcessOutput processOutput;
+
+ private ExecAction(@NonNull JavaProcessInfo javaProcessInfo,
+ @NonNull ProcessOutput processOutput) {
+ this.javaProcessInfo = javaProcessInfo;
+ this.processOutput = processOutput;
+ }
+
+ @Override
+ public void execute(JavaExecSpec javaExecSpec) {
+ javaExecSpec.classpath(new File(javaProcessInfo.getClasspath()));
+ javaExecSpec.setMain(javaProcessInfo.getMainClass());
+ javaExecSpec.args(javaProcessInfo.getArgs());
+ javaExecSpec.jvmArgs(javaProcessInfo.getJvmArgs());
+ javaExecSpec.environment(javaProcessInfo.getEnvironment());
+ javaExecSpec.setStandardOutput(processOutput.getStandardOutput());
+ javaExecSpec.setErrorOutput(processOutput.getErrorOutput());
+
+ // we want the caller to be able to do its own thing.
+ javaExecSpec.setIgnoreExitValue(true);
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleProcessExecutor.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleProcessExecutor.groovy
deleted file mode 100644
index 0e2d2de..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleProcessExecutor.groovy
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal.process
-import com.android.annotations.NonNull
-import com.android.ide.common.process.ProcessExecutor
-import com.android.ide.common.process.ProcessInfo
-import com.android.ide.common.process.ProcessOutput
-import com.android.ide.common.process.ProcessOutputHandler
-import com.android.ide.common.process.ProcessResult
-import groovy.transform.CompileStatic
-import org.gradle.api.Project
-import org.gradle.process.ExecResult
-import org.gradle.process.ExecSpec
-
-/**
- * Implementation of ProcessExecutor that uses Gradle's mechanism to execute external processes.
- */
-@CompileStatic
-public class GradleProcessExecutor implements ProcessExecutor {
-
- @NonNull
- private final Project project;
-
- public GradleProcessExecutor(@NonNull Project project) {
- this.project = project
- }
-
- @NonNull
- @Override
- public ProcessResult execute(
- @NonNull ProcessInfo processInfo,
- @NonNull ProcessOutputHandler processOutputHandler) {
- ProcessOutput output = processOutputHandler.createOutput()
-
- ExecResult result = project.exec(getExecClosure(processInfo, output))
-
- processOutputHandler.handleOutput(output);
-
- return new GradleProcessResult(result)
- }
-
- @NonNull
- private static Closure getExecClosure(
- @NonNull final ProcessInfo processInfo,
- @NonNull final ProcessOutput processOutput) {
- return { ExecSpec execSpec ->
-
- execSpec.setExecutable(processInfo.executable)
- execSpec.args(processInfo.getArgs())
- execSpec.environment(processInfo.getEnvironment())
- execSpec.setStandardOutput(processOutput.getStandardOutput())
- execSpec.setErrorOutput(processOutput.getErrorOutput())
-
- // we want the caller to be able to do its own thing.
- execSpec.setIgnoreExitValue(true)
- }
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleProcessExecutor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleProcessExecutor.java
new file mode 100644
index 0000000..57642aa
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/GradleProcessExecutor.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.process;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessExecutor;
+import com.android.ide.common.process.ProcessInfo;
+import com.android.ide.common.process.ProcessOutput;
+import com.android.ide.common.process.ProcessOutputHandler;
+import com.android.ide.common.process.ProcessResult;
+
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.process.ExecResult;
+import org.gradle.process.ExecSpec;
+
+/**
+ * Implementation of ProcessExecutor that uses Gradle's mechanism to execute external processes.
+ */
+public class GradleProcessExecutor implements ProcessExecutor {
+
+ @NonNull
+ private final Project project;
+
+ public GradleProcessExecutor(@NonNull Project project) {
+ this.project = project;
+ }
+
+ @NonNull
+ @Override
+ public ProcessResult execute(
+ @NonNull ProcessInfo processInfo,
+ @NonNull ProcessOutputHandler processOutputHandler) {
+ ProcessOutput output = processOutputHandler.createOutput();
+
+ final ExecResult result = project.exec(new ExecAction(processInfo, output));
+
+ try {
+ processOutputHandler.handleOutput(output);
+ } catch (final ProcessException e) {
+ return new OutputHandlerFailedGradleProcessResult(e);
+ }
+
+ return new GradleProcessResult(result);
+ }
+
+ private static class ExecAction implements Action<ExecSpec> {
+
+ @NonNull
+ private final ProcessInfo processInfo;
+
+ @NonNull
+ private final ProcessOutput processOutput;
+
+ ExecAction(@NonNull final ProcessInfo processInfo,
+ @NonNull final ProcessOutput processOutput) {
+ this.processInfo = processInfo;
+ this.processOutput = processOutput;
+ }
+
+ @Override
+ public void execute(ExecSpec execSpec) {
+ execSpec.setExecutable(processInfo.getExecutable());
+ execSpec.args(processInfo.getArgs());
+ execSpec.environment(processInfo.getEnvironment());
+ execSpec.setStandardOutput(processOutput.getStandardOutput());
+ execSpec.setErrorOutput(processOutput.getErrorOutput());
+
+ // we want the caller to be able to do its own thing.
+ execSpec.setIgnoreExitValue(true);
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/OutputHandlerFailedGradleProcessResult.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/OutputHandlerFailedGradleProcessResult.java
new file mode 100644
index 0000000..575402c
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/process/OutputHandlerFailedGradleProcessResult.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.process;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessResult;
+
+public class OutputHandlerFailedGradleProcessResult implements ProcessResult {
+ @NonNull
+ private final ProcessException failure;
+
+ OutputHandlerFailedGradleProcessResult(@NonNull ProcessException failure) {
+ this.failure = failure;
+ }
+
+ @Override
+ public ProcessResult assertNormalExitValue() throws ProcessException {
+ throw failure;
+ }
+
+ @Override
+ public int getExitValue() {
+ return -1;
+ }
+
+ @Override
+ public ProcessResult rethrowFailure() throws ProcessException {
+ throw failure;
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/RecordingBuildListener.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/RecordingBuildListener.groovy
deleted file mode 100644
index 0e86e12..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/RecordingBuildListener.groovy
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal.profile
-
-import com.android.builder.profile.ExecutionRecord
-import com.android.builder.profile.ExecutionType
-import com.android.builder.profile.Recorder
-import com.google.common.base.CaseFormat
-import org.gradle.api.Task
-import org.gradle.api.execution.TaskExecutionListener
-import org.gradle.api.tasks.TaskState
-
-import java.util.concurrent.ConcurrentHashMap
-
-/**
- * Implementation of the {@link TaskExecutionListener} that records the execution span of
- * tasks execution and records such spans using the {@link Recorder} facilities.
- */
-class RecordingBuildListener implements TaskExecutionListener {
-
- private final class TaskRecord {
-
- private final long recordId;
- private final long startTime;
-
- TaskRecord(long recordId, long startTime) {
- this.startTime = startTime
- this.recordId = recordId
- }
- }
-
- private final Recorder mRecorder;
-
- RecordingBuildListener(Recorder recorder) {
- mRecorder = recorder
- }
-
- // map of outstanding tasks executing, keyed by their name.
- final Map<String, TaskRecord> taskRecords = new ConcurrentHashMap<>();
-
- @Override
- void beforeExecute(Task task) {
- taskRecords.put(task.getName(), new TaskRecord(
- mRecorder.allocationRecordId(), System.currentTimeMillis()))
- }
-
- @Override
- void afterExecute(Task task, TaskState taskState) {
-
- // find the right ExecutionType.
- String taskImpl = task.getClass().getSimpleName();
- if (taskImpl.endsWith("_Decorated")) {
- taskImpl = taskImpl.substring(0, taskImpl.length() - "_Decorated".length());
- }
- String potentialExecutionTypeName = "TASK_" +
- CaseFormat.LOWER_CAMEL.converterTo(CaseFormat.UPPER_UNDERSCORE).
- convert(taskImpl);
- ExecutionType executionType;
- try {
- executionType = ExecutionType.valueOf(potentialExecutionTypeName)
- } catch (IllegalArgumentException ignored) {
- executionType = ExecutionType.GENERIC_TASK_EXECUTION
- }
-
- List< Recorder.Property> properties = new ArrayList<>();
- properties.add(new Recorder.Property("project", task.getProject().getName()));
- properties.add(new Recorder.Property("task", task.getName()));
- TaskRecord taskRecord = taskRecords.get(task.getName());
- mRecorder.closeRecord(new ExecutionRecord(
- taskRecord.recordId,
- 0 /* parentId */,
- taskRecord.startTime,
- System.currentTimeMillis() - taskRecord.startTime,
- executionType,
- properties))
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/RecordingBuildListener.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/RecordingBuildListener.java
new file mode 100644
index 0000000..4195665
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/RecordingBuildListener.java
@@ -0,0 +1,108 @@
+
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.profile;
+
+import com.android.build.gradle.internal.tasks.DefaultAndroidTask;
+import com.android.builder.profile.ExecutionRecord;
+import com.android.builder.profile.ExecutionType;
+import com.android.builder.profile.Recorder;
+import com.google.common.base.CaseFormat;
+import org.gradle.api.Task;
+import org.gradle.api.execution.TaskExecutionListener;
+import org.gradle.api.tasks.TaskState;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Implementation of the {@link TaskExecutionListener} that records the execution span of
+ * tasks execution and records such spans using the {@link Recorder} facilities.
+ */
+public class RecordingBuildListener implements TaskExecutionListener {
+
+ private static final class TaskRecord {
+
+ private final long recordId;
+ private final long startTime;
+
+ TaskRecord(long recordId, long startTime) {
+ this.startTime = startTime;
+ this.recordId = recordId;
+ }
+ }
+
+ private final Recorder mRecorder;
+
+ public RecordingBuildListener(Recorder recorder) {
+ mRecorder = recorder;
+ }
+
+ // map of outstanding tasks executing, keyed by their name.
+ final Map<String, TaskRecord> taskRecords = new ConcurrentHashMap<String, TaskRecord>();
+
+ @Override
+ public void beforeExecute(Task task) {
+ taskRecords.put(task.getName(), new TaskRecord(
+ mRecorder.allocationRecordId(), System.currentTimeMillis()));
+ }
+
+ @Override
+ public void afterExecute(Task task, TaskState taskState) {
+
+ // find the right ExecutionType.
+ String taskImpl = task.getClass().getSimpleName();
+ if (taskImpl.endsWith("_Decorated")) {
+ taskImpl = taskImpl.substring(0, taskImpl.length() - "_Decorated".length());
+ }
+ String potentialExecutionTypeName = "TASK_" +
+ CaseFormat.LOWER_CAMEL.converterTo(CaseFormat.UPPER_UNDERSCORE).
+ convert(taskImpl);
+ ExecutionType executionType;
+ try {
+ executionType = ExecutionType.valueOf(potentialExecutionTypeName);
+ } catch (IllegalArgumentException ignored) {
+ executionType = ExecutionType.GENERIC_TASK_EXECUTION;
+ }
+
+ List<Recorder.Property> properties = new ArrayList<Recorder.Property>();
+ properties.add(new Recorder.Property("project", task.getProject().getName()));
+ properties.add(new Recorder.Property("task", task.getName()));
+
+ if (task instanceof DefaultAndroidTask) {
+ String variantName = ((DefaultAndroidTask) task).getVariantName();
+ if (variantName == null) {
+ throw new IllegalStateException("Task with type " + task.getClass().getName() +
+ " does not include a variantName");
+ }
+ if (!variantName.isEmpty()) {
+ properties.add(new Recorder.Property("variant", variantName));
+ }
+ }
+
+ TaskRecord taskRecord = taskRecords.get(task.getName());
+ mRecorder.closeRecord(new ExecutionRecord(
+ taskRecord.recordId,
+ 0 /* parentId */,
+ taskRecord.startTime,
+ System.currentTimeMillis() - taskRecord.startTime,
+ executionType,
+ properties));
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/SpanRecorders.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/SpanRecorders.groovy
deleted file mode 100644
index 497b12f..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/SpanRecorders.groovy
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal.profile
-
-import com.android.annotations.NonNull
-import com.android.builder.profile.ExecutionType
-import com.android.builder.profile.Recorder
-import com.android.builder.profile.ThreadRecorder
-import org.gradle.api.Project
-
-/**
- * Groovy language helper to record execution spans.
- */
-class SpanRecorders {
-
- public final static String PROJECT = "project";
- public final static String VARIANT = "variant";
-
- static <T> T record(@NonNull ExecutionType executionType, @NonNull Closure<T> closure) {
- // have to explicitly cast as groovy does not support inner classes with generics...
- return (T) ThreadRecorder.get().record(executionType, new Recorder.Block() {
-
- @Override
- Object call() throws Exception {
- return closure.call()
- }
- })
- }
-
- static <T> T record(@NonNull Project project,
- @NonNull ExecutionType executionType,
- @NonNull Closure<T> closure) {
- // have to explicitly cast as groovy does not support inner classes with generics...
- return (T) ThreadRecorder.get().record(executionType, new Recorder.Block() {
-
- @Override
- Object call() throws Exception {
- return closure.call()
- }
- }, new Recorder.Property(PROJECT, project.getName()))
- }
-
- /**
- * Records an execution span, using a Java {@link Recorder.Block}
- */
- static <T> T record(@NonNull Project project,
- @NonNull ExecutionType executionType,
- @NonNull Recorder.Block<T> block,
- Recorder.Property... properties) {
- List<Recorder.Property> mergedProperties = new ArrayList<>(properties.length + 1);
- mergedProperties.addAll(properties);
- mergedProperties.add(new Recorder.Property(PROJECT, project.getName()))
- return (T) ThreadRecorder.get().record(
- executionType, block,
- mergedProperties.toArray(new Recorder.Property[mergedProperties.size()]))
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/SpanRecorders.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/SpanRecorders.java
new file mode 100644
index 0000000..a319ede
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/profile/SpanRecorders.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.profile;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.concurrency.Immutable;
+import com.android.builder.profile.ExecutionType;
+import com.android.builder.profile.Recorder;
+import com.android.builder.profile.ThreadRecorder;
+import com.google.common.collect.ImmutableList;
+
+import org.gradle.api.Project;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Helper to record execution spans.
+ */
+public class SpanRecorders {
+
+ public static final String PROJECT = "project";
+ public static final String VARIANT = "variant";
+
+
+ /**
+ * Records an execution span, using a Java {@link Recorder.Block}
+ */
+ public static <T> T record(@NonNull Project project,
+ @NonNull ExecutionType executionType,
+ @NonNull Recorder.Block<T> block,
+ Recorder.Property... properties) {
+ Recorder.Property[] mergedProperties = new Recorder.Property[properties.length + 1];
+ mergedProperties[0] = new Recorder.Property(PROJECT, project.getName());
+ System.arraycopy(properties, 0, mergedProperties, 1, properties.length);
+ return (T) ThreadRecorder.get().record(executionType, block, mergedProperties);
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/AndroidTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/AndroidTask.java
index 008de57..db175a6 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/AndroidTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/AndroidTask.java
@@ -28,7 +28,7 @@
/**
* Handle for a {@link Task} that may not yet been created.
- * For tasks created using CollectionBuilder<Task>, they are usually not actually created until
+ * For tasks created using ModelMap<Task>, they are usually not actually created until
* Gradle decides those tasks needs to be executed. This class contains information about those
* tasks and allow dependencies to be specified.
*/
@@ -37,8 +37,6 @@
private String name;
@NonNull
private final Class<T> taskType;
- @Nullable
- private T task;
@NonNull
private final List<AndroidTask<? extends Task>> upstreamTasks;
@NonNull
@@ -154,11 +152,22 @@
}
/**
- * Add a configration action for this task.
+ * Add a configuration action for this task.
* @param taskFactory TaskFactory used to configure the task.
* @param configAction An Action to be executed.
*/
public void configure(TaskFactory taskFactory, Action<? super Task> configAction) {
taskFactory.named(name, configAction);
}
+
+ /**
+ * Potentially instantiates and return the task. Should only be called once the task is
+ * configured.
+ * @param taskFactory the factory for tasks
+ * @return the task instance.
+ */
+ @SuppressWarnings("unchecked")
+ public T get(TaskFactory taskFactory) {
+ return (T) taskFactory.named(name);
+ }
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/GlobalScope.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/GlobalScope.java
index d0b001a..9e5ac12 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/GlobalScope.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/GlobalScope.java
@@ -16,16 +16,17 @@
package com.android.build.gradle.internal.scope;
+import static com.android.builder.core.BuilderConstants.FD_REPORTS;
import static com.android.builder.model.AndroidProject.FD_GENERATED;
import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
-import static com.android.builder.model.AndroidProject.PROPERTY_APK_LOCATION;
import com.android.annotations.NonNull;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.AndroidGradleOptions;
import com.android.build.gradle.internal.SdkHandler;
import com.android.builder.core.AndroidBuilder;
-import com.android.builder.model.AndroidProject;
+import com.google.common.base.Objects;
import org.gradle.api.Project;
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
@@ -43,7 +44,7 @@
@NonNull
private String projectBaseName;
@NonNull
- private BaseExtension extension;
+ private AndroidConfig extension;
@NonNull
private SdkHandler sdkHandler;
@NonNull
@@ -53,12 +54,16 @@
private final File intermediatesDir;
@NonNull
private final File generatedDir;
+ @NonNull
+ private final File reportsDir;
+ @NonNull
+ private final File outputsDir;
public GlobalScope(
@NonNull Project project,
@NonNull AndroidBuilder androidBuilder,
@NonNull String projectBaseName,
- @NonNull BaseExtension extension,
+ @NonNull AndroidConfig extension,
@NonNull SdkHandler sdkHandler,
@NonNull ToolingModelBuilderRegistry toolingRegistry) {
this.project = project;
@@ -69,6 +74,8 @@
this.toolingRegistry = toolingRegistry;
intermediatesDir = new File(getBuildDir(), FD_INTERMEDIATES);
generatedDir = new File(getBuildDir(), FD_GENERATED);
+ reportsDir = new File(getBuildDir(), FD_REPORTS);
+ outputsDir = new File(getBuildDir(), FD_OUTPUTS);
}
@NonNull
@@ -77,7 +84,7 @@
}
@NonNull
- public BaseExtension getExtension() {
+ public AndroidConfig getExtension() {
return extension;
}
@@ -117,16 +124,24 @@
}
@NonNull
+ public File getReportsDir() {
+ return reportsDir;
+ }
+
+ @NonNull
+ public File getOutputsDir() {
+ return outputsDir;
+ }
+
+ @NonNull
public String getDefaultApkLocation() {
return getBuildDir() + "/" + FD_OUTPUTS + "/apk";
}
@NonNull
public String getApkLocation() {
- String apkLocation = getDefaultApkLocation();
- if (project.hasProperty(PROPERTY_APK_LOCATION)) {
- apkLocation = (String) project.getProperties().get(PROPERTY_APK_LOCATION);
- }
- return apkLocation;
+ return Objects.firstNonNull(
+ AndroidGradleOptions.getApkLocation(project),
+ getDefaultApkLocation());
}
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantOutputScope.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantOutputScope.java
index 8534920..dd897be 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantOutputScope.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantOutputScope.java
@@ -16,30 +16,17 @@
package com.android.build.gradle.internal.scope;
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
-
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.build.gradle.internal.TaskFactory;
import com.android.build.gradle.internal.TaskManager;
import com.android.build.gradle.internal.variant.ApkVariantData;
import com.android.build.gradle.internal.variant.BaseVariantOutputData;
import com.android.build.gradle.tasks.CompatibleScreensManifest;
import com.android.build.gradle.tasks.ManifestProcessorTask;
-import com.android.build.gradle.tasks.PackageApplication;
-import com.android.build.gradle.tasks.PackageSplitAbi;
-import com.android.build.gradle.tasks.PackageSplitRes;
import com.android.build.gradle.tasks.ProcessAndroidResources;
-import com.android.build.gradle.tasks.ZipAlign;
-import com.android.builder.model.AndroidProject;
import com.android.utils.StringHelper;
-import com.android.xml.AndroidManifest;
-import com.google.common.collect.ImmutableMap;
-
-import org.gradle.api.Task;
import java.io.File;
-import java.util.Map;
/**
* A scope containing data for a specific variant.
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantScope.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantScope.java
index 05d00df..5bcc4f8 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantScope.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/scope/VariantScope.java
@@ -16,15 +16,18 @@
package com.android.build.gradle.internal.scope;
+import static com.android.build.gradle.internal.TaskManager.DIR_BUNDLES;
import static com.android.builder.model.AndroidProject.FD_GENERATED;
+import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.core.Abi;
import com.android.build.gradle.internal.core.GradleVariantConfiguration;
import com.android.build.gradle.internal.coverage.JacocoInstrumentTask;
import com.android.build.gradle.internal.tasks.CheckManifest;
import com.android.build.gradle.internal.tasks.FileSupplier;
+import com.android.build.gradle.internal.tasks.MergeJavaResourcesTask;
import com.android.build.gradle.internal.tasks.PrepareDependenciesTask;
import com.android.build.gradle.internal.variant.ApkVariantData;
import com.android.build.gradle.internal.variant.BaseVariantData;
@@ -36,22 +39,27 @@
import com.android.build.gradle.tasks.Dex;
import com.android.build.gradle.tasks.GenerateBuildConfig;
import com.android.build.gradle.tasks.GenerateResValues;
+import com.android.build.gradle.tasks.JackTask;
+import com.android.build.gradle.tasks.JavaResourcesProvider;
import com.android.build.gradle.tasks.MergeAssets;
import com.android.build.gradle.tasks.MergeResources;
import com.android.build.gradle.tasks.NdkCompile;
-import com.android.build.gradle.tasks.PreprocessResourcesTask;
import com.android.build.gradle.tasks.ProcessAndroidResources;
import com.android.build.gradle.tasks.RenderscriptCompile;
import com.android.builder.core.VariantConfiguration;
import com.android.builder.core.VariantType;
+import com.android.builder.signing.SignedJarBuilder;
+import com.android.utils.FileUtils;
import com.android.utils.StringHelper;
+import com.google.common.base.Objects;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.gradle.api.Task;
import org.gradle.api.file.FileCollection;
-import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.Sync;
import org.gradle.api.tasks.compile.AbstractCompile;
+import org.gradle.api.tasks.compile.JavaCompile;
import java.io.File;
import java.util.Collection;
@@ -71,7 +79,11 @@
@Nullable
private Collection<Object> ndkBuildable;
@Nullable
- private Collection<File> ndkOutputDirectories;
+ private Collection<File> ndkSoFolder;
+ @Nullable
+ private File ndkObjFolder;
+ @NonNull
+ private Map<Abi, File> ndkDebuggableLibraryFolders = Maps.newHashMap();
@Nullable
private File mergeResourceOutputDir;
@@ -95,23 +107,24 @@
private AndroidTask<GenerateBuildConfig> generateBuildConfigTask;
private AndroidTask<GenerateResValues> generateResValuesTask;
- /**
- * Anchor task for post-processing the merged resources to backport some features to earlier
- * API versions, e.g. generate PNGs from vector drawables (vector drawables were added in 21).
- */
@Nullable
private AndroidTask<Dex> dexTask;
@Nullable
private AndroidTask jacocoIntrumentTask;
- @Nullable
- private AndroidTask<PreprocessResourcesTask> preprocessResourcesTask;
- private AndroidTask<Copy> processJavaResourcesTask;
+ private AndroidTask<Sync> processJavaResourcesTask;
+ private AndroidTask<MergeJavaResourcesTask> mergeJavaResourcesTask;
+ private JavaResourcesProvider javaResourcesProvider;
private AndroidTask<NdkCompile> ndkCompileTask;
- // can be JavaCompile or JackTask depending on user's settings.
+ /** @see BaseVariantData#javaCompilerTask */
@Nullable
- private AndroidTask<? extends AbstractCompile> javaCompileTask;
+ private AndroidTask<? extends AbstractCompile> javaCompilerTask;
+ @Nullable
+ private AndroidTask<JavaCompile> javacTask;
+ @Nullable
+ private AndroidTask<JackTask> jackTask;
+
// empty anchor compile task to set all compilations tasks as dependents.
private AndroidTask<Task> compileTask;
private AndroidTask<JacocoInstrumentTask> jacocoInstrumentTask;
@@ -122,6 +135,8 @@
// TODO : why is Jack not registered as the obfuscationTask ???
private AndroidTask<? extends Task> obfuscationTask;
+ private File resourceOutputDir;
+
public VariantScope(
@NonNull GlobalScope globalScope,
@@ -165,23 +180,44 @@
}
@Nullable
- public Collection<File> getNdkOutputDirectories() {
- return ndkOutputDirectories;
+ public Collection<File> getNdkSoFolder() {
+ return ndkSoFolder;
}
- public void setNdkOutputDirectories(@NonNull Collection<File> ndkOutputDirectories) {
- this.ndkOutputDirectories = ndkOutputDirectories;
+ public void setNdkSoFolder(@NonNull Collection<File> ndkSoFolder) {
+ this.ndkSoFolder = ndkSoFolder;
+ }
+
+ @Nullable
+ public File getNdkObjFolder() {
+ return ndkObjFolder;
+ }
+
+ public void setNdkObjFolder(@NonNull File ndkObjFolder) {
+ this.ndkObjFolder = ndkObjFolder;
+ }
+
+ /**
+ * Return the folder containing the shared object with debugging symbol for the specified ABI.
+ */
+ @Nullable
+ public File getNdkDebuggableLibraryFolders(@NonNull Abi abi) {
+ return ndkDebuggableLibraryFolders.get(abi);
+ }
+
+ public void addNdkDebuggableLibraryFolders(@NonNull Abi abi, @NonNull File searchPath) {
+ this.ndkDebuggableLibraryFolders.put(abi, searchPath);
}
@NonNull
public Set<File> getJniFolders() {
- assert getNdkOutputDirectories() != null;
+ assert getNdkSoFolder() != null;
VariantConfiguration config = getVariantConfiguration();
ApkVariantData apkVariantData = (ApkVariantData) variantData;
// for now only the project's compilation output.
Set<File> set = Sets.newHashSet();
- set.addAll(getNdkOutputDirectories());
+ set.addAll(getNdkSoFolder());
set.add(getRenderscriptLibOutputDir());
set.addAll(config.getLibraryJniFolders());
set.addAll(config.getJniLibsList());
@@ -240,7 +276,7 @@
public File getProguardOutputFile() {
return (variantData instanceof LibraryVariantData) ?
new File(globalScope.getIntermediatesDir(),
- TaskManager.DIR_BUNDLES + "/" + getVariantConfiguration().getDirName()
+ DIR_BUNDLES + "/" + getVariantConfiguration().getDirName()
+ "/classes.jar") :
new File(globalScope.getIntermediatesDir(),
"/classes-proguard/" + getVariantConfiguration().getDirName()
@@ -290,6 +326,15 @@
}
@NonNull
+ public File getFinalResourcesDir() {
+ return Objects.firstNonNull(resourceOutputDir, getDefaultMergeResourcesOutputDir());
+ }
+
+ public void setResourceOutputDir(@NonNull File resourceOutputDir) {
+ this.resourceOutputDir = resourceOutputDir;
+ }
+
+ @NonNull
public File getDefaultMergeResourcesOutputDir() {
return new File(globalScope.getIntermediatesDir(),
"/res/merged/" + getVariantConfiguration().getDirName());
@@ -311,7 +356,7 @@
public File getMergeAssetsOutputDir() {
return getVariantConfiguration().getType() == VariantType.LIBRARY ?
new File(globalScope.getIntermediatesDir(),
- TaskManager.DIR_BUNDLES + "/" + getVariantConfiguration().getDirName() +
+ DIR_BUNDLES + "/" + getVariantConfiguration().getDirName() +
"/assets") :
new File(globalScope.getIntermediatesDir(),
"/assets/" + getVariantConfiguration().getDirName());
@@ -324,15 +369,39 @@
}
@NonNull
+ public File getGeneratedResourcesDir(String name) {
+ return FileUtils.join(
+ globalScope.getGeneratedDir(),
+ "res",
+ name,
+ getVariantConfiguration().getDirName());
+ }
+
+ @NonNull
public File getGeneratedResOutputDir() {
- return new File(globalScope.getGeneratedDir(),
- "res/resValues/" + getVariantConfiguration().getDirName());
+ return getGeneratedResourcesDir("resValues");
+ }
+
+ @NonNull
+ public File getGeneratedPngsOutputDir() {
+ return getGeneratedResourcesDir("pngs");
}
@NonNull
public File getRenderscriptResOutputDir() {
- return new File(globalScope.getGeneratedDir(),
- "res/rs/" + getVariantConfiguration().getDirName());
+ return getGeneratedResourcesDir("rs");
+ }
+
+ @NonNull
+ public File getPackagedJarsJavaResDestinationDir() {
+ return new File(globalScope.getIntermediatesDir(),
+ "packagedJarsJavaResources/" + getVariantConfiguration().getDirName());
+ }
+
+ @NonNull
+ public File getSourceFoldersJavaResDestinationDir() {
+ return new File(globalScope.getIntermediatesDir(),
+ "sourceFolderJavaResources/" + getVariantConfiguration().getDirName());
}
@NonNull
@@ -353,6 +422,76 @@
"source/aidl/" + getVariantConfiguration().getDirName());
}
+ @NonNull
+ public File getAidlIncrementalDir() {
+ return new File(globalScope.getIntermediatesDir(),
+ "incremental/aidl/" + getVariantConfiguration().getDirName());
+ }
+
+ @NonNull
+ public File getAidlParcelableDir() {
+ return new File(globalScope.getIntermediatesDir(),
+ DIR_BUNDLES + "/" + getVariantConfiguration().getDirName() + "/aidl");
+ }
+
+ /**
+ * Returns the location of an intermediate directory that can be used by the Jack toolchain
+ * to store states necessary to support incremental compilation.
+ * @return a variant specific directory.
+ */
+ @NonNull
+ public File getJackIncrementalDir() {
+ return new File(globalScope.getIntermediatesDir(),
+ "incremental/jack/" + getVariantConfiguration().getDirName());
+ }
+
+ @NonNull
+ public File getJackTempDir() {
+ return new File(globalScope.getIntermediatesDir(),
+ "tmp/jack/" + getVariantConfiguration().getDirName());
+ }
+
+ @NonNull
+ public File getJillPackagedLibrariesDir() {
+ return new File(globalScope.getIntermediatesDir(),
+ "jill/" + getVariantConfiguration().getDirName() + "/packaged");
+ }
+
+ @NonNull
+ public File getJillRuntimeLibrariesDir() {
+ return new File(globalScope.getIntermediatesDir(),
+ "jill/" + getVariantConfiguration().getDirName() + "/runtime");
+ }
+
+ @NonNull
+ public File getJackDestinationDir() {
+ return new File(globalScope.getIntermediatesDir(),
+ "dex/" + getVariantConfiguration().getDirName());
+ }
+
+ @NonNull
+ public File getJackClassesZip() {
+ return new File(globalScope.getIntermediatesDir(),
+ "packaged/" + getVariantConfiguration().getDirName() + "/classes.zip");
+ }
+
+ @NonNull
+ public File getProguardOutputFolder() {
+ return new File(globalScope.getBuildDir(), "/" + FD_OUTPUTS + "/mapping/" +
+ getVariantConfiguration().getDirName());
+ }
+
+ @NonNull
+ public File getProcessAndroidResourcesProguardOutputFile() {
+ return new File(globalScope.getIntermediatesDir(),
+ "/proguard-rules/" + getVariantConfiguration().getDirName() + "/aapt_rules.txt");
+ }
+
+ public File getMappingFile() {
+ return new File(globalScope.getOutputsDir(),
+ "/mapping/" + getVariantConfiguration().getDirName() + "/mapping.txt");
+ }
+
// Tasks getters/setters.
public AndroidTask<Task> getPreBuildTask() {
@@ -483,33 +622,85 @@
this.dexTask = dexTask;
}
- @Nullable
- public AndroidTask<PreprocessResourcesTask> getPreprocessResourcesTask() {
- return preprocessResourcesTask;
- }
-
- public void setPreprocessResourcesTask(
- @Nullable AndroidTask<PreprocessResourcesTask> preprocessResourcesTask) {
- this.preprocessResourcesTask = preprocessResourcesTask;
- }
-
- public AndroidTask<Copy> getProcessJavaResourcesTask() {
+ public AndroidTask<Sync> getProcessJavaResourcesTask() {
return processJavaResourcesTask;
}
public void setProcessJavaResourcesTask(
- AndroidTask<Copy> processJavaResourcesTask) {
+ AndroidTask<Sync> processJavaResourcesTask) {
this.processJavaResourcesTask = processJavaResourcesTask;
}
- @Nullable
- public AndroidTask<? extends AbstractCompile> getJavaCompileTask() {
- return javaCompileTask;
+ SignedJarBuilder.IZipEntryFilter packagingOptionsFilter;
+
+ public void setPackagingOptionsFilter(SignedJarBuilder.IZipEntryFilter filter) {
+ this.packagingOptionsFilter = filter;
}
- public void setJavaCompileTask(
- @Nullable AndroidTask<? extends AbstractCompile> javaCompileTask) {
- this.javaCompileTask = javaCompileTask;
+ /**
+ * Returns the {@link SignedJarBuilder.IZipEntryFilter} instance
+ * that manages all resources inclusion in the final APK following the rules defined in
+ * {@link com.android.builder.model.PackagingOptions} settings.
+ */
+ public SignedJarBuilder.IZipEntryFilter getPackagingOptionsFilter() {
+ return packagingOptionsFilter;
+ }
+
+ public void setMergeJavaResourcesTask(AndroidTask<MergeJavaResourcesTask> mergeJavaResourcesTask) {
+ this.mergeJavaResourcesTask = mergeJavaResourcesTask;
+ }
+
+ /**
+ * Returns the task extracting java resources from libraries and merging those with java
+ * resources coming from the variant's source folders.
+ * @return the task merging resources.
+ */
+ public AndroidTask<MergeJavaResourcesTask> getMergeJavaResourcesTask() {
+ return mergeJavaResourcesTask;
+ }
+
+ public void setJavaResourcesProvider(JavaResourcesProvider javaResourcesProvider) {
+ this.javaResourcesProvider = javaResourcesProvider;
+ }
+
+ /**
+ * Returns the {@link JavaResourcesProvider} responsible for providing final merged and possibly
+ * obfuscated java resources for inclusion in the final APK. The provider might change during
+ * the variant build process.
+ * @return the java resources provider.
+ */
+ public JavaResourcesProvider getJavaResourcesProvider() {
+ return javaResourcesProvider;
+ }
+
+ @Nullable
+ public AndroidTask<? extends AbstractCompile> getJavaCompilerTask() {
+ return javaCompilerTask;
+ }
+
+ @Nullable
+ public AndroidTask<JackTask> getJackTask() {
+ return jackTask;
+ }
+
+ public void setJackTask(
+ @Nullable AndroidTask<JackTask> jackTask) {
+ this.jackTask = jackTask;
+ }
+
+ @Nullable
+ public AndroidTask<JavaCompile> getJavacTask() {
+ return javacTask;
+ }
+
+ public void setJavacTask(
+ @Nullable AndroidTask<JavaCompile> javacTask) {
+ this.javacTask = javacTask;
+ }
+
+ public void setJavaCompilerTask(
+ @NonNull AndroidTask<? extends AbstractCompile> javaCompileTask) {
+ this.javaCompilerTask = javaCompileTask;
}
public AndroidTask<Task> getCompileTask() {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/AndroidReportTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/AndroidReportTask.java
index 718561e..38bb68c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/AndroidReportTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/AndroidReportTask.java
@@ -21,6 +21,7 @@
import com.android.annotations.NonNull;
import com.android.build.gradle.internal.test.report.ReportType;
import com.android.build.gradle.internal.test.report.TestReport;
+import com.android.utils.FileUtils;
import com.google.common.collect.Lists;
import org.gradle.api.GradleException;
@@ -126,8 +127,8 @@
File reportOutDir = getReportsDir();
// empty the folders
- emptyFolder(resultsOutDir);
- emptyFolder(reportOutDir);
+ FileUtils.emptyFolder(resultsOutDir);
+ FileUtils.emptyFolder(reportOutDir);
// do the copy.
copyResults(resultsOutDir);
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/BaseTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/BaseTask.java
index 494753f..b64ddd4 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/BaseTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/BaseTask.java
@@ -18,9 +18,11 @@
import com.android.annotations.Nullable;
import com.android.build.gradle.internal.LoggerWrapper;
import com.android.builder.core.AndroidBuilder;
+import com.android.builder.sdk.TargetInfo;
import com.android.sdklib.BuildToolInfo;
import com.android.utils.FileUtils;
import com.android.utils.ILogger;
+import com.google.common.base.Preconditions;
import java.io.File;
@@ -32,8 +34,14 @@
@Nullable
private ILogger iLogger;
- @Nullable
+ /**
+ * Returns the androidBuilder.
+ * @throws IllegalStateException if androidBuilder has not been set,
+ */
+ @NonNull
protected AndroidBuilder getBuilder() {
+ Preconditions.checkState(androidBuilder != null,
+ "androidBuilder required for task '%s'.", getName());
return androidBuilder;
}
@@ -45,13 +53,16 @@
return iLogger;
}
- protected void emptyFolder(File folder) {
- getLogger().info("deleteDir(" + folder + ") returned: " + FileUtils.deleteFolder(folder));
- folder.mkdirs();
- }
-
+ /**
+ * Returns the BuildToolInfo.
+ * @throws IllegalStateException if androidBuilder.targetInfo has not been set,
+ */
+ @NonNull
protected BuildToolInfo getBuildTools() {
- return androidBuilder.getTargetInfo().getBuildTools();
+ TargetInfo targetInfo = getBuilder().getTargetInfo();
+ Preconditions.checkState(targetInfo != null,
+ "androidBuilder.targetInfo required for task '%s'.", getName());
+ return targetInfo.getBuildTools();
}
public void setAndroidBuilder(@NonNull AndroidBuilder androidBuilder) {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DefaultAndroidTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DefaultAndroidTask.java
index e08a73b..49c9953 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DefaultAndroidTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DefaultAndroidTask.java
@@ -16,12 +16,26 @@
package com.android.build.gradle.internal.tasks;
+import com.android.annotations.Nullable;
import com.android.builder.Version;
import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.Input;
public class DefaultAndroidTask extends DefaultTask {
+
+ @Nullable
+ private String variantName;
+
+ @Nullable
+ public String getVariantName() {
+ return variantName;
+ }
+
+ public void setVariantName(@Nullable String variantName) {
+ this.variantName = variantName;
+ }
+
/**
* Force tasks to be re-run if the Android plugin version changes.
* @return the plugin version, of the form "x.y.z"
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DependencyReportTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DependencyReportTask.java
index 68fec17..efcf048 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DependencyReportTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DependencyReportTask.java
@@ -20,6 +20,7 @@
import com.android.build.gradle.internal.AndroidAsciiReportRenderer;
import com.android.build.gradle.internal.variant.BaseVariantData;
+import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.TaskAction;
import org.gradle.logging.StyledTextOutputFactory;
@@ -31,7 +32,7 @@
import java.util.SortedSet;
import java.util.TreeSet;
-public class DependencyReportTask extends DefaultAndroidTask {
+public class DependencyReportTask extends DefaultTask {
private AndroidAsciiReportRenderer renderer = new AndroidAsciiReportRenderer();
@@ -70,7 +71,7 @@
*
* @param variants the variants. Must not be null.
*/
- public void setVariants(@NonNull Collection<BaseVariantData> variants) {
+ public void setVariants(@NonNull Collection<? extends BaseVariantData> variants) {
this.variants.addAll(variants);
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestLibraryTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestLibraryTask.groovy
deleted file mode 100644
index 44498c4..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestLibraryTask.groovy
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal.tasks
-
-/**
- * class to test library project. Exactly the same as DeviceProviderInstrumentTestTask but
- * is needed to be gathered by the reporting plugin.
- */
-class DeviceProviderInstrumentTestLibraryTask extends DeviceProviderInstrumentTestTask {
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestTask.groovy
deleted file mode 100644
index a7bab3a..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestTask.groovy
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.internal.tasks
-
-import com.android.build.gradle.internal.test.report.ReportType
-import com.android.build.gradle.internal.test.report.TestReport
-import com.android.builder.internal.testing.SimpleTestCallable
-import com.android.builder.testing.SimpleTestRunner
-import com.android.builder.testing.TestData
-import com.android.builder.testing.TestRunner
-import com.android.builder.testing.api.DeviceProvider
-import com.android.ide.common.process.ProcessExecutor
-import com.google.common.collect.ImmutableList
-import org.gradle.api.GradleException
-import org.gradle.api.Nullable
-import org.gradle.api.tasks.TaskAction
-import org.gradle.logging.ConsoleRenderer
-/**
- * Run instrumentation tests for a given variant
- */
-public class DeviceProviderInstrumentTestTask extends BaseTask implements AndroidTestTask {
-
- File reportsDir
- File resultsDir
- File coverageDir
-
- String flavorName
-
- @Nullable
- Collection<String> installOptions;
-
- DeviceProvider deviceProvider
- TestData testData;
-
- File adbExec;
- @Nullable
- File splitSelectExec;
- ProcessExecutor processExecutor;
-
- boolean ignoreFailures
- boolean testFailed
-
- @TaskAction
- protected void runTests() {
-
- File resultsOutDir = getResultsDir()
- emptyFolder(resultsOutDir)
-
- File coverageOutDir = getCoverageDir()
- emptyFolder(coverageOutDir)
-
- boolean success = false;
- // If there are tests to run, and the test runner returns with no results, we fail (since
- // this is most likely a problem with the device setup). If no, the task will succeed.
- if (!testsFound()) {
- logger.info("No tests found, nothing to do.")
- // If we don't create the coverage file, createXxxCoverageReport task will fail.
- File emptyCoverageFile = new File(coverageOutDir, SimpleTestCallable.FILE_COVERAGE_EC)
- emptyCoverageFile.createNewFile()
- success = true;
- } else {
- File testApk = testData.getTestApk();
- String flavor = getFlavorName()
- TestRunner testRunner = new SimpleTestRunner(getAdbExec(), getSplitSelectExec(),
- getProcessExecutor());
- deviceProvider.init();
-
- Collection<String> extraArgs = installOptions == null || installOptions.isEmpty() ?
- ImmutableList.of() : installOptions;
- try {
- success = testRunner.runTests(project.name, flavor,
- testApk, testData,
- deviceProvider.devices,
- deviceProvider.getMaxThreads(),
- deviceProvider.getTimeoutInMs(),
- extraArgs,
- resultsOutDir, coverageOutDir, getILogger());
- } finally {
- deviceProvider.terminate();
- }
-
- }
-
- // run the report from the results.
- File reportOutDir = getReportsDir()
- emptyFolder(reportOutDir)
-
- TestReport report = new TestReport(ReportType.SINGLE_FLAVOR, resultsOutDir, reportOutDir)
- report.generateReport()
-
- if (!success) {
- testFailed = true
- String reportUrl = new ConsoleRenderer().asClickableFileUrl(
- new File(reportOutDir, "index.html"));
- String message = "There were failing tests. See the report at: " + reportUrl;
- if (getIgnoreFailures()) {
- logger.warn(message)
- return
- } else {
- throw new GradleException(message)
- }
- }
-
- testFailed = false
- }
-
- /**
- * Determines if there are any tests to run.
- *
- * @return true if there are some tests to run, false otherwise
- */
- private boolean testsFound() {
- // For now we check if there are any test sources. We could inspect the test classes and
- // apply JUnit logic to see if there's something to run, but that would not catch the case
- // where user makes a typo in a test name or forgets to inherit from a JUnit class
- !project.files(testData.getTestDirectories()).asFileTree.empty
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestTask.java
new file mode 100644
index 0000000..fcb4291
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/DeviceProviderInstrumentTestTask.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle.internal.tasks;
+
+import static com.android.builder.core.BuilderConstants.CONNECTED;
+import static com.android.builder.core.BuilderConstants.DEVICE;
+import static com.android.builder.core.BuilderConstants.FD_ANDROID_RESULTS;
+import static com.android.builder.core.BuilderConstants.FD_ANDROID_TESTS;
+import static com.android.builder.core.BuilderConstants.FD_FLAVORS;
+import static com.android.builder.core.BuilderConstants.FD_REPORTS;
+import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
+import static com.android.sdklib.BuildToolInfo.PathId.SPLIT_SELECT;
+
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.test.report.ReportType;
+import com.android.build.gradle.internal.test.report.TestReport;
+import com.android.build.gradle.internal.variant.TestVariantData;
+import com.android.builder.internal.testing.SimpleTestCallable;
+import com.android.builder.sdk.SdkInfo;
+import com.android.builder.sdk.TargetInfo;
+import com.android.builder.testing.ConnectedDeviceProvider;
+import com.android.builder.testing.SimpleTestRunner;
+import com.android.builder.testing.TestData;
+import com.android.builder.testing.TestRunner;
+import com.android.builder.testing.api.DeviceException;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.builder.testing.api.TestException;
+import com.android.ide.common.process.ProcessExecutor;
+import com.android.utils.FileUtils;
+import com.android.utils.StringHelper;
+import com.google.common.collect.ImmutableList;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.Nullable;
+import org.gradle.api.plugins.JavaBasePlugin;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.logging.ConsoleRenderer;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.concurrent.Callable;
+
+/**
+ * Run instrumentation tests for a given variant
+ */
+public class DeviceProviderInstrumentTestTask extends BaseTask implements AndroidTestTask {
+
+ private File reportsDir;
+ private File resultsDir;
+ private File coverageDir;
+
+ private String flavorName;
+
+ @Nullable
+ private Collection<String> installOptions;
+
+ private DeviceProvider deviceProvider;
+ private TestData testData;
+
+ private File adbExec;
+ @Nullable
+ private File splitSelectExec;
+ private ProcessExecutor processExecutor;
+
+ private boolean ignoreFailures;
+ private boolean testFailed;
+
+ @TaskAction
+ protected void runTests() throws DeviceException, IOException, InterruptedException,
+ TestRunner.NoAuthorizedDeviceFoundException, TestException {
+
+ File resultsOutDir = getResultsDir();
+ FileUtils.emptyFolder(resultsOutDir);
+
+ File coverageOutDir = getCoverageDir();
+ FileUtils.emptyFolder(coverageOutDir);
+
+ boolean success = false;
+ // If there are tests to run, and the test runner returns with no results, we fail (since
+ // this is most likely a problem with the device setup). If no, the task will succeed.
+ if (!testsFound()) {
+ getLogger().info("No tests found, nothing to do.");
+ // If we don't create the coverage file, createXxxCoverageReport task will fail.
+ File emptyCoverageFile = new File(coverageOutDir, SimpleTestCallable.FILE_COVERAGE_EC);
+ emptyCoverageFile.createNewFile();
+ success = true;
+ } else {
+ File testApk = testData.getTestApk();
+ String flavor = getFlavorName();
+ TestRunner testRunner = new SimpleTestRunner(
+ getSplitSelectExec(),
+ getProcessExecutor());
+ deviceProvider.init();
+
+ Collection<String> extraArgs = installOptions == null || installOptions.isEmpty()
+ ? ImmutableList.<String>of() : installOptions;
+ try {
+ success = testRunner.runTests(getProject().getName(), flavor,
+ testApk,
+ testData,
+ deviceProvider.getDevices(),
+ deviceProvider.getMaxThreads(),
+ deviceProvider.getTimeoutInMs(),
+ extraArgs,
+ resultsOutDir,
+ coverageOutDir,
+ getILogger());
+ } finally {
+ deviceProvider.terminate();
+ }
+
+ }
+
+ // run the report from the results.
+ File reportOutDir = getReportsDir();
+ FileUtils.emptyFolder(reportOutDir);
+
+ TestReport report = new TestReport(ReportType.SINGLE_FLAVOR, resultsOutDir, reportOutDir);
+ report.generateReport();
+
+ if (!success) {
+ testFailed = true;
+ String reportUrl = new ConsoleRenderer().asClickableFileUrl(
+ new File(reportOutDir, "index.html"));
+ String message = "There were failing tests. See the report at: " + reportUrl;
+ if (getIgnoreFailures()) {
+ getLogger().warn(message);
+ return;
+
+ } else {
+ throw new GradleException(message);
+ }
+ }
+
+ testFailed = false;
+ }
+
+ /**
+ * Determines if there are any tests to run.
+ *
+ * @return true if there are some tests to run, false otherwise
+ */
+ private boolean testsFound() {
+ // For now we check if there are any test sources. We could inspect the test classes and
+ // apply JUnit logic to see if there's something to run, but that would not catch the case
+ // where user makes a typo in a test name or forgets to inherit from a JUnit class
+ return !getProject().files(testData.getTestDirectories()).getAsFileTree().isEmpty();
+ }
+
+ public File getReportsDir() {
+ return reportsDir;
+ }
+
+ public void setReportsDir(File reportsDir) {
+ this.reportsDir = reportsDir;
+ }
+
+ @Override
+ public File getResultsDir() {
+ return resultsDir;
+ }
+
+ public void setResultsDir(File resultsDir) {
+ this.resultsDir = resultsDir;
+ }
+
+ public File getCoverageDir() {
+ return coverageDir;
+ }
+
+ public void setCoverageDir(File coverageDir) {
+ this.coverageDir = coverageDir;
+ }
+
+ public String getFlavorName() {
+ return flavorName;
+ }
+
+ public void setFlavorName(String flavorName) {
+ this.flavorName = flavorName;
+ }
+
+ public Collection<String> getInstallOptions() {
+ return installOptions;
+ }
+
+ public void setInstallOptions(Collection<String> installOptions) {
+ this.installOptions = installOptions;
+ }
+
+ public DeviceProvider getDeviceProvider() {
+ return deviceProvider;
+ }
+
+ public void setDeviceProvider(DeviceProvider deviceProvider) {
+ this.deviceProvider = deviceProvider;
+ }
+
+ public TestData getTestData() {
+ return testData;
+ }
+
+ public void setTestData(TestData testData) {
+ this.testData = testData;
+ }
+
+ public File getAdbExec() {
+ return adbExec;
+ }
+
+ public void setAdbExec(File adbExec) {
+ this.adbExec = adbExec;
+ }
+
+ public File getSplitSelectExec() {
+ return splitSelectExec;
+ }
+
+ public void setSplitSelectExec(File splitSelectExec) {
+ this.splitSelectExec = splitSelectExec;
+ }
+
+ public ProcessExecutor getProcessExecutor() {
+ return processExecutor;
+ }
+
+ public void setProcessExecutor(ProcessExecutor processExecutor) {
+ this.processExecutor = processExecutor;
+ }
+
+ @Override
+ public boolean getIgnoreFailures() {
+ return ignoreFailures;
+ }
+
+ @Override
+ public void setIgnoreFailures(boolean ignoreFailures) {
+ this.ignoreFailures = ignoreFailures;
+ }
+
+ @Override
+ public boolean getTestFailed() {
+ return testFailed;
+ }
+
+
+ public static class ConfigAction implements TaskConfigAction<DeviceProviderInstrumentTestTask> {
+
+ private final VariantScope scope;
+ private final DeviceProvider deviceProvider;
+ private final TestData testData;
+
+ public ConfigAction(VariantScope scope, DeviceProvider deviceProvider, TestData testData) {
+ this.scope = scope;
+ this.deviceProvider = deviceProvider;
+ this.testData = testData;
+ }
+
+ @Override
+ public String getName() {
+ return scope.getTaskName(deviceProvider.getName());
+ }
+
+ @Override
+ public Class<DeviceProviderInstrumentTestTask> getType() {
+ return DeviceProviderInstrumentTestTask.class;
+ }
+
+ @Override
+ public void execute(DeviceProviderInstrumentTestTask task) {
+ final boolean connected = deviceProvider instanceof ConnectedDeviceProvider;
+ String variantName = scope.getTestedVariantData() != null ?
+ scope.getTestedVariantData().getName() : scope.getVariantData().getName();
+ if (connected) {
+ task.setDescription("Installs and runs the tests for " + variantName +
+ " on connected devices.");
+ } else {
+ task.setDescription("Installs and runs the tests for " + variantName +
+ " using provider: " + StringHelper.capitalize(deviceProvider.getName()));
+
+ }
+ task.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
+ task.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+ task.setVariantName(variantName);
+ task.setTestData(testData);
+ task.setFlavorName(testData.getFlavorName());
+ task.setDeviceProvider(deviceProvider);
+ task.setInstallOptions(scope.getGlobalScope().getExtension().getAdbOptions().getInstallOptions());
+ task.setProcessExecutor(scope.getGlobalScope().getAndroidBuilder().getProcessExecutor());
+
+ String flavorFolder = testData.getFlavorName();
+ if (!flavorFolder.isEmpty()) {
+ flavorFolder = FD_FLAVORS + "/" + flavorFolder;
+ }
+ String providerFolder = connected ? CONNECTED : DEVICE + "/" + deviceProvider.getName();
+ final String subFolder = "/" + providerFolder + "/" + flavorFolder;
+
+ ConventionMappingHelper.map(task, "adbExec", new Callable<File>() {
+ @Override
+ public File call() {
+ final SdkInfo info = scope.getGlobalScope().getSdkHandler()
+ .getSdkInfo();
+ return (info == null ? null : info.getAdb());
+ }
+ });
+ ConventionMappingHelper.map(task, "splitSelectExec", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ final TargetInfo info = scope.getGlobalScope().getAndroidBuilder().getTargetInfo();
+ String path = info == null ? null : info.getBuildTools().getPath(SPLIT_SELECT);
+ if (path != null) {
+ File splitSelectExe = new File(path);
+ return splitSelectExe.exists() ? splitSelectExe : null;
+ } else {
+ return null;
+ }
+ }
+ });
+
+ ConventionMappingHelper.map(task, "resultsDir", new Callable<File>() {
+ @Override
+ public File call() {
+ String rootLocation = scope.getGlobalScope().getExtension().getTestOptions().getResultsDir();
+ if (rootLocation == null) {
+ rootLocation = scope.getGlobalScope().getBuildDir() + "/" +
+ FD_OUTPUTS + "/" + FD_ANDROID_RESULTS;
+ }
+ return scope.getGlobalScope().getProject().file(rootLocation + subFolder);
+ }
+ });
+
+ ConventionMappingHelper.map(task, "reportsDir", new Callable<File>() {
+ @Override
+ public File call() {
+ String rootLocation = scope.getGlobalScope().getExtension().getTestOptions().getReportDir();
+ if (rootLocation == null) {
+ rootLocation = scope.getGlobalScope().getBuildDir() + "/" +
+ FD_REPORTS + "/" + FD_ANDROID_TESTS;
+ }
+ return scope.getGlobalScope().getProject().file(rootLocation + subFolder);
+ }
+ });
+
+ String rootLocation = scope.getGlobalScope().getBuildDir() + "/" +
+ FD_OUTPUTS + "/code-coverage";
+ task.setCoverageDir(scope.getGlobalScope().getProject().file(rootLocation + subFolder));
+
+ if (scope.getVariantData() instanceof TestVariantData) {
+ TestVariantData testVariantData = (TestVariantData) scope.getVariantData();
+ if (connected) {
+ testVariantData.connectedTestTask = task;
+ } else {
+ testVariantData.providerTestTaskList.add(task);
+ }
+ }
+
+ task.setEnabled(deviceProvider.isConfigured());
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ExtractJavaResourcesTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ExtractJavaResourcesTask.java
new file mode 100644
index 0000000..9e4bc57
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ExtractJavaResourcesTask.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.tasks;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.ide.common.packaging.PackagingUtils;
+import com.android.utils.FileUtils;
+import com.google.common.io.ByteStreams;
+
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * Extract all packaged jar files java resources into a directory. Each jar file will be extracted
+ * in a jar specific folder, and only java resources are extracted.
+ */
+public class ExtractJavaResourcesTask extends DefaultAndroidTask {
+
+ // the fact we use a SET is not right, we should have an ordered list of jars...
+ // VariantConfiguration.getPackaged|ProvidedJars should use List<>
+ @InputFiles
+ public Set<File> jarInputFiles;
+
+ @OutputDirectory
+ public File outputDir;
+
+ @InputFiles
+ public Set<File> getJarInputFiles() {
+ return jarInputFiles;
+ }
+
+ @TaskAction
+ public void extractJavaResources(final IncrementalTaskInputs incrementalTaskInputs) {
+
+ incrementalTaskInputs.outOfDate(new org.gradle.api.Action<InputFileDetails>() {
+ @Override
+ public void execute(InputFileDetails inputFileDetails) {
+ File inputJar = inputFileDetails.getFile();
+ String folderName = inputJar.getName() +
+ inputJar.getPath().hashCode();
+
+ File outputFolder = new File(outputDir, folderName);
+ if (outputFolder.exists()) {
+ try {
+ FileUtils.deleteFolder(outputFolder);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ if (!outputFolder.mkdirs()) {
+ throw new RuntimeException(
+ "Cannot create folder to extract java resources in for "
+ + inputJar.getAbsolutePath());
+ }
+
+ // create the jar file visitor that will check for out-dated resources.
+
+ JarFile jarFile = null;
+ try {
+ jarFile = new JarFile(inputJar);
+ Enumeration<JarEntry> entries = jarFile.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry jarEntry = entries.nextElement();
+ if (!jarEntry.isDirectory()) {
+ processJarEntry(jarFile, jarEntry, outputFolder);
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ if (jarFile != null) {
+ try {
+ jarFile.close();
+ } catch (IOException e) {
+ // ignore.
+ }
+ }
+ }
+ }
+ });
+
+ incrementalTaskInputs.removed(new org.gradle.api.Action<InputFileDetails>() {
+ @Override
+ public void execute(InputFileDetails inputFileDetails) {
+ File deletedJar = inputFileDetails.getFile();
+ String folderName = deletedJar.getName() +
+ deletedJar.getPath().hashCode();
+ File outputFolder = new File(outputDir, folderName);
+ if (outputFolder.exists()) {
+ try {
+ FileUtils.deleteFolder(outputFolder);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * process one jar entry in an input jar file and optionally stores the entry in the output
+ * folder.
+ * @param jarFile the input jar file
+ * @param jarEntry the jar entry in the jarFile to process
+ * @param outputDir the output folder to use to copy/merge the entry in.
+ * @throws IOException
+ */
+ private static void processJarEntry(JarFile jarFile, JarEntry jarEntry, File outputDir) throws IOException {
+ File outputFile = new File(outputDir, jarEntry.getName());
+ Action action = getAction(jarEntry.getName());
+ if (action == Action.COPY) {
+ if (!outputFile.getParentFile().exists() &&
+ !outputFile.getParentFile().mkdirs()) {
+ throw new RuntimeException("Cannot create directory " + outputFile.getParent());
+ }
+ if (!outputFile.exists() || outputFile.lastModified()
+ < jarEntry.getTime()) {
+ InputStream inputStream = null;
+ OutputStream outputStream = null;
+ try {
+ inputStream = jarFile.getInputStream(jarEntry);
+ if (inputStream != null) {
+ outputStream = new BufferedOutputStream(
+ new FileOutputStream(outputFile));
+ ByteStreams.copy(inputStream, outputStream);
+ outputStream.flush();
+ } else {
+ throw new RuntimeException("Cannot copy " + jarEntry.getName());
+ }
+ } finally {
+ try {
+ if (outputStream != null) {
+ outputStream.close();
+ }
+ } finally {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Define all possible actions for a Jar file entry.
+ */
+ enum Action {
+ /**
+ * Copy the file to the output destination.
+ */
+ COPY,
+ /**
+ * Ignore the file.
+ */
+ IGNORE
+ }
+
+ /**
+ * Provides an {@link Action} for the archive entry.
+ * @param archivePath the archive entry path in the archive.
+ * @return the action to implement.
+ */
+ @NonNull
+ public static Action getAction(@NonNull String archivePath) {
+ // Manifest files are never merged.
+ if (JarFile.MANIFEST_NAME.equals(archivePath)) {
+ return Action.IGNORE;
+ }
+
+ // split the path into segments.
+ String[] segments = archivePath.split("/");
+
+ // empty path? skip to next entry.
+ if (segments.length == 0) {
+ return Action.IGNORE;
+ }
+
+ // Check each folders to make sure they should be included.
+ // Folders like CVS, .svn, etc.. should already have been excluded from the
+ // jar file, but we need to exclude some other folder (like /META-INF) so
+ // we check anyway.
+ for (int i = 0 ; i < segments.length - 1; i++) {
+ if (!PackagingUtils.checkFolderForPackaging(segments[i])) {
+ return Action.IGNORE;
+ }
+ }
+
+ // get the file name from the path
+ String fileName = segments[segments.length-1];
+
+ return PackagingUtils.checkFileForPackaging(fileName)
+ ? Action.COPY
+ : Action.IGNORE;
+ }
+
+ public static class Config implements TaskConfigAction<ExtractJavaResourcesTask> {
+
+ private final VariantScope scope;
+
+ public Config(VariantScope scope) {
+ this.scope = scope;
+ }
+
+ @Override
+ public String getName() {
+ return scope.getTaskName("extract", "PackagedLibrariesJavaResources");
+ }
+
+ @Override
+ public Class<ExtractJavaResourcesTask> getType() {
+ return ExtractJavaResourcesTask.class;
+ }
+
+ @Override
+ public void execute(ExtractJavaResourcesTask extractJavaResourcesTask) {
+ ConventionMappingHelper.map(extractJavaResourcesTask, "jarInputFiles",
+ new Callable<Set<File>>() {
+
+ @Override
+ public Set<File> call() throws Exception {
+ return scope.getVariantConfiguration().getPackagedJars();
+ }
+ });
+ extractJavaResourcesTask.outputDir = scope.getPackagedJarsJavaResDestinationDir();
+ extractJavaResourcesTask.setVariantName(scope.getVariantConfiguration().getFullName());
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/GenerateApkDataTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/GenerateApkDataTask.java
index 573b8f1..2fb3017 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/GenerateApkDataTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/GenerateApkDataTask.java
@@ -21,18 +21,15 @@
import static com.android.SdkConstants.FN_ANDROID_MANIFEST_XML;
import static com.android.builder.core.BuilderConstants.ANDROID_WEAR_MICRO_APK;
-import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.build.gradle.internal.scope.ConventionMappingHelper;
import com.android.build.gradle.internal.scope.TaskConfigAction;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.variant.ApkVariantData;
-import com.android.build.gradle.tasks.AidlCompile;
import com.android.builder.core.AndroidBuilder;
-import com.android.builder.core.VariantConfiguration;
import com.android.ide.common.internal.LoggedErrorException;
import com.android.ide.common.process.ProcessException;
-import com.android.utils.StringHelper;
+import com.android.utils.FileUtils;
import com.google.common.io.Files;
import org.gradle.api.artifacts.Configuration;
@@ -73,7 +70,7 @@
InterruptedException {
// always empty output dir.
File outDir = getResOutputDir();
- emptyFolder(outDir);
+ FileUtils.emptyFolder(outDir);
File apk = getApkFile();
// copy the file into the destination, by sanitizing the name first.
@@ -177,6 +174,7 @@
variantData.generateApkDataTask = task;
task.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+ task.setVariantName(scope.getVariantConfiguration().getFullName());
ConventionMappingHelper.map(task, "resOutputDir", new Callable<File>() {
@Override
public File call() throws Exception {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/IncrementalTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/IncrementalTask.java
index e16a9ac..f28be32 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/IncrementalTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/IncrementalTask.java
@@ -27,6 +27,7 @@
import org.gradle.api.tasks.incremental.InputFileDetails;
import java.io.File;
+import java.io.IOException;
import java.util.List;
import java.util.Map;
@@ -57,7 +58,7 @@
* {@link #isIncremental()} returns false.
*
*/
- protected abstract void doFullTaskAction();
+ protected abstract void doFullTaskAction() throws IOException;
/**
* Optional incremental task action.
@@ -65,7 +66,7 @@
*
* @param changedInputs the changed input files.
*/
- protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) {
+ protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) throws IOException {
// do nothing.
}
@@ -74,7 +75,7 @@
* Calls out to the doTaskAction as needed.
*/
@TaskAction
- void taskAction(IncrementalTaskInputs inputs) {
+ void taskAction(IncrementalTaskInputs inputs) throws IOException {
if (!isIncremental()) {
doFullTaskAction();
return;
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/InstallVariantTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/InstallVariantTask.groovy
deleted file mode 100644
index 8a2086e..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/InstallVariantTask.groovy
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.build.gradle.internal.tasks
-
-import com.android.build.gradle.internal.LoggerWrapper
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.scope.ConventionMappingHelper
-import com.android.build.gradle.internal.scope.TaskConfigAction
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.variant.ApkVariantData
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.builder.core.VariantConfiguration
-import com.android.builder.internal.InstallUtils
-import com.android.builder.sdk.SdkInfo
-import com.android.builder.sdk.TargetInfo
-import com.android.builder.testing.ConnectedDeviceProvider
-import com.android.builder.testing.api.DeviceConfigProviderImpl
-import com.android.builder.testing.api.DeviceConnector
-import com.android.builder.testing.api.DeviceProvider
-import com.android.ide.common.build.SplitOutputMatcher
-import com.android.ide.common.process.ProcessExecutor
-import com.android.utils.ILogger
-import com.google.common.base.Joiner
-import com.google.common.collect.ImmutableList
-import org.gradle.api.Action
-import org.gradle.api.GradleException
-import org.gradle.api.logging.LogLevel
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.TaskAction
-
-import java.util.concurrent.Callable
-
-import static com.android.sdklib.BuildToolInfo.PathId.SPLIT_SELECT
-
-/**
- * Task installing an app variant. It looks at connected device and install the best matching
- * variant output on each device.
- */
-public class InstallVariantTask extends BaseTask {
-
- @InputFile
- File adbExe
-
- @InputFile
- @Optional
- File splitSelectExe
-
- ProcessExecutor processExecutor;
-
- String projectName
-
- @Input
- int timeOutInMs = 0
-
- @Input @Optional
- Collection<String> installOptions;
-
- BaseVariantData<? extends BaseVariantOutputData> variantData
-
- InstallVariantTask() {
- this.getOutputs().upToDateWhen {
- logger.debug("Install task is always run.");
- false;
- }
- }
-
- @TaskAction
- void install() {
- final ILogger iLogger = new LoggerWrapper(getLogger(), LogLevel.LIFECYCLE)
- DeviceProvider deviceProvider = new ConnectedDeviceProvider(getAdbExe(), iLogger)
- deviceProvider.init()
-
- VariantConfiguration variantConfig = variantData.variantConfiguration
- String variantName = variantConfig.fullName
-
- int successfulInstallCount = 0;
-
- for (DeviceConnector device : deviceProvider.getDevices()) {
- if (InstallUtils.checkDeviceApiLevel(
- device, variantConfig.minSdkVersion, iLogger, projectName, variantName)) {
- // When InstallUtils.checkDeviceApiLevel returns false, it logs the reason.
- List<File> apkFiles = SplitOutputMatcher.computeBestOutput(processExecutor,
- getSplitSelectExe(),
- new DeviceConfigProviderImpl(device),
- variantData.outputs,
- variantData.variantConfiguration.getSupportedAbis())
-
- if (apkFiles.isEmpty()) {
- logger.lifecycle(
- "Skipping device '${device.getName()}' for " +
- "'${projectName}:${variantName}': " +
- "Could not find build of variant which supports " +
- "density " + "${device.getDensity()} " +
- "and an ABI in " + Joiner.on(", ").join(device.getAbis()));
- } else {
- logger.lifecycle(
- "Installing APK '${Joiner.on(", ").join(apkFiles*.getName())}'" +
- " on '${device.getName()}'")
-
- List<String> extraArgs = installOptions == null ? ImmutableList.of() :
- installOptions;
- if (apkFiles.size() > 1 || device.getApiLevel() >= 21) {
- device.installPackages(apkFiles, extraArgs, getTimeOutInMs(), getILogger());
- successfulInstallCount++
- } else {
- device.installPackage(apkFiles.get(0), extraArgs, getTimeOutInMs(),
- getILogger())
- successfulInstallCount++
- }
- }
- }
- }
-
- if (successfulInstallCount == 0) {
- throw new GradleException("Failed to install on any devices.")
- } else {
- logger.quiet("Installed on ${successfulInstallCount} " +
- "${successfulInstallCount==1?'device':'devicess'}.");
- }
- }
-
- public static class ConfigAction implements TaskConfigAction<InstallVariantTask> {
-
- private final VariantScope scope;
-
- public ConfigAction(VariantScope scope) {
- this.scope = scope;
- }
-
- @Override
- String getName() {
- return "install${scope.getVariantConfiguration().fullName.capitalize()}";
- }
-
- @Override
- Class<InstallVariantTask> getType() {
- return InstallVariantTask.class
- }
-
- @Override
- public void execute(InstallVariantTask installTask) {
- installTask.setDescription("Installs the " + scope.getVariantData().getDescription() + ".");
- installTask.setGroup(TaskManager.INSTALL_GROUP);
- installTask.setProjectName(scope.getGlobalScope().getProject().getName());
- installTask.setVariantData(scope.getVariantData());
- installTask.setTimeOutInMs(scope.getGlobalScope().getExtension().getAdbOptions().getTimeOutInMs());
- installTask.setInstallOptions(scope.getGlobalScope().getExtension().getAdbOptions().getInstallOptions());
- installTask.setProcessExecutor(scope.getGlobalScope().getAndroidBuilder().getProcessExecutor());
- ConventionMappingHelper.map(installTask, "adbExe", new Closure<File>(this, this) {
- public File doCall(Object it) {
- final SdkInfo info = scope.getGlobalScope().getSdkHandler().getSdkInfo();
- return (info == null ? null : info.getAdb());
- }
-
- public File doCall() {
- return doCall(null);
- }
-
- });
- ConventionMappingHelper.map(installTask, "splitSelectExe", new Callable<File>() {
- @Override
- public File call() throws Exception {
- final TargetInfo info = scope.getGlobalScope().getAndroidBuilder().getTargetInfo();
- String path = info == null ? null : info.getBuildTools().getPath(SPLIT_SELECT);
- if (path != null) {
- File splitSelectExe = new File(path);
- return splitSelectExe.exists() ? splitSelectExe : null;
- } else {
- return null;
- }
- }
- });
- ((ApkVariantData) scope.getVariantData()).installTask = installTask;
- }
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/InstallVariantTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/InstallVariantTask.java
new file mode 100644
index 0000000..8e44598
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/InstallVariantTask.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.build.gradle.internal.tasks;
+
+import static com.android.sdklib.BuildToolInfo.PathId.SPLIT_SELECT;
+
+import com.android.build.gradle.internal.LoggerWrapper;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.variant.ApkVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.builder.core.VariantConfiguration;
+import com.android.builder.internal.InstallUtils;
+import com.android.builder.sdk.SdkInfo;
+import com.android.builder.sdk.TargetInfo;
+import com.android.builder.testing.ConnectedDeviceProvider;
+import com.android.builder.testing.api.DeviceConfigProviderImpl;
+import com.android.builder.testing.api.DeviceConnector;
+import com.android.builder.testing.api.DeviceException;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.ide.common.build.SplitOutputMatcher;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessExecutor;
+import com.android.utils.FileUtils;
+import com.android.utils.ILogger;
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.Task;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Task installing an app variant. It looks at connected device and install the best matching
+ * variant output on each device.
+ */
+public class InstallVariantTask extends BaseTask {
+
+ private File adbExe;
+
+ private File splitSelectExe;
+
+ private ProcessExecutor processExecutor;
+
+ private String projectName;
+
+ private int timeOutInMs = 0;
+
+ private Collection<String> installOptions;
+
+ private BaseVariantData<? extends BaseVariantOutputData> variantData;
+
+ public InstallVariantTask() {
+ this.getOutputs().upToDateWhen(new Spec<Task>() {
+ @Override
+ public boolean isSatisfiedBy(Task task) {
+ getLogger().debug("Install task is always run.");
+ return false;
+ }
+ });
+ }
+
+ @TaskAction
+ public void install() throws DeviceException, ProcessException, InterruptedException {
+ final ILogger iLogger = new LoggerWrapper(getLogger(), LogLevel.LIFECYCLE);
+ DeviceProvider deviceProvider = new ConnectedDeviceProvider(getAdbExe(), iLogger);
+ deviceProvider.init();
+
+ VariantConfiguration variantConfig = variantData.getVariantConfiguration();
+ String variantName = variantConfig.getFullName();
+
+ int successfulInstallCount = 0;
+ List<? extends DeviceConnector> devices = deviceProvider.getDevices();
+ for (final DeviceConnector device : devices) {
+ if (InstallUtils.checkDeviceApiLevel(
+ device, variantConfig.getMinSdkVersion(), iLogger, projectName, variantName)) {
+ // When InstallUtils.checkDeviceApiLevel returns false, it logs the reason.
+ final List<File> apkFiles = SplitOutputMatcher.computeBestOutput(processExecutor,
+ getSplitSelectExe(),
+ new DeviceConfigProviderImpl(device),
+ variantData.getOutputs(),
+ variantData.getVariantConfiguration().getSupportedAbis());
+
+ if (apkFiles.isEmpty()) {
+ getLogger().lifecycle(
+ "Skipping device '{}' for '{}:{}': Could not find build of variant " +
+ "which supports density {} and an ABI in {}",
+ device.getName(), projectName, variantName,
+ device.getDensity(), Joiner.on(", ").join(device.getAbis()));
+ } else {
+ getLogger().lifecycle(
+ "Installing APK '{}' on '{}' for {}:{}",
+ FileUtils.getNamesAsCommaSeparatedList(apkFiles),
+ device.getName(),
+ projectName,
+ variantName);
+
+ final Collection<String> extraArgs =
+ Objects.firstNonNull(installOptions, ImmutableList.<String>of());
+
+ if (apkFiles.size() > 1 || device.getApiLevel() >= 21) {
+ device.installPackages(apkFiles, extraArgs,
+ getTimeOutInMs(), getILogger());
+ successfulInstallCount++;
+ } else {
+ device.installPackage(apkFiles.get(0), extraArgs,
+ getTimeOutInMs(),
+ getILogger());
+ successfulInstallCount++;
+ }
+ }
+ }
+ }
+
+ if (successfulInstallCount == 0) {
+ throw new GradleException("Failed to install on any devices.");
+ } else {
+ getLogger().quiet("Installed on {} {}.",
+ successfulInstallCount,
+ successfulInstallCount==1 ? "device" : "devices");
+ }
+ }
+
+ @InputFile
+ public File getAdbExe() {
+ return adbExe;
+ }
+
+ public void setAdbExe(File adbExe) {
+ this.adbExe = adbExe;
+ }
+
+ @InputFile
+ @Optional
+ public File getSplitSelectExe() {
+ return splitSelectExe;
+ }
+
+ public void setSplitSelectExe(File splitSelectExe) {
+ this.splitSelectExe = splitSelectExe;
+ }
+
+ public ProcessExecutor getProcessExecutor() {
+ return processExecutor;
+ }
+
+ public void setProcessExecutor(ProcessExecutor processExecutor) {
+ this.processExecutor = processExecutor;
+ }
+
+ public String getProjectName() {
+ return projectName;
+ }
+
+ public void setProjectName(String projectName) {
+ this.projectName = projectName;
+ }
+
+ @Input
+ public int getTimeOutInMs() {
+ return timeOutInMs;
+ }
+
+ public void setTimeOutInMs(int timeOutInMs) {
+ this.timeOutInMs = timeOutInMs;
+ }
+
+ @Input
+ @Optional
+ public Collection<String> getInstallOptions() {
+ return installOptions;
+ }
+
+ public void setInstallOptions(Collection<String> installOptions) {
+ this.installOptions = installOptions;
+ }
+
+ public BaseVariantData<? extends BaseVariantOutputData> getVariantData() {
+ return variantData;
+ }
+
+ public void setVariantData(
+ BaseVariantData<? extends BaseVariantOutputData> variantData) {
+ this.variantData = variantData;
+ }
+
+ public static class ConfigAction implements TaskConfigAction<InstallVariantTask> {
+
+ private final VariantScope scope;
+
+ public ConfigAction(VariantScope scope) {
+ this.scope = scope;
+ }
+
+ @Override
+ public String getName() {
+ return scope.getTaskName("install");
+ }
+
+ @Override
+ public Class<InstallVariantTask> getType() {
+ return InstallVariantTask.class;
+ }
+
+ @Override
+ public void execute(InstallVariantTask installTask) {
+ installTask.setDescription(
+ "Installs the " + scope.getVariantData().getDescription() + ".");
+ installTask.setVariantName(scope.getVariantConfiguration().getFullName());
+ installTask.setGroup(TaskManager.INSTALL_GROUP);
+ installTask.setProjectName(scope.getGlobalScope().getProject().getName());
+ installTask.setVariantData(scope.getVariantData());
+ installTask.setTimeOutInMs(
+ scope.getGlobalScope().getExtension().getAdbOptions().getTimeOutInMs());
+ installTask.setInstallOptions(
+ scope.getGlobalScope().getExtension().getAdbOptions().getInstallOptions());
+ installTask.setProcessExecutor(
+ scope.getGlobalScope().getAndroidBuilder().getProcessExecutor());
+ ConventionMappingHelper.map(installTask, "adbExe", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ final SdkInfo info = scope.getGlobalScope().getSdkHandler().getSdkInfo();
+ return (info == null ? null : info.getAdb());
+ }
+ });
+ ConventionMappingHelper.map(installTask, "splitSelectExe", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ final TargetInfo info =
+ scope.getGlobalScope().getAndroidBuilder().getTargetInfo();
+ String path = info == null ? null : info.getBuildTools().getPath(SPLIT_SELECT);
+ if (path != null) {
+ File splitSelectExe = new File(path);
+ return splitSelectExe.exists() ? splitSelectExe : null;
+ } else {
+ return null;
+ }
+ }
+ });
+ ((ApkVariantData) scope.getVariantData()).installTask = installTask;
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/MergeJavaResourcesTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/MergeJavaResourcesTask.java
new file mode 100644
index 0000000..b0aa93f
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/MergeJavaResourcesTask.java
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.tasks;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.tasks.JavaResourcesProvider;
+import com.android.builder.model.PackagingOptions;
+import com.android.builder.signing.SignedJarBuilder;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.Files;
+
+import org.gradle.api.tasks.InputDirectory;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+/**
+ * Merges java resources from temporary expansion folders (created from the packaged jars
+ * resources and source folder java resources) into a single output directory that can be used
+ * during obfuscation and packaging.
+ *
+ * This task is the default {@link JavaResourcesProvider} to provide merged java resources to
+ * the final variant packaging step. However, if the variant obfuscation is turned on, some of
+ * these resources packages might need to be adapted to match the obfuscated code. In such
+ * a scenario, the {@link JavaResourcesProvider} will become the task responsible for obfuscation.
+ */
+@ParallelizableTask
+public class MergeJavaResourcesTask extends DefaultAndroidTask implements JavaResourcesProvider {
+
+ @Nested
+ @Optional
+ @Nullable
+ public PackagingOptions packagingOptions;
+
+ @InputDirectory
+ @Optional
+ @Nullable
+ public File getSourceJavaResourcesFolder() {
+ return sourceJavaResourcesFolder;
+ }
+
+ @InputDirectory
+ @Optional
+ @Nullable
+ public File getPackagedJarsJavaResourcesFolder() {
+ return packagedJarsJavaResourcesFolder;
+ }
+
+ @Nullable
+ private FileFilter packagingOptionsFilter;
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ @Nullable
+ private File sourceJavaResourcesFolder;
+ @SuppressWarnings({"UnusedDeclaration"})
+ @Nullable
+ private File packagedJarsJavaResourcesFolder;
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ @Nullable
+ private File outputDir;
+
+ @OutputDirectory
+ @Nullable
+ public File getOutputDir() {
+ return outputDir;
+ }
+
+ public List<File> getExpandedFolders() {
+ ImmutableList.Builder<File> builder = ImmutableList.builder();
+ if (getSourceJavaResourcesFolder() != null) {
+ builder.add(getSourceJavaResourcesFolder());
+ }
+ if (getPackagedJarsJavaResourcesFolder() != null) {
+ builder.add(getPackagedJarsJavaResourcesFolder());
+ }
+ return builder.build();
+ }
+
+ @TaskAction
+ void extractJavaResources(IncrementalTaskInputs incrementalTaskInputs) {
+
+ if (packagingOptionsFilter == null || getOutputDir() == null) {
+ throw new RuntimeException(
+ "Internal error, packagingOptionsFilter or outputDir is null");
+ }
+ incrementalTaskInputs.outOfDate(new org.gradle.api.Action<InputFileDetails>() {
+ @Override
+ public void execute(InputFileDetails inputFileDetails) {
+ try {
+ packagingOptionsFilter.handleChanged(
+ getOutputDir(), inputFileDetails.getFile());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+
+ incrementalTaskInputs.removed(new org.gradle.api.Action<InputFileDetails>() {
+ @Override
+ public void execute(InputFileDetails inputFileDetails) {
+ try {
+ packagingOptionsFilter.handleRemoved
+ (getOutputDir(), inputFileDetails.getFile());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+
+ }
+
+ /**
+ * Defines a file filter contract which will use {@link PackagingOptions} to take appropriate
+ * action.
+ */
+ @VisibleForTesting
+ static final class FileFilter implements SignedJarBuilder.IZipEntryFilter {
+
+ /**
+ * User's setting for a particular archive entry. This is expressed in the build.gradle
+ * DSL and used by this filter to determine file merging behaviors.
+ */
+ private enum PackagingOption {
+ /**
+ * no action was described for archive entry.
+ */
+ NONE,
+ /**
+ * merge all archive entries with the same archive path.
+ */
+ MERGE,
+ /**
+ * pick to first archive entry with that archive path (not stable).
+ */
+ PICK_FIRST,
+ /**
+ * exclude all archive entries with that archive path.
+ */
+ EXCLUDE
+ }
+
+ @Nullable
+ private final PackagingOptions packagingOptions;
+ @NonNull
+ private final Set<String> excludes;
+ @NonNull
+ private final Set<String> pickFirsts;
+ @NonNull
+ private final MergeJavaResourcesTask owner;
+
+ public FileFilter(@NonNull MergeJavaResourcesTask owner,
+ @Nullable PackagingOptions packagingOptions) {
+ this.owner = owner;
+ this.packagingOptions = packagingOptions;
+ excludes = this.packagingOptions != null ? this.packagingOptions.getExcludes() :
+ Collections.<String>emptySet();
+ pickFirsts = this.packagingOptions != null ? this.packagingOptions.getPickFirsts() :
+ Collections.<String>emptySet();
+ }
+
+ /**
+ * Implementation of the {@link SignedJarBuilder.IZipEntryFilter} contract which only
+ * cares about copying or ignoring files since merging is handled differently.
+ * @param archivePath the archive file path of the entry
+ * @return true if the archive entry satisfies the filter, false otherwise.
+ * @throws ZipAbortException
+ */
+ @Override
+ public boolean checkEntry(@NonNull String archivePath)
+ throws ZipAbortException {
+ PackagingOption packagingOption = getPackagingAction(archivePath);
+ switch(packagingOption) {
+ case EXCLUDE:
+ return false;
+ case PICK_FIRST:
+ List<File> allFiles = getAllFiles(archivePath);
+ return allFiles.isEmpty();
+ case MERGE:
+ case NONE:
+ return true;
+ default:
+ throw new RuntimeException("Unhandled PackagingOption " + packagingOption);
+ }
+ }
+
+ /**
+ * Notification of an incremental file changed since last successful run of the task.
+ *
+ * Usually, we just copy the changed file into the merged folder. However, if the user
+ * specified {@link PackagingOption#PICK_FIRST}, the file will only be copied if it the
+ * first pick. Also, if the user specified {@link PackagingOption#MERGE}, all the files
+ * with the same entry archive path will be re-merged.
+ *
+ * @param outputDir merged resources folder.
+ * @param changedFile changed file located in a temporary expansion folder
+ * @throws IOException
+ */
+ void handleChanged(@NonNull File outputDir, @NonNull File changedFile)
+ throws IOException {
+ String archivePath = getArchivePath(changedFile);
+ PackagingOption packagingOption = getPackagingAction(archivePath);
+ switch (packagingOption) {
+ case EXCLUDE:
+ return;
+ case MERGE:
+ // one of the merged file has changed, re-merge all of them.
+ mergeAll(outputDir, archivePath);
+ return;
+ case PICK_FIRST:
+ copy(changedFile, outputDir, archivePath);
+ return;
+ case NONE:
+ copy(changedFile, outputDir, archivePath);
+ }
+ }
+
+ /**
+ * Notification of a file removal.
+ *
+ * file was removed, we need to check that it was not a pickFirst item (since we
+ * may now need to pick another one) or a merged item since we would need to re-merge
+ * all remaining items.
+ *
+ * @param outputDir expected merged output directory.
+ * @param removedFile removed file from the temporary resources folders.
+ * @throws IOException
+ */
+ public void handleRemoved(@NonNull File outputDir, @NonNull File removedFile)
+ throws IOException {
+
+
+ String archivePath = getArchivePath(removedFile);
+ // first delete the output file, it will be eventually replaced.
+ File outFile = new File(outputDir, archivePath);
+ if (outFile.exists()) {
+ if (!outFile.delete()) {
+ throw new IOException("Cannot delete " + outFile.getAbsolutePath());
+ }
+ }
+ FileFilter.PackagingOption itemPackagingOption = getPackagingAction(archivePath);
+
+ switch(itemPackagingOption) {
+ case PICK_FIRST:
+ // this was a picked up item, make sure we copy the first still available
+ com.google.common.base.Optional<File> firstPick = getFirstPick(archivePath);
+ if (firstPick.isPresent()) {
+ copy(firstPick.get(), outputDir, archivePath);
+ }
+ return;
+ case MERGE:
+ // re-merge all
+ mergeAll(outputDir, archivePath);
+ return;
+ case EXCLUDE:
+ case NONE:
+ // do nothing
+ return;
+ default:
+ throw new RuntimeException("Unhandled package option"
+ + itemPackagingOption);
+
+ }
+ }
+
+ private static void copy(@NonNull File inputFile,
+ @NonNull File outputDir,
+ @NonNull String archivePath) throws IOException {
+
+ File outputFile = new File(outputDir, archivePath);
+ createParentFolderIfNecessary(outputFile);
+ Files.copy(inputFile, outputFile);
+ }
+
+ private void mergeAll(@NonNull File outputDir, @NonNull String archivePath)
+ throws IOException {
+
+ File outputFile = new File(outputDir, archivePath);
+ if (outputFile.exists() && !outputFile.delete()) {
+ throw new RuntimeException("Cannot delete " + outputFile);
+ }
+ createParentFolderIfNecessary(outputFile);
+ List<File> allFiles = getAllFiles(archivePath);
+ if (!allFiles.isEmpty()) {
+ OutputStream os = null;
+ try {
+ os = new BufferedOutputStream(new FileOutputStream(outputFile));
+ // take each file in order and merge them.
+ for (File file : allFiles) {
+ Files.copy(file, os);
+ }
+ } finally {
+ if (os != null) {
+ os.close();
+ }
+ }
+ }
+ }
+
+ private static void createParentFolderIfNecessary(@NonNull File outputFile) {
+ File parentFolder = outputFile.getParentFile();
+ if (!parentFolder.exists()) {
+ if (!parentFolder.mkdirs()) {
+ throw new RuntimeException("Cannot create folder " + parentFolder);
+ }
+ }
+ }
+
+ /**
+ * Return the first file from the temporary expansion folders that satisfy the archive path.
+ * @param archivePath the entry archive path.
+ * @return the first file reference of {@link com.google.common.base.Optional#absent()} if
+ * none exist in any temporary expansion folders.
+ */
+ @NonNull
+ private com.google.common.base.Optional<File> getFirstPick(
+ @NonNull final String archivePath) {
+
+ return com.google.common.base.Optional.fromNullable(
+ forEachExpansionFolder(new FolderAction() {
+ @Nullable
+ @Override
+ public File on(File folder) {
+ File expandedFile = new File(folder, archivePath);
+ if (expandedFile.exists()) {
+ return expandedFile;
+ }
+ return null;
+ }
+ }));
+ }
+
+ /**
+ * Returns all files from temporary expansion folders with the same archive path.
+ * @param archivePath the entry archive path.
+ * @return a list possibly empty of {@link File}s that satisfy the archive path.
+ */
+ @NonNull
+ private List<File> getAllFiles(@NonNull final String archivePath) {
+ final ImmutableList.Builder<File> matchingFiles = ImmutableList.builder();
+ forEachExpansionFolder(new FolderAction() {
+ @Nullable
+ @Override
+ public File on(File folder) {
+ File expandedFile = new File(folder, archivePath);
+ if (expandedFile.exists()) {
+ matchingFiles.add(expandedFile);
+ }
+ return null;
+ }
+ });
+ return matchingFiles.build();
+ }
+
+ /**
+ * An action on a folder.
+ */
+ private interface FolderAction {
+
+ /**
+ * Perform an action on a folder and stop the processing if something is returned
+ * @param folder the folder to perform the action on.
+ * @return a file to stop processing or null to continue to the next expansion folder
+ * if any.
+ */
+ @Nullable
+ File on(File folder);
+ }
+
+ /**
+ * Perform the passed action on each expansion folder.
+ * @param action the action to perform on each folder.
+ * @return a file if any action returned a value, or null if none returned a value.
+ */
+ @Nullable
+ private File forEachExpansionFolder(@NonNull FolderAction action) {
+ for (File expansionParentFolder : owner.getExpandedFolders()) {
+ File[] expansionFolders = expansionParentFolder.listFiles();
+ if (expansionFolders != null) {
+ for (File expansionFolder : expansionFolders) {
+ if (expansionFolder.isDirectory()) {
+ File value = action.on(expansionFolder);
+ if (value != null) {
+ return value;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the expansion folder for an expanded file. This represents the location
+ * where the packaged jar our source directories java resources were extracted into.
+ * @param expandedFile the java resource file.
+ * @return the expansion folder used to extract the java resource into.
+ */
+ @NonNull
+ private File getExpansionFolder(@NonNull final File expandedFile) {
+ File expansionFolder = forEachExpansionFolder(new FolderAction() {
+ @Nullable
+ @Override
+ public File on(File folder) {
+ return expandedFile.getAbsolutePath().startsWith(folder.getAbsolutePath())
+ ? folder : null;
+ }
+ });
+ if (expansionFolder == null) {
+ throw new RuntimeException("Cannot determine expansion folder for " + expandedFile
+ + " with folders " + Joiner.on(",").join(owner.getExpandedFolders()));
+ }
+ return expansionFolder;
+ }
+
+ /**
+ * Determines the archive entry path relative to its expansion folder. The archive entry
+ * path is the path that was used to save the entry in the original .jar file that got
+ * expanded in the expansion folder.
+ * @param expandedFile the expanded file to find the relative archive entry from.
+ * @return the expanded file relative path to its expansion folder.
+ */
+ @NonNull
+ private String getArchivePath(@NonNull File expandedFile) {
+ File expansionFolder = getExpansionFolder(expandedFile);
+ return expandedFile.getAbsolutePath()
+ .substring(expansionFolder.getAbsolutePath().length() + 1);
+ }
+
+ /**
+ * Determine the user's intention for a particular archive entry.
+ * @param archivePath the archive entry
+ * @return a {@link FileFilter.PackagingOption} as provided by the user in the build.gradle
+ */
+ @NonNull
+ private PackagingOption getPackagingAction(@NonNull String archivePath) {
+ if (packagingOptions != null) {
+ if (pickFirsts.contains(archivePath)) {
+ return PackagingOption.PICK_FIRST;
+ }
+ if (packagingOptions.getMerges().contains(archivePath)) {
+ return PackagingOption.MERGE;
+ }
+ if (excludes.contains(archivePath)) {
+ return PackagingOption.EXCLUDE;
+ }
+ }
+ return PackagingOption.NONE;
+ }
+ }
+
+ @NonNull
+ @Override
+ public ImmutableList<JavaResourcesLocation> getJavaResourcesLocations() {
+ return ImmutableList.of(new JavaResourcesLocation(Type.FOLDER, getOutputDir()));
+ }
+
+ public static class Config implements TaskConfigAction<MergeJavaResourcesTask> {
+
+ private final VariantScope scope;
+
+ public Config(VariantScope variantScope) {
+ this.scope = variantScope;
+ }
+
+ @Override
+ public String getName() {
+ return scope.getTaskName("merge", "JavaResources");
+ }
+
+ @Override
+ public Class<MergeJavaResourcesTask> getType() {
+ return MergeJavaResourcesTask.class;
+ }
+
+ @Override
+ public void execute(MergeJavaResourcesTask mergeJavaResourcesTask) {
+ mergeJavaResourcesTask.setVariantName(scope.getVariantConfiguration().getFullName());
+
+ ConventionMappingHelper.map(mergeJavaResourcesTask, "sourceJavaResourcesFolder",
+ new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ return scope.getSourceFoldersJavaResDestinationDir().exists()
+ ? scope.getSourceFoldersJavaResDestinationDir()
+ : null;
+ }
+ });
+
+ ConventionMappingHelper.map(mergeJavaResourcesTask, "packagedJarsJavaResourcesFolder",
+ new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ return scope.getPackagedJarsJavaResDestinationDir().exists()
+ ? scope.getPackagedJarsJavaResDestinationDir()
+ : null;
+ }
+ });
+
+ File outputDir = scope.getJavaResourcesDestinationDir();
+ if (!outputDir.exists() && !outputDir.mkdirs()) {
+ throw new RuntimeException("Cannot create output directory " + outputDir);
+ }
+ mergeJavaResourcesTask.outputDir = outputDir;
+
+ PackagingOptions packagingOptions =
+ scope.getGlobalScope().getExtension().getPackagingOptions();
+ mergeJavaResourcesTask.packagingOptionsFilter =
+ new FileFilter(mergeJavaResourcesTask, packagingOptions);
+ mergeJavaResourcesTask.packagingOptions = packagingOptions;
+ scope.setPackagingOptionsFilter(mergeJavaResourcesTask.packagingOptionsFilter);
+ }
+ }
+}
+
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/MockableAndroidJarTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/MockableAndroidJarTask.java
index cf8972b..bf70530 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/MockableAndroidJarTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/MockableAndroidJarTask.java
@@ -17,9 +17,8 @@
package com.android.build.gradle.internal.tasks;
import com.android.builder.testing.MockableJarGenerator;
-import com.android.sdklib.internal.project.ProjectProperties;
-import groovy.transform.CompileStatic;
+import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.OutputFile;
@@ -31,7 +30,7 @@
/**
* Task for generating a mockable android.jar
*/
-public class MockableAndroidJarTask extends DefaultAndroidTask {
+public class MockableAndroidJarTask extends DefaultTask {
private File mAndroidJar;
@@ -56,13 +55,25 @@
return mReturnDefaultValues;
}
+ public void setReturnDefaultValues(boolean returnDefaultValues) {
+ mReturnDefaultValues = returnDefaultValues;
+ }
+
@OutputFile
public File getOutputFile() {
return mOutputFile;
}
+ public void setOutputFile(File outputFile) {
+ mOutputFile = outputFile;
+ }
+
@InputFile
public File getAndroidJar() {
return mAndroidJar;
}
+
+ public void setAndroidJar(File androidJar) {
+ mAndroidJar = androidJar;
+ }
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/NdkTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/NdkTask.groovy
index 5d40f2b..afe5b5e 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/NdkTask.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/NdkTask.groovy
@@ -16,7 +16,7 @@
package com.android.build.gradle.internal.tasks
-import com.android.build.gradle.internal.core.NdkConfig
+import com.android.build.gradle.internal.dsl.CoreNdkOptions
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
@@ -25,7 +25,7 @@
*/
class NdkTask extends BaseTask {
- NdkConfig ndkConfig
+ CoreNdkOptions ndkConfig
@Input @Optional
String getModuleName() {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareDependenciesTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareDependenciesTask.groovy
deleted file mode 100755
index 2207d19..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareDependenciesTask.groovy
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.internal.tasks
-
-import com.android.build.gradle.internal.dependency.DependencyChecker
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.builder.model.ApiVersion
-import com.android.builder.model.SyncIssue
-import com.android.sdklib.SdkVersionInfo
-import com.android.utils.Pair
-import groovy.transform.CompileStatic
-import org.gradle.api.GradleException
-import org.gradle.api.tasks.TaskAction
-
-@CompileStatic
-public class PrepareDependenciesTask extends BaseTask {
- BaseVariantData variant
- final List<DependencyChecker> checkers = []
- final Set<Pair<Integer, String>> androidDependencies = []
-
- public void addDependency(Pair<Integer, String> api) {
- androidDependencies.add(api)
- }
-
- @TaskAction
- protected void prepare() {
- ApiVersion minSdkVersion = variant.variantConfiguration.minSdkVersion
- int minSdk = 1
- if (minSdkVersion != null) {
- if (minSdkVersion.getCodename() != null) {
- minSdk = SdkVersionInfo.getApiByBuildCode(minSdkVersion.getCodename(), true)
- } else {
- minSdk = minSdkVersion.getApiLevel()
- }
- }
-
- boolean foundError = false;
-
- for (DependencyChecker checker : checkers) {
- checker.legacyApiLevels.each { mavenVersion, api ->
- if (api > minSdk) {
- foundError = true;
- def configurationName = checker.configurationDependencies.name.capitalize()
- logger.error(
- "Variant ${configurationName} has a dependency on version ${mavenVersion.version} " +
- "of the legacy ${mavenVersion.group} Maven artifact, which corresponds to " +
- "API level ${api}. This is not compatible with min SDK of this module, " +
- "which is ${minSdk}. Please use the 'gradle dependencies' task to debug your " +
- "dependencies graph.")
- }
- }
-
- for (SyncIssue syncIssue : checker.getSyncIssues()) {
- foundError = true
- logger.error(syncIssue.message);
- }
- }
-
- if (foundError) {
- throw new GradleException("Dependency Error. See console for details.");
- }
-
- }
-
- def addChecker(DependencyChecker checker) {
- checkers.add(checker)
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareDependenciesTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareDependenciesTask.java
new file mode 100755
index 0000000..e605002
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareDependenciesTask.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle.internal.tasks;
+
+import com.android.build.gradle.internal.dependency.DependencyChecker;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.builder.model.ApiVersion;
+import com.android.builder.model.SyncIssue;
+import com.android.sdklib.SdkVersionInfo;
+import com.android.utils.StringHelper;
+import com.google.common.collect.Lists;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.artifacts.ModuleVersionIdentifier;
+import org.gradle.api.tasks.TaskAction;
+
+import java.util.List;
+import java.util.Map;
+
+public class PrepareDependenciesTask extends BaseTask {
+
+ private BaseVariantData variant;
+ private final List<DependencyChecker> checkers = Lists.newArrayList();
+
+ @TaskAction
+ protected void prepare() {
+ ApiVersion minSdkVersion = variant.getVariantConfiguration().getMinSdkVersion();
+ int minSdk = 1;
+ if (minSdkVersion.getCodename() != null) {
+ minSdk = SdkVersionInfo.getApiByBuildCode(minSdkVersion.getCodename(), true);
+ } else {
+ minSdk = minSdkVersion.getApiLevel();
+ }
+
+ boolean foundError = false;
+
+ for (DependencyChecker checker : checkers) {
+ for (Map.Entry<ModuleVersionIdentifier, Integer> entry :
+ checker.getLegacyApiLevels().entrySet()) {
+ ModuleVersionIdentifier mavenVersion = entry.getKey();
+ int api = entry.getValue();
+ if (api > minSdk) {
+ foundError = true;
+ String configurationName = checker.getConfigurationDependencies().getName();
+ getLogger().error(
+ "Variant {} has a dependency on version {} of the legacy {} Maven " +
+ "artifact, which corresponds to API level {}. This is not " +
+ "compatible with min SDK of this module, which is {}. " +
+ "Please use the 'gradle dependencies' task to debug your " +
+ "dependencies graph.",
+ StringHelper.capitalize(configurationName),
+ mavenVersion.getVersion(),
+ mavenVersion.getGroup(),
+ api,
+ minSdk);
+ }
+ }
+
+ for (SyncIssue syncIssue : checker.getSyncIssues()) {
+ foundError = true;
+ getLogger().error(syncIssue.getMessage());
+ }
+ }
+
+ if (foundError) {
+ throw new GradleException("Dependency Error. See console for details.");
+ }
+
+ }
+
+ public void addChecker(DependencyChecker checker) {
+ checkers.add(checker);
+ }
+
+ public BaseVariantData getVariant() {
+ return variant;
+ }
+
+ public void setVariant(BaseVariantData variant) {
+ this.variant = variant;
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareLibraryTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareLibraryTask.groovy
deleted file mode 100644
index e3645be..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareLibraryTask.groovy
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.internal.tasks
-
-import com.android.build.gradle.internal.LibraryCache
-import com.android.builder.Version
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.TaskAction
-
-public class PrepareLibraryTask extends DefaultAndroidTask {
- @InputFile
- File bundle
-
- @OutputDirectory
- File explodedDir
-
- @TaskAction
- def prepare() {
- //LibraryCache.getCache().unzipLibrary(this.name, project, getBundle(), getExplodedDir())
- LibraryCache.unzipAar(getBundle(), getExplodedDir(), project)
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareLibraryTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareLibraryTask.java
new file mode 100644
index 0000000..1ac6372
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/PrepareLibraryTask.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle.internal.tasks;
+
+import com.android.build.gradle.internal.LibraryCache;
+
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+
+public class PrepareLibraryTask extends DefaultAndroidTask {
+ private File bundle;
+ private File explodedDir;
+
+ @TaskAction
+ public void prepare() {
+ //LibraryCache.getCache().unzipLibrary(this.name, project, getBundle(), getExplodedDir())
+ LibraryCache.unzipAar(getBundle(), getExplodedDir(), getProject());
+ }
+
+ @InputFile
+ public File getBundle() {
+ return bundle;
+ }
+
+ @OutputDirectory
+ public File getExplodedDir() {
+ return explodedDir;
+ }
+
+ public void setBundle(File bundle) {
+ this.bundle = bundle;
+ }
+
+ public void setExplodedDir(File explodedDir) {
+ this.explodedDir = explodedDir;
+ }
+
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SigningReportTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SigningReportTask.groovy
deleted file mode 100644
index 5f2f0b3..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SigningReportTask.groovy
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal.tasks
-
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.builder.model.SigningConfig
-import com.android.ide.common.signing.CertificateInfo
-import com.android.ide.common.signing.KeystoreHelper
-import com.android.ide.common.signing.KeytoolException
-import com.google.common.collect.Maps
-import org.gradle.api.tasks.TaskAction
-import org.gradle.logging.StyledTextOutput
-import org.gradle.logging.StyledTextOutputFactory
-
-import java.security.MessageDigest
-import java.security.NoSuchAlgorithmException
-import java.security.cert.Certificate
-import java.security.cert.CertificateEncodingException
-import java.text.DateFormat
-
-import static org.gradle.logging.StyledTextOutput.Style.Description
-import static org.gradle.logging.StyledTextOutput.Style.Failure
-import static org.gradle.logging.StyledTextOutput.Style.Identifier
-import static org.gradle.logging.StyledTextOutput.Style.Normal
-
-/**
- * Report tasks displaying the signing information for all variants.
- */
-class SigningReportTask extends BaseTask {
-
- private Set<BaseVariantData> variants = [];
-
- @TaskAction
- public void generate() throws IOException {
-
- StyledTextOutput textOutput = getServices().get(
- StyledTextOutputFactory.class).create(getClass())
-
- Map<SigningConfig, SigningInfo> cache = Maps.newHashMap()
-
- for (BaseVariantData variant : variants) {
- textOutput.withStyle(Identifier).text("Variant: ")
- textOutput.withStyle(Description).text(variant.name)
- textOutput.println()
-
- // get the data
- com.android.build.gradle.internal.dsl.SigningConfig signingConfig = (com.android.build.gradle.internal.dsl.SigningConfig) variant.variantConfiguration.signingConfig
- if (signingConfig == null) {
- textOutput.withStyle(Identifier).text("Config: ")
- textOutput.withStyle(Normal).text("none")
- textOutput.println()
- } else {
- SigningInfo signingInfo = getSigningInfo(signingConfig, cache)
-
-
- textOutput.withStyle(Identifier).text("Config: ")
- textOutput.withStyle(Description).text(signingConfig.name)
- textOutput.println()
-
- textOutput.withStyle(Identifier).text("Store: ")
- textOutput.withStyle(Description).text(signingConfig.getStoreFile())
- textOutput.println()
-
- textOutput.withStyle(Identifier).text("Alias: ")
- textOutput.withStyle(Description).text(signingConfig.getKeyAlias())
- textOutput.println()
-
- if (signingInfo.isValid()) {
- if (signingInfo.error != null) {
- textOutput.withStyle(Identifier).text("Error: ")
- textOutput.withStyle(Failure).text(signingInfo.error)
- textOutput.println()
- } else {
- textOutput.withStyle(Identifier).text("MD5: ")
- textOutput.withStyle(Description).text(signingInfo.md5)
- textOutput.println()
-
- textOutput.withStyle(Identifier).text("SHA1: ")
- textOutput.withStyle(Description).text(signingInfo.sha1)
- textOutput.println()
-
- textOutput.withStyle(Identifier).text("Valid until: ")
- DateFormat df = DateFormat.getDateInstance(DateFormat.FULL)
- textOutput.withStyle(Description).text(df.format(signingInfo.notAfter))
- textOutput.println()
- }
- }
- }
-
- textOutput.withStyle(Normal).text("----------")
- textOutput.println()
- }
- }
-
- /**
- * Sets the configurations to generate the report for.
- *
- * @param configurations The configuration. Must not be null.
- */
- public void setVariants(Collection<BaseVariantData> variants) {
- this.variants.addAll(variants);
- }
-
- private static SigningInfo getSigningInfo(SigningConfig signingConfig,
- Map<SigningConfig, SigningInfo> cache) {
- SigningInfo signingInfo = cache.get(signingConfig)
-
- if (signingInfo == null) {
- signingInfo = new SigningInfo()
-
- if (signingConfig.isSigningReady()) {
- try {
- CertificateInfo certificateInfo = KeystoreHelper.getCertificateInfo(
- signingConfig.getStoreType(), signingConfig.getStoreFile(),
- signingConfig.getStorePassword(), signingConfig.getKeyPassword(),
- signingConfig.getKeyAlias())
- if (certificateInfo != null) {
- signingInfo.md5 = getFingerprint(certificateInfo.certificate, "MD5")
- signingInfo.sha1 = getFingerprint(certificateInfo.certificate, "SHA1")
- signingInfo.notAfter = certificateInfo.certificate.notAfter
- } else {
-
- }
- } catch (KeytoolException e) {
- signingInfo.error = e.getMessage()
- } catch (FileNotFoundException e) {
- signingInfo.error = "Missing keystore"
- }
- }
-
- cache.put(signingConfig, signingInfo)
- }
-
- return signingInfo
- }
-
- private final static class SigningInfo {
- String md5
- String sha1
- Date notAfter
- String error
-
- boolean isValid() {
- return md5 != null || error != null
- }
- }
-
- /**
- * Returns the {@link Certificate} fingerprint as returned by <code>keytool</code>.
- *
- * @param certificate
- * @param hashAlgorithm
- */
- public static String getFingerprint(Certificate cert, String hashAlgorithm) {
- if (cert == null) {
- return null;
- }
- try {
- MessageDigest digest = MessageDigest.getInstance(hashAlgorithm);
- return toHexadecimalString(digest.digest(cert.getEncoded()));
- } catch(NoSuchAlgorithmException e) {
- // ignore
- } catch(CertificateEncodingException e) {
- // ignore
- }
- return null;
- }
-
- private static String toHexadecimalString(byte[] value) {
- StringBuilder sb = new StringBuilder();
- int len = value.length;
- for (int i = 0; i < len; i++) {
- int num = ((int) value[i]) & 0xff;
- if (num < 0x10) {
- sb.append('0');
- }
- sb.append(Integer.toHexString(num));
- if (i < len - 1) {
- sb.append(':');
- }
- }
- return sb.toString().toUpperCase(Locale.US);
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SigningReportTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SigningReportTask.java
new file mode 100644
index 0000000..dbb8167
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SigningReportTask.java
@@ -0,0 +1,203 @@
+/*
+ * 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.build.gradle.internal.tasks;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.builder.model.SigningConfig;
+import com.android.ide.common.signing.CertificateInfo;
+import com.android.ide.common.signing.KeystoreHelper;
+import com.android.ide.common.signing.KeytoolException;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.logging.StyledTextOutput;
+import org.gradle.logging.StyledTextOutputFactory;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.text.DateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import static org.gradle.logging.StyledTextOutput.Style.Description;
+import static org.gradle.logging.StyledTextOutput.Style.Failure;
+import static org.gradle.logging.StyledTextOutput.Style.Identifier;
+import static org.gradle.logging.StyledTextOutput.Style.Normal;
+
+/**
+ * Report tasks displaying the signing information for all variants.
+ */
+public class SigningReportTask extends DefaultTask {
+
+ private Set<BaseVariantData> variants = Sets.newHashSet();
+
+ @TaskAction
+ public void generate() throws IOException {
+
+ StyledTextOutput textOutput = getServices().get(
+ StyledTextOutputFactory.class).create(getClass());
+
+ Map<SigningConfig, SigningInfo> cache = Maps.newHashMap();
+
+ for (BaseVariantData variant : variants) {
+ textOutput.withStyle(Identifier).text("Variant: ");
+ textOutput.withStyle(Description).text(variant.getName());
+ textOutput.println();
+
+ // get the data
+ SigningConfig signingConfig = variant.getVariantConfiguration().getSigningConfig();
+ if (signingConfig == null) {
+ textOutput.withStyle(Identifier).text("Config: ");
+ textOutput.withStyle(Normal).text("none");
+ textOutput.println();
+ } else {
+ SigningInfo signingInfo = getSigningInfo(signingConfig, cache);
+
+
+ textOutput.withStyle(Identifier).text("Config: ");
+ textOutput.withStyle(Description).text(signingConfig.getName());
+ textOutput.println();
+
+ textOutput.withStyle(Identifier).text("Store: ");
+ textOutput.withStyle(Description).text(signingConfig.getStoreFile());
+ textOutput.println();
+
+ textOutput.withStyle(Identifier).text("Alias: ");
+ textOutput.withStyle(Description).text(signingConfig.getKeyAlias());
+ textOutput.println();
+
+ if (signingInfo.isValid()) {
+ if (signingInfo.error != null) {
+ textOutput.withStyle(Identifier).text("Error: ");
+ textOutput.withStyle(Failure).text(signingInfo.error);
+ textOutput.println();
+ } else {
+ textOutput.withStyle(Identifier).text("MD5: ");
+ textOutput.withStyle(Description).text(signingInfo.md5);
+ textOutput.println();
+
+ textOutput.withStyle(Identifier).text("SHA1: ");
+ textOutput.withStyle(Description).text(signingInfo.sha1);
+ textOutput.println();
+
+ textOutput.withStyle(Identifier).text("Valid until: ");
+ DateFormat df = DateFormat.getDateInstance(DateFormat.FULL);
+ textOutput.withStyle(Description).text(df.format(signingInfo.notAfter));
+ textOutput.println();
+ }
+ }
+ }
+
+ textOutput.withStyle(Normal).text("----------");
+ textOutput.println();
+ }
+ }
+
+ /**
+ * Sets the configurations to generate the report for.
+ */
+ public void setVariants(@NonNull Collection<? extends BaseVariantData> variants) {
+ this.variants.addAll(variants);
+ }
+
+ private static SigningInfo getSigningInfo(
+ @NonNull SigningConfig signingConfig,
+ @NonNull Map<SigningConfig, SigningInfo> cache) {
+ SigningInfo signingInfo = cache.get(signingConfig);
+
+ if (signingInfo == null) {
+ signingInfo = new SigningInfo();
+
+ if (signingConfig.isSigningReady()) {
+ try {
+ CertificateInfo certificateInfo = KeystoreHelper.getCertificateInfo(
+ signingConfig.getStoreType(), signingConfig.getStoreFile(),
+ signingConfig.getStorePassword(), signingConfig.getKeyPassword(),
+ signingConfig.getKeyAlias());
+ if (certificateInfo != null) {
+ signingInfo.md5 = getFingerprint(certificateInfo.getCertificate(), "MD5");
+ signingInfo.sha1 = getFingerprint(certificateInfo.getCertificate(), "SHA1");
+ signingInfo.notAfter = certificateInfo.getCertificate().getNotAfter();
+ }
+ } catch (KeytoolException e) {
+ signingInfo.error = e.getMessage();
+ } catch (FileNotFoundException e) {
+ signingInfo.error = "Missing keystore";
+ }
+ }
+
+ cache.put(signingConfig, signingInfo);
+ }
+
+ return signingInfo;
+ }
+
+ private static final class SigningInfo {
+ String md5;
+ String sha1;
+ Date notAfter;
+ String error;
+
+ boolean isValid() {
+ return md5 != null || error != null;
+ }
+ }
+
+ /**
+ * Returns the {@link Certificate} fingerprint as returned by <code>keytool</code>.
+ */
+ public static String getFingerprint(Certificate cert, String hashAlgorithm) {
+ if (cert == null) {
+ return null;
+ }
+ try {
+ MessageDigest digest = MessageDigest.getInstance(hashAlgorithm);
+ return toHexadecimalString(digest.digest(cert.getEncoded()));
+ } catch(NoSuchAlgorithmException e) {
+ // ignore
+ } catch(CertificateEncodingException e) {
+ // ignore
+ }
+ return null;
+ }
+
+ private static String toHexadecimalString(byte[] value) {
+ StringBuilder sb = new StringBuilder();
+ int len = value.length;
+ for (int i = 0; i < len; i++) {
+ int num = ((int) value[i]) & 0xff;
+ if (num < 0x10) {
+ sb.append('0');
+ }
+ sb.append(Integer.toHexString(num));
+ if (i < len - 1) {
+ sb.append(':');
+ }
+ }
+ return sb.toString().toUpperCase(Locale.US);
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SourceSetsTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SourceSetsTask.java
index 112bd0a..c242e39 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SourceSetsTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/SourceSetsTask.java
@@ -16,13 +16,14 @@
package com.android.build.gradle.internal.tasks;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
import com.android.build.gradle.api.AndroidSourceDirectorySet;
import com.android.build.gradle.api.AndroidSourceSet;
import com.android.builder.core.VariantType;
import com.google.common.collect.Lists;
import org.gradle.api.Project;
+import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.diagnostics.AbstractReportTask;
import org.gradle.api.tasks.diagnostics.internal.ReportRenderer;
import org.gradle.api.tasks.diagnostics.internal.TextReportRenderer;
@@ -40,16 +41,26 @@
private final TextReportRenderer mRenderer = new TextReportRenderer();
+ private AndroidConfig config;
+
@Override
protected ReportRenderer getRenderer() {
return mRenderer;
}
+ @Input
+ public AndroidConfig getConfig() {
+ return config;
+ }
+
+ public void setConfig(AndroidConfig config) {
+ this.config = config;
+ }
+
@Override
protected void generate(Project project) throws IOException {
- BaseExtension extension = project.getExtensions().findByType(BaseExtension.class);
- if (extension != null) {
- for (AndroidSourceSet sourceSet : extension.getSourceSets()) {
+ if (config != null) {
+ for (AndroidSourceSet sourceSet : config.getSourceSets()) {
mRenderer.getBuilder().subheading(sourceSet.getName());
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/TestServerTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/TestServerTask.groovy
deleted file mode 100644
index a1d0841..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/TestServerTask.groovy
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal.tasks
-
-import com.android.builder.testing.api.TestServer
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.TaskAction
-
-/**
- * Task sending APKs out to a {@link TestServer}
- */
-public class TestServerTask extends DefaultAndroidTask {
-
- @InputFile
- File testApk
-
- @InputFile @Optional
- File testedApk
-
- @Input
- String variantName
-
- TestServer testServer
-
- @TaskAction
- void sendToServer() {
- testServer.uploadApks(getVariantName(), getTestApk(), getTestedApk())
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/TestServerTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/TestServerTask.java
new file mode 100644
index 0000000..9e248e4
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/TestServerTask.java
@@ -0,0 +1,79 @@
+/*
+ * 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.build.gradle.internal.tasks;
+
+import com.android.annotations.NonNull;
+import com.android.builder.testing.api.TestServer;
+import com.google.common.base.Preconditions;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+
+/**
+ * Task sending APKs out to a {@link TestServer}
+ */
+public class TestServerTask extends DefaultAndroidTask {
+
+ private File testApk;
+
+ File testedApk;
+
+ TestServer testServer;
+
+ @TaskAction
+ public void sendToServer() {
+ testServer.uploadApks(getVariantName(), getTestApk(), getTestedApk());
+ }
+
+ @InputFile
+ public File getTestApk() {
+ return testApk;
+ }
+
+ public void setTestApk(File testApk) {
+ this.testApk = testApk;
+ }
+
+ @InputFile @Optional
+ public File getTestedApk() {
+ return testedApk;
+ }
+
+ public void setTestedApk(File testedApk) {
+ this.testedApk = testedApk;
+ }
+
+ @NonNull
+ @Override
+ @Input
+ public String getVariantName() {
+ return Preconditions.checkNotNull(super.getVariantName(),
+ "Test server task must have a variant name.");
+ }
+
+ public TestServer getTestServer() {
+ return testServer;
+ }
+
+ public void setTestServer(TestServer testServer) {
+ this.testServer = testServer;
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/UninstallTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/UninstallTask.java
index e83b8db..73adc95 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/UninstallTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/UninstallTask.java
@@ -141,6 +141,7 @@
uninstallTask.setGroup(TaskManager.INSTALL_GROUP);
uninstallTask.setVariant(scope.getVariantData());
uninstallTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+ uninstallTask.setVariantName(scope.getVariantConfiguration().getFullName());
uninstallTask.setTimeOutInMs(
scope.getGlobalScope().getExtension().getAdbOptions().getTimeOutInMs());
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ValidateSigningTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ValidateSigningTask.groovy
deleted file mode 100644
index feabc35..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ValidateSigningTask.groovy
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal.tasks
-
-import com.android.build.gradle.internal.LoggerWrapper
-import com.android.builder.model.SigningConfig
-import com.android.ide.common.signing.KeystoreHelper
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.TaskAction
-import org.gradle.tooling.BuildException
-
-/**
- * A validate task that creates the debug keystore if it's missing.
- * It only creates it if it's in the default debug keystore location.
- *
- * It's linked to a given SigningConfig
- *
- */
-class ValidateSigningTask extends BaseTask {
-
- SigningConfig signingConfig
-
- /**
- * Annotated getter for task input.
- *
- * This is an Input and not an InputFile because the file might not exist.
- * This is not actually used by the task, this is only for Gradle to check inputs.
- *
- * @return the path of the keystore.
- */
- @Input @Optional
- String getStoreLocation() {
- File f = signingConfig.getStoreFile()
- if (f != null) {
- return f.absolutePath
- }
- return null;
- }
-
- @TaskAction
- void validate() {
-
- File storeFile = signingConfig.getStoreFile()
- if (storeFile != null && !storeFile.exists()) {
- if (KeystoreHelper.defaultDebugKeystoreLocation().equals(storeFile.absolutePath)) {
- getLogger().info("Creating default debug keystore at %s" + storeFile.absolutePath)
- if (!KeystoreHelper.createDebugStore(
- signingConfig.getStoreType(), signingConfig.getStoreFile(),
- signingConfig.getStorePassword(), signingConfig.getKeyPassword(),
- signingConfig.getKeyAlias(), getILogger())) {
- throw new BuildException("Unable to recreate missing debug keystore.", null);
- }
- }
- }
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ValidateSigningTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ValidateSigningTask.java
new file mode 100644
index 0000000..0c3c898
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/ValidateSigningTask.java
@@ -0,0 +1,100 @@
+/*
+ * 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.build.gradle.internal.tasks;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.android.builder.model.SigningConfig;
+import com.android.ide.common.signing.KeystoreHelper;
+import com.android.ide.common.signing.KeytoolException;
+import com.android.prefs.AndroidLocation;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.tooling.BuildException;
+
+import java.io.File;
+
+/**
+ * A validate task that creates the debug keystore if it's missing.
+ * It only creates it if it's in the default debug keystore location.
+ *
+ * It's linked to a given SigningConfig
+ *
+ */
+public class ValidateSigningTask extends BaseTask {
+
+ private SigningConfig signingConfig;
+
+ public void setSigningConfig(SigningConfig signingConfig) {
+ this.signingConfig = signingConfig;
+ }
+
+ public SigningConfig getSigningConfig() {
+ return signingConfig;
+ }
+
+ /**
+ * Annotated getter for task input.
+ *
+ * This is an Input and not an InputFile because the file might not exist.
+ * This is not actually used by the task, this is only for Gradle to check inputs.
+ *
+ * @return the path of the keystore.
+ */
+ @Input @Optional
+ public String getStoreLocation() {
+ File f = signingConfig.getStoreFile();
+ if (f != null) {
+ return f.getAbsolutePath();
+ }
+ return null;
+ }
+
+ @TaskAction
+ public void validate() throws AndroidLocation.AndroidLocationException, KeytoolException {
+ File storeFile = signingConfig.getStoreFile();
+ if (storeFile == null) {
+ throw new IllegalArgumentException(
+ "Keystore file not set for signing config " + signingConfig.getName());
+ }
+ if (!storeFile.exists()) {
+ if (KeystoreHelper.defaultDebugKeystoreLocation().equals(storeFile.getAbsolutePath())) {
+ checkState(signingConfig.isSigningReady(), "Debug signing config not ready.");
+
+ getLogger().info(
+ "Creating default debug keystore at {}",
+ storeFile.getAbsolutePath());
+
+ //noinspection ConstantConditions - isSigningReady() called above
+ if (!KeystoreHelper.createDebugStore(
+ signingConfig.getStoreType(), signingConfig.getStoreFile(),
+ signingConfig.getStorePassword(), signingConfig.getKeyPassword(),
+ signingConfig.getKeyAlias(), getILogger())) {
+ throw new BuildException("Unable to recreate missing debug keystore.", null);
+ }
+ } else {
+ throw new IllegalArgumentException(
+ String.format(
+ "Keystore file %s not found for signing config '%s'.",
+ storeFile.getAbsolutePath(),
+ signingConfig.getName()));
+ }
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateMainDexList.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateMainDexList.groovy
index 122960c..c5bf7a4 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateMainDexList.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateMainDexList.groovy
@@ -18,22 +18,20 @@
package com.android.build.gradle.internal.tasks.multidex
-import com.android.build.gradle.internal.TaskManager
+import com.android.build.gradle.internal.PostCompilationData
import com.android.build.gradle.internal.scope.ConventionMappingHelper
import com.android.build.gradle.internal.scope.TaskConfigAction
import com.android.build.gradle.internal.scope.VariantScope
import com.android.build.gradle.internal.tasks.BaseTask
-import com.android.builder.model.AndroidProject
import com.google.common.base.Charsets
import com.google.common.base.Joiner
import com.google.common.io.Files
-import org.gradle.api.Task
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
+import java.util.concurrent.Callable
/**
* Task to create the main (non-obfuscated) list of classes to keep.
@@ -106,11 +104,11 @@
VariantScope scope;
- private Closure<List<File>> inputFiles
+ private Callable<List<File>> inputFiles
- ConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
+ ConfigAction(VariantScope scope, PostCompilationData pcData) {
this.scope = scope
- inputFiles = pcData.inputFiles;
+ inputFiles = pcData.inputFilesCallable;
}
@Override
@@ -126,9 +124,10 @@
@Override
void execute(CreateMainDexList createMainDexList) {
createMainDexList.androidBuilder = scope.globalScope.androidBuilder
+ createMainDexList.setVariantName(scope.getVariantConfiguration().getFullName())
def files = inputFiles
- createMainDexList.allClassesJarFile = files().first()
+ createMainDexList.allClassesJarFile = files.call().first()
ConventionMappingHelper.map(createMainDexList, "componentsJarFile") {
scope.getProguardComponentsJarFile()
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateManifestKeepList.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateManifestKeepList.groovy
index b4dbd8c..894352f 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateManifestKeepList.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/CreateManifestKeepList.groovy
@@ -18,7 +18,7 @@
package com.android.build.gradle.internal.tasks.multidex
-import com.android.build.gradle.internal.TaskManager
+import com.android.build.gradle.internal.PostCompilationData
import com.android.build.gradle.internal.scope.ConventionMappingHelper
import com.android.build.gradle.internal.scope.TaskConfigAction
import com.android.build.gradle.internal.scope.VariantScope
@@ -36,6 +36,7 @@
import javax.xml.parsers.SAXParser
import javax.xml.parsers.SAXParserFactory
+
class CreateManifestKeepList extends DefaultAndroidTask {
@InputFile
@@ -120,9 +121,9 @@
public static class ConfigAction implements TaskConfigAction<CreateManifestKeepList> {
VariantScope scope;
- TaskManager.PostCompilationData pcData;
+ PostCompilationData pcData;
- ConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
+ ConfigAction(VariantScope scope, PostCompilationData pcData) {
this.scope = scope
this.pcData = pcData
}
@@ -139,6 +140,8 @@
@Override
void execute(CreateManifestKeepList manifestKeepListTask) {
+ manifestKeepListTask.setVariantName(scope.getVariantConfiguration().getFullName())
+
// since all the output have the same manifest, besides the versionCode,
// we can take any of the output and use that.
final BaseVariantOutputData output = scope.variantData.outputs.get(0)
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/JarMergingTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/JarMergingTask.groovy
index 78b968f..0a81415 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/JarMergingTask.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/JarMergingTask.groovy
@@ -14,12 +14,10 @@
* limitations under the License.
*/
-
-
package com.android.build.gradle.internal.tasks.multidex
import com.android.build.gradle.internal.tasks.DefaultAndroidTask
-import com.android.build.gradle.internal.TaskManager
+import com.android.build.gradle.internal.PostCompilationData
import com.android.build.gradle.internal.scope.ConventionMappingHelper
import com.android.build.gradle.internal.scope.TaskConfigAction
import com.android.build.gradle.internal.scope.VariantScope
@@ -32,6 +30,7 @@
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
+import java.util.concurrent.Callable
import java.util.jar.JarEntry
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry
@@ -161,14 +160,14 @@
private VariantScope scope
- private Closure<File> inputDir;
+ private Callable<File> inputDir;
- private Closure<List<File>> inputLibraries;
+ private Callable<List<File>> inputLibraries;
- ConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
+ ConfigAction(VariantScope scope, PostCompilationData pcData) {
this.scope = scope
- inputDir = pcData.inputDir
- inputLibraries = pcData.inputLibraries
+ inputDir = pcData.inputDirCallable
+ inputLibraries = pcData.inputLibrariesCallable
}
@Override
@@ -183,6 +182,7 @@
@Override
void execute(JarMergingTask jarMergingTask) {
+ jarMergingTask.setVariantName(scope.getVariantConfiguration().getFullName())
ConventionMappingHelper.map(jarMergingTask, "inputJars", inputLibraries)
ConventionMappingHelper.map(jarMergingTask, "inputDir", inputDir)
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/RetraceMainDexList.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/RetraceMainDexList.groovy
index 1e46d97..23fdade 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/RetraceMainDexList.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/tasks/multidex/RetraceMainDexList.groovy
@@ -18,7 +18,7 @@
package com.android.build.gradle.internal.tasks.multidex
-import com.android.build.gradle.internal.TaskManager
+import com.android.build.gradle.internal.PostCompilationData
import com.android.build.gradle.internal.scope.ConventionMappingHelper
import com.android.build.gradle.internal.scope.TaskConfigAction
import com.android.build.gradle.internal.scope.VariantScope
@@ -128,9 +128,9 @@
VariantScope scope;
- TaskManager.PostCompilationData pcData;
+ PostCompilationData pcData;
- ConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
+ ConfigAction(VariantScope scope, PostCompilationData pcData) {
this.scope = scope
this.pcData = pcData
}
@@ -147,6 +147,7 @@
@Override
void execute(RetraceMainDexList retraceTask) {
+ retraceTask.setVariantName(scope.getVariantConfiguration().getFullName())
ConventionMappingHelper.map(retraceTask, "mainDexListFile") { scope.getMainDexListFile() }
ConventionMappingHelper.map(retraceTask, "mappingFile") {
scope.variantData.getMappingFile()
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/AbstractTestDataImpl.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/AbstractTestDataImpl.java
index b602863..8bada32 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/AbstractTestDataImpl.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/AbstractTestDataImpl.java
@@ -16,12 +16,17 @@
package com.android.build.gradle.internal.test;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import com.android.annotations.NonNull;
import com.android.builder.core.VariantConfiguration;
import com.android.builder.model.ApiVersion;
import com.android.builder.testing.TestData;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
import java.util.Locale;
+import java.util.Map;
/**
* Common implementation of {@link TestData} for embedded test projects (in androidTest folder)
@@ -30,10 +35,14 @@
public abstract class AbstractTestDataImpl implements TestData {
@NonNull
- private final VariantConfiguration testVariantConfig;
+ private final VariantConfiguration<?, ?, ?> testVariantConfig;
- public AbstractTestDataImpl(@NonNull VariantConfiguration testVariantConfig) {
- this.testVariantConfig = testVariantConfig;
+ @NonNull
+ private Map<String, String> extraInstrumentationTestRunnerArgs;
+
+ public AbstractTestDataImpl(@NonNull VariantConfiguration<?, ?, ?> testVariantConfig) {
+ this.testVariantConfig = checkNotNull(testVariantConfig);
+ this.extraInstrumentationTestRunnerArgs = Maps.newHashMap();
}
@NonNull
@@ -42,6 +51,21 @@
return testVariantConfig.getInstrumentationRunner();
}
+ @NonNull
+ @Override
+ public Map<String, String> getInstrumentationRunnerArguments() {
+ return ImmutableMap.<String, String>builder()
+ .putAll(testVariantConfig.getInstrumentationRunnerArguments())
+ .putAll(extraInstrumentationTestRunnerArgs)
+ .build();
+ }
+
+ public void setExtraInstrumentationTestRunnerArgs(
+ @NonNull Map<String, String> extraInstrumentationTestRunnerArgs) {
+ this.extraInstrumentationTestRunnerArgs =
+ ImmutableMap.copyOf(extraInstrumentationTestRunnerArgs);
+ }
+
@Override
public boolean isTestCoverageEnabled() {
return testVariantConfig.isTestCoverageEnabled();
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/TestApplicationTestData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/TestApplicationTestData.java
index 02304fe..11e9b6f 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/TestApplicationTestData.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/test/TestApplicationTestData.java
@@ -29,16 +29,13 @@
import com.android.builder.testing.TestData;
import com.android.builder.testing.api.DeviceConfigProvider;
import com.android.ide.common.build.SplitOutputMatcher;
-import com.android.ide.common.build.SplitSelectTool;
import com.android.ide.common.process.ProcessException;
import com.android.ide.common.process.ProcessExecutor;
import com.android.sdklib.BuildToolInfo;
import com.android.utils.ILogger;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApkVariantData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApkVariantData.java
index 077e3cd..28edd3e 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApkVariantData.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApkVariantData.java
@@ -18,7 +18,7 @@
import com.android.annotations.NonNull;
import com.android.build.FilterData;
import com.android.build.OutputFile;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
import com.android.build.gradle.internal.TaskManager;
import com.android.build.gradle.internal.core.GradleVariantConfiguration;
import com.android.build.gradle.tasks.Dex;
@@ -39,10 +39,10 @@
public DefaultTask uninstallTask;
protected ApkVariantData(
- @NonNull BaseExtension baseExtension,
+ @NonNull AndroidConfig androidConfig,
@NonNull TaskManager taskManager,
@NonNull GradleVariantConfiguration config) {
- super(baseExtension, taskManager, config);
+ super(androidConfig, taskManager, config);
}
@Override
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantData.java
index ef68abb..99251f1 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantData.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantData.java
@@ -17,7 +17,7 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
import com.android.build.gradle.internal.TaskManager;
import com.android.build.gradle.internal.core.GradleVariantConfiguration;
import com.android.builder.core.VariantType;
@@ -35,10 +35,10 @@
private Set<String> compatibleScreens = null;
public ApplicationVariantData(
- @NonNull BaseExtension baseExtension,
+ @NonNull AndroidConfig androidConfig,
@NonNull GradleVariantConfiguration config,
@NonNull TaskManager taskManager) {
- super(baseExtension, taskManager, config);
+ super(androidConfig, taskManager, config);
testVariants = Maps.newEnumMap(VariantType.class);
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantFactory.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantFactory.java
index f638ea3..7b42dd1 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantFactory.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/ApplicationVariantFactory.java
@@ -16,18 +16,16 @@
package com.android.build.gradle.internal.variant;
+import static com.android.build.OutputFile.NO_FILTER;
import static com.android.builder.core.BuilderConstants.DEBUG;
import static com.android.builder.core.BuilderConstants.RELEASE;
import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
import com.android.build.FilterData;
import com.android.build.OutputFile;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
import com.android.build.gradle.api.ApplicationVariant;
import com.android.build.gradle.api.BaseVariantOutput;
-import com.android.build.gradle.internal.BuildTypeData;
-import com.android.build.gradle.internal.ProductFlavorData;
import com.android.build.gradle.internal.TaskManager;
import com.android.build.gradle.internal.VariantModel;
import com.android.build.gradle.internal.api.ApkVariantImpl;
@@ -36,7 +34,6 @@
import com.android.build.gradle.internal.api.ReadOnlyObjectProvider;
import com.android.build.gradle.internal.core.GradleVariantConfiguration;
import com.android.build.gradle.internal.dsl.BuildType;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
import com.android.build.gradle.internal.dsl.ProductFlavor;
import com.android.build.gradle.internal.dsl.SigningConfig;
import com.android.build.gradle.internal.model.FilterDataImpl;
@@ -49,6 +46,7 @@
import org.gradle.api.Project;
import org.gradle.internal.reflect.Instantiator;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -63,14 +61,14 @@
Instantiator instantiator;
@NonNull
- protected final BaseExtension extension;
+ protected final AndroidConfig extension;
@NonNull
private final AndroidBuilder androidBuilder;
public ApplicationVariantFactory(
@NonNull Instantiator instantiator,
@NonNull AndroidBuilder androidBuilder,
- @NonNull BaseExtension extension) {
+ @NonNull AndroidConfig extension) {
this.instantiator = instantiator;
this.androidBuilder = androidBuilder;
this.extension = extension;
@@ -80,23 +78,41 @@
@NonNull
public BaseVariantData createVariantData(
@NonNull GradleVariantConfiguration variantConfiguration,
- @NonNull Set<String> densities,
- @NonNull Set<String> abis,
- @NonNull Set<String> compatibleScreens,
@NonNull TaskManager taskManager) {
ApplicationVariantData variant =
new ApplicationVariantData(extension, variantConfiguration, taskManager);
+ variant.calculateFilters(extension.getSplits());
+
+ Set<String> densities = variant.getFilters(OutputFile.FilterType.DENSITY);
+ Set<String> abis = variant.getFilters(OutputFile.FilterType.ABI);
+
if (!densities.isEmpty()) {
- variant.setCompatibleScreens(compatibleScreens);
+ variant.setCompatibleScreens(extension.getSplits().getDensity()
+ .getCompatibleScreens());
}
// create its outputs
if (variant.getSplitHandlingPolicy() ==
BaseVariantData.SplitHandlingPolicy.PRE_21_POLICY) {
+
+ // Always dd an entry with no filter for universal and add it FIRST,
+ // since code assume that the first variant output will be the universal one.
+ List<String> orderedDensities = new ArrayList<String>();
+ orderedDensities.add(NO_FILTER);
+ orderedDensities.addAll(densities);
+
+ List<String> orderedAbis = new ArrayList<String>();
+ // if the abi list is empty or we must generate a universal apk, add a NO_FILTER
+ if (abis.isEmpty() || (extension.getSplits().getAbi().isEnable() &&
+ extension.getSplits().getAbi().isUniversalApk())) {
+ orderedAbis.add(NO_FILTER);
+ }
+ orderedAbis.addAll(abis);
+
// create its outputs
- for (String density : densities) {
- for (String abi : abis) {
+ for (String density : orderedDensities) {
+ for (String abi : orderedAbis) {
ImmutableList.Builder<FilterData> builder = ImmutableList.builder();
if (density != null) {
builder.add(FilterDataImpl.build(OutputFile.DENSITY, density));
@@ -183,7 +199,7 @@
@Override
public void createDefaultComponents(
@NonNull NamedDomainObjectContainer<BuildType> buildTypes,
- @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
+ @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
@NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs) {
// must create signing config first so that build type 'debug' can be initialized
// with the debug signing config.
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java
index 222ac06..d154358 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantData.java
@@ -19,7 +19,7 @@
import com.android.annotations.Nullable;
import com.android.build.FilterData;
import com.android.build.OutputFile;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
import com.android.build.gradle.api.AndroidSourceSet;
import com.android.build.gradle.internal.TaskManager;
import com.android.build.gradle.internal.api.DefaultAndroidSourceSet;
@@ -35,8 +35,8 @@
import com.android.build.gradle.tasks.AidlCompile;
import com.android.build.gradle.tasks.BinaryFileProviderTask;
import com.android.build.gradle.tasks.GenerateBuildConfig;
-import com.android.build.gradle.tasks.PreprocessResourcesTask;
import com.android.build.gradle.tasks.GenerateResValues;
+import com.android.build.gradle.tasks.JackTask;
import com.android.build.gradle.tasks.MergeAssets;
import com.android.build.gradle.tasks.MergeResources;
import com.android.build.gradle.tasks.NdkCompile;
@@ -52,8 +52,10 @@
import org.gradle.api.Task;
import org.gradle.api.logging.Logging;
import org.gradle.api.tasks.Copy;
+import org.gradle.api.tasks.Sync;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.api.tasks.compile.AbstractCompile;
+import org.gradle.api.tasks.compile.JavaCompile;
import java.io.File;
import java.util.ArrayList;
@@ -84,7 +86,7 @@
@NonNull
- protected final BaseExtension baseExtension;
+ protected final AndroidConfig androidConfig;
@NonNull
protected final TaskManager taskManager;
@NonNull
@@ -114,13 +116,13 @@
public Copy copyApkTask;
public GenerateApkDataTask generateApkDataTask;
- public PreprocessResourcesTask preprocessResourcesTask;
-
- public Copy processJavaResourcesTask;
+ public Sync processJavaResourcesTask;
public NdkCompile ndkCompileTask;
- // can be JavaCompile or JackTask depending on user's settings.
- public AbstractCompile javaCompileTask;
+ /** Can be JavaCompile or JackTask depending on user's settings. */
+ public AbstractCompile javaCompilerTask;
+ public JavaCompile javacTask;
+ public JackTask jackTask;
public Jar classesJarTask;
// empty anchor compile task to set all compilations tasks as dependents.
public Task compileTask;
@@ -131,7 +133,6 @@
// TODO : why is Jack not registered as the obfuscationTask ???
public Task obfuscationTask;
- public File obfuscatedClassesJar;
// Task to assemble the variant and all its output.
public Task assembleVariantTask;
@@ -157,22 +158,22 @@
public BaseVariantData(
- @NonNull BaseExtension baseExtension,
+ @NonNull AndroidConfig androidConfig,
@NonNull TaskManager taskManager,
@NonNull GradleVariantConfiguration variantConfiguration) {
- this.baseExtension = baseExtension;
+ this.androidConfig = androidConfig;
this.variantConfiguration = variantConfiguration;
this.taskManager = taskManager;
// eventually, this will require a more open ended comparison.
mSplitHandlingPolicy =
- baseExtension.getGeneratePureSplits()
+ androidConfig.getGeneratePureSplits()
&& variantConfiguration.getMinSdkVersion().getApiLevel() >= 21
? SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY
: SplitHandlingPolicy.PRE_21_POLICY;
// warn the user in case we are forced to ignore the generatePureSplits flag.
- if (baseExtension.getGeneratePureSplits()
+ if (androidConfig.getGeneratePureSplits()
&& mSplitHandlingPolicy != SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY) {
Logging.getLogger(BaseVariantData.class).warn(
String.format("Variant %s, MinSdkVersion %s is too low (<21) "
@@ -180,9 +181,8 @@
variantConfiguration.getFullName(),
variantConfiguration.getMinSdkVersion().getApiLevel()));
}
- variantConfiguration.checkSourceProviders();
-
scope = new VariantScope(taskManager.getGlobalScope(), this);
+ taskManager.configureScopeForNdk(scope);
}
@@ -289,7 +289,12 @@
sourceGenTask.dependsOn(task);
for (File f : generatedSourceFolders) {
- javaCompileTask.source(f);
+ javacTask.source(f);
+
+ // Jack task is not always created.
+ if (jackTask != null) {
+ jackTask.source(f);
+ }
}
addJavaSourceFoldersToModel(generatedSourceFolders);
@@ -299,7 +304,12 @@
sourceGenTask.dependsOn(task);
for (File f : generatedSourceFolders) {
- javaCompileTask.source(f);
+ javacTask.source(f);
+
+ // Jack task is not always created.
+ if (jackTask != null) {
+ jackTask.source(f);
+ }
}
addJavaSourceFoldersToModel(generatedSourceFolders);
@@ -487,7 +497,7 @@
if (filterType.isAuto(splits)) {
filtersList.addAll(getAllFilters(resourceSets, filterType.folderPrefix));
} else {
- filtersList.addAll(removeAllNullEntries(filterType.getConfiguredFilters(splits)));
+ filtersList.addAll(filterType.getConfiguredFilters(splits));
}
return filtersList;
}
@@ -638,23 +648,6 @@
}
@NonNull
- public File getFinalResourcesDir() {
- return preprocessResourcesTask != null
- ? preprocessResourcesTask.getOutputResDirectory()
- : mergeResourcesTask.getOutputDir();
- }
-
- private static <T> Set<T> removeAllNullEntries(Collection<T> input) {
- HashSet<T> output = new HashSet<T>();
- for (T element : input) {
- if (element != null) {
- output.add(element);
- }
- }
- return output;
- }
-
- @NonNull
public VariantScope getScope() {
return scope;
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantOutputData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantOutputData.java
index e0c47c8..4c78e6a 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantOutputData.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/BaseVariantOutputData.java
@@ -74,7 +74,7 @@
scope = new VariantOutputScope(variantData.getScope(), this);
}
- @Nullable
+ @NonNull
@Override
public ApkOutputFile getMainOutputFile() {
return mainApkOutputFile;
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
index 7b573cd..2aeb1d6 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantData.java
@@ -19,7 +19,7 @@
import com.android.annotations.Nullable;
import com.android.build.FilterData;
import com.android.build.OutputFile;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
import com.android.build.gradle.internal.TaskManager;
import com.android.build.gradle.internal.core.GradleVariantConfiguration;
import com.android.build.gradle.tasks.ExtractAnnotations;
@@ -44,10 +44,10 @@
public ExtractAnnotations generateAnnotationsTask = null;
public LibraryVariantData(
- @NonNull BaseExtension baseExtension,
+ @NonNull AndroidConfig androidConfig,
@NonNull TaskManager taskManager,
@NonNull GradleVariantConfiguration config) {
- super(baseExtension, taskManager, config);
+ super(androidConfig, taskManager, config);
testVariants = Maps.newEnumMap(VariantType.class);
// create default output
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantFactory.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantFactory.groovy
deleted file mode 100644
index e94c33d..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantFactory.groovy
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal.variant
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.gradle.LibraryExtension
-import com.android.build.gradle.api.BaseVariantOutput
-import com.android.build.gradle.api.LibraryVariant
-import com.android.build.gradle.internal.BuildTypeData
-import com.android.build.gradle.internal.ProductFlavorData
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.VariantModel
-import com.android.build.gradle.internal.api.LibraryVariantImpl
-import com.android.build.gradle.internal.api.LibraryVariantOutputImpl
-import com.android.build.gradle.internal.api.ReadOnlyObjectProvider
-import com.android.build.gradle.internal.core.GradleVariantConfiguration
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.ProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.VariantType
-import com.google.common.collect.Lists
-import org.gradle.api.GradleException
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.Project
-import org.gradle.internal.reflect.Instantiator
-
-import static com.android.builder.core.BuilderConstants.DEBUG
-import static com.android.builder.core.BuilderConstants.RELEASE
-
-public class LibraryVariantFactory implements VariantFactory {
-
- @NonNull
- Instantiator instantiator
- @NonNull
- private final LibraryExtension extension
- @NonNull
- private final AndroidBuilder androidBuilder;
-
- public LibraryVariantFactory(
- @NonNull Instantiator instantiator,
- @NonNull AndroidBuilder androidBuilder,
- @NonNull LibraryExtension extension) {
- this.instantiator = instantiator;
- this.androidBuilder = androidBuilder;
- this.extension = extension;
- }
-
- @Override
- @NonNull
- public BaseVariantData createVariantData(
- @NonNull GradleVariantConfiguration variantConfiguration,
- @NonNull Set<String> densities,
- @NonNull Set<String> abis,
- @NonNull Set<String> compatibleScreens,
- @NonNull TaskManager taskManager) {
- return new LibraryVariantData(extension, taskManager, variantConfiguration)
- }
-
- @Override
- @NonNull
- public LibraryVariant createVariantApi(
- @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
- @NonNull ReadOnlyObjectProvider readOnlyObjectProvider) {
- LibraryVariantImpl variant = instantiator.newInstance(
- LibraryVariantImpl.class, variantData, androidBuilder, readOnlyObjectProvider)
-
- // now create the output objects
- List<? extends BaseVariantOutputData> outputList = variantData.getOutputs();
- List<BaseVariantOutput> apiOutputList = Lists.newArrayListWithCapacity(outputList.size());
-
- for (BaseVariantOutputData variantOutputData : outputList) {
- LibVariantOutputData libOutput = (LibVariantOutputData) variantOutputData;
-
- LibraryVariantOutputImpl output = instantiator.newInstance(
- LibraryVariantOutputImpl.class, libOutput);
-
- apiOutputList.add(output);
- }
-
- variant.addOutputs(apiOutputList);
-
- return variant
- }
-
- @NonNull
- @Override
- public VariantType getVariantConfigurationType() {
- return VariantType.LIBRARY
- }
-
- @Override
- boolean isLibrary() {
- return true
- }
-
- @Override
- boolean hasTestScope() {
- return true
- }
-
- /***
- * Prevent customization of applicationId or applicationIdSuffix.
- */
- @Override
- public void validateModel(@NonNull VariantModel model) {
- if (model.getDefaultConfig().getProductFlavor().getApplicationId() != null) {
- throw new GradleException("Library projects cannot set applicationId. " +
- "applicationId is set to '" +
- model.getDefaultConfig().getProductFlavor().getApplicationId() +
- "' in default config.");
- }
-
- for (BuildTypeData buildType : model.getBuildTypes().values()) {
- if (buildType.getBuildType().getApplicationIdSuffix() != null) {
- throw new GradleException("Library projects cannot set applicationId. " +
- "applicationIdSuffix is set to '" +
- buildType.getBuildType().getApplicationIdSuffix() +
- "' in build type '" + buildType.getBuildType().getName() + "'.");
- }
- }
- for (ProductFlavorData productFlavor : model.getProductFlavors().values()) {
- if (productFlavor.getProductFlavor().getApplicationId() != null) {
- throw new GradleException("Library projects cannot set applicationId. " +
- "applicationId is set to '" +
- productFlavor.getProductFlavor().getApplicationId() + "' in flavor '" +
- productFlavor.getProductFlavor().getName() + "'.");
- }
- }
-
- }
-
- @Override
- void preVariantWork(Project project) {
- // nothing to be done here.
- }
-
- @Override
- public void createDefaultComponents(
- @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
- @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
- @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs) {
- // must create signing config first so that build type 'debug' can be initialized
- // with the debug signing config.
- signingConfigs.create(DEBUG);
- buildTypes.create(DEBUG);
- buildTypes.create(RELEASE);
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantFactory.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantFactory.java
new file mode 100644
index 0000000..636c1d0
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/LibraryVariantFactory.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.variant;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.api.BaseVariantOutput;
+import com.android.build.gradle.api.LibraryVariant;
+import com.android.build.gradle.internal.BuildTypeData;
+import com.android.build.gradle.internal.ProductFlavorData;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.VariantModel;
+import com.android.build.gradle.internal.api.LibraryVariantImpl;
+import com.android.build.gradle.internal.api.LibraryVariantOutputImpl;
+import com.android.build.gradle.internal.api.ReadOnlyObjectProvider;
+import com.android.build.gradle.internal.core.GradleVariantConfiguration;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.VariantType;
+import com.google.common.collect.Lists;
+import org.gradle.api.GradleException;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.Project;
+import org.gradle.internal.reflect.Instantiator;
+
+import static com.android.builder.core.BuilderConstants.DEBUG;
+import static com.android.builder.core.BuilderConstants.RELEASE;
+
+import java.util.List;
+
+public class LibraryVariantFactory implements VariantFactory {
+
+ @NonNull
+ private Instantiator instantiator;
+ @NonNull
+ private final AndroidConfig extension;
+ @NonNull
+ private final AndroidBuilder androidBuilder;
+
+ public LibraryVariantFactory(
+ @NonNull Instantiator instantiator,
+ @NonNull AndroidBuilder androidBuilder,
+ @NonNull AndroidConfig extension) {
+ this.instantiator = instantiator;
+ this.androidBuilder = androidBuilder;
+ this.extension = extension;
+ }
+
+ @Override
+ @NonNull
+ public BaseVariantData createVariantData(
+ @NonNull GradleVariantConfiguration variantConfiguration,
+ @NonNull TaskManager taskManager) {
+ return new LibraryVariantData(extension, taskManager, variantConfiguration);
+ }
+
+ @Override
+ @NonNull
+ public LibraryVariant createVariantApi(
+ @NonNull BaseVariantData<? extends BaseVariantOutputData> variantData,
+ @NonNull ReadOnlyObjectProvider readOnlyObjectProvider) {
+ LibraryVariantImpl variant = instantiator.newInstance(
+ LibraryVariantImpl.class, variantData, androidBuilder, readOnlyObjectProvider);
+
+ // now create the output objects
+ List<? extends BaseVariantOutputData> outputList = variantData.getOutputs();
+ List<BaseVariantOutput> apiOutputList = Lists.newArrayListWithCapacity(outputList.size());
+
+ for (BaseVariantOutputData variantOutputData : outputList) {
+ LibVariantOutputData libOutput = (LibVariantOutputData) variantOutputData;
+
+ LibraryVariantOutputImpl output = instantiator.newInstance(
+ LibraryVariantOutputImpl.class, libOutput);
+
+ apiOutputList.add(output);
+ }
+
+ variant.addOutputs(apiOutputList);
+
+ return variant;
+ }
+
+ @NonNull
+ @Override
+ public VariantType getVariantConfigurationType() {
+ return VariantType.LIBRARY;
+ }
+
+ @Override
+ public boolean isLibrary() {
+ return true;
+ }
+
+ @Override
+ public boolean hasTestScope() {
+ return true;
+ }
+
+ /***
+ * Prevent customization of applicationId or applicationIdSuffix.
+ */
+ @Override
+ public void validateModel(@NonNull VariantModel model) {
+ if (model.getDefaultConfig().getProductFlavor().getApplicationId() != null) {
+ throw new GradleException("Library projects cannot set applicationId. " +
+ "applicationId is set to '" +
+ model.getDefaultConfig().getProductFlavor().getApplicationId() +
+ "' in default config.");
+ }
+
+ for (BuildTypeData buildType : model.getBuildTypes().values()) {
+ if (buildType.getBuildType().getApplicationIdSuffix() != null) {
+ throw new GradleException("Library projects cannot set applicationId. " +
+ "applicationIdSuffix is set to '" +
+ buildType.getBuildType().getApplicationIdSuffix() +
+ "' in build type '" + buildType.getBuildType().getName() + "'.");
+ }
+ }
+ for (ProductFlavorData productFlavor : model.getProductFlavors().values()) {
+ if (productFlavor.getProductFlavor().getApplicationId() != null) {
+ throw new GradleException("Library projects cannot set applicationId. " +
+ "applicationId is set to '" +
+ productFlavor.getProductFlavor().getApplicationId() + "' in flavor '" +
+ productFlavor.getProductFlavor().getName() + "'.");
+ }
+ }
+
+ }
+
+ @Override
+ public void preVariantWork(Project project) {
+ // nothing to be done here.
+ }
+
+ @Override
+ public void createDefaultComponents(
+ @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
+ @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
+ @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs) {
+ // must create signing config first so that build type 'debug' can be initialized
+ // with the debug signing config.
+ signingConfigs.create(DEBUG);
+ buildTypes.create(DEBUG);
+ buildTypes.create(RELEASE);
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantData.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantData.java
index 1f3d534..49c31a9 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantData.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantData.java
@@ -18,7 +18,7 @@
import com.android.annotations.NonNull;
import com.android.build.FilterData;
import com.android.build.OutputFile;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.AndroidConfig;
import com.android.build.gradle.internal.TaskManager;
import com.android.build.gradle.internal.core.GradleVariantConfiguration;
import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask;
@@ -38,11 +38,11 @@
private final TestedVariantData testedVariantData;
public TestVariantData(
- @NonNull BaseExtension baseExtension,
+ @NonNull AndroidConfig androidConfig,
@NonNull TaskManager taskManager,
@NonNull GradleVariantConfiguration config,
@NonNull TestedVariantData testedVariantData) {
- super(baseExtension, taskManager, config);
+ super(androidConfig, taskManager, config);
this.testedVariantData = testedVariantData;
// create default output
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantFactory.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantFactory.groovy
deleted file mode 100644
index 12dde47..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantFactory.groovy
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal.variant
-
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.internal.BuildTypeData
-import com.android.build.gradle.internal.ProductFlavorData
-import com.android.build.gradle.internal.core.GradleVariantConfiguration
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.ProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.build.gradle.TestExtension
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.VariantType
-import com.android.builder.model.SourceProvider
-import org.gradle.api.GradleException
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.Project
-import org.gradle.api.artifacts.dsl.DependencyHandler
-import org.gradle.internal.reflect.Instantiator
-
-import static com.android.builder.core.BuilderConstants.DEBUG
-
-/**
- * Customization of ApplcationVariantFactory for test-only projects.
- */
-public class TestVariantFactory extends ApplicationVariantFactory {
-
- public TestVariantFactory(
- @NonNull Instantiator instantiator,
- @NonNull AndroidBuilder androidBuilder,
- @NonNull BaseExtension extension) {
- super(instantiator, androidBuilder, extension)
- }
-
- @Override
- public boolean hasTestScope() {
- return false
- }
-
- @Override
- public void preVariantWork(Project project) {
- TestExtension testExtension = (TestExtension) extension
-
- String path = testExtension.targetProjectPath
- if (path == null) {
- throw new GradleException("targetProjectPath cannot be null in test project ${project.name}")
- }
-
- if (testExtension.targetVariant == null) {
- throw new GradleException("targetVariant cannot be null in test project ${project.name}")
- }
-
- String variant = "${testExtension.targetVariant}-classes"
-
- DependencyHandler handler = project.getDependencies()
- handler.add("provided", handler.project(path: path, configuration: variant))
- }
-
- @Override
- public void createDefaultComponents(
- @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
- @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
- @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs) {
- // don't call super as we don't want the default app version.
- // must create signing config first so that build type 'debug' can be initialized
- // with the debug signing config.
- signingConfigs.create(DEBUG);
- buildTypes.create(DEBUG);
- }
-
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantFactory.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantFactory.java
new file mode 100644
index 0000000..60c9443
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/TestVariantFactory.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.variant;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.TestAndroidConfig;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.BuilderConstants;
+import com.google.common.collect.ImmutableMap;
+
+import org.gradle.api.GradleException;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.dsl.DependencyHandler;
+import org.gradle.internal.reflect.Instantiator;
+
+/**
+ * Customization of ApplcationVariantFactory for test-only projects.
+ */
+public class TestVariantFactory extends ApplicationVariantFactory {
+
+ public TestVariantFactory(
+ @NonNull Instantiator instantiator,
+ @NonNull AndroidBuilder androidBuilder,
+ @NonNull AndroidConfig extension) {
+ super(instantiator, androidBuilder, extension);
+ }
+
+ @Override
+ public boolean hasTestScope() {
+ return false;
+ }
+
+ @Override
+ public void preVariantWork(final Project project) {
+ final TestAndroidConfig testExtension = (TestAndroidConfig) extension;
+
+ String path = testExtension.getTargetProjectPath();
+ if (path == null) {
+ throw new GradleException(
+ "targetProjectPath cannot be null in test project " + project.getName());
+ }
+
+ if (testExtension.getTargetVariant() == null) {
+ throw new GradleException(
+ "targetVariant cannot be null in test project " + project.getName());
+ }
+
+ DependencyHandler handler = project.getDependencies();
+ handler.add("provided", handler.project(ImmutableMap.of(
+ "path", path,
+ "configuration", testExtension.getTargetVariant() + "-classes"
+ )));
+ }
+
+ @Override
+ public void createDefaultComponents(
+ @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
+ @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
+ @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs) {
+ // don't call super as we don't want the default app version.
+ // must create signing config first so that build type 'debug' can be initialized
+ // with the debug signing config.
+ signingConfigs.create(BuilderConstants.DEBUG);
+ buildTypes.create(BuilderConstants.DEBUG);
+ }
+
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantFactory.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantFactory.java
index ff85f16..4544f96 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantFactory.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantFactory.java
@@ -17,26 +17,18 @@
package com.android.build.gradle.internal.variant;
import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
import com.android.build.gradle.api.BaseVariant;
-import com.android.build.gradle.internal.BuildTypeData;
-import com.android.build.gradle.internal.ProductFlavorData;
import com.android.build.gradle.internal.TaskManager;
import com.android.build.gradle.internal.VariantModel;
import com.android.build.gradle.internal.api.ReadOnlyObjectProvider;
import com.android.build.gradle.internal.core.GradleVariantConfiguration;
import com.android.build.gradle.internal.dsl.BuildType;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
import com.android.build.gradle.internal.dsl.ProductFlavor;
import com.android.build.gradle.internal.dsl.SigningConfig;
import com.android.builder.core.VariantType;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.Project;
-import org.gradle.api.Task;
-
-import java.util.Map;
-import java.util.Set;
/**
* Interface for Variant Factory.
@@ -49,9 +41,6 @@
@NonNull
BaseVariantData createVariantData(
@NonNull GradleVariantConfiguration variantConfiguration,
- @NonNull Set<String> densities,
- @NonNull Set<String> abi,
- @NonNull Set<String> compatibleScreens,
@NonNull TaskManager taskManager);
@NonNull
@@ -77,6 +66,6 @@
void createDefaultComponents(
@NonNull NamedDomainObjectContainer<BuildType> buildTypes,
- @NonNull NamedDomainObjectContainer<GroupableProductFlavor> productFlavors,
+ @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
@NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs);
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantHelper.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantHelper.groovy
deleted file mode 100644
index 7d91e40..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantHelper.groovy
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.internal.variant
-
-import com.android.annotations.NonNull
-import com.google.common.collect.Sets
-import org.gradle.api.Project
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.maven.MavenDeployer
-import org.gradle.api.plugins.MavenPlugin
-import org.gradle.api.tasks.Upload
-
-/**
- */
-public class VariantHelper {
-
- public static void setupDefaultConfig(@NonNull Project project, @NonNull Configuration configuration) {
- // The library artifact is published (inter-project( for the "default" configuration so
- // we make sure "default" extends from the actual configuration used for building.
- Configuration defaultConfig = project.configurations["default"]
- defaultConfig.setExtendsFrom(Collections.singleton(configuration))
-
- // for the maven publication (for now), we need to manually include all the configuration
- // object in a special mapping.
- // It's not possible to put the top level config object as extended from config won't
- // be included.
- Set<Configuration> flattenedConfigs = flattenConfigurations(configuration)
-
- project.plugins.withType(MavenPlugin) {
- project.tasks.withType(Upload) { task ->
- task.repositories.withType(MavenDeployer) { repo ->
- for (Configuration config : flattenedConfigs) {
- repo.pom.scopeMappings.addMapping(300,
- project.configurations[config.name],
- "compile")
- }
- }
- }
- }
- }
-
- /**
- * Build a set of configuration containing all the Configuration object that a given
- * configuration extends from, directly or transitively.
- *
- * @param configuration the configuration
- * @return a set of config.
- */
- private static Set<Configuration> flattenConfigurations(@NonNull Configuration configuration) {
- Set<Configuration> configs = Sets.newHashSet()
- configs.add(configuration)
-
- for (Configuration extend : configuration.getExtendsFrom()) {
- configs.addAll(flattenConfigurations(extend))
- }
-
- return configs
- }
-
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantHelper.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantHelper.java
new file mode 100644
index 0000000..f777d0b
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/internal/variant/VariantHelper.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.variant;
+
+import com.android.annotations.NonNull;
+import com.google.common.collect.Sets;
+
+import org.gradle.api.Action;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.maven.MavenDeployer;
+import org.gradle.api.plugins.MavenPlugin;
+import org.gradle.api.tasks.Upload;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ */
+public class VariantHelper {
+
+ public static void setupDefaultConfig(
+ @NonNull final Project project,
+ @NonNull Configuration configuration) {
+ // The library artifact is published (inter-project( for the "default" configuration so
+ // we make sure "default" extends from the actual configuration used for building.
+ Configuration defaultConfig = project.getConfigurations().getAt("default");
+ defaultConfig.setExtendsFrom(Collections.singleton(configuration));
+
+ // for the maven publication (for now), we need to manually include all the configuration
+ // object in a special mapping.
+ // It's not possible to put the top level config object as extended from config won't
+ // be included.
+ final Set<Configuration> flattenedConfigs = flattenConfigurations(configuration);
+
+ project.getPlugins().withType(MavenPlugin.class, new Action<MavenPlugin>() {
+ @Override
+ public void execute(MavenPlugin mavenPlugin) {
+ project.getTasks().withType(Upload.class, new Action<Upload>() {
+ @Override
+ public void execute(Upload upload) {
+ upload.getRepositories().withType(
+ MavenDeployer.class,
+ new Action<MavenDeployer>() {
+ @Override
+ public void execute(MavenDeployer mavenDeployer) {
+ for (Configuration config : flattenedConfigs) {
+ mavenDeployer.getPom().getScopeMappings().addMapping(
+ 300,
+ project.getConfigurations().getByName(
+ config.getName()),
+ "compile");
+ }
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Build a set of configuration containing all the Configuration object that a given
+ * configuration extends from, directly or transitively.
+ *
+ * @param configuration the configuration
+ * @return a set of config.
+ */
+ private static Set<Configuration> flattenConfigurations(@NonNull Configuration configuration) {
+ Set<Configuration> configs = Sets.newHashSet();
+ configs.add(configuration);
+
+ for (Configuration extend : configuration.getExtendsFrom()) {
+ configs.addAll(flattenConfigurations(extend));
+ }
+
+ return configs;
+ }
+
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/profiling/ProfilingBuildListener.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/profiling/ProfilingBuildListener.groovy
deleted file mode 100644
index 9af97ab..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/profiling/ProfilingBuildListener.groovy
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.profiling
-
-import groovy.json.JsonBuilder
-import groovy.transform.CompileStatic
-import org.gradle.BuildListener
-import org.gradle.BuildResult
-import org.gradle.api.Project
-import org.gradle.api.ProjectEvaluationListener
-import org.gradle.api.ProjectState
-import org.gradle.api.Task
-import org.gradle.api.artifacts.DependencyResolutionListener
-import org.gradle.api.artifacts.ResolvableDependencies
-import org.gradle.api.execution.TaskExecutionGraph
-import org.gradle.api.execution.TaskExecutionGraphListener
-import org.gradle.api.execution.TaskExecutionListener
-import org.gradle.api.initialization.Settings
-import org.gradle.api.invocation.Gradle
-import org.gradle.api.tasks.TaskState
-import org.gradle.initialization.BuildCompletionListener
-
-/**
- * A simple build profiler that listens to the Gradle build and outputs Chrome trace data.
- *
- * <p>Here's an example of how to enable it from a Gradle init script (-I):
- * <pre><code>
- * import com.android.build.gradle.profiling.ProfilingBuildListener
- * gradle.addListener(new ProfilingBuildListener("build.trace"))
- * </code></pre>
- *
- * <p>See <a href="https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit">
- * Chrome trace event format<a>
- */
-@CompileStatic
-public class ProfilingBuildListener implements BuildListener, TaskExecutionListener,
- TaskExecutionGraphListener, ProjectEvaluationListener, DependencyResolutionListener,
- BuildCompletionListener {
-
- PrintWriter traceWriter;
-
- public ProfilingBuildListener(String traceFile) {
- traceWriter = new PrintWriter(new FileOutputStream(new File(traceFile)));
- traceWriter.append('[')
- }
-
- @Override
- void buildStarted(Gradle gradle) {
- }
-
- @Override
- void settingsEvaluated(Settings settings) {
- }
-
- @Override
- void projectsLoaded(Gradle gradle) {
- }
-
- @Override
- void projectsEvaluated(Gradle gradle) {
- }
-
- @Override
- void buildFinished(BuildResult buildResult) {
- }
-
- @Override
- void graphPopulated(TaskExecutionGraph taskExecutionGraph) {
- }
-
- @Override
- void beforeExecute(Task task) {
- long now = System.currentTimeMillis();
- task.convention.add("__started", now);
-
- writeStart(task.name)
- }
-
- @Override
- void afterExecute(Task task, TaskState taskState) {
- long start = (long) task.convention.getByName("__started")
- long now = System.currentTimeMillis()
-
- def args = [
- project: task.project.name,
- description: task.description,
- didWork: taskState.didWork,
- failure: taskState.getFailure(),
- ]
- writeEnd(task.name, now - start, args)
- }
-
- @Override
- void completed() {
- def builder = new JsonBuilder([
- cat: 'gradle',
- name: 'DONE',
- pid: 0,
- tid: Thread.currentThread().name,
- ts: System.currentTimeMillis() * 1000,
- ph: 'I'
- ])
- writeOut(builder)
- traceWriter.append(']')
- traceWriter.close()
- }
-
- @Override
- void beforeResolve(ResolvableDependencies resolvableDependencies) {
- writeStart(resolvableDependencies.name)
- }
-
- @Override
- void afterResolve(ResolvableDependencies resolvableDependencies) {
- def args = [
- path: resolvableDependencies.path,
- description: 'Resolving ' + resolvableDependencies.name,
- resultSize: resolvableDependencies.resolutionResult.allDependencies.size(),
- ]
-
- writeEnd(resolvableDependencies.name, 0, args)
- }
-
- @Override
- void beforeEvaluate(Project project) {
- long now = System.currentTimeMillis();
- project.convention.add("__started", now);
-
- writeStart(project.name)
- }
-
- @Override
- void afterEvaluate(Project project, ProjectState projectState) {
- long start = (long) project.convention.getByName("__started")
- long now = System.currentTimeMillis()
-
- def args = [
- project: project.path,
- description: project.description,
- executed: projectState.executed,
- failure: projectState.failure,
- ]
- writeEnd(project.name, now - start, args)
- }
-
- /**
- * Writes the start of an event with the current timestamp and thread.
- */
- public void writeStart(String eventName) {
- long now = System.currentTimeMillis();
- def builder = new JsonBuilder([
- cat: 'gradle',
- name: eventName,
- pid: 0,
- tid: Thread.currentThread().name,
- ts: now * 1000,
- ph: 'B'
- ])
- writeOut(builder)
- }
-
- /**
- * Writes the end of an event with the current timestamp, adding total duration (wall-clock time)
- * and a user-specified property map.
- */
- public void writeEnd(String eventName, long duration, Object properties) {
- long now = System.currentTimeMillis();
- def builder = new JsonBuilder([
- cat: 'gradle',
- name: eventName,
- pid: 0,
- tid: Thread.currentThread().name,
- ts: now * 1000,
- ph: 'E',
- dur: duration,
- args: properties
- ])
- writeOut(builder)
- }
-
- private synchronized void writeOut(JsonBuilder builder) {
- builder.writeTo(traceWriter)
- traceWriter.append(',')
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.groovy
deleted file mode 100644
index 38dda4b..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.groovy
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.tasks
-
-import com.android.SdkConstants
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.annotations.concurrency.GuardedBy
-import com.android.build.gradle.api.ApkVariant
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.scope.ConventionMappingHelper
-import com.android.build.gradle.internal.scope.TaskConfigAction
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.tasks.IncrementalTask
-import com.android.build.gradle.internal.variant.ApkVariantData
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.builder.compiling.DependencyFileProcessor
-import com.android.builder.core.VariantConfiguration
-import com.android.builder.core.VariantType
-import com.android.builder.internal.incremental.DependencyData
-import com.android.builder.internal.incremental.DependencyDataStore
-import com.android.builder.model.AndroidProject
-import com.android.ide.common.internal.WaitableExecutor
-import com.android.ide.common.res2.FileStatus
-import com.google.common.collect.Lists
-import com.google.common.collect.Multimap
-import org.gradle.api.file.FileTree
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.util.PatternSet
-
-import java.util.concurrent.Callable
-
-import static com.android.builder.model.AndroidProject.FD_GENERATED
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-
-/**
- * Task to compile aidl files. Supports incremental update.
- */
-public class AidlCompile extends IncrementalTask {
-
- private static final String DEPENDENCY_STORE = "dependency.store"
-
- // ----- PUBLIC TASK API -----
-
- @OutputDirectory
- File sourceOutputDir
-
- @OutputDirectory @Optional
- File aidlParcelableDir
-
- // ----- PRIVATE TASK API -----
- @Input
- String getBuildToolsVersion() {
- getBuildTools().getRevision()
- }
-
- List<File> sourceDirs
-
- @InputFiles
- List<File> importDirs
-
- final PatternSet patternSet = new PatternSet().include("**/*.aidl")
-
- @InputFiles
- FileTree getSourceFiles() {
- FileTree src = null
- Set<File> sources = getSourceDirs()
- if (!sources.isEmpty()) {
- src = getProject().files(new ArrayList<Object>(sources)).getAsFileTree().matching(patternSet)
- }
- return src == null ? getProject().files().getAsFileTree() : src
- }
-
- private static class DepFileProcessor implements DependencyFileProcessor {
-
- @GuardedBy("this")
- List<DependencyData> dependencyDataList = Lists.newArrayList()
-
- List<DependencyData> getDependencyDataList() {
- return dependencyDataList
- }
-
- @Override
- DependencyData processFile(@NonNull File dependencyFile) {
- DependencyData data = DependencyData.parseDependencyFile(dependencyFile)
- if (data != null) {
- synchronized (this) {
- dependencyDataList.add(data)
- }
- }
-
- return data;
- }
- }
-
- protected boolean isIncremental() {
- // TODO fix once dep file parsing is resolved.
- return false
- }
-
- /**
- * Action methods to compile all the files.
- *
- * The method receives a {@link DependencyFileProcessor} to be used by the
- * {@link com.android.builder.internal.compiler.SourceSearcher.SourceFileProcessor} during
- * the compilation.
- *
- * @param dependencyFileProcessor a DependencyFileProcessor
- */
- private void compileAllFiles(DependencyFileProcessor dependencyFileProcessor) {
- getBuilder().compileAllAidlFiles(
- getSourceDirs(),
- getSourceOutputDir(),
- getAidlParcelableDir(),
- getImportDirs(),
- dependencyFileProcessor)
- }
-
- /**
- * Returns the import folders.
- */
- @NonNull
- private List<File> getImportFolders() {
- List<File> fullImportDir = Lists.newArrayList()
- fullImportDir.addAll(getImportDirs())
- fullImportDir.addAll(getSourceDirs())
-
- return fullImportDir
- }
-
- /**
- * Compiles a single file.
- * @param sourceFolder the file to compile.
- * @param file the file to compile.
- * @param importFolders the import folders.
- * @param dependencyFileProcessor a DependencyFileProcessor
- */
- private void compileSingleFile(
- @NonNull File sourceFolder,
- @NonNull File file,
- @Nullable List<File> importFolders,
- @NonNull DependencyFileProcessor dependencyFileProcessor) {
- getBuilder().compileAidlFile(
- sourceFolder,
- file,
- getSourceOutputDir(),
- getAidlParcelableDir(),
- importFolders,
- dependencyFileProcessor)
- }
-
- @Override
- protected void doFullTaskAction() {
- // this is full run, clean the previous output
- File destinationDir = getSourceOutputDir()
- emptyFolder(destinationDir)
-
- File parcelableDir = getAidlParcelableDir()
- if (parcelableDir != null) {
- emptyFolder(parcelableDir)
- }
-
- DepFileProcessor processor = new DepFileProcessor()
-
- compileAllFiles(processor)
-
- List<DependencyData> dataList = processor.getDependencyDataList()
-
- DependencyDataStore store = new DependencyDataStore()
- store.addData(dataList)
-
- store.saveTo(new File(getIncrementalFolder(), DEPENDENCY_STORE))
- }
-
- @Override
- protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) {
- File incrementalData = new File(getIncrementalFolder(), DEPENDENCY_STORE)
- DependencyDataStore store = new DependencyDataStore()
- Multimap<String, DependencyData> inputMap
- try {
- inputMap = store.loadFrom(incrementalData)
- } catch (Exception ignored) {
- incrementalData.delete()
- project.logger.info(
- "Failed to read dependency store: full task run!")
- doFullTaskAction()
- return
- }
-
- final List<File> importFolders = getImportFolders()
- final DepFileProcessor processor = new DepFileProcessor()
-
- // use an executor to parallelize the compilation of multiple files.
- WaitableExecutor<Void> executor = new WaitableExecutor<Void>()
-
- Map<String,DependencyData> mainFileMap = store.getMainFileMap()
-
- for (Map.Entry<File, FileStatus> entry : changedInputs.entrySet()) {
- FileStatus status = entry.getValue()
-
- switch (status) {
- case FileStatus.NEW:
- executor.execute(new Callable<Void>() {
- @Override
- Void call() throws Exception {
- File file = entry.getKey()
- compileSingleFile(getSourceFolder(file), file, importFolders, processor)
- }
- })
- break
- case FileStatus.CHANGED:
- List<DependencyData> impactedData = inputMap.get(entry.getKey().absolutePath)
- if (impactedData != null) {
- final int count = impactedData.size();
- for (int i = 0; i < count; i++) {
- final DependencyData data = impactedData.get(i);
-
- executor.execute(new Callable<Void>() {
- @Override
- Void call() throws Exception {
- File file = new File(data.getMainFile());
- compileSingleFile(getSourceFolder(file), file,
- importFolders, processor)
- }
- })
- }
- }
- break
- case FileStatus.REMOVED:
- final DependencyData data2 = mainFileMap.get(entry.getKey().absolutePath)
- if (data2 != null) {
- executor.execute(new Callable<Void>() {
- @Override
- Void call() throws Exception {
- cleanUpOutputFrom(data2)
- }
- })
- store.remove(data2)
- }
- break
- }
- }
-
- try {
- executor.waitForTasksWithQuickFail(true /*cancelRemaining*/)
- } catch (Throwable t) {
- incrementalData.delete()
- throw t
- }
-
- // get all the update data for the recompiled objects
- store.updateAll(processor.getDependencyDataList())
-
- store.saveTo(incrementalData)
- }
-
- private File getSourceFolder(@NonNull File file) {
- File parentDir = file
- while ((parentDir = parentDir.getParentFile()) != null) {
- for (File folder : getSourceDirs()) {
- if (parentDir.equals(folder)) {
- return folder;
- }
- }
- }
-
- assert false
- }
-
- private static void cleanUpOutputFrom(@NonNull DependencyData dependencyData) {
- for (String output : dependencyData.getOutputFiles()) {
- new File(output).delete()
- }
- for (String output : dependencyData.getSecondaryOutputFiles()) {
- new File(output).delete()
- }
- }
-
-
- public static class ConfigAction implements TaskConfigAction<AidlCompile> {
-
- @NonNull
- VariantScope scope
-
- ConfigAction(@NonNull VariantScope scope) {
- this.scope = scope
- }
-
- @Override
- @NonNull
- String getName() {
- return scope.getTaskName("compile", "Aidl")
- }
-
- @Override
- @NonNull
- Class<AidlCompile> getType() {
- return AidlCompile
- }
-
- @Override
- void execute(AidlCompile compileTask) {
- VariantConfiguration variantConfiguration = scope.variantConfiguration
-
- scope.variantData.aidlCompileTask = compileTask
-
- compileTask.androidBuilder = scope.globalScope.androidBuilder
- compileTask.incrementalFolder =
- new File(
- "$scope.globalScope.buildDir/${FD_INTERMEDIATES}/incremental/aidl/${variantConfiguration.dirName}")
-
- ConventionMappingHelper.map(compileTask, "sourceDirs") { variantConfiguration.aidlSourceList }
- ConventionMappingHelper.map(compileTask, "importDirs") { variantConfiguration.aidlImports }
-
- ConventionMappingHelper.map(compileTask, "sourceOutputDir") {
- //new File(scope.globalScope.generatedDir, "source/aidl/${variantConfiguration.dirName}")
- scope.getAidlSourceOutputDir()
- }
-
- if (variantConfiguration.type == VariantType.LIBRARY) {
- compileTask.aidlParcelableDir = new File(
- "$scope.globalScope.buildDir/${FD_INTERMEDIATES}/$TaskManager.DIR_BUNDLES/${variantConfiguration.dirName}/$SdkConstants.FD_AIDL")
- }
-
- }
- }
-
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.java
new file mode 100644
index 0000000..bb98123
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AidlCompile.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle.tasks;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.concurrency.GuardedBy;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.tasks.IncrementalTask;
+import com.android.builder.compiling.DependencyFileProcessor;
+import com.android.builder.core.VariantConfiguration;
+import com.android.builder.core.VariantType;
+import com.android.builder.internal.incremental.DependencyData;
+import com.android.builder.internal.incremental.DependencyDataStore;
+import com.android.ide.common.internal.LoggedErrorException;
+import com.android.ide.common.internal.WaitableExecutor;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessOutputHandler;
+import com.android.ide.common.res2.FileStatus;
+import com.android.utils.FileUtils;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+
+import org.gradle.api.file.FileTree;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.util.PatternSet;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * Task to compile aidl files. Supports incremental update.
+ */
+public class AidlCompile extends IncrementalTask {
+
+ private static final String DEPENDENCY_STORE = "dependency.store";
+ private static final PatternSet PATTERN_SET = new PatternSet().include("**/*.aidl");
+
+ // ----- PUBLIC TASK API -----
+ private File sourceOutputDir;
+ private File aidlParcelableDir;
+
+ // ----- PRIVATE TASK API -----
+ @Input
+ String getBuildToolsVersion() {
+ return getBuildTools().getRevision().toString();
+ }
+ private List<File> sourceDirs;
+ private List<File> importDirs;
+
+ @InputFiles
+ FileTree getSourceFiles() {
+ FileTree src = null;
+ List<File> sources = getSourceDirs();
+ if (!sources.isEmpty()) {
+ src = getProject().files(sources).getAsFileTree().matching(PATTERN_SET);
+ }
+ return src == null ? getProject().files().getAsFileTree() : src;
+ }
+
+ private static class DepFileProcessor implements DependencyFileProcessor {
+
+ @GuardedBy("this")
+ List<DependencyData> dependencyDataList = Lists.newArrayList();
+
+ List<DependencyData> getDependencyDataList() {
+ return dependencyDataList;
+ }
+
+ @Override
+ public DependencyData processFile(@NonNull File dependencyFile) throws IOException {
+ DependencyData data = DependencyData.parseDependencyFile(dependencyFile);
+ if (data != null) {
+ synchronized (this) {
+ dependencyDataList.add(data);
+ }
+ }
+
+ return data;
+ }
+ }
+
+ @Override
+ protected boolean isIncremental() {
+ // TODO fix once dep file parsing is resolved.
+ return false;
+ }
+
+ /**
+ * Action methods to compile all the files.
+ *
+ * The method receives a {@link DependencyFileProcessor} to be used by the
+ * {@link com.android.builder.internal.compiler.SourceSearcher.SourceFileProcessor} during
+ * the compilation.
+ *
+ * @param dependencyFileProcessor a DependencyFileProcessor
+ */
+ private void compileAllFiles(DependencyFileProcessor dependencyFileProcessor)
+ throws InterruptedException, ProcessException, LoggedErrorException, IOException {
+ getBuilder().compileAllAidlFiles(
+ getSourceDirs(),
+ getSourceOutputDir(),
+ getAidlParcelableDir(),
+ getImportDirs(),
+ dependencyFileProcessor,
+ new LoggedProcessOutputHandler(getILogger()));
+ }
+
+ /**
+ * Returns the import folders.
+ */
+ @NonNull
+ private List<File> getImportFolders() {
+ List<File> fullImportDir = Lists.newArrayList();
+ fullImportDir.addAll(getImportDirs());
+ fullImportDir.addAll(getSourceDirs());
+
+ return fullImportDir;
+ }
+
+ /**
+ * Compiles a single file.
+ * @param sourceFolder the file to compile.
+ * @param file the file to compile.
+ * @param importFolders the import folders.
+ * @param dependencyFileProcessor a DependencyFileProcessor
+ */
+ private void compileSingleFile(
+ @NonNull File sourceFolder,
+ @NonNull File file,
+ @Nullable List<File> importFolders,
+ @NonNull DependencyFileProcessor dependencyFileProcessor,
+ @NonNull ProcessOutputHandler processOutputHandler)
+ throws InterruptedException, ProcessException, LoggedErrorException, IOException {
+ getBuilder().compileAidlFile(
+ sourceFolder,
+ file,
+ getSourceOutputDir(),
+ getAidlParcelableDir(),
+ importFolders,
+ dependencyFileProcessor,
+ processOutputHandler);
+ }
+
+ @Override
+ protected void doFullTaskAction() throws IOException {
+ // this is full run, clean the previous output
+ File destinationDir = getSourceOutputDir();
+ File parcelableDir = getAidlParcelableDir();
+ FileUtils.emptyFolder(destinationDir);
+ if (parcelableDir != null) {
+ FileUtils.emptyFolder(parcelableDir);
+ }
+
+
+ DepFileProcessor processor = new DepFileProcessor();
+
+ try {
+ compileAllFiles(processor);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ List<DependencyData> dataList = processor.getDependencyDataList();
+
+ DependencyDataStore store = new DependencyDataStore();
+ store.addData(dataList);
+
+ try {
+ store.saveTo(new File(getIncrementalFolder(), DEPENDENCY_STORE));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) throws IOException {
+ File incrementalData = new File(getIncrementalFolder(), DEPENDENCY_STORE);
+ DependencyDataStore store = new DependencyDataStore();
+ Multimap<String, DependencyData> inputMap;
+ try {
+ inputMap = store.loadFrom(incrementalData);
+ } catch (Exception ignored) {
+ incrementalData.delete();
+ getProject().getLogger().info(
+ "Failed to read dependency store: full task run!");
+ doFullTaskAction();
+ return;
+ }
+
+ final List<File> importFolders = getImportFolders();
+ final DepFileProcessor processor = new DepFileProcessor();
+ final ProcessOutputHandler processOutputHandler =
+ new LoggedProcessOutputHandler(getILogger());
+
+ // use an executor to parallelize the compilation of multiple files.
+ WaitableExecutor<Void> executor = new WaitableExecutor<Void>();
+
+ Map<String,DependencyData> mainFileMap = store.getMainFileMap();
+
+ for (final Map.Entry<File, FileStatus> entry : changedInputs.entrySet()) {
+ FileStatus status = entry.getValue();
+
+ switch (status) {
+ case NEW:
+ executor.execute(new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ File file = entry.getKey();
+ compileSingleFile(getSourceFolder(file), file, importFolders,
+ processor, processOutputHandler);
+ return null;
+ }
+ });
+ break;
+ case CHANGED:
+ Collection<DependencyData> impactedData =
+ inputMap.get(entry.getKey().getAbsolutePath());
+ if (impactedData != null) {
+ for (final DependencyData data: impactedData) {
+ executor.execute(new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ File file = new File(data.getMainFile());
+ compileSingleFile(getSourceFolder(file), file,
+ importFolders, processor, processOutputHandler);
+ return null;
+ }
+ });
+ }
+ }
+ break;
+ case REMOVED:
+ final DependencyData data2 = mainFileMap.get(entry.getKey().getAbsolutePath());
+ if (data2 != null) {
+ executor.execute(new Callable<Void>() {
+ @Override
+ public Void call() throws Exception {
+ cleanUpOutputFrom(data2);
+ return null;
+ }
+ });
+ store.remove(data2);
+ }
+ break;
+ }
+ }
+
+ try {
+ executor.waitForTasksWithQuickFail(true /*cancelRemaining*/);
+ } catch (Throwable t) {
+ incrementalData.delete();
+ throw new RuntimeException(t);
+ }
+
+ // get all the update data for the recompiled objects
+ store.updateAll(processor.getDependencyDataList());
+
+ try {
+ store.saveTo(incrementalData);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private File getSourceFolder(@NonNull File file) {
+ File parentDir = file;
+ while ((parentDir = parentDir.getParentFile()) != null) {
+ for (File folder : getSourceDirs()) {
+ if (parentDir.equals(folder)) {
+ return folder;
+ }
+ }
+ }
+
+ throw new IllegalArgumentException(String.format("File '%s' is not in a source dir", file));
+ }
+
+ private static void cleanUpOutputFrom(@NonNull DependencyData dependencyData) {
+ for (String output : dependencyData.getOutputFiles()) {
+ new File(output).delete();
+ }
+ for (String output : dependencyData.getSecondaryOutputFiles()) {
+ new File(output).delete();
+ }
+ }
+
+ @OutputDirectory
+ public File getSourceOutputDir() {
+ return sourceOutputDir;
+ }
+
+ public void setSourceOutputDir(File sourceOutputDir) {
+ this.sourceOutputDir = sourceOutputDir;
+ }
+
+ @OutputDirectory @Optional
+ public File getAidlParcelableDir() {
+ return aidlParcelableDir;
+ }
+
+ public void setAidlParcelableDir(File aidlParcelableDir) {
+ this.aidlParcelableDir = aidlParcelableDir;
+ }
+
+ public List<File> getSourceDirs() {
+ return sourceDirs;
+ }
+
+ public void setSourceDirs(List<File> sourceDirs) {
+ this.sourceDirs = sourceDirs;
+ }
+
+ @InputFiles
+ public List<File> getImportDirs() {
+ return importDirs;
+ }
+
+ public void setImportDirs(List<File> importDirs) {
+ this.importDirs = importDirs;
+ }
+
+ public static class ConfigAction implements TaskConfigAction<AidlCompile> {
+
+ @NonNull
+ VariantScope scope;
+
+ public ConfigAction(@NonNull VariantScope scope) {
+ this.scope = scope;
+ }
+
+ @Override
+ @NonNull
+ public String getName() {
+ return scope.getTaskName("compile", "Aidl");
+ }
+
+ @Override
+ @NonNull
+ public Class<AidlCompile> getType() {
+ return AidlCompile.class;
+ }
+
+ @Override
+ public void execute(AidlCompile compileTask) {
+ final VariantConfiguration<?,?,?> variantConfiguration = scope.getVariantConfiguration();
+
+ scope.getVariantData().aidlCompileTask = compileTask;
+
+ compileTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+ compileTask.setVariantName(scope.getVariantConfiguration().getFullName());
+ compileTask.setIncrementalFolder(scope.getAidlIncrementalDir());
+
+ ConventionMappingHelper.map(compileTask, "sourceDirs", new Callable<List<File>>() {
+ @Override
+ public List<File> call() throws Exception {
+ return variantConfiguration.getAidlSourceList();
+ }
+ });
+ ConventionMappingHelper.map(compileTask, "importDirs", new Callable<List<File>>() {
+ @Override
+ public List<File> call() throws Exception {
+ return variantConfiguration.getAidlImports();
+ }
+ });
+
+ ConventionMappingHelper.map(compileTask, "sourceOutputDir", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ return scope.getAidlSourceOutputDir();
+ }
+ });
+
+ if (variantConfiguration.getType() == VariantType.LIBRARY) {
+ compileTask.setAidlParcelableDir(scope.getAidlParcelableDir());
+ }
+ }
+ }
+
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AndroidProGuardTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AndroidProGuardTask.java
index cd8fa12..19c4fd2 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AndroidProGuardTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/AndroidProGuardTask.java
@@ -16,13 +16,13 @@
package com.android.build.gradle.tasks;
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
+import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.build.gradle.LibraryExtension;
import com.android.build.gradle.internal.DependencyManager;
+import com.android.build.gradle.internal.PostCompilationData;
import com.android.build.gradle.internal.TaskManager;
import com.android.build.gradle.internal.scope.TaskConfigAction;
import com.android.build.gradle.internal.scope.VariantScope;
@@ -31,12 +31,16 @@
import com.android.build.gradle.internal.variant.BaseVariantOutputData;
import com.android.build.gradle.internal.variant.LibraryVariantData;
import com.android.builder.core.VariantConfiguration;
-import com.android.utils.StringHelper;
+import com.android.builder.tasks.Job;
+import com.android.builder.tasks.JobContext;
+import com.android.ide.common.packaging.PackagingUtils;
+import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
-import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.gradle.api.Action;
import org.gradle.api.Task;
import org.gradle.api.tasks.InputFile;
@@ -46,12 +50,12 @@
import java.io.File;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Callable;
import groovy.lang.Closure;
import proguard.ParseException;
@@ -61,7 +65,7 @@
* Decoration for the {@link ProGuardTask} so it implements shared interfaces with our custom
* tasks.
*/
-public class AndroidProGuardTask extends ProGuardTask implements FileSupplier {
+public class AndroidProGuardTask extends ProGuardTask implements FileSupplier, JavaResourcesProvider {
/**
* resulting obfuscation mapping file.
@@ -80,6 +84,8 @@
@Optional
File testedAppMappingFile;
+ File obfuscatedClassesJar;
+
@Override
public void printmapping(Object printMapping) throws ParseException {
mappingFile = (File) printMapping;
@@ -104,7 +110,32 @@
@Override
@TaskAction
- public void proguard() throws ParseException, IOException {
+ public void proguard() throws IOException, ParseException {
+ final Job<Void> job = new Job<Void>(getName(),
+ new com.android.builder.tasks.Task<Void>() {
+ @Override
+ public void run(@NonNull Job<Void> job,
+ @NonNull JobContext<Void> context) throws IOException {
+ try {
+ AndroidProGuardTask.this.doMinification();
+ } catch (ParseException e) {
+ throw new IOException(e);
+ }
+ }
+ });
+ try {
+ SimpleWorkQueue.push(job);
+
+ // wait for the task completion.
+ job.await();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ public void doMinification() throws ParseException, IOException {
// only set the tested application mapping file if it exists (it must at this point or that
// means the tested application did not request obfuscation).
if (testedAppMappingFile != null && testedAppMappingFile.exists()) {
@@ -113,19 +144,26 @@
super.proguard();
}
+ @NonNull
+ @Override
+ public ImmutableList<JavaResourcesLocation> getJavaResourcesLocations() {
+ return ImmutableList.of(new JavaResourcesLocation(Type.JAR, obfuscatedClassesJar));
+ }
+
public static class ConfigAction implements TaskConfigAction<AndroidProGuardTask> {
private VariantScope scope;
- private Closure<File> inputDir;
+ private Callable<File> inputDir;
+ private Callable<File> javaResourcesInputDir;
- private Closure<List<File>> inputLibraries;
+ private Callable<List<File>> inputLibraries;
- public ConfigAction(VariantScope scope,
- TaskManager.PostCompilationData pcData) {
+ public ConfigAction(VariantScope scope, final PostCompilationData pcData) {
this.scope = scope;
- inputDir = pcData.getInputDir();
- inputLibraries = pcData.getInputLibraries();
+ inputDir = pcData.getInputDirCallable();
+ javaResourcesInputDir = pcData.getJavaResourcesInputDirCallable();
+ inputLibraries = pcData.getInputLibrariesCallable();
}
@Override
@@ -156,14 +194,7 @@
// --- Output File ---
- final File outFile = variantData instanceof LibraryVariantData ? new File(
- String.valueOf(scope.getGlobalScope().getBuildDir()) + "/" + FD_INTERMEDIATES + "/"
- + TaskManager.DIR_BUNDLES + "/" + variantData.getVariantConfiguration()
- .getDirName() + "/classes.jar") : new File(
- String.valueOf(scope.getGlobalScope().getBuildDir()) + "/" + FD_INTERMEDIATES
- + "/classes-proguard/" + variantData.getVariantConfiguration().getDirName()
- + "/classes.jar");
- variantData.obfuscatedClassesJar = outFile;
+ proguardTask.obfuscatedClassesJar = scope.getProguardOutputFile();
// --- Proguard Config ---
@@ -178,6 +209,7 @@
proguardTask.keep("class * {*;}");
proguardTask.keep("interface * {*;}");
proguardTask.keep("enum * {*;}");
+ proguardTask.keepattributes();
// Input the mapping from the tested app so that we can deal with obfuscated code.
proguardTask.applymapping(testedVariantData.getMappingFile());
@@ -194,21 +226,16 @@
proguardTask.dontwarn("org.jacoco.**");
}
- proguardTask.configuration(new Closure<Collection<File>>(this, this) {
- public Collection<File> doCall(Object it) {
+ proguardTask.configuration(new Callable<Collection<File>>() {
+ @Override
+ public Collection<File> call() throws Exception {
List<File> proguardFiles = variantConfig.getProguardFiles(true,
- Collections.singletonList(scope.getGlobalScope().getExtension()
- .getDefaultProguardFile(
- TaskManager.DEFAULT_PROGUARD_CONFIG_FILE)));
+ Collections.singletonList(getDefaultProguardFile(
+ TaskManager.DEFAULT_PROGUARD_CONFIG_FILE)));
proguardFiles.add(
variantOutputData.processResourcesTask.getProguardOutputFile());
return proguardFiles;
}
-
- public Collection<File> doCall() {
- return doCall(null);
- }
-
});
}
@@ -226,8 +253,7 @@
// exclude R files and such from output
String exclude = "!" + packageName + "/R.class";
exclude += (", !" + packageName + "/R$*.class");
- if (!((LibraryExtension) scope.getGlobalScope().getExtension())
- .getPackageBuildConfig()) {
+ if (!scope.getGlobalScope().getExtension().getPackageBuildConfig()) {
exclude += (", !" + packageName + "/Manifest.class");
exclude += (", !" + packageName + "/Manifest$*.class");
exclude += (", !" + packageName + "/BuildConfig.class");
@@ -242,24 +268,21 @@
proguardTask.libraryjars(map1, inputDir);
// injar: the local dependencies
- Closure inJars = new Closure<List<File>>(this, this) {
- public List<File> doCall(Object it) {
+ Callable inJars = new Callable<List<File>>() {
+ @Override
+ public List<File> call() throws Exception {
return DependencyManager
.getPackagedLocalJarFileList(variantData.getVariantDependency());
}
-
- public List<File> doCall() {
- return doCall(null);
- }
-
};
proguardTask.injars(ImmutableMap.of("filter", "!META-INF/MANIFEST.MF"), inJars);
// libjar: the library dependencies. In this case we take all the compile-scope
// dependencies
- Closure libJars = new Closure<Iterable<File>>(this, this) {
- public Iterable<File> doCall(Object it) {
+ Callable libJars = new Callable<Iterable<File>>() {
+ @Override
+ public Iterable<File> call() throws Exception {
// get all the compiled jar.
Set<File> compiledJars = scope.getGlobalScope().getAndroidBuilder()
.getCompileClasspath(variantConfig);
@@ -274,10 +297,6 @@
}
});
}
-
- public Iterable<File> doCall() {
- return doCall(null);
- }
};
proguardTask.libraryjars(ImmutableMap.of("filter", "!META-INF/MANIFEST.MF"), libJars);
@@ -287,22 +306,30 @@
} else {
// injar: the compilation output
proguardTask.injars(inputDir);
+ if (javaResourcesInputDir != null) {
+ proguardTask.injars(javaResourcesInputDir);
+ }
// injar: the packaged dependencies
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(1);
- map.put("filter", "!META-INF/MANIFEST.MF");
+
+ // add a filter to explicitly add files of the following extensions to the
+ // resulting proguarded classes.jar by only including the extensions that were
+ // not filtered by the libraries resource extraction task.
+ String filter = Joiner.on(", **/*.").join(
+ PackagingUtils.NON_RESOURCES_EXTENSIONS);
+
+ map.put("filter", "!META-INF/MANIFEST.MF" + (Strings.isNullOrEmpty(filter)
+ ? ""
+ : ", **/*." + filter));
proguardTask.injars(map, inputLibraries);
// the provided-only jars as libraries.
- Closure libJars = new Closure<List<File>>(this, this) {
- public List<File> doCall(Object it) {
+ Callable<List<File>> libJars = new Callable<List<File>>() {
+ @Override
+ public List<File> call() throws Exception {
return variantData.getVariantConfiguration().getProvidedOnlyJars();
}
-
- public List<File> doCall() {
- return doCall(null);
- }
-
};
proguardTask.libraryjars(libJars);
@@ -326,18 +353,14 @@
if (testedVariantData != null) {
// input the tested app as library
- proguardTask.libraryjars(testedVariantData.javaCompileTask.getDestinationDir());
+ proguardTask.libraryjars(testedVariantData.javacTask.getDestinationDir());
// including its dependencies
- Closure testedPackagedJars = new Closure<Set<File>>(this, this) {
- public Set<File> doCall(Object it) {
+ Callable testedPackagedJars = new Callable<Set<File>>() {
+ @Override
+ public Set<File> call() throws Exception {
return scope.getGlobalScope().getAndroidBuilder()
.getPackagedJars(testedVariantData.getVariantConfiguration());
}
-
- public Set<File> doCall() {
- return doCall(null);
- }
-
};
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(1);
@@ -347,7 +370,7 @@
// --- Out files ---
- proguardTask.outjars(outFile);
+ proguardTask.outjars(scope.getProguardOutputFile());
final File proguardOut = new File(
String.valueOf(scope.getGlobalScope().getBuildDir()) + "/" + FD_OUTPUTS
@@ -376,5 +399,13 @@
throw new RuntimeException(e);
}
}
+
+ private File getDefaultProguardFile(String name) {
+ File sdkDir = scope.getGlobalScope().getSdkHandler().getAndCheckSdkFolder();
+ return new File(sdkDir,
+ SdkConstants.FD_TOOLS + File.separatorChar
+ + SdkConstants.FD_PROGUARD + File.separatorChar
+ + name);
+ }
}
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/CompatibleScreensManifest.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/CompatibleScreensManifest.groovy
index 7320ecc..ca05d0c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/CompatibleScreensManifest.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/CompatibleScreensManifest.groovy
@@ -106,6 +106,7 @@
@Override
void execute(CompatibleScreensManifest csmTask) {
+ csmTask.setVariantName(scope.getVariantScope().getVariantConfiguration().getFullName())
csmTask.screenDensity = scope.variantOutputData.getMainOutputFile().getFilter(
com.android.build.OutputFile.DENSITY)
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy
index 36af87b..fbd5ea0 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Dex.groovy
@@ -17,7 +17,6 @@
import com.android.SdkConstants
import com.android.annotations.Nullable
-import com.android.build.gradle.internal.TaskManager
import com.android.build.gradle.internal.core.GradleVariantConfiguration
import com.android.build.gradle.internal.dsl.DexOptions
import com.android.build.gradle.internal.scope.ConventionMappingHelper
@@ -26,8 +25,10 @@
import com.android.build.gradle.internal.tasks.BaseTask
import com.android.build.gradle.internal.variant.ApkVariantData
import com.android.build.gradle.internal.variant.TestVariantData
-import com.android.utils.StringHelper
+import com.android.ide.common.process.LoggedProcessOutputHandler
+import com.android.utils.FileUtils
import org.codehaus.groovy.runtime.DefaultGroovyMethods
+import com.android.build.gradle.internal.PostCompilationData
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.InputFile
@@ -77,9 +78,6 @@
boolean multiDexEnabled = false
@Input
- boolean legacyMultiDexMode = false
-
- @Input
boolean optimize = true
@InputFile @Optional
@@ -140,7 +138,7 @@
boolean incremental) {
File outFolder = getOutputFolder()
if (!incremental) {
- emptyFolder(outFolder)
+ FileUtils.emptyFolder(outFolder)
}
File tmpFolder = getTmpFolder()
@@ -161,14 +159,13 @@
getLibraries(),
outFolder,
getMultiDexEnabled(),
- getLegacyMultiDexMode(),
getMainDexListFile(),
getDexOptions(),
getAdditionalParameters(),
tmpFolder,
incremental,
getOptimize(),
- )
+ new LoggedProcessOutputHandler(getILogger()))
}
@@ -176,9 +173,9 @@
private final VariantScope scope;
- private final TaskManager.PostCompilationData pcData;
+ private final PostCompilationData pcData;
- public ConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
+ public ConfigAction(VariantScope scope, PostCompilationData pcData) {
this.scope = scope;
this.pcData = pcData;
}
@@ -206,7 +203,8 @@
boolean isLegacyMultiDexMode = config.isLegacyMultiDexMode();
variantData.dexTask = dexTask;
- dexTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+ dexTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder())
+ dexTask.setVariantName(config.getFullName())
ConventionMappingHelper.map(dexTask, "outputFolder", new Callable<File>() {
@Override
public File call() throws Exception {
@@ -218,16 +216,15 @@
+ "/tmp/dex/" + config.getDirName()));
dexTask.setDexOptions(scope.getGlobalScope().getExtension().getDexOptions());
dexTask.setMultiDexEnabled(isMultiDexEnabled);
- dexTask.setLegacyMultiDexMode(isLegacyMultiDexMode);
// dx doesn't work with receving --no-optimize in debug so we disable it for now.
dexTask.setOptimize(true);//!variantData.variantConfiguration.buildType.debuggable
// inputs
- if (pcData.getInputDir() != null) {
- ConventionMappingHelper.map(dexTask, "inputDir", pcData.getInputDir());
+ if (pcData.getInputDirCallable() != null) {
+ ConventionMappingHelper.map(dexTask, "inputDir", pcData.getInputDirCallable());
}
- ConventionMappingHelper.map(dexTask, "inputFiles", pcData.getInputFiles());
- ConventionMappingHelper.map(dexTask, "libraries", pcData.getInputLibraries());
+ ConventionMappingHelper.map(dexTask, "inputFiles", pcData.getInputFilesCallable());
+ ConventionMappingHelper.map(dexTask, "libraries", pcData.getInputLibrariesCallable());
if (isMultiDexEnabled && isLegacyMultiDexMode) {
// configure the dex task to receive the generated class list.
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy
index ddd5f5b..4867946 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ExtractAnnotations.groovy
@@ -45,6 +45,22 @@
import static com.android.SdkConstants.DOT_JAVA
import static com.android.SdkConstants.UTF_8
+/**
+ * Task which extracts annotations from the source files, and writes them to one of
+ * two possible destinations:
+ * <ul>
+ * <li> A "external annotations" file (pointed to by {@link ExtractAnnotations#output})
+ * which records the annotations in a zipped XML format for use by the IDE and by
+ * lint to associate the (source retention) annotations back with the compiled code</li>
+ * <li> For any {@code Keep} annotated elements, a Proguard keep file (pointed to by
+ * {@link ExtractAnnotations#proguard}, which lists APIs (classes, methods and fields)
+ * that should not be removed even if no references in code are found to those APIs.</li>
+ * <p>
+ * We typically only extract external annotations when building libraries; ProGuard annotations
+ * are extracted when building libraries (to record in the AAR), <b>or</b> when building an
+ * app module where ProGuarding is enabled.
+ * </ul>
+ */
class ExtractAnnotations extends AbstractAndroidCompile {
public BaseVariantData variant
@@ -53,9 +69,15 @@
public List<String> bootClasspath
/** The output .zip file to write the annotations database to, if any */
+ @Optional
@OutputFile
public File output
+ /** The output proguard file to write any @Keep rules into, if any */
+ @Optional
+ @OutputFile
+ public File proguard
+
/**
* An optional pointer to an API file to filter the annotations by (any annotations
* not found in the API file are considered hidden/not exposed.) This is in the same
@@ -137,15 +159,20 @@
}
}
- Extractor extractor = new Extractor(database, classDir,
- project.logger.isEnabled(LogLevel.INFO), false);
+
+ def displayInfo = project.logger.isEnabled(LogLevel.INFO)
+ def includeClassRetentionAnnotations = false
+ def sortAnnotations = false
+
+ Extractor extractor = new Extractor(database, classDir, displayInfo,
+ includeClassRetentionAnnotations, sortAnnotations);
extractor.extractFromProjectSource(parsedUnits)
if (mergeJars != null) {
for (File jar : mergeJars) {
extractor.mergeExisting(jar);
}
}
- extractor.export(output)
+ extractor.export(output, proguard)
extractor.removeTypedefClasses();
} finally {
if (environment != null) {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateBuildConfig.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateBuildConfig.groovy
index 0216881..816c4c5 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateBuildConfig.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateBuildConfig.groovy
@@ -25,6 +25,8 @@
import com.android.builder.compiling.BuildConfigGenerator
import com.android.builder.core.VariantConfiguration
import com.android.builder.model.ClassField
+import com.android.utils.FileUtils
+import com.google.common.base.Strings
import com.google.common.collect.Lists
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
@@ -89,21 +91,16 @@
}
@TaskAction
- void generate() {
+ void generate() throws IOException {
// must clear the folder in case the packagename changed, otherwise,
// there'll be two classes.
File destinationDir = getSourceOutputDir()
- emptyFolder(destinationDir)
+ FileUtils.emptyFolder(destinationDir)
BuildConfigGenerator generator = new BuildConfigGenerator(
getSourceOutputDir(),
getBuildConfigPackageName());
- String vn = getVersionName()
- if (vn == null) {
- vn = ""
- }
-
// Hack (see IDEA-100046): We want to avoid reporting "condition is always true"
// from the data flow inspection, so use a non-constant value. However, that defeats
// the purpose of this flag (when not in debug mode, if (BuildConfig.DEBUG && ...) will
@@ -117,7 +114,7 @@
.addField("String", "BUILD_TYPE", "\"${getBuildTypeName()}\"")
.addField("String", "FLAVOR", "\"${getFlavorName()}\"")
.addField("int", "VERSION_CODE", Integer.toString(getVersionCode()))
- .addField("String", "VERSION_NAME", "\"${vn}\"")
+ .addField("String", "VERSION_NAME", "\"${Strings.nullToEmpty(getVersionName())}\"")
.addItems(getItems())
List<String> flavors = getFlavorNamesWithDimensionNames()
@@ -165,6 +162,7 @@
VariantConfiguration variantConfiguration = variantData.variantConfiguration
generateBuildConfigTask.androidBuilder = scope.globalScope.androidBuilder
+ generateBuildConfigTask.setVariantName(scope.getVariantConfiguration().getFullName())
ConventionMappingHelper.map(generateBuildConfigTask, "buildConfigPackageName") {
variantConfiguration.originalApplicationId
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateResValues.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateResValues.groovy
index 6fe016e..f55f769 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateResValues.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateResValues.groovy
@@ -14,7 +14,6 @@
* limitations under the License.
*/
package com.android.build.gradle.tasks
-
import com.android.annotations.NonNull
import com.android.build.gradle.internal.scope.ConventionMappingHelper
import com.android.build.gradle.internal.scope.TaskConfigAction
@@ -22,7 +21,6 @@
import com.android.build.gradle.internal.tasks.BaseTask
import com.android.builder.compiling.ResValueGenerator
import com.android.builder.core.VariantConfiguration
-import com.android.builder.model.AndroidProject
import com.android.builder.model.ClassField
import com.google.common.collect.Lists
import org.gradle.api.tasks.Input
@@ -30,8 +28,6 @@
import org.gradle.api.tasks.ParallelizableTask
import org.gradle.api.tasks.TaskAction
-import static com.android.builder.model.AndroidProject.FD_GENERATED
-
@ParallelizableTask
public class GenerateResValues extends BaseTask {
@@ -105,6 +101,7 @@
VariantConfiguration variantConfiguration = scope.variantData.variantConfiguration
generateResValuesTask.androidBuilder = scope.globalScope.androidBuilder
+ generateResValuesTask.setVariantName(variantConfiguration.getFullName())
ConventionMappingHelper.map(generateResValuesTask, "items") {
variantConfiguration.resValues
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateSplitAbiRes.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateSplitAbiRes.groovy
deleted file mode 100644
index 87e97ad..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateSplitAbiRes.groovy
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.tasks
-
-import com.android.build.gradle.internal.dsl.AaptOptions
-import com.android.build.gradle.internal.tasks.BaseTask
-import com.android.builder.core.AaptPackageProcessBuilder
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.Nested
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.OutputFiles
-import org.gradle.api.tasks.ParallelizableTask
-import org.gradle.api.tasks.TaskAction
-
-/**
- * Generates all metadata (like AndroidManifest.xml) necessary for a ABI dimension split APK.
- */
-@ParallelizableTask
-class GenerateSplitAbiRes extends BaseTask {
-
- @Input
- String applicationId
-
- @Input
- int versionCode
-
- @Input
- @Optional
- String versionName
-
- @Input
- String outputBaseName
-
- @Input
- Set<String> splits
-
- File outputDirectory
-
- @OutputFiles
- List<File> getOutputFiles() {
- List<File> outputFiles = new ArrayList<>();
- for (String split : getSplits()) {
- outputFiles.add(getOutputFileForSplit(split))
- }
- return outputFiles;
- }
-
- @Input
- boolean debuggable
-
- @Nested
- AaptOptions aaptOptions
-
- @TaskAction
- protected void doFullTaskAction() {
-
- for (String split : getSplits()) {
- String resPackageFileName = getOutputFileForSplit(split).getAbsolutePath()
-
- File tmpDirectory = new File(getOutputDirectory(), "${getOutputBaseName()}")
- tmpDirectory.mkdirs()
-
- File tmpFile = new File(tmpDirectory, "AndroidManifest.xml")
-
- OutputStreamWriter fileWriter = new OutputStreamWriter(new FileOutputStream(tmpFile), "UTF-8");
- String versionNameToUse = getVersionName();
- if (versionNameToUse == null) {
- versionNameToUse = String.valueOf(getVersionCode())
- }
- try {
- fileWriter.append(
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
- "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
- " package=\"" + getApplicationId() + "\"\n" +
- " android:versionCode=\"" + getVersionCode() + "\"\n" +
- " android:versionName=\"" + versionNameToUse + "\"\n" +
- " split=\"lib_${getOutputBaseName()}\">\n" +
- " <uses-sdk android:minSdkVersion=\"21\"/>\n" +
- "</manifest> ")
- fileWriter.flush()
- } finally {
- fileWriter.close()
- }
-
- AaptPackageProcessBuilder aaptPackageCommandBuilder =
- new AaptPackageProcessBuilder(tmpFile, getAaptOptions())
- .setDebuggable(getDebuggable())
- .setResPackageOutput(resPackageFileName);
-
- getBuilder().processResources(aaptPackageCommandBuilder, false /* enforceUniquePackageName */)
- }
- }
-
- private File getOutputFileForSplit(String split) {
- return new File(getOutputDirectory(), "resources-${getOutputBaseName()}-${split}.ap_")
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateSplitAbiRes.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateSplitAbiRes.java
new file mode 100644
index 0000000..7ce9a68
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/GenerateSplitAbiRes.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.tasks;
+
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.tasks.BaseTask;
+import com.android.builder.core.AaptPackageProcessBuilder;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.ide.common.process.ProcessException;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputFiles;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Generates all metadata (like AndroidManifest.xml) necessary for a ABI dimension split APK.
+ */
+@ParallelizableTask
+public class GenerateSplitAbiRes extends BaseTask {
+
+ private String applicationId;
+
+ private int versionCode;
+
+ private String versionName;
+
+ private String outputBaseName;
+
+ private Set<String> splits;
+
+ private File outputDirectory;
+
+ private boolean debuggable;
+
+ private AaptOptions aaptOptions;
+
+ @OutputFiles
+ public List<File> getOutputFiles() {
+ List<File> outputFiles = new ArrayList<File>();
+ for (String split : getSplits()) {
+ outputFiles.add(getOutputFileForSplit(split));
+ }
+
+ return outputFiles;
+ }
+
+ @TaskAction
+ protected void doFullTaskAction() throws IOException, InterruptedException, ProcessException {
+
+ for (String split : getSplits()) {
+ String resPackageFileName = getOutputFileForSplit(split).getAbsolutePath();
+
+ File tmpDirectory = new File(getOutputDirectory(), getOutputBaseName());
+ tmpDirectory.mkdirs();
+
+ File tmpFile = new File(tmpDirectory, "AndroidManifest.xml");
+
+ String versionNameToUse = getVersionName();
+ if (versionNameToUse == null) {
+ versionNameToUse = String.valueOf(getVersionCode());
+ }
+
+ OutputStreamWriter fileWriter = new OutputStreamWriter(new FileOutputStream(tmpFile), "UTF-8");
+ try {
+ fileWriter.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"" + getApplicationId() + "\"\n"
+ + " android:versionCode=\"" + getVersionCode() + "\"\n"
+ + " android:versionName=\"" + versionNameToUse + "\"\n"
+ + " split=\"lib_" + getOutputBaseName() + "\">\n"
+ + " <uses-sdk android:minSdkVersion=\"21\"/>\n" + "</manifest> ");
+ fileWriter.flush();
+ } finally {
+ fileWriter.close();
+ }
+
+ AaptPackageProcessBuilder aaptPackageCommandBuilder =
+ new AaptPackageProcessBuilder(tmpFile, getAaptOptions())
+ .setDebuggable(isDebuggable())
+ .setResPackageOutput(resPackageFileName);
+
+ getBuilder().processResources(
+ aaptPackageCommandBuilder,
+ false /* enforceUniquePackageName */,
+ new LoggedProcessOutputHandler(getILogger()));
+ }
+ }
+
+ private File getOutputFileForSplit(final String split) {
+ return new File(getOutputDirectory(),
+ "resources-" + getOutputBaseName() + "-" + split + ".ap_");
+ }
+
+ @Input
+ public String getApplicationId() {
+ return applicationId;
+ }
+
+ public void setApplicationId(String applicationId) {
+ this.applicationId = applicationId;
+ }
+
+ @Input
+ public int getVersionCode() {
+ return versionCode;
+ }
+
+ public void setVersionCode(int versionCode) {
+ this.versionCode = versionCode;
+ }
+
+ @Input
+ @Optional
+ public String getVersionName() {
+ return versionName;
+ }
+
+ public void setVersionName(String versionName) {
+ this.versionName = versionName;
+ }
+
+ @Input
+ public String getOutputBaseName() {
+ return outputBaseName;
+ }
+
+ public void setOutputBaseName(String outputBaseName) {
+ this.outputBaseName = outputBaseName;
+ }
+
+ @Input
+ public Set<String> getSplits() {
+ return splits;
+ }
+
+ public void setSplits(Set<String> splits) {
+ this.splits = splits;
+ }
+
+ public File getOutputDirectory() {
+ return outputDirectory;
+ }
+
+ public void setOutputDirectory(File outputDirectory) {
+ this.outputDirectory = outputDirectory;
+ }
+
+ @Input
+ public boolean isDebuggable() {
+ return debuggable;
+ }
+
+ public void setDebuggable(boolean debuggable) {
+ this.debuggable = debuggable;
+ }
+
+ @Nested
+ public AaptOptions getAaptOptions() {
+ return aaptOptions;
+ }
+
+ public void setAaptOptions(AaptOptions aaptOptions) {
+ this.aaptOptions = aaptOptions;
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JackTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JackTask.groovy
deleted file mode 100644
index 1a973e5..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JackTask.groovy
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.tasks
-
-import com.android.annotations.NonNull
-import com.android.build.gradle.internal.tasks.AbstractAndroidCompile
-import com.android.build.gradle.internal.tasks.FileSupplier
-import com.android.builder.core.AndroidBuilder
-import com.android.sdklib.BuildToolInfo
-import com.android.sdklib.repository.FullRevision
-import com.google.common.base.Charsets
-import com.google.common.io.Files
-import org.gradle.api.Task
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.OutputFile
-import org.gradle.api.tasks.ParallelizableTask
-import org.gradle.api.tasks.TaskAction
-/**
- * Jack task.
- */
-@ParallelizableTask
-public class JackTask extends AbstractAndroidCompile
- implements FileSupplier, BinaryFileProviderTask {
-
- final static FullRevision JACK_MIN_REV = new FullRevision(21, 1, 0)
-
- AndroidBuilder androidBuilder
-
- boolean isVerbose
-
- boolean isDebugLog
-
- @InputFile
- File getJackExe() {
- new File(androidBuilder.targetInfo.buildTools.getPath(BuildToolInfo.PathId.JACK))
- }
-
- @InputFiles
- Collection<File> packagedLibraries
-
- @InputFiles
- @Optional
- Collection<File> proguardFiles
-
- @Input
- boolean debug
-
- File tempFolder
-
- @OutputFile
- File jackFile
-
- @OutputFile
- @Optional
- File mappingFile
-
- @Input
- boolean multiDexEnabled
-
- @Input
- int minSdkVersion
-
- @Input
- @Optional
- String javaMaxHeapSize
-
- @TaskAction
- void compile() {
-
- if (System.getenv("USE_JACK_API") == null ||
- !androidBuilder.convertByteCodeUsingJackApis(
- getDestinationDir(),
- getJackFile(),
- getClasspath().files,
- getPackagedLibraries(),
- getSource().files,
- getProguardFiles(),
- getMappingFile(),
- isMultiDexEnabled(),
- getMinSdkVersion())) {
-
- androidBuilder.convertByteCodeWithJack(
- getDestinationDir(),
- getJackFile(),
- computeBootClasspath(),
- getPackagedLibraries(),
- computeEcjOptionFile(),
- getProguardFiles(),
- getMappingFile(),
- isMultiDexEnabled(),
- getMinSdkVersion(),
- isDebugLog,
- getJavaMaxHeapSize())
- }
- }
-
- private File computeEcjOptionFile() {
- File folder = getTempFolder()
- folder.mkdirs()
- File file = new File(folder, "ecj-options.txt");
-
- StringBuffer sb = new StringBuffer()
-
- for (File sourceFile : getSource().files) {
- sb.append(sourceFile.absolutePath).append('\n')
- }
-
- file.getParentFile().mkdirs()
-
- Files.write(sb.toString(), file, Charsets.UTF_8)
-
- return file
- }
-
- private String computeBootClasspath() {
- StringBuilder sb = new StringBuilder()
-
- boolean first = true;
- for (File file : getClasspath().files) {
- if (!first) {
- sb.append(':')
- } else {
- first = false
- }
- sb.append(file.getAbsolutePath())
- }
-
- return sb.toString()
- }
-
- @Override
- @NonNull
- BinaryFileProviderTask.Artifact getArtifact() {
- return new BinaryFileProviderTask.Artifact(
- BinaryFileProviderTask.BinaryArtifactType.JACK,
- getJackFile())
- }
-
- // ----- FileSupplierTask ----
- @NonNull
- @Override
- Task getTask() {
- return this
- }
-
- @Override
- File get() {
- return getMappingFile()
- }
-}
\ No newline at end of file
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JackTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JackTask.java
new file mode 100644
index 0000000..6f6848f
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JackTask.java
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.tasks;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.core.GradleVariantConfiguration;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.GlobalScope;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.tasks.AbstractAndroidCompile;
+import com.android.build.gradle.internal.tasks.FileSupplier;
+import com.android.build.gradle.internal.variant.ApplicationVariantData;
+import com.android.build.gradle.tasks.factory.AbstractCompilesUtil;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.tasks.Job;
+import com.android.builder.tasks.JobContext;
+import com.android.builder.tasks.Task;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.ide.common.process.ProcessException;
+import com.android.sdklib.BuildToolInfo;
+import com.android.sdklib.repository.FullRevision;
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+
+import org.gradle.api.Project;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Jack task.
+ */
+@ParallelizableTask
+public class JackTask extends AbstractAndroidCompile
+ implements FileSupplier, BinaryFileProviderTask, JavaResourcesProvider {
+
+ public static final FullRevision JACK_MIN_REV = new FullRevision(21, 1, 0);
+
+ private AndroidBuilder androidBuilder;
+
+ private boolean isVerbose;
+ private boolean isDebugLog;
+
+ private Collection<File> packagedLibraries;
+ private Collection<File> proguardFiles;
+ private Collection<File> jarJarRuleFiles;
+
+ private boolean debug;
+
+ private File tempFolder;
+ private File jackFile;
+ private File javaResourcesFolder;
+
+ private File mappingFile;
+
+ private boolean multiDexEnabled;
+
+ private int minSdkVersion;
+
+ private String javaMaxHeapSize;
+
+ private File incrementalDir;
+
+ @Override
+ @TaskAction
+ public void compile() {
+ final Job<Void> job = new Job<Void>(getName(), new Task<Void>() {
+ @Override
+ public void run(@NonNull Job<Void> job, @NonNull JobContext<Void> context)
+ throws IOException {
+ try {
+ JackTask.this.doMinification();
+ } catch (ProcessException e) {
+ throw new IOException(e);
+ }
+ }
+
+ });
+ try {
+ SimpleWorkQueue.push(job);
+
+ // wait for the task completion.
+ job.await();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ private void doMinification() throws ProcessException, IOException {
+
+ if (System.getenv("USE_JACK_API") != null) {
+ androidBuilder.convertByteCodeUsingJackApis(
+ getDestinationDir(),
+ getJackFile(),
+ getClasspath().getFiles(),
+ getPackagedLibraries(),
+ getSource().getFiles(),
+ getProguardFiles(),
+ getMappingFile(),
+ getJarJarRuleFiles(),
+ getIncrementalDir(),
+ getJavaResourcesFolder(),
+ isMultiDexEnabled(),
+ getMinSdkVersion());
+ } else {
+ // no incremental support through command line so far.
+ androidBuilder.convertByteCodeWithJack(
+ getDestinationDir(),
+ getJackFile(),
+ computeBootClasspath(),
+ getPackagedLibraries(),
+ computeEcjOptionFile(),
+ getProguardFiles(),
+ getMappingFile(),
+ getJarJarRuleFiles(),
+ isMultiDexEnabled(),
+ getMinSdkVersion(),
+ isDebugLog,
+ getJavaMaxHeapSize(),
+ new LoggedProcessOutputHandler(androidBuilder.getLogger()));
+ }
+
+ }
+
+ private File computeEcjOptionFile() throws IOException {
+ File folder = getTempFolder();
+ //noinspection ResultOfMethodCallIgnored
+ folder.mkdirs();
+ File file = new File(folder, "ecj-options.txt");
+
+ StringBuilder sb = new StringBuilder();
+
+ for (File sourceFile : getSource().getFiles()) {
+ sb.append(sourceFile.getAbsolutePath()).append("\n");
+ }
+
+ //noinspection ResultOfMethodCallIgnored
+ file.getParentFile().mkdirs();
+
+ Files.write(sb.toString(), file, Charsets.UTF_8);
+
+ return file;
+ }
+
+ private String computeBootClasspath() {
+ return Joiner.on(':').join(
+ Iterables.transform(getClasspath().getFiles(), GET_ABSOLUTE_PATH));
+ }
+
+ private static final Function<File, String> GET_ABSOLUTE_PATH = new Function<File, String>() {
+ @Override
+ public String apply(File file) {
+ return file.getAbsolutePath();
+ }
+ };
+
+
+ @InputFile
+ public File getJackExe() {
+ return new File(
+ androidBuilder.getTargetInfo().getBuildTools().getPath(BuildToolInfo.PathId.JACK));
+ }
+
+ public AndroidBuilder getAndroidBuilder() {
+ return androidBuilder;
+ }
+
+ public void setAndroidBuilder(AndroidBuilder androidBuilder) {
+ this.androidBuilder = androidBuilder;
+ }
+
+ public boolean getIsVerbose() {
+ return isVerbose;
+ }
+
+ public void setIsVerbose(boolean isVerbose) {
+ this.isVerbose = isVerbose;
+ }
+
+ public boolean getIsDebugLog() {
+ return isDebugLog;
+ }
+
+ public void setIsDebugLog(boolean isDebugLog) {
+ this.isDebugLog = isDebugLog;
+ }
+
+ @InputFiles
+ public Collection<File> getPackagedLibraries() {
+ return packagedLibraries;
+ }
+
+ public void setPackagedLibraries(Collection<File> packagedLibraries) {
+ this.packagedLibraries = packagedLibraries;
+ }
+
+ @InputFiles
+ @Optional
+ public Collection<File> getProguardFiles() {
+ return proguardFiles;
+ }
+
+ public void setProguardFiles(Collection<File> proguardFiles) {
+ this.proguardFiles = proguardFiles;
+ }
+
+ @InputFiles
+ @Optional
+ public Collection<File> getJarJarRuleFiles() {
+ return jarJarRuleFiles;
+ }
+
+ public void setJarJarRuleFiles(Collection<File> jarJarRuleFiles) {
+ this.jarJarRuleFiles = jarJarRuleFiles;
+ }
+
+ @Input
+ public boolean getDebug() {
+ return debug;
+ }
+
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ }
+
+ public File getTempFolder() {
+ return tempFolder;
+ }
+
+ public void setTempFolder(File tempFolder) {
+ this.tempFolder = tempFolder;
+ }
+
+ @OutputFile
+ public File getJackFile() {
+ return jackFile;
+ }
+
+ public void setJackFile(File jackFile) {
+ this.jackFile = jackFile;
+ }
+
+ @OutputFile
+ @Optional
+ public File getMappingFile() {
+ return mappingFile;
+ }
+
+ public void setMappingFile(File mappingFile) {
+ this.mappingFile = mappingFile;
+ }
+
+ @Input
+ public boolean isMultiDexEnabled() {
+ return multiDexEnabled;
+ }
+
+ public void setMultiDexEnabled(boolean multiDexEnabled) {
+ this.multiDexEnabled = multiDexEnabled;
+ }
+
+ @Input
+ public int getMinSdkVersion() {
+ return minSdkVersion;
+ }
+
+ public void setMinSdkVersion(int minSdkVersion) {
+ this.minSdkVersion = minSdkVersion;
+ }
+
+ @Input
+ @Optional
+ public String getJavaMaxHeapSize() {
+ return javaMaxHeapSize;
+ }
+
+ public void setJavaMaxHeapSize(String javaMaxHeapSize) {
+ this.javaMaxHeapSize = javaMaxHeapSize;
+ }
+
+ @Input
+ @Optional
+ public File getIncrementalDir() {
+ return incrementalDir;
+ }
+
+ public void setIncrementalDir(File incrementalDir) {
+ this.incrementalDir = incrementalDir;
+ }
+
+ @Input
+ @Optional
+ public File getJavaResourcesFolder() {
+ return javaResourcesFolder;
+ }
+
+ public void setJavaResourcesFolder(File javaResourcesFolder) {
+ this.javaResourcesFolder = javaResourcesFolder;
+ }
+
+ @NonNull
+ @Override
+ public ImmutableList<JavaResourcesLocation> getJavaResourcesLocations() {
+ return ImmutableList.of(
+ new JavaResourcesLocation(Type.FOLDER, getDestinationDir()));
+ }
+
+ @Override
+ @NonNull
+ public BinaryFileProviderTask.Artifact getArtifact() {
+ return new BinaryFileProviderTask.Artifact(
+ BinaryFileProviderTask.BinaryArtifactType.JACK,
+ getJackFile());
+ }
+
+ // ----- FileSupplierTask ----
+ @NonNull
+ @Override
+ public org.gradle.api.Task getTask() {
+ return this;
+ }
+
+ @Override
+ public File get() {
+ return getMappingFile();
+ }
+
+ public static class ConfigAction implements TaskConfigAction<JackTask> {
+
+ private final VariantScope scope;
+ private final boolean isVerbose;
+ private final boolean isDebugLog;
+
+ public ConfigAction(VariantScope scope, boolean isVerbose, boolean isDebugLog) {
+ this.scope = scope;
+ this.isVerbose = isVerbose;
+ this.isDebugLog = isDebugLog;
+ }
+
+ @Override
+ public String getName() {
+ return scope.getTaskName("compile", "JavaWithJack");
+ }
+
+ @Override
+ public Class<JackTask> getType() {
+ return JackTask.class;
+ }
+
+ @Override
+ public void execute(JackTask jackTask) {
+ jackTask.setIsVerbose(isVerbose);
+ jackTask.setIsDebugLog(isDebugLog);
+
+ GlobalScope globalScope = scope.getGlobalScope();
+
+ jackTask.androidBuilder = globalScope.getAndroidBuilder();
+ jackTask.setJavaMaxHeapSize(
+ globalScope.getExtension().getDexOptions().getJavaMaxHeapSize());
+
+ jackTask.setSource(scope.getVariantData().getJavaSources());
+
+ final GradleVariantConfiguration config = scope.getVariantData().getVariantConfiguration();
+ jackTask.setMultiDexEnabled(config.isMultiDexEnabled());
+ jackTask.setMinSdkVersion(config.getMinSdkVersion().getApiLevel());
+ jackTask.incrementalDir = scope.getJackIncrementalDir();
+
+ // if the tested variant is an app, add its classpath. For the libraries,
+ // it's done automatically since the classpath includes the library output as a normal
+ // dependency.
+ if (scope.getTestedVariantData() instanceof ApplicationVariantData) {
+ ConventionMappingHelper.map(jackTask, "classpath", new Callable<FileCollection>() {
+ @Override
+ public FileCollection call() throws Exception {
+ Project project = scope.getGlobalScope().getProject();
+ return project.fileTree(scope.getJillRuntimeLibrariesDir()).plus(
+ project.fileTree(
+ scope.getTestedVariantData().getScope()
+ .getJillRuntimeLibrariesDir())).plus(
+ project.fileTree(
+ scope.getTestedVariantData().getScope().getJackClassesZip()
+ ));
+ }
+ });
+ } else {
+ ConventionMappingHelper.map(jackTask, "classpath", new Callable<FileCollection>() {
+ @Override
+ public FileCollection call() throws Exception {
+ return scope.getGlobalScope().getProject().fileTree(
+ scope.getJillRuntimeLibrariesDir());
+ }
+ });
+ }
+
+ ConventionMappingHelper.map(jackTask, "packagedLibraries", new Callable<Collection<File>>() {
+ @Override
+ public Collection<File> call() throws Exception {
+ return scope.getGlobalScope().getProject()
+ .fileTree(scope.getJillPackagedLibrariesDir()).getFiles();
+ }
+ });
+
+ jackTask.setDestinationDir(scope.getJackDestinationDir());
+ jackTask.setJackFile(scope.getJackClassesZip());
+ jackTask.setTempFolder(new File(scope.getGlobalScope().getIntermediatesDir(),
+ "/tmp/jack/" + scope.getVariantConfiguration().getDirName()));
+
+ jackTask.setJavaResourcesFolder(scope.getJavaResourcesDestinationDir());
+ scope.setJavaResourcesProvider(jackTask);
+
+ if (config.isMinifyEnabled()) {
+ ConventionMappingHelper.map(jackTask, "proguardFiles", new Callable<List<File>>() {
+ @Override
+ public List<File> call() throws Exception {
+ // since all the output use the same resources, we can use the first output
+ // to query for a proguard file.
+ File sdkDir = scope.getGlobalScope().getSdkHandler().getAndCheckSdkFolder();
+ File defaultProguardFile = new File(sdkDir,
+ SdkConstants.FD_TOOLS + File.separatorChar
+ + SdkConstants.FD_PROGUARD + File.separatorChar
+ + TaskManager.DEFAULT_PROGUARD_CONFIG_FILE);
+
+ List<File> proguardFiles = config.getProguardFiles(true /*includeLibs*/,
+ ImmutableList.of(defaultProguardFile));
+ File proguardResFile = scope.getProcessAndroidResourcesProguardOutputFile();
+ proguardFiles.add(proguardResFile);
+ // for tested app, we only care about their aapt config since the base
+ // configs are the same files anyway.
+ if (scope.getTestedVariantData() != null) {
+ proguardResFile = scope.getTestedVariantData().getScope()
+ .getProcessAndroidResourcesProguardOutputFile();
+ proguardFiles.add(proguardResFile);
+ }
+
+ return proguardFiles;
+ }
+ });
+
+ jackTask.mappingFile = new File(scope.getProguardOutputFolder(), "mapping.txt");
+ }
+
+
+ ConventionMappingHelper.map(jackTask, "jarJarRuleFiles", new Callable<List<File>>() {
+ @Override
+ public List<File> call() throws Exception {
+ List<File> jarJarRuleFiles = Lists.newArrayListWithCapacity(
+ config.getJarJarRuleFiles().size());
+ Project project = scope.getGlobalScope().getProject();
+ for (File file: config.getJarJarRuleFiles()) {
+ jarJarRuleFiles.add(project.file(file));
+ }
+ return jarJarRuleFiles;
+ }
+ });
+
+ AbstractCompilesUtil.configureLanguageLevel(
+ jackTask,
+ scope.getGlobalScope().getExtension().getCompileOptions(),
+ scope.getGlobalScope().getExtension().getCompileSdkVersion()
+ );
+
+ scope.getVariantData().jackTask = jackTask;
+ scope.getVariantData().javaCompilerTask = jackTask;
+ scope.getVariantData().mappingFileProviderTask = jackTask;
+ scope.getVariantData().binayFileProviderTask = jackTask;
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JavaResourcesProvider.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JavaResourcesProvider.java
new file mode 100644
index 0000000..99c2963
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JavaResourcesProvider.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.tasks;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.TaskFactory;
+import com.android.build.gradle.internal.scope.AndroidTask;
+import com.google.common.collect.ImmutableList;
+
+import java.io.File;
+
+/**
+ * Denotes a provider for the java resources ready to be packaged in the final variant APK.
+ *
+ * These java resources can be the merged results of all the packaged libraries resources as well
+ * as obfuscated resources (that match obfuscated java code loading such resources).
+ *
+ * Depending on the configuration of the variant, the actual provider can be the obfuscation task
+ * or the resource merging task or something else.
+ */
+public interface JavaResourcesProvider {
+
+ /**
+ * Denotes how the java resources are provided (as a jar or as folder).
+ */
+ enum Type { JAR, FOLDER }
+
+ final class JavaResourcesLocation {
+ final Type type;
+ final File location;
+
+ public JavaResourcesLocation(Type type, File location) {
+ this.type = type;
+ this.location = location;
+ }
+ }
+
+ @NonNull
+ ImmutableList<JavaResourcesLocation> getJavaResourcesLocations();
+
+ /**
+ * Adapter for tasks that are not created yet.
+ */
+ class Adapter {
+ @NonNull
+ public static JavaResourcesProvider build(@NonNull final TaskFactory tasks,
+ @NonNull final AndroidTask<? extends JavaResourcesProvider> androidTask) {
+ return new JavaResourcesProvider() {
+ @NonNull
+ @Override
+ public ImmutableList<JavaResourcesLocation> getJavaResourcesLocations() {
+ return androidTask.get(tasks).getJavaResourcesLocations();
+ }
+ };
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JillTask.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JillTask.groovy
deleted file mode 100644
index 9c591bb..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JillTask.groovy
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.build.gradle.tasks
-import com.android.SdkConstants
-import com.android.annotations.NonNull
-import com.android.build.gradle.internal.tasks.BaseTask
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.DexOptions
-import com.android.ide.common.internal.WaitableExecutor
-import com.android.sdklib.repository.FullRevision
-import com.google.common.base.Charsets
-import com.google.common.collect.Lists
-import com.google.common.collect.Sets
-import com.google.common.hash.HashCode
-import com.google.common.hash.HashFunction
-import com.google.common.hash.Hashing
-import com.google.common.io.Files
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Nested
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.ParallelizableTask
-import org.gradle.api.tasks.TaskAction
-import org.gradle.api.tasks.incremental.IncrementalTaskInputs
-
-import java.util.concurrent.Callable
-
-import static com.android.build.gradle.tasks.JackTask.JACK_MIN_REV
-
-@ParallelizableTask
-public class JillTask extends BaseTask {
-
- // ----- PUBLIC TASK API -----
-
- // ----- PRIVATE TASK API -----
- @Input
- String getBuildToolsVersion() {
- getBuildTools().getRevision()
- }
-
- @SuppressWarnings("GroovyUnusedDeclaration")
- @InputFiles
- Collection<File> inputLibs
-
- @OutputDirectory
- File outputFolder
-
- @Nested
- com.android.build.gradle.internal.dsl.DexOptions dexOptions
-
- @TaskAction
- void taskAction(IncrementalTaskInputs taskInputs) {
- FullRevision revision = getBuilder().targetInfo.buildTools.revision
- if (revision.compareTo(JACK_MIN_REV) < 0) {
- throw new RuntimeException("Jack requires Build Tools ${JACK_MIN_REV.toString()} or later")
- }
-
- final File outFolder = getOutputFolder()
-
- // if we are not in incremental mode, then outOfDate will contain
- // all th files, but first we need to delete the previous output
- if (!taskInputs.isIncremental()) {
- emptyFolder(outFolder)
- }
-
- final Set<String> hashs = Sets.newHashSet()
- final WaitableExecutor<Void> executor = new WaitableExecutor<Void>()
- final List<File> inputFileDetails = Lists.newArrayList()
-
- final AndroidBuilder builder = getBuilder()
-
- taskInputs.outOfDate { final change ->
- inputFileDetails.add(change.file)
- }
-
- for (final File file : inputFileDetails) {
- Callable<Void> action = new JillCallable(file, hashs, outFolder, builder)
- executor.execute(action)
- }
-
- taskInputs.removed { change ->
- //noinspection GroovyAssignabilityCheck
- File jackFile = getJackFileName(outFolder, change.file)
- jackFile.delete()
- }
-
- executor.waitForTasksWithQuickFail(false)
- }
-
- private final class JillCallable implements Callable<Void> {
- @NonNull
- private final File fileToProcess
- @NonNull
- private final Set<String> hashs
- @NonNull
- private final DexOptions options = getDexOptions()
- @NonNull
- final File outFolder
- @NonNull
- private final AndroidBuilder builder
-
- private JillCallable(
- @NonNull File file,
- @NonNull Set<String> hashs,
- @NonNull File outFolder,
- @NonNull AndroidBuilder builder) {
- this.fileToProcess = file
- this.hashs = hashs
- this.outFolder = outFolder
- this.builder = builder
- }
-
- @Override
- Void call() throws Exception {
- // TODO remove once we can properly add a library as a dependency of its test.
- String hash = getFileHash(fileToProcess)
-
- synchronized (hashs) {
- if (hashs.contains(hash)) {
- return null
- }
-
- hashs.add(hash)
- }
-
- //noinspection GroovyAssignabilityCheck
- File jackFile = getJackFileName(outFolder, fileToProcess)
- //noinspection GroovyAssignabilityCheck
- builder.convertLibraryToJack(fileToProcess, jackFile, options)
-
- return null
- }
- }
-
- /**
- * Returns the hash of a file.
- * @param file the file to hash
- * @return
- */
- private static String getFileHash(@NonNull File file) {
- HashCode hashCode = Files.hash(file, Hashing.sha1())
- return hashCode.toString()
- }
-
- /**
- * Returns a unique File for the converted library, even
- * if there are 2 libraries with the same file names (but different
- * paths)
- *
- * @param outFolder the output folder.
- * @param inputFile the library
- * @return
- */
- @NonNull
- static File getJackFileName(@NonNull File outFolder, @NonNull File inputFile) {
- // get the filename
- String name = inputFile.getName()
- // remove the extension
- int pos = name.lastIndexOf('.')
- if (pos != -1) {
- name = name.substring(0, pos)
- }
-
- // add a hash of the original file path.
- String input = inputFile.getAbsolutePath()
- HashFunction hashFunction = Hashing.sha1()
- HashCode hashCode = hashFunction.hashString(input, Charsets.UTF_16LE)
-
- return new File(outFolder, name + "-" + hashCode.toString() + SdkConstants.DOT_JAR)
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JillTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JillTask.java
new file mode 100644
index 0000000..5340a5c
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/JillTask.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.tasks;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.dsl.DexOptions;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.GlobalScope;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.tasks.BaseTask;
+import com.android.builder.core.AndroidBuilder;
+import com.android.ide.common.internal.LoggedErrorException;
+import com.android.ide.common.internal.WaitableExecutor;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.sdklib.repository.FullRevision;
+import com.android.utils.FileUtils;
+import com.google.common.base.Charsets;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hashing;
+import com.google.common.io.Files;
+
+import org.gradle.api.Action;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+@ParallelizableTask
+public class JillTask extends BaseTask {
+
+ private Collection<File> inputLibs;
+
+ private File outputFolder;
+
+ private DexOptions dexOptions;
+
+ @TaskAction
+ public void taskAction(IncrementalTaskInputs taskInputs)
+ throws LoggedErrorException, InterruptedException, IOException {
+ FullRevision revision = getBuilder().getTargetInfo().getBuildTools().getRevision();
+ if (revision.compareTo(JackTask.JACK_MIN_REV) < 0) {
+ throw new RuntimeException(
+ "Jack requires Build Tools " + JackTask.JACK_MIN_REV.toString()
+ + " or later");
+ }
+
+ final File outFolder = getOutputFolder();
+
+ // if we are not in incremental mode, then outOfDate will contain
+ // all th files, but first we need to delete the previous output
+ if (!taskInputs.isIncremental()) {
+ FileUtils.emptyFolder(outFolder);
+ }
+
+ final Set<String> hashs = Sets.newHashSet();
+ final WaitableExecutor<Void> executor = new WaitableExecutor<Void>();
+ final List<File> inputFileDetails = Lists.newArrayList();
+
+ final AndroidBuilder builder = getBuilder();
+
+ taskInputs.outOfDate(new Action<InputFileDetails>() {
+ @Override
+ public void execute(InputFileDetails change) {
+ inputFileDetails.add(change.getFile());
+ }
+ });
+
+ for (final File file : inputFileDetails) {
+ Callable<Void> action = new JillCallable(this, file, hashs, outFolder, builder);
+ executor.execute(action);
+ }
+
+ taskInputs.removed(new Action<InputFileDetails>() {
+ @Override
+ public void execute(InputFileDetails change) {
+ File jackFile = getJackFileName(outFolder, ((InputFileDetails) change).getFile());
+ //noinspection ResultOfMethodCallIgnored
+ jackFile.delete();
+ }
+ });
+
+ executor.waitForTasksWithQuickFail(false);
+ }
+
+ @Input
+ public String getBuildToolsVersion() {
+ return getBuildTools().getRevision().toString();
+ }
+
+ @InputFiles
+ public Collection<File> getInputLibs() {
+ return inputLibs;
+ }
+
+ public void setInputLibs(Collection<File> inputLibs) {
+ this.inputLibs = inputLibs;
+ }
+
+ @OutputDirectory
+ public File getOutputFolder() {
+ return outputFolder;
+ }
+
+ public void setOutputFolder(File outputFolder) {
+ this.outputFolder = outputFolder;
+ }
+
+ @Nested
+ public DexOptions getDexOptions() {
+ return dexOptions;
+ }
+
+ public void setDexOptions(DexOptions dexOptions) {
+ this.dexOptions = dexOptions;
+ }
+
+ private final class JillCallable implements Callable<Void> {
+
+ @NonNull
+ private final File fileToProcess;
+
+ @NonNull
+ private final Set<String> hashs;
+
+ @NonNull
+ private final com.android.builder.core.DexOptions options = getDexOptions();
+
+ @NonNull
+ private final File outFolder;
+
+ @NonNull
+ private final AndroidBuilder builder;
+
+ private JillCallable(JillTask enclosing, @NonNull File file, @NonNull Set<String> hashs,
+ @NonNull File outFolder, @NonNull AndroidBuilder builder) {
+ this.fileToProcess = file;
+ this.hashs = hashs;
+ this.outFolder = outFolder;
+ this.builder = builder;
+ }
+
+ @Override
+ public Void call() throws Exception {
+ // TODO remove once we can properly add a library as a dependency of its test.
+ String hash = getFileHash(fileToProcess);
+
+ synchronized (hashs) {
+ if (hashs.contains(hash)) {
+ return null;
+ }
+
+ hashs.add(hash);
+ }
+
+ //noinspection GroovyAssignabilityCheck
+ File jackFile = getJackFileName(outFolder, fileToProcess);
+ //noinspection GroovyAssignabilityCheck
+ builder.convertLibraryToJack(fileToProcess, jackFile, options,
+ new LoggedProcessOutputHandler(builder.getLogger()));
+
+ return null;
+ }
+
+ @NonNull
+ public final File getOutFolder() {
+ return outFolder;
+ }
+ }
+
+ /**
+ * Returns the hash of a file.
+ *
+ * @param file the file to hash
+ */
+ private static String getFileHash(@NonNull File file) throws IOException {
+ HashCode hashCode = Files.hash(file, Hashing.sha1());
+ return hashCode.toString();
+ }
+
+ /**
+ * Returns a unique File for the converted library, even if there are 2 libraries with the same
+ * file names (but different paths)
+ *
+ * @param outFolder the output folder.
+ * @param inputFile the library
+ */
+ @NonNull
+ public static File getJackFileName(@NonNull File outFolder, @NonNull File inputFile) {
+ // get the filename
+ String name = inputFile.getName();
+ // remove the extension
+ int pos = name.lastIndexOf('.');
+ if (pos != -1) {
+ name = name.substring(0, pos);
+ }
+
+ // add a hash of the original file path.
+ String input = inputFile.getAbsolutePath();
+ HashFunction hashFunction = Hashing.sha1();
+ HashCode hashCode = hashFunction.hashString(input, Charsets.UTF_16LE);
+
+ return new File(outFolder, name + "-" + hashCode.toString() + SdkConstants.DOT_JAR);
+ }
+
+ public static class RuntimeTaskConfigAction implements TaskConfigAction<JillTask> {
+
+ private final VariantScope variantScope;
+
+ // TODO: If task can be shared between variants, change to GlobalScope.
+ public RuntimeTaskConfigAction(VariantScope scope) {
+ this.variantScope = scope;
+ }
+
+ @Override
+ public String getName() {
+ return variantScope.getTaskName("jill", "RuntimeLibraries");
+ }
+
+ @Override
+ public Class<JillTask> getType() {
+ return JillTask.class;
+ }
+
+ @Override
+ public void execute(JillTask jillTask) {
+ final GlobalScope globalScope = variantScope.getGlobalScope();
+ final AndroidBuilder androidBuilder = globalScope.getAndroidBuilder();
+
+ jillTask.setAndroidBuilder(androidBuilder);
+ jillTask.setVariantName(variantScope.getVariantConfiguration().getFullName());
+ jillTask.setDexOptions(globalScope.getExtension().getDexOptions());
+
+ ConventionMappingHelper.map(jillTask, "inputLibs", new Callable<List<File>>() {
+ @Override
+ public List<File> call() throws Exception {
+ return androidBuilder.getBootClasspath();
+ }
+ });
+
+ jillTask.setOutputFolder(variantScope.getJillRuntimeLibrariesDir());
+ }
+ }
+
+ public static class PackagedConfigAction implements TaskConfigAction<JillTask> {
+
+ private final VariantScope variantScope;
+
+ public PackagedConfigAction(VariantScope scope) {
+ this.variantScope = scope;
+ }
+
+ @Override
+ public String getName() {
+ return variantScope.getTaskName("jill", "PackagedLibraries");
+ }
+
+ @Override
+ public Class<JillTask> getType() {
+ return JillTask.class;
+ }
+
+ @Override
+ public void execute(JillTask jillTask) {
+ final GlobalScope globalScope = variantScope.getGlobalScope();
+ final AndroidBuilder androidBuilder = globalScope.getAndroidBuilder();
+
+ jillTask.setAndroidBuilder(androidBuilder);
+ jillTask.setVariantName(variantScope.getVariantConfiguration().getFullName());
+ jillTask.setDexOptions(globalScope.getExtension().getDexOptions());
+
+ ConventionMappingHelper.map(jillTask, "inputLibs", new Callable<Set<File>>() {
+ @Override
+ public Set<File> call() throws Exception {
+ return androidBuilder.getPackagedJars(variantScope.getVariantConfiguration());
+ }
+ });
+
+ jillTask.setOutputFolder(variantScope.getJillPackagedLibrariesDir());
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy
index 94b4793..d3b4e42 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/Lint.groovy
@@ -15,13 +15,10 @@
*/
package com.android.build.gradle.tasks
-
import com.android.annotations.NonNull
import com.android.annotations.Nullable
import com.android.build.gradle.internal.LintGradleClient
import com.android.build.gradle.internal.dsl.LintOptions
-import com.android.build.gradle.internal.model.ModelBuilder
-import com.android.build.gradle.internal.scope.AndroidTask
import com.android.build.gradle.internal.scope.TaskConfigAction
import com.android.build.gradle.internal.scope.VariantScope
import com.android.build.gradle.internal.tasks.DefaultAndroidTask
@@ -41,12 +38,12 @@
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.tasks.ParallelizableTask
import org.gradle.api.tasks.TaskAction
+import org.gradle.tooling.provider.model.ToolingModelBuilder
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
@ParallelizableTask
public class Lint extends DefaultAndroidTask {
@NonNull private LintOptions mLintOptions
- @Nullable private String mVariantName
@Nullable private File mSdkHome
private boolean mFatalOnly
private ToolingModelBuilderRegistry mToolingRegistry
@@ -59,10 +56,6 @@
mSdkHome = sdkHome
}
- public void setVariantName(@NonNull String variantName) {
- mVariantName = variantName
- }
-
void setToolingRegistry(ToolingModelBuilderRegistry toolingRegistry) {
mToolingRegistry = toolingRegistry
}
@@ -75,8 +68,8 @@
@TaskAction
public void lint() {
def modelProject = createAndroidProject(project)
- if (mVariantName != null) {
- lintSingleVariant(modelProject, mVariantName)
+ if (getVariantName() != null) {
+ lintSingleVariant(modelProject, getVariantName())
} else {
lintAllVariants(modelProject)
}
@@ -230,7 +223,7 @@
private AndroidProject createAndroidProject(@NonNull Project gradleProject) {
String modelName = AndroidProject.class.getName()
- ModelBuilder modelBuilder = mToolingRegistry.getBuilder(modelName) as ModelBuilder
+ ToolingModelBuilder modelBuilder = mToolingRegistry.getBuilder(modelName)
assert modelBuilder != null
return (AndroidProject) modelBuilder.buildAll(modelName, gradleProject)
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeAssets.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeAssets.groovy
index 3195d82..ab6d2dc 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeAssets.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeAssets.groovy
@@ -31,6 +31,7 @@
import com.android.ide.common.res2.FileValidity
import com.android.ide.common.res2.MergedAssetWriter
import com.android.ide.common.res2.MergingException
+import com.android.utils.FileUtils
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.ParallelizableTask
@@ -65,7 +66,7 @@
protected void doFullTaskAction() {
// this is full run, clean the previous output
File destinationDir = getOutputDir()
- emptyFolder(destinationDir)
+ FileUtils.emptyFolder(destinationDir)
List<AssetSet> assetSets = getInputAssetSets()
@@ -146,6 +147,9 @@
println e.getMessage()
merger.cleanBlob(getIncrementalFolder())
throw new ResourceException(e.getMessage(), e)
+ } finally {
+ // some clean up after the task to help multi variant/module builds.
+ fileValidity.clear();
}
}
@@ -178,6 +182,7 @@
variantData.mergeAssetsTask = mergeAssetsTask
mergeAssetsTask.androidBuilder = scope.globalScope.androidBuilder
+ mergeAssetsTask.setVariantName(variantConfig.getFullName())
mergeAssetsTask.incrementalFolder =
new File(
"$scope.globalScope.buildDir/${AndroidProject.FD_INTERMEDIATES}/incremental/mergeAssets/${variantConfig.dirName}")
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeManifests.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeManifests.groovy
index 63a84ab..277638c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeManifests.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeManifests.groovy
@@ -20,14 +20,11 @@
import com.android.build.gradle.internal.scope.ConventionMappingHelper
import com.android.build.gradle.internal.scope.TaskConfigAction
import com.android.build.gradle.internal.scope.VariantOutputScope
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.tasks.PrepareDependenciesTask
import com.android.build.gradle.internal.variant.ApkVariantOutputData
import com.android.build.gradle.internal.variant.BaseVariantData
import com.android.build.gradle.internal.variant.BaseVariantOutputData
import com.android.builder.core.VariantConfiguration
import com.android.builder.dependency.LibraryDependency
-import com.android.builder.model.AndroidProject
import com.android.manifmerger.ManifestMerger2
import com.google.common.collect.Lists
import org.gradle.api.tasks.Input
@@ -36,7 +33,6 @@
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.ParallelizableTask
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
import static com.android.builder.model.AndroidProject.FD_OUTPUTS
/**
@@ -175,6 +171,7 @@
variantOutputData.manifestProcessorTask = processManifestTask
processManifestTask.androidBuilder = scope.globalScope.androidBuilder
+ processManifestTask.setVariantName(config.getFullName())
processManifestTask.dependsOn variantData.prepareDependenciesTask
if (variantData.generateApkDataTask != null) {
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeResources.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeResources.groovy
deleted file mode 100644
index 3a56b00..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeResources.groovy
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.tasks
-
-import com.android.annotations.NonNull
-import com.android.build.gradle.internal.scope.ConventionMappingHelper
-import com.android.build.gradle.internal.scope.TaskConfigAction
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.tasks.IncrementalTask
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.builder.core.VariantType
-import com.android.builder.png.QueuedCruncher
-import com.android.ide.common.internal.PngCruncher
-import com.android.ide.common.res2.FileStatus
-import com.android.ide.common.res2.FileValidity
-import com.android.ide.common.res2.MergedResourceWriter
-import com.android.ide.common.res2.MergingException
-import com.android.ide.common.res2.ResourceMerger
-import com.android.ide.common.res2.ResourceSet
-import com.android.sdklib.BuildToolInfo
-import com.android.sdklib.repository.FullRevision
-import com.google.common.collect.Lists
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.ParallelizableTask
-import org.gradle.api.tasks.OutputFile
-
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-
-@ParallelizableTask
-public class MergeResources extends IncrementalTask {
-
- // ----- PUBLIC TASK API -----
-
- /** Directory to write the merged resources to */
- @OutputDirectory
- File outputDir
-
- /** Optional file to write any publicly imported resource types and names to */
- @Optional
- @OutputFile
- File publicFile
-
- // ----- PRIVATE TASK API -----
-
- // fake input to detect changes. Not actually used by the task
- @InputFiles
- Iterable<File> getRawInputFolders() {
- return flattenSourceSets(getInputResourceSets())
- }
-
- @Input
- String getBuildToolsVersion() {
- getBuildTools().getRevision()
- }
-
- @Input
- boolean process9Patch
-
- @Input
- boolean crunchPng
-
- @Input
- boolean useNewCruncher;
-
- @Input
- boolean insertSourceMarkers = true
-
- @Input
- boolean normalizeResources
-
- // actual inputs
- List<ResourceSet> inputResourceSets
-
- private final FileValidity<ResourceSet> fileValidity = new FileValidity<ResourceSet>();
-
- @Override
- protected boolean isIncremental() {
- return true
- }
-
- private PngCruncher getCruncher() {
- if (getUseNewCruncher()) {
- if (builder.getTargetInfo().buildTools.getRevision().getMajor() >= 22) {
- return QueuedCruncher.Builder.INSTANCE.newCruncher(
- builder.getTargetInfo().buildTools.getPath(
- BuildToolInfo.PathId.AAPT), builder.getLogger())
- }
- logger.info("New PNG cruncher will be enabled with build tools 22 and above.")
- }
- return builder.aaptCruncher;
- }
-
- @Override
- protected void doFullTaskAction() {
- // this is full run, clean the previous output
- File destinationDir = getOutputDir()
- emptyFolder(destinationDir)
-
- List<ResourceSet> resourceSets = getInputResourceSets()
-
- // create a new merger and populate it with the sets.
- ResourceMerger merger = new ResourceMerger()
-
- try {
- for (ResourceSet resourceSet : resourceSets) {
- resourceSet.setNormalizeResources(normalizeResources)
- // set needs to be loaded.
- resourceSet.loadFromFiles(getILogger())
- merger.addDataSet(resourceSet)
- }
-
- // get the merged set and write it down.
- MergedResourceWriter writer = new MergedResourceWriter(
- destinationDir, getCruncher(),
- getCrunchPng(), getProcess9Patch(), getPublicFile())
- writer.setInsertSourceMarkers(getInsertSourceMarkers())
-
- merger.mergeData(writer, false /*doCleanUp*/)
-
- // No exception? Write the known state.
- merger.writeBlobTo(getIncrementalFolder(), writer)
- } catch (MergingException e) {
- println e.getMessage()
- merger.cleanBlob(getIncrementalFolder())
- throw new ResourceException(e.getMessage(), e)
- }
- }
-
- @Override
- protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) {
- // create a merger and load the known state.
- ResourceMerger merger = new ResourceMerger()
- try {
- if (!merger.loadFromBlob(getIncrementalFolder(), true /*incrementalState*/)) {
- doFullTaskAction()
- return
- }
-
- // compare the known state to the current sets to detect incompatibility.
- // This is in case there's a change that's too hard to do incrementally. In this case
- // we'll simply revert to full build.
- List<ResourceSet> resourceSets = getInputResourceSets()
- for (ResourceSet resourceSet : resourceSets) {
- resourceSet.setNormalizeResources(normalizeResources)
- }
-
- if (!merger.checkValidUpdate(resourceSets)) {
- project.logger.info("Changed Resource sets: full task run!")
- doFullTaskAction()
- return
- }
-
- // The incremental process is the following:
- // Loop on all the changed files, find which ResourceSet it belongs to, then ask
- // the resource set to update itself with the new file.
- for (Map.Entry<File, FileStatus> entry : changedInputs.entrySet()) {
- File changedFile = entry.getKey()
-
- merger.findDataSetContaining(changedFile, fileValidity)
- if (fileValidity.status == FileValidity.FileStatus.UNKNOWN_FILE) {
- doFullTaskAction()
- return
- } else if (fileValidity.status == FileValidity.FileStatus.VALID_FILE) {
- if (!fileValidity.dataSet.updateWith(
- fileValidity.sourceFile, changedFile, entry.getValue(), getILogger())) {
- project.logger.info(
- String.format("Failed to process %s event! Full task run",
- entry.getValue()))
- doFullTaskAction()
- return
- }
- }
- }
-
- MergedResourceWriter writer = new MergedResourceWriter(
- getOutputDir(), getCruncher(),
- getCrunchPng(), getProcess9Patch(), getPublicFile())
- writer.setInsertSourceMarkers(getInsertSourceMarkers())
- merger.mergeData(writer, false /*doCleanUp*/)
- // No exception? Write the known state.
- merger.writeBlobTo(getIncrementalFolder(), writer)
- } catch (MergingException e) {
- println e.getMessage()
- merger.cleanBlob(getIncrementalFolder())
- throw new ResourceException(e.getMessage(), e)
- }
- }
-
- public static class ConfigAction implements TaskConfigAction<MergeResources> {
-
- @NonNull
- VariantScope scope
- @NonNull
- String taskNamePrefix
- @NonNull
- File outputLocation;
- boolean includeDependencies;
- boolean process9Patch;
-
- ConfigAction(VariantScope scope, String taskNamePrefix, File outputLocation,
- boolean includeDependencies, boolean process9Patch) {
- this.scope = scope
- this.taskNamePrefix = taskNamePrefix
- this.outputLocation = outputLocation
- this.includeDependencies = includeDependencies
- this.process9Patch = process9Patch
-
- scope.setMergeResourceOutputDir(outputLocation)
- }
-
- @Override
- String getName() {
- return scope.getTaskName(taskNamePrefix, "Resources")
- }
-
- @Override
- Class<MergeResources> getType() {
- return MergeResources
- }
-
- @Override
- void execute(MergeResources mergeResourcesTask) {
- BaseVariantData<? extends BaseVariantOutputData> variantData = scope.variantData
-
- mergeResourcesTask.androidBuilder = scope.globalScope.androidBuilder
- mergeResourcesTask.incrementalFolder = new File(
- "$scope.globalScope.buildDir/${FD_INTERMEDIATES}/incremental/" +
- "${taskNamePrefix}Resources/${variantData.variantConfiguration.dirName}")
-
- mergeResourcesTask.process9Patch = process9Patch
- mergeResourcesTask.crunchPng = scope.globalScope.extension.aaptOptions.getCruncherEnabled()
- mergeResourcesTask.normalizeResources = scope.globalScope.extension.buildToolsRevision.compareTo(new FullRevision(21, 0, 0)) < 0
-
- ConventionMappingHelper.map(mergeResourcesTask, "useNewCruncher") { scope.globalScope.getExtension().aaptOptions.useNewCruncher }
-
- ConventionMappingHelper.map(mergeResourcesTask, "inputResourceSets") {
- List<File> generatedResFolders = Lists.newArrayList(
- scope.getRenderscriptResOutputDir(),
- scope.getGeneratedResOutputDir())
- if (variantData.extraGeneratedResFolders != null) {
- generatedResFolders += variantData.extraGeneratedResFolders
- }
- if (variantData.generateApkDataTask != null &&
- variantData.getVariantConfiguration().getBuildType().isEmbedMicroApp()) {
- generatedResFolders.add(variantData.generateApkDataTask.getResOutputDir())
- }
- variantData.variantConfiguration.getResourceSets(generatedResFolders,
- includeDependencies)
- }
-
- ConventionMappingHelper.map(mergeResourcesTask, "outputDir") {
- outputLocation ?: scope.getDefaultMergeResourcesOutputDir()
- }
- variantData.mergeResourcesTask = mergeResourcesTask
- }
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeResources.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeResources.java
new file mode 100644
index 0000000..5805f27
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/MergeResources.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle.tasks;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.tasks.IncrementalTask;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.png.QueuedCruncher;
+import com.android.builder.png.VectorDrawableRenderer;
+import com.android.ide.common.internal.PngCruncher;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.ide.common.res2.FileStatus;
+import com.android.ide.common.res2.FileValidity;
+import com.android.ide.common.res2.GeneratedResourceSet;
+import com.android.ide.common.res2.MergedResourceWriter;
+import com.android.ide.common.res2.MergingException;
+import com.android.ide.common.res2.ResourceMerger;
+import com.android.ide.common.res2.ResourcePreprocessor;
+import com.android.ide.common.res2.ResourceSet;
+import com.android.sdklib.BuildToolInfo;
+import com.android.sdklib.repository.FullRevision;
+import com.android.utils.FileUtils;
+import com.google.common.collect.Lists;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.StopExecutionException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+@ParallelizableTask
+public class MergeResources extends IncrementalTask {
+
+ /**
+ * The first version of build tools that normalizes resources when packaging the APK.
+ *
+ * <p>This means that e.g. drawable-hdpi becomes drawable-hdpi-v4 to make it clear it was not
+ * available before API 4.
+ */
+ public static final FullRevision NORMALIZE_RESOURCES_BUILD_TOOLS = new FullRevision(21, 0, 0);
+
+ // ----- PUBLIC TASK API -----
+
+ /**
+ * Directory to write the merged resources to
+ */
+ private File outputDir;
+
+ // ----- PRIVATE TASK API -----
+
+ /**
+ * Optional file to write any publicly imported resource types and names to
+ */
+ private File publicFile;
+
+ private boolean process9Patch;
+
+ private boolean crunchPng;
+
+ private boolean useNewCruncher;
+
+ private boolean insertSourceMarkers = true;
+
+ private boolean normalizeResources;
+
+ private ResourcePreprocessor preprocessor;
+
+ // actual inputs
+ private List<ResourceSet> inputResourceSets;
+
+ private final FileValidity<ResourceSet> fileValidity = new FileValidity<ResourceSet>();
+
+ // fake input to detect changes. Not actually used by the task
+ @InputFiles
+ public Iterable<File> getRawInputFolders() {
+ return flattenSourceSets(getInputResourceSets());
+ }
+
+ @Input
+ public String getBuildToolsVersion() {
+ return getBuildTools().getRevision().toString();
+ }
+
+ @Override
+ protected boolean isIncremental() {
+ return true;
+ }
+
+ private PngCruncher getCruncher() {
+ if (getUseNewCruncher()) {
+ if (getBuilder().getTargetInfo().getBuildTools().getRevision().getMajor() >= 22) {
+ return QueuedCruncher.Builder.INSTANCE.newCruncher(
+ getBuilder().getTargetInfo().getBuildTools().getPath(
+ BuildToolInfo.PathId.AAPT), getILogger());
+ }
+ getLogger().info("New PNG cruncher will be enabled with build tools 22 and above.");
+ }
+ return getBuilder().getAaptCruncher(new LoggedProcessOutputHandler(getBuilder().getLogger()));
+ }
+
+ @Override
+ protected void doFullTaskAction() throws IOException {
+ // this is full run, clean the previous output
+ File destinationDir = getOutputDir();
+ FileUtils.emptyFolder(destinationDir);
+
+ List<ResourceSet> resourceSets = getConfiguredResourceSets();
+
+ // create a new merger and populate it with the sets.
+ ResourceMerger merger = new ResourceMerger();
+
+ try {
+ for (ResourceSet resourceSet : resourceSets) {
+ resourceSet.loadFromFiles(getILogger());
+ merger.addDataSet(resourceSet);
+ }
+
+ // get the merged set and write it down.
+ MergedResourceWriter writer = new MergedResourceWriter(
+ destinationDir, getCruncher(),
+ getCrunchPng(), getProcess9Patch(), getPublicFile(), preprocessor);
+ writer.setInsertSourceMarkers(getInsertSourceMarkers());
+
+ merger.mergeData(writer, false /*doCleanUp*/);
+
+ // No exception? Write the known state.
+ merger.writeBlobTo(getIncrementalFolder(), writer);
+ throw new StopExecutionException("Stop for now.");
+ } catch (MergingException e) {
+ System.out.println(e.getMessage());
+ merger.cleanBlob(getIncrementalFolder());
+ throw new ResourceException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) throws IOException {
+ // create a merger and load the known state.
+ ResourceMerger merger = new ResourceMerger();
+ try {
+ if (!merger.loadFromBlob(getIncrementalFolder(), true /*incrementalState*/)) {
+ doFullTaskAction();
+ return;
+ }
+
+ for (ResourceSet resourceSet : merger.getDataSets()) {
+ resourceSet.setNormalizeResources(normalizeResources);
+ resourceSet.setPreprocessor(preprocessor);
+ }
+
+ List<ResourceSet> resourceSets = getConfiguredResourceSets();
+
+ // compare the known state to the current sets to detect incompatibility.
+ // This is in case there's a change that's too hard to do incrementally. In this case
+ // we'll simply revert to full build.
+ if (!merger.checkValidUpdate(resourceSets)) {
+ getLogger().info("Changed Resource sets: full task run!");
+ doFullTaskAction();
+ return;
+ }
+
+ // The incremental process is the following:
+ // Loop on all the changed files, find which ResourceSet it belongs to, then ask
+ // the resource set to update itself with the new file.
+ for (Map.Entry<File, FileStatus> entry : changedInputs.entrySet()) {
+ File changedFile = entry.getKey();
+
+ merger.findDataSetContaining(changedFile, fileValidity);
+ if (fileValidity.getStatus() == FileValidity.FileStatus.UNKNOWN_FILE) {
+ doFullTaskAction();
+ return;
+ } else if (fileValidity.getStatus() == FileValidity.FileStatus.VALID_FILE) {
+ if (!fileValidity.getDataSet().updateWith(
+ fileValidity.getSourceFile(), changedFile, entry.getValue(),
+ getILogger())) {
+ getLogger().info(
+ String.format("Failed to process %s event! Full task run",
+ entry.getValue()));
+ doFullTaskAction();
+ return;
+ }
+ }
+ }
+
+ MergedResourceWriter writer = new MergedResourceWriter(
+ getOutputDir(), getCruncher(),
+ getCrunchPng(), getProcess9Patch(), getPublicFile(), preprocessor);
+ writer.setInsertSourceMarkers(getInsertSourceMarkers());
+ merger.mergeData(writer, false /*doCleanUp*/);
+ // No exception? Write the known state.
+ merger.writeBlobTo(getIncrementalFolder(), writer);
+ } catch (MergingException e) {
+ merger.cleanBlob(getIncrementalFolder());
+ throw new ResourceException(e.getMessage(), e);
+ } finally {
+ // some clean up after the task to help multi variant/module builds.
+ fileValidity.clear();
+ }
+ }
+
+ @NonNull
+ private List<ResourceSet> getConfiguredResourceSets() {
+ List<ResourceSet> resourceSets = Lists.newArrayList(getInputResourceSets());
+ List<ResourceSet> generatedSets = Lists.newArrayListWithCapacity(resourceSets.size());
+
+ for (ResourceSet resourceSet : resourceSets) {
+ resourceSet.setNormalizeResources(normalizeResources);
+ resourceSet.setPreprocessor(preprocessor);
+ ResourceSet generatedSet = new GeneratedResourceSet(resourceSet);
+ resourceSet.setGeneratedSet(generatedSet);
+ generatedSets.add(generatedSet);
+ }
+
+ // Put all generated sets at the start of the list.
+ resourceSets.addAll(0, generatedSets);
+ return resourceSets;
+ }
+
+ @Input
+ public boolean isProcess9Patch() {
+ return process9Patch;
+ }
+
+ @Input
+ public boolean isCrunchPng() {
+ return crunchPng;
+ }
+
+ @Input
+ public boolean isUseNewCruncher() {
+ return useNewCruncher;
+ }
+
+ @Input
+ public boolean isInsertSourceMarkers() {
+ return insertSourceMarkers;
+ }
+
+ @Input
+ public boolean isNormalizeResources() {
+ return normalizeResources;
+ }
+
+ public void setNormalizeResources(boolean normalizeResources) {
+ this.normalizeResources = normalizeResources;
+ }
+
+ public List<ResourceSet> getInputResourceSets() {
+ return inputResourceSets;
+ }
+
+ public void setInputResourceSets(
+ List<ResourceSet> inputResourceSets) {
+ this.inputResourceSets = inputResourceSets;
+ }
+
+ public boolean getUseNewCruncher() {
+ return useNewCruncher;
+ }
+
+ public void setUseNewCruncher(boolean useNewCruncher) {
+ this.useNewCruncher = useNewCruncher;
+ }
+
+ @OutputDirectory
+ public File getOutputDir() {
+ return outputDir;
+ }
+
+ public void setOutputDir(File outputDir) {
+ this.outputDir = outputDir;
+ }
+
+ public boolean getCrunchPng() {
+ return crunchPng;
+ }
+
+ public void setCrunchPng(boolean crunchPng) {
+ this.crunchPng = crunchPng;
+ }
+
+ public boolean getProcess9Patch() {
+ return process9Patch;
+ }
+
+ public void setProcess9Patch(boolean process9Patch) {
+ this.process9Patch = process9Patch;
+ }
+
+ @Optional
+ @OutputFile
+ public File getPublicFile() {
+ return publicFile;
+ }
+
+ public void setPublicFile(File publicFile) {
+ this.publicFile = publicFile;
+ }
+
+ public boolean getInsertSourceMarkers() {
+ return insertSourceMarkers;
+ }
+
+ public void setInsertSourceMarkers(boolean insertSourceMarkers) {
+ this.insertSourceMarkers = insertSourceMarkers;
+ }
+
+ public static class ConfigAction implements TaskConfigAction<MergeResources> {
+
+ @NonNull
+ private VariantScope scope;
+
+ @NonNull
+ private String taskNamePrefix;
+
+ @Nullable
+ private File outputLocation;
+
+ private boolean includeDependencies;
+
+ private boolean process9Patch;
+
+ public ConfigAction(
+ @NonNull VariantScope scope,
+ @NonNull String taskNamePrefix,
+ @Nullable File outputLocation,
+ boolean includeDependencies,
+ boolean process9Patch) {
+ this.scope = scope;
+ this.taskNamePrefix = taskNamePrefix;
+ this.outputLocation = outputLocation;
+ this.includeDependencies = includeDependencies;
+ this.process9Patch = process9Patch;
+
+ scope.setMergeResourceOutputDir(outputLocation);
+ }
+
+ @Override
+ public String getName() {
+ return scope.getTaskName(taskNamePrefix, "Resources");
+ }
+
+ @Override
+ public Class<MergeResources> getType() {
+ return MergeResources.class;
+ }
+
+ @Override
+ public void execute(MergeResources mergeResourcesTask) {
+ final BaseVariantData<? extends BaseVariantOutputData> variantData =
+ scope.getVariantData();
+ final AndroidConfig extension = scope.getGlobalScope().getExtension();
+
+ mergeResourcesTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+ mergeResourcesTask.setVariantName(scope.getVariantConfiguration().getFullName());
+ mergeResourcesTask.setIncrementalFolder(new File(
+ scope.getGlobalScope().getBuildDir() + "/" + AndroidProject.FD_INTERMEDIATES +
+ "/incremental/" + taskNamePrefix + "Resources/" +
+ variantData.getVariantConfiguration().getDirName()));
+
+ mergeResourcesTask.process9Patch = process9Patch;
+ mergeResourcesTask.crunchPng = extension.getAaptOptions()
+ .getCruncherEnabled();
+ mergeResourcesTask.normalizeResources =
+ extension.getBuildToolsRevision()
+ .compareTo(NORMALIZE_RESOURCES_BUILD_TOOLS) < 0;
+
+ // Only one pre-processor for now. The code will need slight changes when we add more.
+ mergeResourcesTask.preprocessor = new VectorDrawableRenderer(
+ scope.getGeneratedPngsOutputDir(),
+ extension.getPreprocessingOptions().getTypedDensities(),
+ mergeResourcesTask.getILogger());
+
+ ConventionMappingHelper.map(mergeResourcesTask, "useNewCruncher",
+ new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ return extension.getAaptOptions()
+ .getUseNewCruncher();
+ }
+ });
+
+ ConventionMappingHelper.map(mergeResourcesTask, "inputResourceSets",
+ new Callable<List<ResourceSet>>() {
+ @Override
+ public List<ResourceSet> call() throws Exception {
+ List<File> generatedResFolders = Lists.newArrayList(
+ scope.getRenderscriptResOutputDir(),
+ scope.getGeneratedResOutputDir());
+ if (variantData.getExtraGeneratedResFolders() != null) {
+ generatedResFolders.addAll(
+ variantData.getExtraGeneratedResFolders());
+ }
+ if (variantData.generateApkDataTask != null &&
+ variantData.getVariantConfiguration().getBuildType()
+ .isEmbedMicroApp()) {
+ generatedResFolders.add(
+ variantData.generateApkDataTask.getResOutputDir());
+ }
+ return variantData.getVariantConfiguration()
+ .getResourceSets(generatedResFolders, includeDependencies);
+ }
+ });
+ ConventionMappingHelper.map(mergeResourcesTask, "outputDir", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ return outputLocation != null ? outputLocation
+ : scope.getDefaultMergeResourcesOutputDir();
+ }
+ });
+
+ variantData.mergeResourcesTask = mergeResourcesTask;
+ }
+ }
+}
+
+
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy
index d7d3e66..c06a19d 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/NdkCompile.groovy
@@ -15,11 +15,15 @@
*/
package com.android.build.gradle.tasks
+
import com.android.annotations.NonNull
-import com.android.build.gradle.internal.core.NdkConfig
+import com.android.build.gradle.internal.dsl.CoreNdkOptions
import com.android.build.gradle.internal.tasks.NdkTask
+import com.android.ide.common.process.LoggedProcessOutputHandler
import com.android.ide.common.process.ProcessInfoBuilder
+import com.android.ide.common.process.ProcessOutputHandler
import com.android.sdklib.IAndroidTarget
+import com.android.utils.FileUtils
import com.google.common.base.Charsets
import com.google.common.base.Joiner
import com.google.common.collect.Lists
@@ -41,6 +45,8 @@
class NdkCompile extends NdkTask {
+ public static String USE_DEPRECATED_NDK = "android.useDeprecatedNdk";
+
List<File> sourceFolders
@OutputFile
@@ -65,6 +71,9 @@
@Input
boolean ndkCygwinMode
+ @Input
+ boolean isForTesting
+
@SkipWhenEmpty
@InputFiles
FileTree getSource() {
@@ -78,6 +87,34 @@
@TaskAction
void taskAction(IncrementalTaskInputs inputs) {
+ if (!project.hasProperty(USE_DEPRECATED_NDK)) {
+ // Normally, we would catch the user when they try to configure the NDK, but NDK do
+ // not need to be configured by default. Throw this exception during task execution in
+ // case we miss it.
+ throw new RuntimeException(
+ "Error: NDK integration is deprecated in the current plugin. Consider trying " +
+ "the new experimental plugin. For details, see " +
+ "http://tools.android.com/tech-docs/new-build-system/gradle-experimental. " +
+ "Set \"$USE_DEPRECATED_NDK=true\" in gradle.properties to " +
+ "continue using the current NDK integration.");
+ }
+
+
+ if (isNdkOptionUnset()) {
+ logger.warn("Warning: Native C/C++ source code is found, but it seems that NDK " +
+ "option is not configured. Note that if you have an Android.mk, it is not " +
+ "used for compilation. The recommended workaround is to remove the default " +
+ "jni source code directory by adding: \n " +
+ "android {\n" +
+ " sourceSets {\n" +
+ " main {\n" +
+ " jni.srcDirs = []\n" +
+ " }\n" +
+ " }\n" +
+ "}\n" +
+ "to build.gradle, manually compile the code with ndk-build, " +
+ "and then place the resulting shared object in src/main/jniLibs.");
+ }
FileTree sourceFileTree = getSource()
Set<File> sourceFiles = sourceFileTree.matching(new PatternSet().exclude("**/*.h")).files
@@ -85,8 +122,8 @@
if (sourceFiles.isEmpty()) {
makefile.delete()
- emptyFolder(getSoFolder())
- emptyFolder(getObjFolder())
+ FileUtils.emptyFolder(getSoFolder())
+ FileUtils.emptyFolder(getObjFolder())
return
}
@@ -103,8 +140,8 @@
if (!inputs.isIncremental()) {
project.logger.info("Unable do incremental execution: full task run")
generateMakefile = true
- emptyFolder(getSoFolder())
- emptyFolder(getObjFolder())
+ FileUtils.emptyFolder(getSoFolder())
+ FileUtils.emptyFolder(getObjFolder())
} else {
// look for added or removed files *only*
@@ -130,7 +167,7 @@
}
private void writeMakefile(@NonNull Set<File> sourceFiles, @NonNull File makefile) {
- NdkConfig ndk = getNdkConfig()
+ CoreNdkOptions ndk = getNdkConfig()
StringBuilder sb = new StringBuilder()
@@ -138,12 +175,20 @@
'LOCAL_PATH := $(call my-dir)\n' +
'include \$(CLEAR_VARS)\n\n')
- sb.append('LOCAL_MODULE := ').append(ndk.moduleName != null ? ndk.moduleName : project.name).append('\n')
+ String moduleName = ndk.moduleName != null ? ndk.moduleName : project.name
+ if (isForTesting) {
+ moduleName = moduleName + "_test"
+ }
+
+ sb.append('LOCAL_MODULE := ').append(moduleName).append('\n')
if (ndk.cFlags != null) {
sb.append('LOCAL_CFLAGS := ').append(ndk.cFlags).append('\n')
}
+ // To support debugging from Android Studio.
+ sb.append("LOCAL_LDFLAGS := -Wl,--build-id\n")
+
List<String> fullLdlibs = Lists.newArrayList()
if (ndk.ldLibs != null) {
fullLdlibs.addAll(ndk.ldLibs)
@@ -189,7 +234,7 @@
}
private void runNdkBuild(@NonNull File ndkLocation, @NonNull File makefile) {
- NdkConfig ndk = getNdkConfig()
+ CoreNdkOptions ndk = getNdkConfig()
ProcessInfoBuilder builder = new ProcessInfoBuilder()
@@ -241,6 +286,17 @@
builder.addArgs("-j" + ndk.getJobs());
}
- getBuilder().executeProcess(builder.createProcess()).rethrowFailure().assertNormalExitValue()
+ ProcessOutputHandler handler = new LoggedProcessOutputHandler(getBuilder().getLogger());
+ getBuilder().executeProcess(builder.createProcess(), handler)
+ .rethrowFailure().assertNormalExitValue()
+ }
+
+ private boolean isNdkOptionUnset() {
+ // If none of the NDK options are set, then it is likely that NDK is not configured.
+ return (getModuleName() == null &&
+ getcFlags() == null &&
+ getLdLibs() == null &&
+ getAbiFilters() == null &&
+ getStl() == null);
}
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java
index ffff5ae..16ab90c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageApplication.java
@@ -6,8 +6,8 @@
import com.android.build.gradle.internal.annotations.ApkFile;
import com.android.build.gradle.internal.core.GradleVariantConfiguration;
import com.android.build.gradle.internal.dsl.AbiSplitOptions;
+import com.android.build.gradle.internal.dsl.CoreSigningConfig;
import com.android.build.gradle.internal.dsl.PackagingOptions;
-import com.android.build.gradle.internal.dsl.SigningConfig;
import com.android.build.gradle.internal.scope.ConventionMappingHelper;
import com.android.build.gradle.internal.scope.TaskConfigAction;
import com.android.build.gradle.internal.scope.VariantOutputScope;
@@ -18,7 +18,9 @@
import com.android.build.gradle.internal.variant.ApkVariantOutputData;
import com.android.build.gradle.internal.variant.BaseVariantData;
import com.android.builder.packaging.DuplicateFileException;
+import com.android.builder.signing.SignedJarBuilder;
import com.android.utils.StringHelper;
+import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import org.codehaus.groovy.runtime.StringGroovyMethods;
@@ -92,6 +94,14 @@
this.jniFolders = jniFolders;
}
+ public File getMergingFolder() {
+ return mergingFolder;
+ }
+
+ public void setMergingFolder(File mergingFolder) {
+ this.mergingFolder = mergingFolder;
+ }
+
@OutputFile
public File getOutputFile() {
return outputFile;
@@ -123,6 +133,8 @@
private Set<File> jniFolders;
+ private File mergingFolder;
+
@ApkFile
private File outputFile;
@@ -132,10 +144,12 @@
private boolean jniDebugBuild;
- private SigningConfig signingConfig;
+ private CoreSigningConfig signingConfig;
private PackagingOptions packagingOptions;
+ private SignedJarBuilder.IZipEntryFilter packagingOptionsFilter;
+
@InputFiles
public Set<File> getPackagedJars() {
return packagedJars;
@@ -160,11 +174,11 @@
@Nested
@Optional
- public SigningConfig getSigningConfig() {
+ public CoreSigningConfig getSigningConfig() {
return signingConfig;
}
- public void setSigningConfig(SigningConfig signingConfig) {
+ public void setSigningConfig(CoreSigningConfig signingConfig) {
this.signingConfig = signingConfig;
}
@@ -177,6 +191,14 @@
this.packagingOptions = packagingOptions;
}
+ public SignedJarBuilder.IZipEntryFilter getPackagingOptionsFilter() {
+ return packagingOptionsFilter;
+ }
+
+ public void setPackagingOptionsFilter(SignedJarBuilder.IZipEntryFilter filter) {
+ this.packagingOptionsFilter = filter;
+ }
+
@InputFiles
public FileTree getNativeLibraries() {
FileTree src = null;
@@ -194,8 +216,10 @@
final File dir = getJavaResourceDir();
getBuilder().packageApk(getResourceFile().getAbsolutePath(), getDexFolder(),
getDexedLibraries(), getPackagedJars(),
- (dir == null ? null : dir.getAbsolutePath()), getJniFolders(), getAbiFilters(),
- getJniDebugBuild(), getSigningConfig(), getPackagingOptions(),
+ (dir == null ? null : dir.getAbsolutePath()), getJniFolders(),
+ getMergingFolder(), getAbiFilters(), getJniDebugBuild(), getSigningConfig(),
+ getPackagingOptions(),
+ getPackagingOptionsFilter(),
getOutputFile().getAbsolutePath());
} catch (DuplicateFileException e) {
Logger logger = getLogger();
@@ -259,6 +283,8 @@
variantOutputData.packageApplicationTask = packageApp;
packageApp.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+ packageApp.setVariantName(
+ scope.getVariantScope().getVariantConfiguration().getFullName());
if (config.isMinifyEnabled() && config.getBuildType().isShrinkResources() && !config
.getUseJack()) {
@@ -280,44 +306,61 @@
ConventionMappingHelper.map(packageApp, "dexFolder", new Callable<File>() {
@Override
public File call() {
- if (scope.getVariantScope().getDexTask() != null) {
- return scope.getVariantScope().getDexOutputFolder();
- }
-
- if (scope.getVariantScope().getJavaCompileTask() != null) {
- return scope.getVariantScope().getJavaOutputDir();
- }
-
- if (variantData.javaCompileTask != null) {
- return variantData.javaCompileTask.getDestinationDir();
- }
- return null;
+ return scope.getVariantScope().getDexOutputFolder();
}
});
- ConventionMappingHelper.map(packageApp, "dexedLibraries", new Callable<Collection<File>>() {
- @Override
- public Collection<File> call() {
- if (config.isMultiDexEnabled() && !config.isLegacyMultiDexMode()
- && variantData.preDexTask != null) {
- return scope.getGlobalScope().getProject()
- .fileTree(variantData.preDexTask.getOutputFolder()).getFiles();
- }
+ ConventionMappingHelper.map(packageApp, "dexedLibraries",
+ new Callable<Collection<File>>() {
+ @Override
+ public Collection<File> call() {
+ if (config.isMultiDexEnabled() && !config.isLegacyMultiDexMode()
+ && variantData.preDexTask != null) {
+ return scope.getGlobalScope().getProject()
+ .fileTree(variantData.preDexTask.getOutputFolder())
+ .getFiles();
+ }
- return Collections.emptyList();
- }
- });
+ return Collections.emptyList();
+ }
+ });
+ scope.getVariantScope().getJavaResourcesProvider();
ConventionMappingHelper.map(packageApp, "packagedJars", new Callable<Set<File>>() {
@Override
public Set<File> call() {
- return scope.getGlobalScope().getAndroidBuilder().getPackagedJars(config);
+ ImmutableSet.Builder<File> jarFiles = ImmutableSet.builder();
+ for (JavaResourcesProvider.JavaResourcesLocation javaResourcesProvider :
+ scope.getVariantScope().getJavaResourcesProvider().getJavaResourcesLocations()) {
+ if (javaResourcesProvider.type == JavaResourcesProvider.Type.JAR) {
+ jarFiles.add(javaResourcesProvider.location);
+ }
+ }
+ return jarFiles.build();
}
});
ConventionMappingHelper.map(packageApp, "javaResourceDir", new Callable<File>() {
@Override
public File call() {
- return getOptionalDir(variantData.processJavaResourcesTask.getDestinationDir());
+ ImmutableSet.Builder<File> folders = ImmutableSet.builder();
+ for (JavaResourcesProvider.JavaResourcesLocation javaResourcesProvider :
+ scope.getVariantScope().getJavaResourcesProvider()
+ .getJavaResourcesLocations()) {
+ if (javaResourcesProvider.type == JavaResourcesProvider.Type.FOLDER) {
+ folders.add(javaResourcesProvider.location);
+ }
+ }
+ ImmutableSet<File> resourceFolders = folders.build();
+ if (resourceFolders.size() > 1) {
+ throw new RuntimeException("More than one java resources folders : " +
+ Joiner.on(",").join(resourceFolders));
+ }
+ return resourceFolders.isEmpty() ? null : resourceFolders.iterator().next();
}
});
+
+ packageApp.setMergingFolder(new File(scope.getGlobalScope().getIntermediatesDir(),
+ variantOutputData.getFullName() + "/merging"));
+
+
ConventionMappingHelper.map(packageApp, "jniFolders", new Callable<Set<File>>() {
@Override
public Set<File> call() {
@@ -351,7 +394,7 @@
}
});
- SigningConfig sc = (SigningConfig) config.getSigningConfig();
+ CoreSigningConfig sc = (CoreSigningConfig) config.getSigningConfig();
packageApp.setSigningConfig(sc);
if (sc != null) {
String validateSigningTaskName = "validate" + StringHelper.capitalize(sc.getName()) + "Signing";
@@ -363,6 +406,8 @@
"validate" + StringHelper.capitalize(sc.getName()) + "Signing",
ValidateSigningTask.class);
validateSigningTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+ validateSigningTask.setVariantName(
+ scope.getVariantScope().getVariantConfiguration().getFullName());
validateSigningTask.setSigningConfig(sc);
}
@@ -376,6 +421,14 @@
}
});
+ ConventionMappingHelper.map(packageApp, "packagingOptionsFilter",
+ new Callable<SignedJarBuilder.IZipEntryFilter>() {
+ @Override
+ public SignedJarBuilder.IZipEntryFilter call() throws Exception {
+ return scope.getVariantScope().getPackagingOptionsFilter();
+ }
+ });
+
ConventionMappingHelper.map(packageApp, "outputFile", new Callable<File>() {
@Override
public File call() throws Exception {
@@ -392,6 +445,7 @@
.capitalize(variantOutputData.getFullName())
+ "Resources", ShrinkResources.class);
task.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+ task.setVariantName(scope.getVariantScope().getVariantConfiguration().getFullName());
task.variantOutputData = variantOutputData;
final String outputBaseName = variantOutputData.getBaseName();
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitAbi.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitAbi.groovy
deleted file mode 100644
index 31e519e..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitAbi.groovy
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.tasks
-
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.FilterData
-import com.android.build.OutputFile
-import com.android.build.gradle.api.ApkOutputFile
-import com.android.build.gradle.internal.dsl.PackagingOptions
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.build.gradle.internal.model.FilterDataImpl
-import com.google.common.base.Joiner
-import com.google.common.collect.ImmutableList
-import com.google.common.collect.ImmutableSet
-import com.google.common.util.concurrent.Callables
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Nested
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.ParallelizableTask
-import org.gradle.api.tasks.OutputFiles
-import org.gradle.api.tasks.TaskAction
-
-import java.util.regex.Matcher
-import java.util.regex.Pattern
-
-/**
- * Package a abi dimension specific split APK
- */
-@ParallelizableTask
-class PackageSplitAbi extends SplitRelatedTask {
-
- ImmutableList<ApkOutputFile> outputFiles;
-
- @InputFiles
- Collection<File> inputFiles
-
- File outputDirectory
-
- @Input
- Set<String> splits
-
- @Input
- String outputBaseName
-
- @Input
- boolean jniDebuggable
-
- @Nested @Optional
- SigningConfig signingConfig
-
- @Nested
- PackagingOptions packagingOptions
-
- @Input
- Collection<File> jniFolders;
-
- @OutputFiles
- public List<File> getOutputFiles() {
- return getOutputSplitFiles()*.getOutputFile();
- }
-
- @Override
- @Nullable
- File getApkMetadataFile() {
- return null;
- }
-
- @NonNull
- public synchronized ImmutableList<ApkOutputFile> getOutputSplitFiles() {
-
- if (outputFiles == null) {
- ImmutableList.Builder<ApkOutputFile> builder = ImmutableList.builder();
- for (String split : splits) {
- String apkName = getApkName(split);
- ApkOutputFile apkOutput = new ApkOutputFile(
- OutputFile.OutputType.SPLIT,
- ImmutableList.<FilterData> of(
- FilterDataImpl.build(OutputFile.ABI, apkName)),
- Callables.returning(new File(outputDirectory, apkName)))
- builder.add(apkOutput)
- }
- outputFiles = builder.build()
- }
- return outputFiles;
- }
-
- private boolean isAbiSplit(String fileName) {
- for (String abi : getSplits()) {
- if (fileName.contains(abi)) {
- return true;
- }
- }
- return false;
- }
-
- @TaskAction
- protected void doFullTaskAction() {
-
- // resources- and .ap_ should be shared in a setting somewhere. see BasePlugin:1206
- final Pattern pattern = Pattern.compile(
- "resources-${getOutputBaseName()}-(.*).ap_")
- List<String> unprocessedSplits = new ArrayList(splits);
- for (File file : inputFiles) {
- Matcher matcher = pattern.matcher(file.getName());
- if (matcher.matches() && isAbiSplit(file.getName())) {
- String apkName = getApkName(matcher.group(1))
-
- File outFile = new File(getOutputDirectory(), apkName);
- getBuilder().packageApk(
- file.absolutePath,
- null, /* dexFolder */
- null, /* dexedLibraries */
- ImmutableList.of(),
- null, /* getJavaResourceDir */
- getJniFolders(),
- ImmutableSet.of(matcher.group(1)),
- getJniDebuggable(),
- getSigningConfig(),
- getPackagingOptions(),
- outFile.absolutePath)
- unprocessedSplits.remove(matcher.group(1));
- }
- }
- if (!unprocessedSplits.isEmpty()) {
- String message = String.format("Could not find resource package for %1$s",
- Joiner.on(',').join(unprocessedSplits));
- logger.error(message);
- throw new IllegalStateException(message);
- }
- }
-
- @Override
- List<FilterData> getSplitsData() {
- ImmutableList.Builder<FilterData> filterDataBuilder = ImmutableList.builder()
- addAllFilterData(filterDataBuilder, splits, OutputFile.FilterType.ABI);
- return filterDataBuilder.build()
- }
-
- private String getApkName(String split) {
- String apkName = "${project.archivesBaseName}-${getOutputBaseName()}_${split}"
- return apkName + (getSigningConfig() == null
- ? "-unsigned.apk"
- : "-unaligned.apk")
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitAbi.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitAbi.java
new file mode 100644
index 0000000..17c92f7
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitAbi.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.tasks;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.FilterData;
+import com.android.build.OutputFile;
+import com.android.build.gradle.api.ApkOutputFile;
+import com.android.build.gradle.internal.dsl.PackagingOptions;
+import com.android.build.gradle.internal.model.FilterDataImpl;
+import com.android.builder.model.SigningConfig;
+import com.android.builder.packaging.DuplicateFileException;
+import com.android.builder.packaging.PackagerException;
+import com.android.builder.packaging.SigningException;
+import com.android.builder.signing.SignedJarBuilder;
+import com.android.ide.common.signing.KeytoolException;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Callables;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputFiles;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Package a abi dimension specific split APK
+ */
+@ParallelizableTask
+public class PackageSplitAbi extends SplitRelatedTask {
+
+ private ImmutableList<ApkOutputFile> outputFiles;
+
+ private Collection<File> inputFiles;
+
+ private File outputDirectory;
+
+ private Set<String> splits;
+
+ private String outputBaseName;
+
+ private boolean jniDebuggable;
+
+ private SigningConfig signingConfig;
+
+ private PackagingOptions packagingOptions;
+
+ private Collection<File> jniFolders;
+
+ private File mergingFolder;
+
+ private SignedJarBuilder.IZipEntryFilter packagingOptionsFilter;
+
+ @OutputFiles
+ public List<File> getOutputFiles() {
+ ImmutableList.Builder<File> builder = ImmutableList.builder();
+ for (ApkOutputFile apk : getOutputSplitFiles()) {
+ builder.add(apk.getOutputFile());
+ }
+ return builder.build();
+ }
+
+ @Override
+ @Nullable
+ public File getApkMetadataFile() {
+ return null;
+ }
+
+ @Override
+ @NonNull
+ public synchronized ImmutableList<ApkOutputFile> getOutputSplitFiles() {
+
+ if (outputFiles == null) {
+ ImmutableList.Builder<ApkOutputFile> builder = ImmutableList.builder();
+ for (String split : splits) {
+ String apkName = getApkName(split);
+ ApkOutputFile apkOutput = new ApkOutputFile(
+ OutputFile.OutputType.SPLIT,
+ ImmutableList.of(FilterDataImpl.build(OutputFile.ABI, apkName)),
+ Callables.returning(new File(outputDirectory, apkName)));
+ builder.add(apkOutput);
+ }
+
+ outputFiles = builder.build();
+ }
+ return outputFiles;
+ }
+
+ private boolean isAbiSplit(String fileName) {
+ for (String abi : getSplits()) {
+ if (fileName.contains(abi)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @TaskAction
+ protected void doFullTaskAction() throws FileNotFoundException, SigningException,
+ KeytoolException, DuplicateFileException, PackagerException {
+
+ // resources- and .ap_ should be shared in a setting somewhere. see BasePlugin:1206
+ final Pattern pattern = Pattern.compile("resources-" + getOutputBaseName() + "-(.*).ap_");
+ List<String> unprocessedSplits = Lists.newArrayList(splits);
+ for (File file : inputFiles) {
+ Matcher matcher = pattern.matcher(file.getName());
+ if (matcher.matches() && isAbiSplit(file.getName())) {
+ String apkName = getApkName(matcher.group(1));
+
+ File outFile = new File(getOutputDirectory(), apkName);
+ getBuilder().packageApk(
+ file.getAbsolutePath(),
+ null, /* dexFolder */
+ ImmutableList.<File>of(), /* dexedLibraries */
+ ImmutableList.<File>of(),
+ null, /* getJavaResourceDir */
+ getJniFolders(),
+ getMergingFolder(),
+ ImmutableSet.of(matcher.group(1)),
+ isJniDebuggable(),
+ getSigningConfig(),
+ getPackagingOptions(),
+ getPackagingOptionsFilter(),
+ outFile.getAbsolutePath());
+ unprocessedSplits.remove(matcher.group(1));
+ }
+ }
+ if (!unprocessedSplits.isEmpty()) {
+ String message = "Could not find resource package for "
+ + Joiner.on(',').join(unprocessedSplits);
+ getLogger().error(message);
+ throw new IllegalStateException(message);
+ }
+ }
+
+ @Override
+ public List<FilterData> getSplitsData() {
+ ImmutableList.Builder<FilterData> filterDataBuilder = ImmutableList.builder();
+ SplitRelatedTask.addAllFilterData(filterDataBuilder, splits, OutputFile.FilterType.ABI);
+ return filterDataBuilder.build();
+ }
+
+ private String getApkName(final String split) {
+ String archivesBaseName = (String)getProject().getProperties().get("archivesBaseName");
+ String apkName = archivesBaseName + "-" + getOutputBaseName() + "_" + split;
+ return apkName + (getSigningConfig() == null ? "-unsigned.apk" : "-unaligned.apk");
+ }
+
+ public void setOutputFiles(ImmutableList<ApkOutputFile> outputFiles) {
+ this.outputFiles = outputFiles;
+ }
+
+ @InputFiles
+ public Collection<File> getInputFiles() {
+ return inputFiles;
+ }
+
+ public void setInputFiles(Collection<File> inputFiles) {
+ this.inputFiles = inputFiles;
+ }
+
+ public File getOutputDirectory() {
+ return outputDirectory;
+ }
+
+ public void setOutputDirectory(File outputDirectory) {
+ this.outputDirectory = outputDirectory;
+ }
+
+ @Input
+ public Set<String> getSplits() {
+ return splits;
+ }
+
+ public void setSplits(Set<String> splits) {
+ this.splits = splits;
+ }
+
+ @Input
+ public String getOutputBaseName() {
+ return outputBaseName;
+ }
+
+ public void setOutputBaseName(String outputBaseName) {
+ this.outputBaseName = outputBaseName;
+ }
+
+ @Input
+ public boolean isJniDebuggable() {
+ return jniDebuggable;
+ }
+
+ public void setJniDebuggable(boolean jniDebuggable) {
+ this.jniDebuggable = jniDebuggable;
+ }
+
+ @Nested
+ @Optional
+ public SigningConfig getSigningConfig() {
+ return signingConfig;
+ }
+
+ public void setSigningConfig(SigningConfig signingConfig) {
+ this.signingConfig = signingConfig;
+ }
+
+ @Nested
+ public PackagingOptions getPackagingOptions() {
+ return packagingOptions;
+ }
+
+ public void setPackagingOptions(PackagingOptions packagingOptions) {
+ this.packagingOptions = packagingOptions;
+ }
+
+ @Input
+ public Collection<File> getJniFolders() {
+ return jniFolders;
+ }
+
+ public void setJniFolders(Collection<File> jniFolders) {
+ this.jniFolders = jniFolders;
+ }
+
+ public File getMergingFolder() {
+ return mergingFolder;
+ }
+
+ public void setMergingFolder(File mergingFolder) {
+ this.mergingFolder = mergingFolder;
+ }
+
+ public SignedJarBuilder.IZipEntryFilter getPackagingOptionsFilter() {
+ return packagingOptionsFilter;
+ }
+
+ public void setPackagingOptionsFilter(SignedJarBuilder.IZipEntryFilter packagingOptionsFilter) {
+ this.packagingOptionsFilter = packagingOptionsFilter;
+ }
+
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitRes.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitRes.groovy
deleted file mode 100644
index d78279c..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitRes.groovy
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.tasks
-
-import com.android.annotations.NonNull
-import com.android.build.FilterData
-import com.android.build.OutputFile
-import com.android.build.gradle.api.ApkOutputFile
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.build.gradle.internal.model.FilterDataImpl
-import com.google.common.collect.ImmutableList
-import com.google.common.util.concurrent.Callables
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Nested
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.OutputFiles
-import org.gradle.api.tasks.ParallelizableTask
-import org.gradle.api.tasks.TaskAction
-
-import java.util.regex.Matcher
-import java.util.regex.Pattern
-
-/**
- * Package each split resources into a specific signed apk file.
- */
-@ParallelizableTask
-class PackageSplitRes extends SplitRelatedTask {
-
- @Input
- Set<String> densitySplits
-
- @Input
- Set<String> languageSplits
-
- @Input
- String outputBaseName
-
- @Nested @Optional
- SigningConfig signingConfig
-
- @InputFiles
- List<File> getInputFiles() {
- ImmutableList.Builder<File> builder = ImmutableList.builder();
- forEachInputFile { split, file ->
- builder.add(file)
- }
- return builder.build()
- }
-
- @OutputFiles
- public List<File> getOutputFiles() {
- getOutputSplitFiles()*.getOutputFile()
- }
-
- File getApkMetadataFile() {
- return null
- }
-
- /**
- * This directories are not officially input/output to the task as
- * they are shared among tasks. To be parallelizable, we must only
- * define our I/O in terms of files...
- */
- File inputDirectory
- File outputDirectory
-
- /**
- * Calculates the list of output files, coming from the list of input files, mangling the
- * output file name.
- */
- public List<ApkOutputFile> getOutputSplitFiles() {
- ImmutableList.Builder<ApkOutputFile> builder = ImmutableList.builder();
- forEachInputFile { String split, File file ->
- // find the split identification, if null, the split is not requested any longer.
- FilterData filterData = null;
- for (String density : densitySplits) {
- if (split.startsWith(density)) {
- filterData = FilterDataImpl.build(
- OutputFile.FilterType.DENSITY.toString(), density)
- }
- }
- if (languageSplits.contains(unMangleSplitName(split))) {
- filterData = FilterDataImpl.build(
- OutputFile.FilterType.LANGUAGE.toString(), unMangleSplitName(split));
- }
- if (filterData != null) {
- builder.add(new ApkOutputFile(OutputFile.OutputType.SPLIT,
- ImmutableList.of(filterData),
- Callables.<File>returning(
- new File(outputDirectory, this.getOutputFileNameForSplit(split)))))
- }
- }
- return builder.build();
- }
-
- @TaskAction
- protected void doFullTaskAction() {
-
- forEachInputFile { String split, File file ->
- File outFile = new File(outputDirectory, this.getOutputFileNameForSplit(split));
- getBuilder().signApk(file, signingConfig, outFile)
- }
- }
-
- /**
- * Runs the closure for each task input file, providing the split identifier (possibly with
- * a suffix generated by aapt) and the input file handle.
- * @param closure groovy closure to run on each input file.
- */
- public void forEachInputFile(Closure closure) {
- Pattern resourcePattern = Pattern.compile(
- "resources-${outputBaseName}.ap__(.*)")
-
- // make a copy of the expected densities and languages filters.
- List<String> densitiesCopy = new ArrayList<>(densitySplits)
- List<String> languagesCopy = new ArrayList<>(languageSplits)
-
- // resources- and .ap_ should be shared in a setting somewhere. see BasePlugin:1206
- for (File file : inputDirectory.listFiles()) {
- Matcher match = resourcePattern.matcher(file.getName())
- // each time we match, we remove the associated filter from our copies.
- if (match.matches() && !match.group(1).isEmpty()
- && isValidSplit(densitiesCopy, languagesCopy, match.group(1))) {
- closure(match.group(1), file)
- }
- }
- // manually invoke the closure for filters we did not find associated files, apply best
- // guess on the actual file names.
- for (String density : densitiesCopy) {
- closure(density,
- new File(inputDirectory, "resources-${outputBaseName}.ap__${density}"));
- }
- for (String language : languagesCopy) {
- closure(language,
- new File(inputDirectory, "resources-${outputBaseName}.ap__${language}"));
-
- }
- }
-
- /**
- * Returns true if the passed split identifier is a valid identifier (valid mean it is a
- * requested split for this task). A density split identifier can be suffixed with characters
- * added by aapt.
- */
- private static boolean isValidSplit(
- List<String> densities,
- List<String> languages,
- @NonNull String splitWithOptionalSuffix) {
- for (String density : densities) {
- if (splitWithOptionalSuffix.startsWith(density)) {
- densities.remove(density);
- return true;
- }
- }
- String mangledName = unMangleSplitName(splitWithOptionalSuffix);
- if (languages.contains(mangledName)) {
- languages.remove(mangledName);
- return true;
- }
- return false;
- }
-
- String getOutputFileNameForSplit(String split) {
- String apkName = "${project.archivesBaseName}-${outputBaseName}_${split}"
- return apkName + (signingConfig == null ? "-unsigned.apk" : "-unaligned.apk")
- }
-
- @Override
- List<FilterData> getSplitsData() {
- ImmutableList.Builder<FilterData> filterDataBuilder = ImmutableList.builder();
- addAllFilterData(filterDataBuilder, densitySplits, OutputFile.FilterType.DENSITY);
- addAllFilterData(filterDataBuilder, languageSplits, OutputFile.FilterType.LANGUAGE);
- return filterDataBuilder.build();
- }
-
- /**
- * Un-mangle a split name as created by the aapt tool to retrieve a split name as configured
- * in the project's build.gradle.
- *
- * when dealing with several split language in a single split, each language (+ optional region)
- * will be seperated by an underscore.
- *
- * note that there is currently an aapt bug, remove the 'r' in the region so for instance,
- * fr-rCA becomes fr-CA, temporarily put it back until it is fixed.
- *
- * @param splitWithOptionalSuffix the mangled split name.
- * @return
- */
- static String unMangleSplitName(String splitWithOptionalSuffix) {
- String mangledName = splitWithOptionalSuffix.replaceAll('_', ',');
- return mangledName.contains("-r") ? mangledName : mangledName.replace("-", "-r");
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitRes.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitRes.java
new file mode 100644
index 0000000..7f8bcfb
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PackageSplitRes.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.tasks;
+
+import com.android.annotations.NonNull;
+import com.android.build.FilterData;
+import com.android.build.OutputFile;
+import com.android.build.gradle.api.ApkOutputFile;
+import com.android.build.gradle.internal.model.FilterDataImpl;
+import com.android.builder.model.SigningConfig;
+import com.android.builder.packaging.SigningException;
+import com.android.builder.signing.SignedJarBuilder;
+import com.android.ide.common.signing.KeytoolException;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Callables;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputFiles;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Package each split resources into a specific signed apk file.
+ */
+@ParallelizableTask
+public class PackageSplitRes extends SplitRelatedTask {
+
+ private Set<String> densitySplits;
+
+ private Set<String> languageSplits;
+
+ private String outputBaseName;
+
+ private SigningConfig signingConfig;
+
+ /**
+ * This directories are not officially input/output to the task as they are shared among tasks.
+ * To be parallelizable, we must only define our I/O in terms of files...
+ */
+ private File inputDirectory;
+
+ private File outputDirectory;
+
+ @InputFiles
+ public List<File> getInputFiles() {
+ final ImmutableList.Builder<File> builder = ImmutableList.builder();
+ forEachInputFile(new SplitFileHandler() {
+ @Override
+ public void execute(String split, File file) {
+ builder.add(file);
+ }
+ });
+ return builder.build();
+ }
+
+ @OutputFiles
+ public List<File> getOutputFiles() {
+ ImmutableList.Builder<File> builder = ImmutableList.builder();
+ for (ApkOutputFile apk : getOutputSplitFiles()) {
+ builder.add(apk.getOutputFile());
+ }
+ return builder.build();
+ }
+
+ @Override
+ public File getApkMetadataFile() {
+ return null;
+ }
+
+ /**
+ * Calculates the list of output files, coming from the list of input files, mangling the output
+ * file name.
+ */
+ @Override
+ public List<ApkOutputFile> getOutputSplitFiles() {
+ final ImmutableList.Builder<ApkOutputFile> builder = ImmutableList.builder();
+ forEachInputFile(new SplitFileHandler() {
+ @Override
+ public void execute(String split, File file) {
+ // find the split identification, if null, the split is not requested any longer.
+ FilterData filterData = null;
+ for (String density : densitySplits) {
+ if (split.startsWith(density)) {
+ filterData = FilterDataImpl.build(
+ OutputFile.FilterType.DENSITY.toString(), density);
+ }
+
+ }
+
+ if (languageSplits.contains(unMangleSplitName(split))) {
+ filterData = FilterDataImpl.build(
+ OutputFile.FilterType.LANGUAGE.toString(), unMangleSplitName(split));
+ }
+ if (filterData != null) {
+ builder.add(new ApkOutputFile(
+ OutputFile.OutputType.SPLIT,
+ ImmutableList.of(filterData),
+ Callables.returning(
+ new File(outputDirectory, getOutputFileNameForSplit(split)))));
+ }
+
+ }
+ });
+ return builder.build();
+ }
+
+ @TaskAction
+ protected void doFullTaskAction() {
+ forEachInputFile(
+ new SplitFileHandler() {
+ @Override
+ public void execute(String split, File file) {
+ File outFile = new File(outputDirectory,
+ getOutputFileNameForSplit(split));
+ try {
+ getBuilder().signApk(file, signingConfig, outFile);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } catch (KeytoolException e) {
+ throw new RuntimeException(e);
+ } catch (SigningException e) {
+ throw new RuntimeException(e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ } catch (SignedJarBuilder.IZipEntryFilter.ZipAbortException e) {
+ throw new RuntimeException(e);
+ } catch (com.android.builder.signing.SigningException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ }
+
+ private interface SplitFileHandler {
+ void execute(String split, File file);
+ }
+
+ /**
+ * Runs the handler for each task input file, providing the split identifier (possibly with a
+ * suffix generated by aapt) and the input file handle.
+ */
+ private void forEachInputFile(SplitFileHandler handler) {
+ Pattern resourcePattern = Pattern.compile("resources-" + outputBaseName + ".ap__(.*)");
+
+ // make a copy of the expected densities and languages filters.
+ List<String> densitiesCopy = Lists.newArrayList(densitySplits);
+ List<String> languagesCopy = Lists.newArrayList(languageSplits);
+
+ // resources- and .ap_ should be shared in a setting somewhere. see BasePlugin:1206
+ File[] fileLists = inputDirectory.listFiles();
+ if (fileLists != null) {
+ for (File file : fileLists) {
+ Matcher match = resourcePattern.matcher(file.getName());
+ // each time we match, we remove the associated filter from our copies.
+ if (match.matches() && !match.group(1).isEmpty()
+ && isValidSplit(densitiesCopy, languagesCopy, match.group(1))) {
+ handler.execute(match.group(1), file);
+ }
+ }
+ }
+ // manually invoke the handler for filters we did not find associated files, apply best
+ // guess on the actual file names.
+ for (String density : densitiesCopy) {
+ handler.execute(density,
+ new File(inputDirectory, "resources-" + outputBaseName + ".ap__" + density));
+ }
+ for (String language : languagesCopy) {
+ handler.execute(language,
+ new File(inputDirectory, "resources-" + outputBaseName + ".ap__" + language));
+
+ }
+ }
+
+ /**
+ * Returns true if the passed split identifier is a valid identifier (valid mean it is a
+ * requested split for this task). A density split identifier can be suffixed with characters
+ * added by aapt.
+ */
+ private static boolean isValidSplit(
+ List<String> densities,
+ List<String> languages,
+ @NonNull String splitWithOptionalSuffix) {
+ for (String density : densities) {
+ if (splitWithOptionalSuffix.startsWith(density)) {
+ densities.remove(density);
+ return true;
+ }
+ }
+ String mangledName = unMangleSplitName(splitWithOptionalSuffix);
+ if (languages.contains(mangledName)) {
+ languages.remove(mangledName);
+ return true;
+ }
+ return false;
+ }
+
+ public String getOutputFileNameForSplit(final String split) {
+ String archivesBaseName = (String)getProject().getProperties().get("archivesBaseName");
+ String apkName = archivesBaseName + "-" + outputBaseName + "_" + split;
+ return apkName + (signingConfig == null ? "-unsigned.apk" : "-unaligned.apk");
+ }
+
+ @Override
+ public List<FilterData> getSplitsData() {
+ ImmutableList.Builder<FilterData> filterDataBuilder = ImmutableList.builder();
+ addAllFilterData(filterDataBuilder, densitySplits, OutputFile.FilterType.DENSITY);
+ addAllFilterData(filterDataBuilder, languageSplits, OutputFile.FilterType.LANGUAGE);
+ return filterDataBuilder.build();
+ }
+
+ /**
+ * Un-mangle a split name as created by the aapt tool to retrieve a split name as configured in
+ * the project's build.gradle.
+ *
+ * when dealing with several split language in a single split, each language (+ optional region)
+ * will be seperated by an underscore.
+ *
+ * note that there is currently an aapt bug, remove the 'r' in the region so for instance,
+ * fr-rCA becomes fr-CA, temporarily put it back until it is fixed.
+ *
+ * @param splitWithOptionalSuffix the mangled split name.
+ */
+ public static String unMangleSplitName(String splitWithOptionalSuffix) {
+ String mangledName = splitWithOptionalSuffix.replaceAll("_", ",");
+ return mangledName.contains("-r") ? mangledName : mangledName.replace("-", "-r");
+ }
+
+ @Input
+ public Set<String> getDensitySplits() {
+ return densitySplits;
+ }
+
+ public void setDensitySplits(Set<String> densitySplits) {
+ this.densitySplits = densitySplits;
+ }
+
+ @Input
+ public Set<String> getLanguageSplits() {
+ return languageSplits;
+ }
+
+ public void setLanguageSplits(Set<String> languageSplits) {
+ this.languageSplits = languageSplits;
+ }
+
+ @Input
+ public String getOutputBaseName() {
+ return outputBaseName;
+ }
+
+ public void setOutputBaseName(String outputBaseName) {
+ this.outputBaseName = outputBaseName;
+ }
+
+ @Nested
+ @Optional
+ public SigningConfig getSigningConfig() {
+ return signingConfig;
+ }
+
+ public void setSigningConfig(SigningConfig signingConfig) {
+ this.signingConfig = signingConfig;
+ }
+
+ public File getInputDirectory() {
+ return inputDirectory;
+ }
+
+ public void setInputDirectory(File inputDirectory) {
+ this.inputDirectory = inputDirectory;
+ }
+
+ public File getOutputDirectory() {
+ return outputDirectory;
+ }
+
+ public void setOutputDirectory(File outputDirectory) {
+ this.outputDirectory = outputDirectory;
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreDex.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreDex.groovy
deleted file mode 100644
index 0f3b5ba..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreDex.groovy
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.tasks
-import com.android.SdkConstants
-import com.android.annotations.NonNull
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.scope.ConventionMappingHelper
-import com.android.build.gradle.internal.scope.TaskConfigAction
-import com.android.build.gradle.internal.scope.VariantScope
-import com.android.build.gradle.internal.tasks.BaseTask
-import com.android.build.gradle.internal.variant.ApkVariantData
-import com.android.build.gradle.internal.variant.TestVariantData
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.DexOptions
-import com.android.builder.core.VariantConfiguration
-import com.android.builder.core.VariantType
-import com.android.builder.model.AndroidProject
-import com.android.ide.common.internal.WaitableExecutor
-import com.google.common.base.Charsets
-import com.google.common.collect.Lists
-import com.google.common.collect.Sets
-import com.google.common.hash.HashCode
-import com.google.common.hash.HashFunction
-import com.google.common.hash.Hashing
-import com.google.common.io.Files
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.Nested
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.ParallelizableTask
-import org.gradle.api.tasks.TaskAction
-import org.gradle.api.tasks.incremental.IncrementalTaskInputs
-
-import java.util.concurrent.Callable
-
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-
-@ParallelizableTask
-public class PreDex extends BaseTask {
-
- // ----- PUBLIC TASK API -----
-
- // ----- PRIVATE TASK API -----
- @Input
- String getBuildToolsVersion() {
- getBuildTools().getRevision()
- }
-
- // this is used automatically by Gradle, even though nothing
- // in the class uses it.
- @SuppressWarnings("GroovyUnusedDeclaration")
- @InputFiles
- Collection<File> inputFiles
-
- @OutputDirectory
- File outputFolder
-
- @Nested
- com.android.build.gradle.internal.dsl.DexOptions dexOptions
-
- @Input
- boolean multiDex
-
- @TaskAction
- void taskAction(IncrementalTaskInputs taskInputs) {
-
- final boolean multiDexEnabled = getMultiDex()
-
- final File outFolder = getOutputFolder()
-
- boolean incremental = taskInputs.isIncremental()
- // if we are not in incremental mode, then outOfDate will contain
- // all the files, but first we need to delete the previous output
- if (!incremental) {
- emptyFolder(outFolder)
- }
-
- final Set<String> hashs = Sets.newHashSet()
- final WaitableExecutor<Void> executor = new WaitableExecutor<Void>()
- final List<File> inputFileDetails = Lists.newArrayList()
-
- taskInputs.outOfDate { final change ->
- inputFileDetails.add(change.file)
- }
-
- for (final File file : inputFileDetails) {
- Callable<Void> action = new PreDexTask(outFolder, file, hashs,
- multiDexEnabled);
- executor.execute(action);
- }
-
- if (incremental) {
- taskInputs.removed { change ->
- //noinspection GroovyAssignabilityCheck
- File preDexedFile = getDexFileName(outFolder, change.file)
- if (preDexedFile.isDirectory()) {
- logger.info("deleteDir(" + preDexedFile + ") returned: " + preDexedFile.deleteDir())
- } else {
- logger.info("delete(" + preDexedFile + ") returned: " + preDexedFile.delete())
- }
- }
- }
-
- executor.waitForTasksWithQuickFail(false)
- }
-
- private final class PreDexTask implements Callable<Void> {
- private final File outFolder
- private final File fileToProcess
- private final Set<String> hashs
- private final boolean multiDexEnabled
- private final DexOptions options = getDexOptions()
- private final AndroidBuilder builder = getBuilder()
-
-
- private PreDexTask(
- File outFolder,
- File file,
- Set<String> hashs,
- boolean multiDexEnabled) {
- this.outFolder = outFolder
- this.fileToProcess = file
- this.hashs = hashs
- this.multiDexEnabled = multiDexEnabled
- }
-
- @Override
- Void call() throws Exception {
- // TODO remove once we can properly add a library as a dependency of its test.
- String hash = getFileHash(fileToProcess)
-
- synchronized (hashs) {
- if (hashs.contains(hash)) {
- return null
- }
-
- hashs.add(hash)
- }
-
- //noinspection GroovyAssignabilityCheck
- File preDexedFile = getDexFileName(outFolder, fileToProcess)
-
- if (multiDexEnabled) {
- preDexedFile.mkdirs()
- }
-
- //noinspection GroovyAssignabilityCheck
- builder.preDexLibrary(fileToProcess, preDexedFile, multiDexEnabled, options)
-
- return null
- }
- }
-
- /**
- * Returns the hash of a file.
- * @param file the file to hash
- * @return
- */
- private static String getFileHash(@NonNull File file) {
- HashCode hashCode = Files.hash(file, Hashing.sha1())
- return hashCode.toString()
- }
-
- /**
- * Returns a unique File for the pre-dexed library, even
- * if there are 2 libraries with the same file names (but different
- * paths)
- *
- * If multidex is enabled the return File is actually a folder.
- *
- * @param outFolder the output folder.
- * @param inputFile the library
- * @param multidex whether multidex is enabled.
- * @return
- */
- @NonNull
- static File getDexFileName(@NonNull File outFolder, @NonNull File inputFile) {
- // get the filename
- String name = inputFile.getName()
- // remove the extension
- int pos = name.lastIndexOf('.')
- if (pos != -1) {
- name = name.substring(0, pos)
- }
-
- // add a hash of the original file path.
- String input = inputFile.getAbsolutePath();
- HashFunction hashFunction = Hashing.sha1()
- HashCode hashCode = hashFunction.hashString(input, Charsets.UTF_16LE)
-
- return new File(outFolder, name + "-" + hashCode.toString() + SdkConstants.DOT_JAR)
- }
-
- public static class ConfigAction implements TaskConfigAction<PreDex> {
-
- VariantScope scope;
-
- TaskManager.PostCompilationData pcData
-
- ConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
- this.scope = scope
- this.pcData = pcData
- }
-
- @Override
- String getName() {
- return scope.getTaskName("preDex")
- }
-
- @Override
- Class<PreDex> getType() {
- return PreDex.class
- }
-
- @Override
- void execute(PreDex preDexTask) {
- ApkVariantData variantData = (ApkVariantData) scope.variantData;
- VariantConfiguration config = variantData.variantConfiguration
-
- boolean isTestForApp = config.type.isForTesting() &&
- (variantData as TestVariantData).testedVariantData.variantConfiguration.type ==
- VariantType.DEFAULT
- boolean isMultiDexEnabled = config.isMultiDexEnabled() && !isTestForApp
-
- variantData.preDexTask = preDexTask
- preDexTask.androidBuilder = scope.globalScope.androidBuilder
- preDexTask.dexOptions = scope.globalScope.getExtension().dexOptions
- preDexTask.multiDex = isMultiDexEnabled
-
- ConventionMappingHelper.map(preDexTask, "inputFiles", pcData.inputLibraries)
- ConventionMappingHelper.map(preDexTask, "outputFolder") {
- scope.getPreDexOutputDir();
- }
- }
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreDex.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreDex.java
new file mode 100644
index 0000000..24b7d25
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreDex.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle.tasks;
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.tasks.BaseTask;
+import com.android.build.gradle.internal.variant.ApkVariantData;
+import com.android.build.gradle.internal.variant.TestVariantData;
+import com.android.build.gradle.internal.PostCompilationData;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.DexOptions;
+import com.android.builder.core.VariantConfiguration;
+import com.android.builder.core.VariantType;
+import com.android.ide.common.internal.LoggedErrorException;
+import com.android.ide.common.internal.WaitableExecutor;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.ide.common.process.ProcessOutputHandler;
+import com.android.utils.FileUtils;
+import com.google.common.base.Charsets;
+import com.google.common.base.Throwables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.hash.HashCode;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hashing;
+import com.google.common.io.Files;
+
+import org.gradle.api.Action;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.ParallelizableTask;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.incremental.IncrementalTaskInputs;
+import org.gradle.api.tasks.incremental.InputFileDetails;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+@ParallelizableTask
+public class PreDex extends BaseTask {
+
+ @Input
+ public String getBuildToolsVersion() {
+ return getBuildTools().getRevision().toString();
+ }
+
+ private Collection<File> inputFiles;
+
+ private File outputFolder;
+
+ private com.android.build.gradle.internal.dsl.DexOptions dexOptions;
+
+ private boolean multiDex;
+
+ @TaskAction
+ void taskAction(IncrementalTaskInputs taskInputs)
+ throws IOException, LoggedErrorException, InterruptedException {
+
+ final boolean multiDexEnabled = isMultiDex();
+
+ final File outFolder = getOutputFolder();
+
+ boolean incremental = taskInputs.isIncremental();
+ // if we are not in incremental mode, then outOfDate will contain
+ // all the files, but first we need to delete the previous output
+ if (!incremental) {
+ FileUtils.emptyFolder(outFolder);
+ }
+
+ final Set<String> hashs = Sets.newHashSet();
+ final WaitableExecutor<Void> executor = new WaitableExecutor<Void>();
+ final List<File> inputFileDetails = Lists.newArrayList();
+
+ taskInputs.outOfDate(new Action<InputFileDetails>() {
+ @Override
+ public void execute(InputFileDetails change) {
+ inputFileDetails.add(change.getFile());
+ }
+ });
+
+ ProcessOutputHandler outputHandler = new LoggedProcessOutputHandler(getILogger());
+ for (final File file : inputFileDetails) {
+ Callable<Void> action = new PreDexTask(outFolder, file, hashs,
+ multiDexEnabled, outputHandler);
+ executor.execute(action);
+ }
+
+ if (incremental) {
+ taskInputs.removed(new Action<InputFileDetails>() {
+ @Override
+ public void execute(InputFileDetails change) {
+ File preDexedFile = getDexFileName(outFolder, change.getFile());
+
+ try {
+ FileUtils.deleteFolder(preDexedFile);
+ } catch (IOException e) {
+ getLogger().info("Could not delete {}\n{}",
+ preDexedFile, Throwables.getStackTraceAsString(e));
+ }
+ }
+ });
+ }
+
+ executor.waitForTasksWithQuickFail(false);
+ }
+
+ private final class PreDexTask implements Callable<Void> {
+ private final File outFolder;
+ private final File fileToProcess;
+ private final Set<String> hashs;
+ private final boolean multiDexEnabled;
+ private final DexOptions options = getDexOptions();
+ private final AndroidBuilder builder = getBuilder();
+ private final ProcessOutputHandler mOutputHandler;
+
+
+ private PreDexTask(
+ File outFolder,
+ File file,
+ Set<String> hashs,
+ boolean multiDexEnabled,
+ ProcessOutputHandler outputHandler) {
+ this.mOutputHandler = outputHandler;
+ this.outFolder = outFolder;
+ this.fileToProcess = file;
+ this.hashs = hashs;
+ this.multiDexEnabled = multiDexEnabled;
+ }
+
+ @Override
+ public Void call() throws Exception {
+ // TODO remove once we can properly add a library as a dependency of its test.
+ String hash = getFileHash(fileToProcess);
+
+ synchronized (hashs) {
+ if (hashs.contains(hash)) {
+ return null;
+ }
+
+ hashs.add(hash);
+ }
+
+ File preDexedFile = getDexFileName(outFolder, fileToProcess);
+
+ if (multiDexEnabled) {
+ preDexedFile.mkdirs();
+ }
+
+ builder.preDexLibrary(
+ fileToProcess, preDexedFile, multiDexEnabled, options, mOutputHandler);
+
+ return null;
+ }
+ }
+
+ // this is used automatically by Gradle, even though nothing
+ // in the class uses it.
+ @SuppressWarnings("unused")
+ @InputFiles
+ public Collection<File> getInputFiles() {
+ return inputFiles;
+ }
+
+ public void setInputFiles(Collection<File> inputFiles) {
+ this.inputFiles = inputFiles;
+ }
+
+ @OutputDirectory
+ public File getOutputFolder() {
+ return outputFolder;
+ }
+
+ public void setOutputFolder(File outputFolder) {
+ this.outputFolder = outputFolder;
+ }
+
+ @Nested
+ public com.android.build.gradle.internal.dsl.DexOptions getDexOptions() {
+ return dexOptions;
+ }
+
+ public void setDexOptions(com.android.build.gradle.internal.dsl.DexOptions dexOptions) {
+ this.dexOptions = dexOptions;
+ }
+
+ @Input
+ public boolean isMultiDex() {
+ return multiDex;
+ }
+
+ public void setMultiDex(boolean multiDex) {
+ this.multiDex = multiDex;
+ }
+
+ /**
+ * Returns the hash of a file.
+ * @param file the file to hash
+ */
+ private static String getFileHash(@NonNull File file) throws IOException {
+ HashCode hashCode = Files.hash(file, Hashing.sha1());
+ return hashCode.toString();
+ }
+
+ /**
+ * Returns a unique File for the pre-dexed library, even
+ * if there are 2 libraries with the same file names (but different
+ * paths)
+ *
+ * If multidex is enabled the return File is actually a folder.
+ *
+ * @param outFolder the output folder.
+ * @param inputFile the library.
+ */
+ @NonNull
+ static File getDexFileName(@NonNull File outFolder, @NonNull File inputFile) {
+ // get the filename
+ String name = inputFile.getName();
+ // remove the extension
+ int pos = name.lastIndexOf('.');
+ if (pos != -1) {
+ name = name.substring(0, pos);
+ }
+
+ // add a hash of the original file path.
+ String input = inputFile.getAbsolutePath();
+ HashFunction hashFunction = Hashing.sha1();
+ HashCode hashCode = hashFunction.hashString(input, Charsets.UTF_16LE);
+
+ return new File(outFolder, name + "-" + hashCode.toString() + SdkConstants.DOT_JAR);
+ }
+
+ public static class ConfigAction implements TaskConfigAction<PreDex> {
+
+ private VariantScope scope;
+
+ private Callable<List<File>> inputLibraries;
+
+ public ConfigAction(VariantScope scope, PostCompilationData pcData) {
+ this.scope = scope;
+ this.inputLibraries = pcData.getInputLibrariesCallable();
+ }
+
+ @Override
+ public String getName() {
+ return scope.getTaskName("preDex");
+ }
+
+ @Override
+ public Class<PreDex> getType() {
+ return PreDex.class;
+ }
+
+ @Override
+ public void execute(PreDex preDexTask) {
+ ApkVariantData variantData = (ApkVariantData) scope.getVariantData();
+ VariantConfiguration config = variantData.getVariantConfiguration();
+
+ boolean isTestForApp = config.getType().isForTesting() &&
+ ((TestVariantData) variantData).getTestedVariantData()
+ .getVariantConfiguration().getType() == VariantType.DEFAULT;
+ boolean isMultiDexEnabled = config.isMultiDexEnabled() && !isTestForApp;
+
+ variantData.preDexTask = preDexTask;
+ preDexTask.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+ preDexTask.setVariantName(config.getFullName());
+ preDexTask.dexOptions = scope.getGlobalScope().getExtension().getDexOptions();
+ preDexTask.multiDex = isMultiDexEnabled;
+
+ ConventionMappingHelper.map(preDexTask, "inputFiles", inputLibraries);
+ ConventionMappingHelper.map(preDexTask, "outputFolder", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ return scope.getPreDexOutputDir();
+ }
+ });
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreprocessResourcesTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreprocessResourcesTask.java
deleted file mode 100644
index 668fcec..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/PreprocessResourcesTask.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.tasks;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.android.annotations.NonNull;
-import com.android.build.gradle.internal.tasks.IncrementalTask;
-import com.android.builder.png.VectorDrawableRenderer;
-import com.android.ide.common.res2.FileStatus;
-import com.android.resources.Density;
-import com.google.common.base.Charsets;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.SetMultimap;
-import com.google.common.io.Files;
-import com.google.common.reflect.TypeToken;
-import com.google.gson.Gson;
-
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.api.tasks.InputDirectory;
-import org.gradle.api.tasks.OutputDirectory;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.Type;
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * Generates PNGs from Android vector drawable files.
- */
-public class PreprocessResourcesTask extends IncrementalTask {
- public static final int MIN_SDK = VectorDrawableRenderer.MIN_SDK_WITH_VECTOR_SUPPORT;
- private static final Type TYPE_TOKEN = new TypeToken<Map<String, Collection<String>>>() {}.getType();
-
- private final VectorDrawableRenderer renderer = new VectorDrawableRenderer();
- private File outputResDirectory;
- private File generatedResDirectory;
- private File mergedResDirectory;
- private Collection<Density> densitiesToGenerate;
-
- @Override
- protected boolean isIncremental() {
- return true;
- }
-
- @Override
- protected void doIncrementalTaskAction(Map<File, FileStatus> changedInputs) {
- try {
- File incrementalMarker = new File(getIncrementalFolder(), "build_was_incremental");
- Files.touch(incrementalMarker);
-
- // TODO: store and check the set of densities.
-
- File stateFile = getStateFile();
- if (!stateFile.exists()) {
- doFullTaskAction();
- }
-
- SetMultimap<String, String> state = readState(stateFile);
-
- for (Map.Entry<File, FileStatus> entry : changedInputs.entrySet()) {
- switch (entry.getValue()) {
- case NEW:
- case CHANGED:
- getLogger().debug("Incremental change to {}.",
- entry.getKey().getAbsolutePath());
- handleFile(entry.getKey(), state);
- break;
- case REMOVED:
- for (String path : state.get(entry.getKey().getAbsolutePath())) {
- File file = new File(path);
- getLogger().debug("Deleting {}.", file.getAbsolutePath());
- file.delete();
- }
- state.removeAll(entry.getKey());
- break;
- default:
- throw new RuntimeException("Unsupported operation " + entry.getValue());
- }
- }
-
- saveState(state);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- protected void doFullTaskAction() {
- SetMultimap<String, String> state = HashMultimap.create();
- emptyFolder(getOutputResDirectory());
- emptyFolder(getGeneratedResDirectory());
- emptyFolder(getIncrementalFolder());
-
- try {
- for (File resourceFile : getProject().fileTree(getMergedResDirectory())) {
- handleFile(resourceFile, state);
- }
- saveState(state);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- @NonNull
- private static SetMultimap<String, String> readState(@NonNull File stateFile) throws IOException {
- String stateString = Files.toString(stateFile, Charsets.UTF_8);
- Map<String, Collection<String>> stateMap = new Gson().fromJson(stateString, TYPE_TOKEN);
-
- SetMultimap<String, String> state = HashMultimap.create();
- for (Map.Entry<String, Collection<String>> entry : stateMap.entrySet()) {
- state.putAll(entry.getKey(), entry.getValue());
- }
- return state;
- }
-
- private void saveState(@NonNull Multimap<String, String> state) throws IOException {
- File stateFile = getStateFile();
- Files.write(new Gson().toJson(state.asMap(), TYPE_TOKEN), stateFile, Charsets.UTF_8);
- }
-
- @NonNull
- private File getStateFile() {
- return new File(getIncrementalFolder(), "state.json");
- }
-
-
- private void handleFile(@NonNull File resourceFile, @NonNull SetMultimap<String, String> state)
- throws IOException {
- if (renderer.isVectorDrawable(resourceFile)) {
- getLogger().debug("Generating files for {}.", resourceFile.getAbsolutePath());
- Collection<File> generatedFiles = renderer.createPngFiles(
- resourceFile,
- getGeneratedResDirectory(),
- getDensitiesToGenerate());
-
- for (File generatedFile : generatedFiles) {
- getLogger().debug("Copying generated file: {}.", generatedFile.getAbsolutePath());
- copyFile(generatedFile, resourceFile, getGeneratedResDirectory(), state);
- }
- } else {
- getLogger().debug("Copying as-is: {}", resourceFile.getAbsolutePath());
- copyFile(resourceFile, resourceFile, getMergedResDirectory(), state);
- }
- }
-
- private void copyFile(
- @NonNull File fileToUse,
- @NonNull File originalFile,
- @NonNull File resDir,
- @NonNull SetMultimap<String, String> state) throws IOException {
- checkNotNull(resDir);
- String relativePath =
- resDir.toURI().relativize(fileToUse.toURI()).getPath();
-
- File finalFile = new File(getOutputResDirectory(), relativePath);
- Files.createParentDirs(finalFile);
- Files.copy(fileToUse, finalFile);
- state.put(originalFile.getAbsolutePath(), finalFile.getAbsolutePath());
- }
-
- /**
- * Directory in which to put generated files. They will be then copied to the final destination
- * if this would not overwrite anything.
- *
- * @see #getOutputResDirectory()
- */
- @OutputDirectory
- public File getGeneratedResDirectory() {
- return generatedResDirectory;
- }
-
- public void setGeneratedResDirectory(File generatedResDirectory) {
- this.generatedResDirectory = generatedResDirectory;
- }
-
- /**
- * "Input" resource directory, with all resources for the current variant merged.
- */
- @InputDirectory
- public File getMergedResDirectory() {
- return mergedResDirectory;
- }
-
- public void setMergedResDirectory(File mergedResDirectory) {
- this.mergedResDirectory = mergedResDirectory;
- }
-
- /**
- * Resources directory that will be passed to aapt.
- */
- @OutputDirectory
- public File getOutputResDirectory() {
- return outputResDirectory;
- }
-
- public void setOutputResDirectory(File outputResDirectory) {
- this.outputResDirectory = outputResDirectory;
- }
-
- public Collection<Density> getDensitiesToGenerate() {
- return densitiesToGenerate;
- }
-
- public void setDensitiesToGenerate(
- Collection<Density> densitiesToGenerate) {
- this.densitiesToGenerate = densitiesToGenerate;
- }
-
- @NonNull
- @Override
- public Logger getLogger() {
- return Logging.getLogger(getClass());
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.groovy
deleted file mode 100644
index dae7a52..0000000
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.groovy
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle.tasks
-
-import com.android.annotations.NonNull
-import com.android.build.gradle.internal.LoggingUtil
-import com.android.build.gradle.internal.dependency.SymbolFileProviderImpl
-import com.android.build.gradle.internal.dsl.AaptOptions
-import com.android.build.gradle.internal.scope.ConventionMappingHelper
-import com.android.build.gradle.internal.scope.TaskConfigAction
-import com.android.build.gradle.internal.scope.VariantOutputScope
-import com.android.build.gradle.internal.tasks.IncrementalTask
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.builder.core.AaptPackageProcessBuilder
-import com.android.builder.core.VariantConfiguration
-import com.android.builder.core.VariantType
-import com.android.builder.dependency.LibraryDependency
-import com.google.common.collect.Iterators
-import com.google.common.collect.Lists
-import org.gradle.api.logging.Logging
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputDirectory
-import org.gradle.api.tasks.InputFile
-import org.gradle.api.tasks.Nested
-import org.gradle.api.tasks.Optional
-import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.tasks.OutputFile
-import org.gradle.api.tasks.ParallelizableTask
-
-import static com.android.builder.model.AndroidProject.FD_GENERATED
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-
-@ParallelizableTask
-public class ProcessAndroidResources extends IncrementalTask {
-
- // ----- PUBLIC TASK API -----
-
- @InputFile
- File manifestFile
-
- @InputDirectory
- File resDir
-
- @InputDirectory @Optional
- File assetsDir
-
- @OutputDirectory @Optional
- File sourceOutputDir
-
- @OutputDirectory @Optional
- File textSymbolOutputDir
-
- @OutputFile @Optional
- File packageOutputFile
-
- @OutputFile @Optional
- File proguardOutputFile
-
- @Input
- Collection<String> resourceConfigs
-
- @Input @Optional
- String preferredDensity
-
- // ----- PRIVATE TASK API -----
- @Input
- String getBuildToolsVersion() {
- getBuildTools().getRevision()
- }
-
- @Nested @Optional
- List<SymbolFileProviderImpl> libraries
-
- @Input @Optional
- String packageForR
-
- @Nested @Optional
- Collection<String> splits
-
- @Input
- boolean enforceUniquePackageName
-
- // this doesn't change from one build to another, so no need to annotate
- VariantType type
-
- @Input
- boolean debuggable
-
- @Input
- boolean pseudoLocalesEnabled
-
- @Nested
- AaptOptions aaptOptions
-
- private boolean isSplitPackage(File file, File resBaseName) {
- if (file.getName().startsWith(resBaseName.getName())) {
- for (String split : splits) {
- if (file.getName().contains(split)) {
- return true;
- }
- }
- }
- return false;
- }
-
- @Override
- protected void doFullTaskAction() {
- // we have to clean the source folder output in case the package name changed.
- File srcOut = getSourceOutputDir()
- if (srcOut != null) {
- emptyFolder(srcOut)
- }
-
- File resOutBaseNameFile = getPackageOutputFile()
-
- // we have to check the resource output folder in case some splits were removed, we should
- // manually remove them.
- File packageOutputFolder = getResDir()
- if (resOutBaseNameFile != null) {
- for (File file : packageOutputFolder.listFiles()) {
- if (!isSplitPackage(file, resOutBaseNameFile)) {
- file.delete();
- }
- }
- }
-
- AaptPackageProcessBuilder aaptPackageCommandBuilder =
- new AaptPackageProcessBuilder(getManifestFile(), getAaptOptions())
- .setAssetsFolder(getAssetsDir())
- .setResFolder(getResDir())
- .setLibraries(getLibraries())
- .setPackageForR(getPackageForR())
- .setSourceOutputDir(srcOut?.absolutePath)
- .setSymbolOutputDir(getTextSymbolOutputDir()?.absolutePath)
- .setResPackageOutput(resOutBaseNameFile?.absolutePath)
- .setProguardOutput(getProguardOutputFile()?.absolutePath)
- .setType(getType())
- .setDebuggable(getDebuggable())
- .setPseudoLocalesEnabled(getPseudoLocalesEnabled())
- .setResourceConfigs(getResourceConfigs())
- .setSplits(getSplits())
- .setPreferredDensity(getPreferredDensity())
-
- getBuilder().processResources(
- aaptPackageCommandBuilder,
- getEnforceUniquePackageName())
- }
-
- public static class ConfigAction implements TaskConfigAction<ProcessAndroidResources> {
-
- VariantOutputScope scope;
- File symbolLocation;
- boolean generateResourcePackage;
-
- ConfigAction(VariantOutputScope scope, File symbolLocation, generateResourcePackage) {
- this.scope = scope
- this.symbolLocation = symbolLocation
- this.generateResourcePackage = generateResourcePackage
- }
-
- @Override
- String getName() {
- return scope.getTaskName("process", "Resources")
- }
-
- @Override
- Class<ProcessAndroidResources> getType() {
- return ProcessAndroidResources.class;
- }
-
- @Override
- void execute(ProcessAndroidResources processResources) {
- BaseVariantOutputData variantOutputData = scope.variantOutputData
- BaseVariantData variantData = scope.variantScope.variantData
- variantOutputData.processResourcesTask = processResources
- VariantConfiguration config = variantData.getVariantConfiguration()
-
- processResources.androidBuilder = scope.globalScope.androidBuilder
-
- if (variantData.getSplitHandlingPolicy() ==
- BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY) {
- Set<String> allFilters = new HashSet<>();
- allFilters.addAll(variantData.getFilters(com.android.build.OutputFile.FilterType.DENSITY))
- allFilters.addAll(variantData.getFilters(com.android.build.OutputFile.FilterType.LANGUAGE))
- processResources.splits = allFilters;
- }
-
- // only generate code if the density filter is null, and if we haven't generated
- // it yet (if you have abi + density splits, then several abi output will have no
- // densityFilter)
- if (variantOutputData.getMainOutputFile().getFilter(com.android.build.OutputFile.DENSITY) == null
- && variantData.generateRClassTask == null) {
- variantData.generateRClassTask = processResources
- processResources.enforceUniquePackageName = scope.globalScope.getExtension().getEnforceUniquePackageName()
-
- ConventionMappingHelper.map(processResources, "libraries") {
- getTextSymbolDependencies(config.allLibraries)
- }
- ConventionMappingHelper.map(processResources, "packageForR") {
- config.originalApplicationId
- }
-
- // TODO: unify with generateBuilderConfig, compileAidl, and library packaging somehow?
- ConventionMappingHelper.map(processResources, "sourceOutputDir") {
- scope.getVariantScope().getRClassSourceOutputDir();
- }
-
- ConventionMappingHelper.map(processResources, "textSymbolOutputDir") {
- symbolLocation
- }
-
- if (config.buildType.isMinifyEnabled()) {
- if (config.buildType.shrinkResources && config.useJack) {
- LoggingUtil.displayWarning(Logging.getLogger(this.class), scope.globalScope.project,
- "shrinkResources does not yet work with useJack=true")
- }
- ConventionMappingHelper.map(processResources, "proguardOutputFile") {
- new File(
- "$scope.globalScope.buildDir/${FD_INTERMEDIATES}/proguard-rules/${config.dirName}/aapt_rules.txt")
- }
- } else if (config.buildType.shrinkResources) {
- LoggingUtil.displayWarning(Logging.getLogger(this.class), scope.globalScope.project,
- "To shrink resources you must also enable ProGuard")
- }
- }
-
- ConventionMappingHelper.map(processResources, "manifestFile") {
- variantOutputData.manifestProcessorTask.getOutputFile()
- }
-
- ConventionMappingHelper.map(processResources, "resDir") {
- variantData.finalResourcesDir
- }
-
- ConventionMappingHelper.map(processResources, "assetsDir") {
- variantData.mergeAssetsTask.outputDir
- }
-
- if (generateResourcePackage) {
- ConventionMappingHelper.map(processResources, "packageOutputFile") {
- scope.getProcessResourcePackageOutputFile()
- }
- }
-
- ConventionMappingHelper.map(processResources, "type") { config.type }
- ConventionMappingHelper.map(processResources, "debuggable") { config.buildType.debuggable }
- ConventionMappingHelper.map(processResources, "aaptOptions") { scope.globalScope.getExtension().aaptOptions }
- ConventionMappingHelper.map(processResources, "pseudoLocalesEnabled") { config.buildType.pseudoLocalesEnabled }
-
- ConventionMappingHelper.map(processResources, "resourceConfigs") {
- Collection<String> resConfigs = config.mergedFlavor.resourceConfigurations;
- if (resConfigs != null && resConfigs.size() == 1
- && Iterators.getOnlyElement(resConfigs.iterator()).equals("auto")) {
-
- return variantData.discoverListOfResourceConfigs();
- }
- return config.mergedFlavor.resourceConfigurations
- }
-
- ConventionMappingHelper.map(processResources, "preferredDensity") {
- variantOutputData.getMainOutputFile().getFilter(com.android.build.OutputFile.DENSITY)
- }
-
- }
-
- private static <T> Set<T> removeAllNullEntries(Set<T> input) {
- HashSet<T> output = new HashSet<T>();
- for (T element : input) {
- if (element != null) {
- output.add(element);
- }
- }
- return output;
- }
-
- @NonNull
- private static List<SymbolFileProviderImpl> getTextSymbolDependencies(
- List<LibraryDependency> libraries) {
-
- List<SymbolFileProviderImpl> list = Lists.newArrayListWithCapacity(libraries.size())
-
- for (LibraryDependency lib : libraries) {
- list.add(new SymbolFileProviderImpl(lib.manifest, lib.symbolFile))
- }
-
- return list
- }
- }
-}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.java
new file mode 100644
index 0000000..638286c
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessAndroidResources.java
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle.tasks;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.LoggingUtil;
+import com.android.build.gradle.internal.core.GradleVariantConfiguration;
+import com.android.build.gradle.internal.dependency.SymbolFileProviderImpl;
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.build.gradle.internal.scope.TaskConfigAction;
+import com.android.build.gradle.internal.scope.VariantOutputScope;
+import com.android.build.gradle.internal.tasks.IncrementalTask;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.builder.core.AaptPackageProcessBuilder;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.VariantType;
+import com.android.builder.dependency.LibraryDependency;
+import com.android.ide.common.blame.ParsingProcessOutputHandler;
+import com.android.ide.common.blame.parser.ToolOutputParser;
+import com.android.ide.common.blame.parser.aapt.AaptOutputParser;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessOutputHandler;
+import com.android.utils.FileUtils;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+
+import org.gradle.api.logging.Logging;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputDirectory;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Nested;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.ParallelizableTask;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+@ParallelizableTask
+public class ProcessAndroidResources extends IncrementalTask {
+
+ private File manifestFile;
+
+ private File resDir;
+
+ private File assetsDir;
+
+ private File sourceOutputDir;
+
+ private File textSymbolOutputDir;
+
+ private File packageOutputFile;
+
+ private File proguardOutputFile;
+
+ private Collection<String> resourceConfigs;
+
+ private String preferredDensity;
+
+ private List<SymbolFileProviderImpl> libraries;
+
+ private String packageForR;
+
+ private Collection<String> splits;
+
+ private boolean enforceUniquePackageName;
+
+ private VariantType type;
+
+ private boolean debuggable;
+
+ private boolean pseudoLocalesEnabled;
+
+ private AaptOptions aaptOptions;
+
+
+ @Override
+ protected void doFullTaskAction() throws IOException {
+ // we have to clean the source folder output in case the package name changed.
+ File srcOut = getSourceOutputDir();
+ if (srcOut != null) {
+ FileUtils.emptyFolder(srcOut);
+ }
+
+ File resOutBaseNameFile = getPackageOutputFile();
+
+ // we have to check the resource output folder in case some splits were removed, we should
+ // manually remove them.
+ File packageOutputFolder = getResDir();
+ if (resOutBaseNameFile != null) {
+ for (File file : packageOutputFolder.listFiles()) {
+ if (!isSplitPackage(file, resOutBaseNameFile)) {
+ //noinspection ResultOfMethodCallIgnored
+ file.delete();
+ }
+ }
+ }
+
+ AaptPackageProcessBuilder aaptPackageCommandBuilder =
+ new AaptPackageProcessBuilder(getManifestFile(), getAaptOptions())
+ .setAssetsFolder(getAssetsDir())
+ .setResFolder(getResDir())
+ .setLibraries(getLibraries())
+ .setPackageForR(getPackageForR())
+ .setSourceOutputDir(absolutePath(srcOut))
+ .setSymbolOutputDir(absolutePath(getTextSymbolOutputDir()))
+ .setResPackageOutput(absolutePath(resOutBaseNameFile))
+ .setProguardOutput(absolutePath(getProguardOutputFile()))
+ .setType(getType())
+ .setDebuggable(getDebuggable())
+ .setPseudoLocalesEnabled(getPseudoLocalesEnabled())
+ .setResourceConfigs(getResourceConfigs())
+ .setSplits(getSplits())
+ .setPreferredDensity(getPreferredDensity());
+
+ @NonNull
+ AndroidBuilder builder = getBuilder();
+ ProcessOutputHandler processOutputHandler = new ParsingProcessOutputHandler(
+ new ToolOutputParser(new AaptOutputParser(), getILogger()),
+ builder.getErrorReporter());
+ try {
+ builder.processResources(
+ aaptPackageCommandBuilder,
+ getEnforceUniquePackageName(),
+ processOutputHandler);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (ProcessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private boolean isSplitPackage(File file, File resBaseName) {
+ if (file.getName().startsWith(resBaseName.getName())) {
+ for (String split : splits) {
+ if (file.getName().contains(split)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Nullable
+ private static String absolutePath(@Nullable File file) {
+ return file == null ? null : file.getAbsolutePath();
+ }
+
+ public static class ConfigAction implements TaskConfigAction<ProcessAndroidResources> {
+
+ private VariantOutputScope scope;
+ private File symbolLocation;
+ private boolean generateResourcePackage;
+
+ public ConfigAction(
+ VariantOutputScope scope, File symbolLocation, boolean generateResourcePackage) {
+ this.scope = scope;
+ this.symbolLocation = symbolLocation;
+ this.generateResourcePackage = generateResourcePackage;
+ }
+
+ @Override
+ public String getName() {
+ return scope.getTaskName("process", "Resources");
+ }
+
+ @Override
+ public Class<ProcessAndroidResources> getType() {
+ return ProcessAndroidResources.class;
+ }
+
+ @Override
+ public void execute(ProcessAndroidResources processResources) {
+ final BaseVariantOutputData variantOutputData = scope.getVariantOutputData();
+ final BaseVariantData<? extends BaseVariantOutputData> variantData =
+ scope.getVariantScope().getVariantData();
+ variantOutputData.processResourcesTask = processResources;
+ final GradleVariantConfiguration config = variantData.getVariantConfiguration();
+
+ processResources.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+ processResources.setVariantName(config.getFullName());
+
+ if (variantData.getSplitHandlingPolicy() ==
+ BaseVariantData.SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY) {
+ Set<String> allFilters = new HashSet<String>();
+ allFilters.addAll(
+ variantData.getFilters(com.android.build.OutputFile.FilterType.DENSITY));
+ allFilters.addAll(
+ variantData.getFilters(com.android.build.OutputFile.FilterType.LANGUAGE));
+ processResources.splits = allFilters;
+ }
+
+ // only generate code if the density filter is null, and if we haven't generated
+ // it yet (if you have abi + density splits, then several abi output will have no
+ // densityFilter)
+ if (variantOutputData.getMainOutputFile()
+ .getFilter(com.android.build.OutputFile.DENSITY) == null
+ && variantData.generateRClassTask == null) {
+ variantData.generateRClassTask = processResources;
+ processResources.enforceUniquePackageName = scope.getGlobalScope().getExtension()
+ .getEnforceUniquePackageName();
+
+ ConventionMappingHelper.map(processResources, "libraries",
+ new Callable<List<SymbolFileProviderImpl>>() {
+ @Override
+ public List<SymbolFileProviderImpl> call() throws Exception {
+ return getTextSymbolDependencies(config.getAllLibraries());
+ }
+ });
+ ConventionMappingHelper.map(processResources, "packageForR",
+ new Callable<String>() {
+ @Override
+ public String call() throws Exception {
+ return config.getOriginalApplicationId();
+ }
+ });
+
+ // TODO: unify with generateBuilderConfig, compileAidl, and library packaging somehow?
+ processResources
+ .setSourceOutputDir(scope.getVariantScope().getRClassSourceOutputDir());
+ processResources.setTextSymbolOutputDir(symbolLocation);
+
+ if (config.getBuildType().isMinifyEnabled()) {
+ if (config.getBuildType().isShrinkResources() && config.getUseJack()) {
+ LoggingUtil.displayWarning(Logging.getLogger(getClass()),
+ scope.getGlobalScope().getProject(),
+ "shrinkResources does not yet work with useJack=true");
+ }
+ processResources.setProguardOutputFile(
+ scope.getVariantScope().getProcessAndroidResourcesProguardOutputFile());
+
+ } else if (config.getBuildType().isShrinkResources()) {
+ LoggingUtil.displayWarning(Logging.getLogger(getClass()),
+ scope.getGlobalScope().getProject(),
+ "To shrink resources you must also enable ProGuard");
+ }
+ }
+
+ ConventionMappingHelper.map(processResources, "manifestFile", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ return variantOutputData.manifestProcessorTask.getOutputFile();
+ }
+ });
+
+ ConventionMappingHelper.map(processResources, "resDir", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ return scope.getVariantScope().getFinalResourcesDir();
+ }
+ });
+
+ ConventionMappingHelper.map(processResources, "assetsDir", new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ return variantData.mergeAssetsTask.getOutputDir();
+ }
+ });
+
+ if (generateResourcePackage) {
+ processResources.setPackageOutputFile(scope.getProcessResourcePackageOutputFile());
+ }
+
+ processResources.setType(config.getType());
+ processResources.setDebuggable(config.getBuildType().isDebuggable());
+ processResources.setAaptOptions(scope.getGlobalScope().getExtension().getAaptOptions());
+ processResources
+ .setPseudoLocalesEnabled(config.getBuildType().isPseudoLocalesEnabled());
+
+ ConventionMappingHelper.map(processResources, "resourceConfigs",
+ new Callable<Collection<String>>() {
+ @Override
+ public Collection<String> call() throws Exception {
+ Collection<String> resConfigs =
+ config.getMergedFlavor().getResourceConfigurations();
+ if (resConfigs.size() == 1 &&
+ Iterators.getOnlyElement(resConfigs.iterator())
+ .equals("auto")) {
+ return variantData.discoverListOfResourceConfigs();
+ }
+ return config.getMergedFlavor().getResourceConfigurations();
+ }
+ });
+
+ ConventionMappingHelper.map(processResources, "preferredDensity",
+ new Callable<String>() {
+ @Override
+ public String call() throws Exception {
+ return variantOutputData.getMainOutputFile()
+ .getFilter(com.android.build.OutputFile.DENSITY);
+ }
+ });
+
+
+ }
+
+ @NonNull
+ private static List<SymbolFileProviderImpl> getTextSymbolDependencies(
+ List<LibraryDependency> libraries) {
+
+ List<SymbolFileProviderImpl> list = Lists.newArrayListWithCapacity(libraries.size());
+
+ for (LibraryDependency lib : libraries) {
+ list.add(new SymbolFileProviderImpl(lib));
+ }
+
+ return list;
+ }
+ }
+
+ @InputFile
+ public File getManifestFile() {
+ return manifestFile;
+ }
+
+ public void setManifestFile(File manifestFile) {
+ this.manifestFile = manifestFile;
+ }
+
+ @NonNull
+ @InputDirectory
+ public File getResDir() {
+ return resDir;
+ }
+
+ public void setResDir(@NonNull File resDir) {
+ this.resDir = resDir;
+ }
+
+ @OutputDirectory
+ @Optional
+ public File getAssetsDir() {
+ return assetsDir;
+ }
+
+ public void setAssetsDir(File assetsDir) {
+ this.assetsDir = assetsDir;
+ }
+
+ @OutputDirectory
+ @Optional
+ public File getSourceOutputDir() {
+ return sourceOutputDir;
+ }
+
+ public void setSourceOutputDir(File sourceOutputDir) {
+ this.sourceOutputDir = sourceOutputDir;
+ }
+
+ @OutputDirectory
+ @Optional
+ public File getTextSymbolOutputDir() {
+ return textSymbolOutputDir;
+ }
+
+ public void setTextSymbolOutputDir(File textSymbolOutputDir) {
+ this.textSymbolOutputDir = textSymbolOutputDir;
+ }
+
+ @OutputFile
+ @Optional
+ public File getPackageOutputFile() {
+ return packageOutputFile;
+ }
+
+ public void setPackageOutputFile(File packageOutputFile) {
+ this.packageOutputFile = packageOutputFile;
+ }
+
+ @OutputFile
+ @Optional
+ public File getProguardOutputFile() {
+ return proguardOutputFile;
+ }
+
+ public void setProguardOutputFile(File proguardOutputFile) {
+ this.proguardOutputFile = proguardOutputFile;
+ }
+
+ @Input
+ public Collection<String> getResourceConfigs() {
+ return resourceConfigs;
+ }
+
+ public void setResourceConfigs(Collection<String> resourceConfigs) {
+ this.resourceConfigs = resourceConfigs;
+ }
+
+ @Input
+ @Optional
+ public String getPreferredDensity() {
+ return preferredDensity;
+ }
+
+ public void setPreferredDensity(String preferredDensity) {
+ this.preferredDensity = preferredDensity;
+ }
+
+ @Input
+ String getBuildToolsVersion() {
+ return getBuildTools().getRevision().toString();
+ }
+
+ @Nested
+ @Optional
+ public List<SymbolFileProviderImpl> getLibraries() {
+ return libraries;
+ }
+
+ public void setLibraries(
+ List<SymbolFileProviderImpl> libraries) {
+ this.libraries = libraries;
+ }
+
+ @Input
+ @Optional
+ public String getPackageForR() {
+ return packageForR;
+ }
+
+ public void setPackageForR(String packageForR) {
+ this.packageForR = packageForR;
+ }
+
+ @Nested
+ @Optional
+ public Collection<String> getSplits() {
+ return splits;
+ }
+
+ public void setSplits(Collection<String> splits) {
+ this.splits = splits;
+ }
+
+ @Input
+ public boolean getEnforceUniquePackageName() {
+ return enforceUniquePackageName;
+ }
+
+ public void setEnforceUniquePackageName(boolean enforceUniquePackageName) {
+ this.enforceUniquePackageName = enforceUniquePackageName;
+ }
+
+ /** Does not change between incremental builds, so does not need to be @Input. */
+ public VariantType getType() {
+ return type;
+ }
+
+ public void setType(VariantType type) {
+ this.type = type;
+ }
+
+ @Input
+ public boolean getDebuggable() {
+ return debuggable;
+ }
+
+ public void setDebuggable(boolean debuggable) {
+ this.debuggable = debuggable;
+ }
+
+ @Input
+ public boolean getPseudoLocalesEnabled() {
+ return pseudoLocalesEnabled;
+ }
+
+ public void setPseudoLocalesEnabled(boolean pseudoLocalesEnabled) {
+ this.pseudoLocalesEnabled = pseudoLocalesEnabled;
+ }
+
+ @Nested
+ public AaptOptions getAaptOptions() {
+ return aaptOptions;
+ }
+
+ public void setAaptOptions(AaptOptions aaptOptions) {
+ this.aaptOptions = aaptOptions;
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessManifest.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessManifest.groovy
index e738598..ce492fe 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessManifest.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessManifest.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle.tasks
-
import com.android.build.gradle.internal.TaskManager
import com.android.build.gradle.internal.scope.ConventionMappingHelper
import com.android.build.gradle.internal.scope.TaskConfigAction
@@ -23,7 +22,6 @@
import com.android.build.gradle.internal.variant.BaseVariantOutputData
import com.android.builder.core.AndroidBuilder
import com.android.builder.core.VariantConfiguration
-import com.android.builder.model.AndroidProject
import com.android.builder.model.ProductFlavor
import com.android.manifmerger.ManifestMerger2
import org.gradle.api.tasks.Input
@@ -31,7 +29,6 @@
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.ParallelizableTask
-
/**
* a Task that only merge a single manifest with its overlays.
*/
@@ -136,6 +133,7 @@
variantOutputData.manifestProcessorTask = processManifest
processManifest.androidBuilder = androidBuilder
+ processManifest.setVariantName(config.getFullName())
processManifest.variantConfiguration = config
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessTestManifest.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessTestManifest.groovy
index 0144816..1ee359b 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessTestManifest.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ProcessTestManifest.groovy
@@ -14,23 +14,19 @@
* limitations under the License.
*/
package com.android.build.gradle.tasks
-
import com.android.build.gradle.internal.DependencyManager
import com.android.build.gradle.internal.dependency.ManifestDependencyImpl
import com.android.build.gradle.internal.scope.ConventionMappingHelper
import com.android.build.gradle.internal.scope.TaskConfigAction
-import com.android.build.gradle.internal.scope.VariantOutputScope
import com.android.build.gradle.internal.scope.VariantScope
import com.android.build.gradle.internal.variant.BaseVariantOutputData
import com.android.builder.core.VariantConfiguration
-import com.android.builder.model.AndroidProject
import com.google.common.collect.Lists
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.ParallelizableTask
-
/**
* A task that processes the manifest
*/
@@ -145,6 +141,7 @@
variantOutputData.manifestProcessorTask = processTestManifestTask
processTestManifestTask.androidBuilder = scope.globalScope.androidBuilder
+ processTestManifestTask.setVariantName(config.getFullName())
ConventionMappingHelper.map(processTestManifestTask, "testApplicationId") {
config.applicationId
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/RenderscriptCompile.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/RenderscriptCompile.groovy
index 6946fad..18e581c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/RenderscriptCompile.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/RenderscriptCompile.groovy
@@ -15,21 +15,19 @@
*/
package com.android.build.gradle.tasks
-
import com.android.annotations.NonNull
import com.android.build.gradle.internal.core.GradleVariantConfiguration
import com.android.build.gradle.internal.scope.ConventionMappingHelper
import com.android.build.gradle.internal.scope.TaskConfigAction
-import com.android.build.gradle.internal.scope.VariantOutputScope
import com.android.build.gradle.internal.scope.VariantScope
import com.android.build.gradle.internal.tasks.NdkTask
import com.android.build.gradle.internal.variant.BaseVariantData
import com.android.build.gradle.internal.variant.BaseVariantOutputData
-import com.android.builder.core.VariantConfiguration
-import com.android.builder.model.AndroidProject
import com.android.builder.model.ApiVersion
import com.android.builder.model.ProductFlavor
+import com.android.ide.common.process.LoggedProcessOutputHandler
import com.android.sdklib.SdkVersionInfo
+import com.android.utils.FileUtils
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputDirectory
@@ -37,7 +35,6 @@
import static com.android.builder.model.AndroidProject.FD_GENERATED
import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-
/**
* Task to compile Renderscript files. Supports incremental update.
*/
@@ -86,19 +83,19 @@
boolean ndkMode
@TaskAction
- void taskAction() {
+ void taskAction() throws IOException {
// this is full run (always), clean the previous outputs
File sourceDestDir = getSourceOutputDir()
- emptyFolder(sourceDestDir)
+ FileUtils.emptyFolder(sourceDestDir)
File resDestDir = getResOutputDir()
- emptyFolder(resDestDir)
+ FileUtils.emptyFolder(resDestDir)
File objDestDir = getObjOutputDir()
- emptyFolder(objDestDir)
+ FileUtils.emptyFolder(objDestDir)
File libDestDir = getLibOutputDir()
- emptyFolder(libDestDir)
+ FileUtils.emptyFolder(libDestDir)
// get the import folders. If the .rsh files are not directly under the import folders,
// we need to get the leaf folders, as this is what llvm-rs-cc expects.
@@ -117,7 +114,8 @@
getOptimLevel(),
getNdkMode(),
getSupportMode(),
- getNdkConfig()?.abiFilters)
+ getNdkConfig()?.abiFilters,
+ new LoggedProcessOutputHandler(getILogger()))
}
// ----- ConfigAction -----
@@ -150,6 +148,7 @@
ProductFlavor mergedFlavor = config.mergedFlavor
boolean ndkMode = config.renderscriptNdkModeEnabled
renderscriptTask.androidBuilder = scope.globalScope.androidBuilder
+ renderscriptTask.setVariantName(config.getFullName())
ConventionMappingHelper.map(renderscriptTask, "targetApi") {
int targetApi = mergedFlavor.renderscriptTargetApi != null ?
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
index 7a81061..649a8d0 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
@@ -333,37 +333,16 @@
// compressed in the source .ap_ file must be left uncompressed
// here, since for example RAW files need to remain uncompressed in
// the APK such that they can be mmap'ed at runtime.
- //
- // Create the JarEntry manually rather than setting up
- // the JarEntry via new JarEntry(entry):
- // We don't know what the exact compressed size will
- // be for this file; it depends on the compression level,
- // and we don't have a way to know what it was for the
- // input file. Empirically it seems to be 9 (when it is
- // set to a different level the compressed sizes we produce
- // are larger than the ones we see in typical .ap_ input files).
- // However, we don't want to set the size specifically; we leave it
- // as the default value such that the actual compressed size
- // is used; without that we can hit errors like
- // java.util.zip.ZipException: invalid entry compressed size
- // (expected 7344 but got 7352 bytes); ignoring
- // (see issue 80115 for more).
- //
- // However, for stored (not compressed) entries, we *do* have
- // to set the size and compressed size; without it, the zip
- // encoder will throw an exception. Luckily, for uncompressed files
- // we predictably know the compressed size. Therefore, we special case
- // this based on the entry's method.
- JarEntry outEntry = new JarEntry(entry.getName());
- if (entry.getTime() != -1L) {
- outEntry.setTime(entry.getTime());
- }
- int method = entry.getMethod();
- outEntry.setMethod(method);
- if (method == ZipEntry.STORED) {
- outEntry.setCompressedSize(entry.getCompressedSize());
- outEntry.setSize(entry.getSize());
- outEntry.setCrc(entry.getCrc());
+ // Preserve the STORED method of the input entry.
+ JarEntry outEntry;
+ if (entry.getMethod() == JarEntry.STORED) {
+ outEntry = new JarEntry(entry);
+ } else {
+ // Create a new entry so that the compressed len is recomputed.
+ outEntry = new JarEntry(name);
+ if (entry.getTime() != -1L) {
+ outEntry.setTime(entry.getTime());
+ }
}
zos.putNextEntry(outEntry);
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy
index c45c5db..4031e6c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ShrinkResources.groovy
@@ -20,13 +20,10 @@
import com.android.build.gradle.internal.scope.TaskConfigAction
import com.android.build.gradle.internal.scope.VariantOutputScope
import com.android.build.gradle.internal.tasks.BaseTask
-import com.android.build.gradle.internal.variant.ApkVariantOutputData
import com.android.build.gradle.internal.variant.BaseVariantData
import com.android.build.gradle.internal.variant.BaseVariantOutputData
import com.android.builder.core.AaptPackageProcessBuilder
-import com.android.builder.model.AndroidProject
-import com.android.utils.StringHelper
-import org.codehaus.groovy.runtime.StringGroovyMethods
+import com.android.ide.common.process.LoggedProcessOutputHandler
import org.gradle.api.logging.LogLevel
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputFile
@@ -35,8 +32,6 @@
import java.util.concurrent.Callable
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-
/**
* Task which strips out unused resources
* <p>
@@ -83,13 +78,13 @@
try {
def processResourcesTask = variantData.generateRClassTask
File sourceDir = processResourcesTask.sourceOutputDir
- File resourceDir = variantData.finalResourcesDir
+ File resourceDir = variantData.getScope().getFinalResourcesDir()
File mergedManifest = variantOutputData.manifestProcessorTask.manifestOutputFile
// Analyze resources and usages and strip out unused
def analyzer = new ResourceUsageAnalyzer(
sourceDir,
- variantData.obfuscatedClassesJar,
+ variantData.getScope().getProguardOutputFile(),
mergedManifest,
variantData.getMappingFile(),
resourceDir)
@@ -133,6 +128,7 @@
getBuilder().processResources(
aaptPackageCommandBuilder,
processResourcesTask.getEnforceUniquePackageName(),
+ new LoggedProcessOutputHandler(getBuilder().getLogger())
)
} else {
// Just rewrite the .ap_ file to strip out the res/ files for unused resources
@@ -206,6 +202,7 @@
BaseVariantData<? extends BaseVariantOutputData> variantData =
scope.variantScope.variantData
task.setAndroidBuilder(scope.getGlobalScope().getAndroidBuilder());
+ task.setVariantName(scope.getVariantScope().getVariantConfiguration().getFullName());
task.variantOutputData = scope.variantOutputData;
final String outputBaseName = scope.variantOutputData.getBaseName();
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/SimpleWorkQueue.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/SimpleWorkQueue.java
new file mode 100644
index 0000000..f54f286
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/SimpleWorkQueue.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.tasks;
+
+import com.android.annotations.NonNull;
+import com.android.builder.tasks.Job;
+import com.android.builder.tasks.JobContext;
+import com.android.builder.tasks.QueueThreadContextAdapter;
+import com.android.builder.tasks.WorkQueue;
+import com.android.utils.StdLogger;
+
+/**
+ * Common utilities to use a simple shared instance of {@link WorkQueue}.
+ * The context for job will be empty, and it is the responsibility of the
+ * {@link com.android.builder.tasks.WorkQueue.QueueTask} to have enough context to run.
+ */
+public class SimpleWorkQueue {
+
+ /**
+ * Simple {@link WorkQueue} context implementation that simply runs the proguard job.
+ */
+ private static class EmptyThreadContext extends
+ QueueThreadContextAdapter<Void> {
+
+ @Override
+ public void runTask(@NonNull Job<Void> job) throws Exception {
+ job.runTask(new JobContext<Void>(null /* payload */));
+ job.finished();
+ }
+ }
+
+ /**
+ * singleton work queue for all proguard invocations.
+ */
+ private static final WorkQueue<Void> WORK_QUEUE =
+ new WorkQueue<Void>(
+ new StdLogger(StdLogger.Level.VERBOSE),
+ new EmptyThreadContext(), "Tasks limiter", 4);
+
+
+ static void push(Job<Void> job) throws InterruptedException {
+ WORK_QUEUE.push(job);
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/TestModuleProGuardTask.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/TestModuleProGuardTask.java
index 52ad9f2..136db9e 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/TestModuleProGuardTask.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/TestModuleProGuardTask.java
@@ -16,7 +16,11 @@
package com.android.build.gradle.tasks;
+import com.android.builder.core.VariantConfiguration;
+
import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.logging.Logger;
import org.gradle.api.tasks.TaskAction;
import java.io.IOException;
@@ -29,8 +33,9 @@
* input files like the tested application classes and the tested application mapping file.
*/
public class TestModuleProGuardTask extends ProGuardTask {
+ private Logger logger;
private Configuration mappingConfiguration;
- private Configuration classesConfiguration;
+ private VariantConfiguration variantConfiguration;
/**
@@ -45,18 +50,36 @@
* Sets the {@link Configuration} to later retrieve the test application classes jar file.
*/
public void setClassesConfiguration(Configuration configuration) {
- this.classesConfiguration = configuration;
dependsOn(configuration);
}
+
+ public void setVariantConfiguration(
+ VariantConfiguration variantConfiguration) {
+ this.variantConfiguration = variantConfiguration;
+ }
+
+ public void setLogger(Logger logger) {
+ this.logger = logger;
+ }
+
@Override
@TaskAction
public void proguard() throws ParseException, IOException {
- if (mappingConfiguration.getFiles().isEmpty() || classesConfiguration.getFiles().isEmpty()) {
- return;
+ if (logger.isEnabled(LogLevel.INFO)) {
+ logger.info("test module mapping file " + mappingConfiguration.getSingleFile());
+ for (Object file : variantConfiguration.getPackagedJars()) {
+ logger.info("test module proguard input " + file);
+
+ }
+ for (Object file : variantConfiguration.getProvidedOnlyJars()) {
+ logger.info("test module proguard library " + file);
+ }
}
- applymapping(mappingConfiguration.getSingleFile());
- libraryjars(classesConfiguration.getSingleFile());
+
+ if (mappingConfiguration.getSingleFile().isFile()) {
+ applymapping(mappingConfiguration.getSingleFile());
+ }
super.proguard();
}
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ZipAlign.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ZipAlign.java
index 15e910e..6df5096 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ZipAlign.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/ZipAlign.java
@@ -9,7 +9,6 @@
import com.android.build.gradle.internal.scope.VariantOutputScope;
import com.android.build.gradle.internal.tasks.FileSupplier;
import com.android.build.gradle.internal.variant.ApkVariantOutputData;
-import com.android.utils.StringHelper;
import org.gradle.api.Action;
import org.gradle.api.DefaultTask;
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriver.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriver.java
new file mode 100644
index 0000000..78ec03d
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriver.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.tasks.annotations;
+
+import static com.android.SdkConstants.DOT_JAVA;
+import static java.io.File.pathSeparator;
+import static java.io.File.pathSeparatorChar;
+
+import com.android.annotations.NonNull;
+import com.android.tools.lint.EcjParser;
+import com.android.utils.Pair;
+import com.google.common.base.Charsets;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.io.Files;
+
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
+import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
+import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.util.Util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The extract annotations driver is a command line interface to extracting annotations
+ * from a source tree. It's similar to the gradle
+ * {@link com.android.build.gradle.tasks.ExtractAnnotations} task,
+ * but usable from the command line and outside Gradle, for example
+ * to extract annotations from the Android framework itself (which is not built with
+ * Gradle). It also allows other options only interesting for extracting
+ * platform annotations, such as filtering all APIs and constants through an
+ * API white-list (such that we for example can pull annotations from the master
+ * branch which has the latest metadata, but only expose APIs that are actually in
+ * a released platform), as well as translating android.annotation annotations into
+ * android.support.annotations.
+ */
+public class ExtractAnnotationsDriver {
+ public static void main(String[] args) {
+ new ExtractAnnotationsDriver().run(args);
+ }
+
+ private static void usage(PrintStream output) {
+ output.println("Usage: " + ExtractAnnotationsDriver.class.getSimpleName() + " <flags>");
+ output.println(" --sources <paths> : Source directories to extract annotations from. ");
+ output.println(" Separate paths with " + pathSeparator + ", and you can use @ ");
+ output.println(" as a filename prefix to have the filenames fed from a file");
+ output.println("--classpath <paths> : Directories and .jar files to resolve symbols from");
+ output.println("--output <zip path> : The .zip file to write the extracted annotations to, if any");
+ output.println("--proguard <path> : The proguard.cfg file to write the keep rules to, if any");
+ output.println();
+ output.println("Optional flags:");
+ output.println("--merge-zips <paths> : Existing external annotation files to merge in");
+ output.println("--quiet : Don't print summary information");
+ output.println("--rmtypedefs <folder> : Remove typedef classes found in the given folder");
+ output.println("--allow-missing-types : Don't fail even if some types can't be resolved");
+ output.println("--allow-errors : Don't fail even if there are some compiler errors");
+ output.println("--encoding <encoding> : Encoding (defaults to utf-8)");
+ output.println("--language-level <level> : Java source language level, typically 1.6 (default) or 1.7");
+ output.println("--api-filter <api.txt> : A framework API definition to restrict included APIs to");
+ output.println("--hide-filtered : If filtering out non-APIs, supply this flag to hide listing matches");
+ output.println("--skip-class-retention : Don't extract annotations that have class retention");
+ System.exit(-1);
+ }
+
+ @SuppressWarnings("MethodMayBeStatic")
+ public void run(@NonNull String[] args) {
+ List<String> classpath = Lists.newArrayList();
+ List<File> sources = Lists.newArrayList();
+ List<File> mergePaths = Lists.newArrayList();
+ List<File> apiFilters = null;
+ File rmTypeDefs = null;
+ boolean verbose = true;
+ boolean allowMissingTypes = false;
+ boolean allowErrors = false;
+ boolean listFiltered = true;
+ boolean skipClassRetention = false;
+
+ String encoding = Charsets.UTF_8.name();
+ File output = null;
+ File proguard = null;
+ long languageLevel = EcjParser.getLanguageLevel(1, 7);
+ if (args.length == 1 && "--help".equals(args[0])) {
+ usage(System.out);
+ }
+ if (args.length < 2) {
+ usage(System.err);
+ }
+ for (int i = 0, n = args.length; i < n; i++) {
+ String flag = args[i];
+
+ if (flag.equals("--quiet")) {
+ verbose = false;
+ continue;
+ } else if (flag.equals("--allow-missing-types")) {
+ allowMissingTypes = true;
+ continue;
+ } else if (flag.equals("--allow-errors")) {
+ allowErrors = true;
+ continue;
+ } else if (flag.equals("--hide-filtered")) {
+ listFiltered = false;
+ continue;
+ } else if (flag.equals("--skip-class-retention")) {
+ skipClassRetention = true;
+ continue;
+ }
+ if (i == n - 1) {
+ usage(System.err);
+ }
+ String value = args[i + 1];
+ i++;
+
+ if (flag.equals("--sources")) {
+ sources = getFiles(value);
+ } else if (flag.equals("--classpath")) {
+ classpath = getPaths(value);
+ } else if (flag.equals("--merge-zips")) {
+ mergePaths = getFiles(value);
+ } else if (flag.equals("--output")) {
+ output = new File(value);
+ if (output.exists()) {
+ if (output.isDirectory()) {
+ abort(output + " is a directory");
+ }
+ boolean deleted = output.delete();
+ if (!deleted) {
+ abort("Could not delete previous version of " + output);
+ }
+ } else if (output.getParentFile() != null && !output.getParentFile().exists()) {
+ abort(output.getParentFile() + " does not exist");
+ }
+ } else if (flag.equals("--proguard")) {
+ proguard = new File(value);
+ if (proguard.exists()) {
+ if (proguard.isDirectory()) {
+ abort(proguard + " is a directory");
+ }
+ boolean deleted = proguard.delete();
+ if (!deleted) {
+ abort("Could not delete previous version of " + proguard);
+ }
+ } else if (proguard.getParentFile() != null && !proguard.getParentFile().exists()) {
+ abort(proguard.getParentFile() + " does not exist");
+ }
+ } else if (flag.equals("--encoding")) {
+ encoding = value;
+ } else if (flag.equals("--api-filter")) {
+ if (apiFilters == null) {
+ apiFilters = Lists.newArrayList();
+ }
+ for (String path : Splitter.on(",").omitEmptyStrings().split(value)) {
+ File apiFilter = new File(path);
+ if (!apiFilter.isFile()) {
+ String message = apiFilter + " does not exist or is not a file";
+ abort(message);
+ }
+ apiFilters.add(apiFilter);
+ }
+ } else if (flag.equals("--language-level")) {
+ if ("1.6".equals(value)) {
+ languageLevel = EcjParser.getLanguageLevel(1, 6);
+ } else if ("1.7".equals(value)) {
+ languageLevel = EcjParser.getLanguageLevel(1, 7);
+ } else {
+ abort("Unsupported language level " + value);
+ }
+ } else if (flag.equals("--rmtypedefs")) {
+ rmTypeDefs = new File(value);
+ if (!rmTypeDefs.isDirectory()) {
+ abort(rmTypeDefs + " is not a directory");
+ }
+ } else {
+ System.err.println("Unknown flag " + flag + ": Use --help for usage information");
+ }
+ }
+
+ if (sources.isEmpty()) {
+ abort("Must specify at least one source path");
+ }
+ if (classpath.isEmpty()) {
+ abort("Must specify classpath pointing to at least android.jar or the framework");
+ }
+ if (output == null && proguard == null) {
+ abort("Must specify output path with --output or a proguard path with --proguard");
+ }
+
+ // API definition files
+ ApiDatabase database = null;
+ if (apiFilters != null && !apiFilters.isEmpty()) {
+ try {
+ List<String> lines = Lists.newArrayList();
+ for (File file : apiFilters) {
+ lines.addAll(Files.readLines(file, Charsets.UTF_8));
+ }
+ database = new ApiDatabase(lines);
+ } catch (IOException e) {
+ abort("Could not open API database " + apiFilters + ": " + e.getLocalizedMessage());
+ }
+ }
+
+ Extractor extractor = new Extractor(database, rmTypeDefs, verbose, !skipClassRetention,
+ true);
+ extractor.setListIgnored(listFiltered);
+
+ try {
+ Pair<Collection<CompilationUnitDeclaration>, INameEnvironment>
+ pair = parseSources(sources, classpath, encoding, languageLevel);
+ Collection<CompilationUnitDeclaration> units = pair.getFirst();
+
+ boolean abort = false;
+ int errorCount = 0;
+ for (CompilationUnitDeclaration unit : units) {
+ // so maybe I don't need my map!!
+ IProblem[] problems = unit.compilationResult().getAllProblems();
+ if (problems != null) {
+ for (IProblem problem : problems) {
+ if (problem.isError()) {
+ errorCount++;
+ String message = problem.getMessage();
+ if (allowMissingTypes) {
+ if (message.contains("cannot be resolved")) {
+ continue;
+ }
+ }
+
+ System.out.println("Error: " +
+ new String(problem.getOriginatingFileName()) + ":" +
+ problem.getSourceLineNumber() + ": " + message);
+ abort = !allowErrors;
+ }
+ }
+ }
+ }
+ if (errorCount > 0) {
+ System.err.println("Found " + errorCount + " errors");
+ }
+ if (abort) {
+ abort("Not extracting annotations (compilation problems encountered)");
+ }
+
+ INameEnvironment environment = pair.getSecond();
+ extractor.extractFromProjectSource(units);
+
+ if (mergePaths != null) {
+ for (File jar : mergePaths) {
+ extractor.mergeExisting(jar);
+ }
+ }
+
+ extractor.export(output, proguard);
+
+ // Remove typedefs?
+ //noinspection VariableNotUsedInsideIf
+ if (rmTypeDefs != null) {
+ extractor.removeTypedefClasses();
+ }
+
+ environment.cleanup();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void abort(@NonNull String message) {
+ System.err.println(message);
+ System.exit(-1);
+ }
+
+ private static List<File> getFiles(String value) {
+ List<File> files = Lists.newArrayList();
+ Splitter splitter = Splitter.on(pathSeparatorChar).omitEmptyStrings().trimResults();
+ for (String path : splitter.split(value)) {
+ if (path.startsWith("@")) {
+ // Special syntax for providing files in a list
+ File sourcePath = new File(path.substring(1));
+ if (!sourcePath.exists()) {
+ abort(sourcePath + " does not exist");
+ }
+ try {
+ for (String line : Files.readLines(sourcePath, Charsets.UTF_8)) {
+ line = line.trim();
+ if (!line.isEmpty()) {
+ File file = new File(line);
+ if (!file.exists()) {
+ System.err.println("Warning: Could not find file " + line +
+ " listed in " + sourcePath);
+ }
+ files.add(file);
+ }
+ }
+ continue;
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ }
+ File file = new File(path);
+ if (!file.exists()) {
+ abort(file + " does not exist");
+ }
+ files.add(file);
+ }
+
+ return files;
+ }
+
+ private static List<String> getPaths(String value) {
+ List<File> files = getFiles(value);
+ List<String> paths = Lists.newArrayListWithExpectedSize(files.size());
+ for (File file : files) {
+ paths.add(file.getPath());
+ }
+ return paths;
+ }
+
+ private static void addJavaSources(List<File> list, File file) {
+ if (file.isDirectory()) {
+ File[] files = file.listFiles();
+ if (files != null) {
+ for (File child : files) {
+ addJavaSources(list, child);
+ }
+ }
+ } else {
+ if (file.isFile() && file.getName().endsWith(DOT_JAVA)) {
+ list.add(file);
+ }
+ }
+ }
+
+ private static List<File> gatherJavaSources(List<File> sourcePath) {
+ List<File> sources = Lists.newArrayList();
+ for (File file : sourcePath) {
+ addJavaSources(sources, file);
+ }
+ return sources;
+ }
+
+ @NonNull
+ private static Pair<Collection<CompilationUnitDeclaration>,INameEnvironment> parseSources(
+ @NonNull List<File> sourcePaths,
+ @NonNull List<String> classpath,
+ @NonNull String encoding,
+ long languageLevel)
+ throws IOException {
+ List<ICompilationUnit> sourceUnits = Lists.newArrayListWithExpectedSize(100);
+
+ for (File source : gatherJavaSources(sourcePaths)) {
+ char[] contents = Util.getFileCharContent(source, encoding);
+ ICompilationUnit unit = new CompilationUnit(contents, source.getPath(), encoding);
+ sourceUnits.add(unit);
+ }
+
+ Map<ICompilationUnit, CompilationUnitDeclaration> outputMap = Maps.newHashMapWithExpectedSize(
+ sourceUnits.size());
+
+ CompilerOptions options = EcjParser.createCompilerOptions();
+ options.docCommentSupport = true; // So I can find @hide
+
+ // Note: We can *not* set options.ignoreMethodBodies=true because it disables
+ // type attribution!
+
+ options.sourceLevel = languageLevel;
+ options.complianceLevel = options.sourceLevel;
+ // We don't generate code, but just in case the parser consults this flag
+ // and makes sure that it's not greater than the source level:
+ options.targetJDK = options.sourceLevel;
+ options.originalComplianceLevel = options.sourceLevel;
+ options.originalSourceLevel = options.sourceLevel;
+ options.inlineJsrBytecode = true; // >= 1.5
+
+ INameEnvironment environment = EcjParser.parse(options, sourceUnits, classpath,
+ outputMap, null);
+ Collection<CompilationUnitDeclaration> parsedUnits = outputMap.values();
+ return Pair.of(parsedUnits, environment);
+ }
+}
\ No newline at end of file
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java
index 3be4cab..8cc6042 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/Extractor.java
@@ -32,6 +32,7 @@
import static com.android.SdkConstants.TYPE_DEF_FLAG_ATTRIBUTE;
import static com.android.SdkConstants.TYPE_DEF_VALUE_ATTRIBUTE;
import static com.android.SdkConstants.VALUE_TRUE;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.INT_RANGE_ANNOTATION;
import static com.android.tools.lint.detector.api.LintUtils.assertionsEnabled;
import com.android.annotations.NonNull;
@@ -61,10 +62,21 @@
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
import org.eclipse.jdt.internal.compiler.ast.NumberLiteral;
+import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.impl.BooleanConstant;
+import org.eclipse.jdt.internal.compiler.impl.ByteConstant;
+import org.eclipse.jdt.internal.compiler.impl.CharConstant;
+import org.eclipse.jdt.internal.compiler.impl.Constant;
+import org.eclipse.jdt.internal.compiler.impl.DoubleConstant;
+import org.eclipse.jdt.internal.compiler.impl.FloatConstant;
+import org.eclipse.jdt.internal.compiler.impl.IntConstant;
+import org.eclipse.jdt.internal.compiler.impl.LongConstant;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
+import org.eclipse.jdt.internal.compiler.impl.ShortConstant;
+import org.eclipse.jdt.internal.compiler.impl.StringConstant;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
@@ -85,13 +97,17 @@
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.io.Writer;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
@@ -134,12 +150,12 @@
* - Ignore annotations defined on @hide elements
*/
public class Extractor {
- /** Whether to sort annotation attributes (otherwise their declaration order is used) */
- private static final boolean SORT_ANNOTATIONS = false;
-
/** Whether we should include type args like <T*gt; in external annotations signatures */
private static final boolean INCLUDE_TYPE_ARGS = false;
+ /** Whether to sort annotation attributes (otherwise their declaration order is used) */
+ private final boolean sortAnnotations;
+
/**
* Whether we should include class-retention annotations into the extracted file;
* we don't need {@code android.support.annotation.Nullable} to be in the extracted XML
@@ -159,11 +175,15 @@
public static final String ANDROID_ANNOTATIONS_PREFIX = "android.annotation.";
public static final String ANDROID_NULLABLE = "android.annotation.Nullable";
public static final String SUPPORT_NULLABLE = "android.support.annotation.Nullable";
+ public static final String SUPPORT_KEEP = "android.support.annotation.Keep";
public static final String RESOURCE_TYPE_ANNOTATIONS_SUFFIX = "Res";
public static final String ANDROID_NOTNULL = "android.annotation.NonNull";
public static final String SUPPORT_NOTNULL = "android.support.annotation.NonNull";
public static final String ANDROID_INT_DEF = "android.annotation.IntDef";
+ public static final String ANDROID_INT_RANGE = "android.annotation.IntRange";
public static final String ANDROID_STRING_DEF = "android.annotation.StringDef";
+ public static final String REQUIRES_PERMISSION = "android.support.annotation.RequiresPermission";
+ public static final String ANDROID_REQUIRES_PERMISSION = "android.annotation.RequiresPermission";
public static final String IDEA_NULLABLE = "org.jetbrains.annotations.Nullable";
public static final String IDEA_NOTNULL = "org.jetbrains.annotations.NotNull";
public static final String IDEA_MAGIC = "org.intellij.lang.annotations.MagicConstant";
@@ -172,7 +192,7 @@
public static final String ATTR_VAL = "val";
@NonNull
- private final Map<String, AnnotationData> types = Maps.newHashMap();
+ private final Map<String, List<AnnotationData>> types = Maps.newHashMap();
@NonNull
private final Set<String> irrelevantAnnotations = Sets.newHashSet();
@@ -180,37 +200,39 @@
private final File classDir;
@NonNull
- private Map<String, Map<String, List<Item>>> itemMap = Maps.newHashMap();
+ private final Map<String, Map<String, List<Item>>> itemMap = Maps.newHashMap();
@Nullable
private final ApiDatabase apiFilter;
private final boolean displayInfo;
- private Map<String,Integer> stats = Maps.newHashMap();
+ private final Map<String,Integer> stats = Maps.newHashMap();
private int filteredCount;
private int mergedCount;
- private Set<CompilationUnitDeclaration> processedFiles = Sets.newHashSetWithExpectedSize(100);
- private Set<String> ignoredAnnotations = Sets.newHashSet();
+ private final Set<CompilationUnitDeclaration> processedFiles = Sets.newHashSetWithExpectedSize(100);
+ private final Set<String> ignoredAnnotations = Sets.newHashSet();
private boolean listIgnored;
- private Map<String,Annotation> typedefs;
- private List<File> classFiles;
+ private Map<String,List<Annotation>> typedefs;
+ private List<String> typedefClasses;
private Map<String,Boolean> sourceRetention;
+ private final List<Item> keepItems = Lists.newArrayList();
public Extractor(@Nullable ApiDatabase apiFilter, @Nullable File classDir, boolean displayInfo,
- boolean includeClassRetentionAnnotations) {
+ boolean includeClassRetentionAnnotations, boolean sortAnnotations) {
this.apiFilter = apiFilter;
this.listIgnored = apiFilter != null;
this.classDir = classDir;
this.displayInfo = displayInfo;
this.includeClassRetentionAnnotations = includeClassRetentionAnnotations;
+ this.sortAnnotations = sortAnnotations;
}
public void extractFromProjectSource(Collection<CompilationUnitDeclaration> units) {
TypedefCollector collector = new TypedefCollector(units, false /*requireHide*/,
true /*requireSourceRetention*/);
typedefs = collector.getTypedefs();
- classFiles = collector.getNonPublicTypedefClassFiles();
+ typedefClasses = collector.getNonPublicTypedefClasses();
for (CompilationUnitDeclaration unit : units) {
analyze(unit);
@@ -218,35 +240,40 @@
}
public void removeTypedefClasses() {
- if (classDir != null && classFiles != null && !classFiles.isEmpty()) {
- int count = 0;
- for (File file : classFiles) {
- if (!file.isAbsolute()) {
- file = new File(classDir, file.getPath());
- }
- if (file.exists()) {
- boolean deleted = file.delete();
- if (deleted) {
- count++;
- } else {
- warning("Could not delete typedef class " + file.getPath());
- }
- }
- }
- info("Deleted " + count + " typedef annotation classes");
+ if (classDir != null && typedefClasses != null && !typedefClasses.isEmpty()) {
+ boolean quiet = false;
+ boolean verbose = false;
+ boolean dryRun = false;
+ //noinspection ConstantConditions
+ TypedefRemover remover = new TypedefRemover(this, quiet, verbose, dryRun);
+ remover.remove(classDir, typedefClasses);
}
}
- public void export(@NonNull File output) {
- if (itemMap.isEmpty()) {
- if (output.exists()) {
- //noinspection ResultOfMethodCallIgnored
- output.delete();
+ public void export(@Nullable File annotationsZip, @Nullable File proguardCfg) {
+ if (proguardCfg != null) {
+ if (keepItems.isEmpty()) {
+ if (proguardCfg.exists()) {
+ //noinspection ResultOfMethodCallIgnored
+ proguardCfg.delete();
+ }
+ } else if (writeKeepRules(proguardCfg)) {
+ info("ProGuard keep rules written to " + proguardCfg);
}
- } else if (writeOutputFile(output)) {
- writeStats();
- info("Annotations written to " + output);
}
+
+ if (annotationsZip != null) {
+ if (itemMap.isEmpty()) {
+ if (annotationsZip.exists()) {
+ //noinspection ResultOfMethodCallIgnored
+ annotationsZip.delete();
+ }
+ } else if (writeExternalAnnotations(annotationsZip)) {
+ writeStats();
+ info("Annotations written to " + annotationsZip);
+ }
+ }
+
}
public void writeStats() {
@@ -486,36 +513,46 @@
if (annotations != null) {
for (Annotation annotation : annotations) {
if (isRelevantAnnotation(annotation)) {
- AnnotationData annotationData = createAnnotation(annotation);
- if (annotationData != null) {
- item.annotations.add(annotationData);
+ String fqn = getFqn(annotation);
+ if (SUPPORT_KEEP.equals(fqn)) {
+ // Put keep rules in a different place; we don't want to write
+ // these out into the external annotations database, they go
+ // into a special proguard file
+ keepItems.add(item);
+ } else {
+ addAnnotation(annotation, fqn, item.annotations);
}
}
}
}
}
- @Nullable
- private AnnotationData createAnnotation(@NonNull Annotation annotation) {
- String fqn = getFqn(annotation);
+ private void addAnnotation(@NonNull Annotation annotation, @Nullable String fqn,
+ @NonNull List<AnnotationData> list) {
if (fqn == null) {
- return null;
+ return;
}
+
if (fqn.equals(ANDROID_NULLABLE) || fqn.equals(SUPPORT_NULLABLE)) {
recordStats(fqn);
- return new AnnotationData(SUPPORT_NULLABLE);
+ list.add(new AnnotationData(SUPPORT_NULLABLE));
+ return;
}
if (fqn.equals(ANDROID_NOTNULL) || fqn.equals(SUPPORT_NOTNULL)) {
recordStats(fqn);
- return new AnnotationData(SUPPORT_NOTNULL);
+ list.add(new AnnotationData(SUPPORT_NOTNULL));
+ return;
}
if (fqn.startsWith(SUPPORT_ANNOTATIONS_PREFIX)
&& fqn.endsWith(RESOURCE_TYPE_ANNOTATIONS_SUFFIX)) {
recordStats(fqn);
- return new AnnotationData(fqn);
- } else if (fqn.startsWith(ANDROID_ANNOTATIONS_PREFIX)) {
+ list.add(new AnnotationData(fqn));
+ return;
+ }
+
+ if (fqn.startsWith(ANDROID_ANNOTATIONS_PREFIX)) {
// System annotations: translate to support library annotations
if (fqn.endsWith(RESOURCE_TYPE_ANNOTATIONS_SUFFIX)) {
// Translate e.g. android.annotation.DrawableRes to
@@ -524,10 +561,11 @@
fqn.substring(ANDROID_ANNOTATIONS_PREFIX.length());
if (!includeClassRetentionAnnotations
&& !hasSourceRetention(resAnnotation, null)) {
- return null;
+ return;
}
recordStats(resAnnotation);
- return new AnnotationData(resAnnotation);
+ list.add(new AnnotationData(resAnnotation));
+ return;
} else if (isRelevantFrameworkAnnotation(fqn)) {
// Translate other android.annotation annotations into corresponding
// support annotations
@@ -535,23 +573,25 @@
fqn.substring(ANDROID_ANNOTATIONS_PREFIX.length());
if (!includeClassRetentionAnnotations
&& !hasSourceRetention(supportAnnotation, null)) {
- return null;
+ return;
}
recordStats(supportAnnotation);
- return createData(supportAnnotation, annotation);
+ list.add(createData(supportAnnotation, annotation));
}
}
if (fqn.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
recordStats(fqn);
- return createData(fqn, annotation);
+ list.add(createData(fqn, annotation));
+ return;
}
if (isMagicConstant(fqn)) {
- return types.get(fqn);
+ List<AnnotationData> indirect = types.get(fqn);
+ if (indirect != null) {
+ list.addAll(indirect);
+ }
}
-
- return null;
}
private void recordStats(String fqn) {
@@ -582,14 +622,12 @@
return false;
}
if (fqn.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
- //noinspection PointlessBooleanExpression,ConstantConditions,RedundantIfStatement
- if (!includeClassRetentionAnnotations && !hasSourceRetention(fqn, annotation)) {
- return false;
+ if (fqn.equals(SUPPORT_KEEP)) {
+ return true; // even with class file retention we want to process these
}
- //noinspection RedundantIfStatement
- if (fqn.endsWith(".Keep")) {
- // TODO: Extract into a proguard file
+ //noinspection PointlessBooleanExpression,ConstantConditions,RedundantIfStatement
+ if (!includeClassRetentionAnnotations && !hasSourceRetention(fqn, annotation)) {
return false;
}
@@ -624,39 +662,88 @@
if (types.containsKey(typeName) ||
typeName.equals(INT_DEF_ANNOTATION) ||
typeName.equals(STRING_DEF_ANNOTATION) ||
+ typeName.equals(INT_RANGE_ANNOTATION) ||
+ typeName.equals(ANDROID_INT_RANGE) ||
typeName.equals(ANDROID_INT_DEF) ||
typeName.equals(ANDROID_STRING_DEF)) {
return true;
}
- Annotation typeDef = typedefs.get(typeName);
+ List<Annotation> typeDefs = typedefs.get(typeName);
// We only support a single level of IntDef type annotations, not arbitrary nesting
- if (typeDef != null) {
- String fqn = getFqn(typeDef);
- if (fqn != null &&
- (fqn.equals(INT_DEF_ANNOTATION) ||
- fqn.equals(STRING_DEF_ANNOTATION) ||
- fqn.equals(ANDROID_INT_DEF) ||
- fqn.equals(ANDROID_STRING_DEF))) {
- AnnotationData a = createAnnotation(typeDef);
- if (a != null) {
- types.put(typeName, a);
- return true;
+ if (typeDefs != null) {
+ boolean match = false;
+ for (Annotation typeDef : typeDefs) {
+ String fqn = getFqn(typeDef);
+ if (isNestedAnnotation(fqn)) {
+ List<AnnotationData> list = types.get(typeName);
+ if (list == null) {
+ list = new ArrayList<AnnotationData>(2);
+ types.put(typeName, list);
+ }
+ addAnnotation(typeDef, fqn, list);
+ match = true;
}
}
- }
+ return match;
+ }
irrelevantAnnotations.add(typeName);
return false;
}
- private boolean writeOutputFile(File dest) {
- try {
- FileOutputStream fileOutputStream = new FileOutputStream(dest);
- JarOutputStream zos = new JarOutputStream(fileOutputStream);
+ static boolean isNestedAnnotation(@Nullable String fqn) {
+ return (fqn != null &&
+ (fqn.equals(INT_DEF_ANNOTATION) ||
+ fqn.equals(STRING_DEF_ANNOTATION) ||
+ fqn.equals(REQUIRES_PERMISSION) ||
+ fqn.equals(ANDROID_REQUIRES_PERMISSION) ||
+ fqn.equals(INT_RANGE_ANNOTATION) ||
+ fqn.equals(ANDROID_INT_RANGE) ||
+ fqn.equals(ANDROID_INT_DEF) ||
+ fqn.equals(ANDROID_STRING_DEF)));
+ }
+
+ private boolean writeKeepRules(@NonNull File proguardCfg) {
+ if (!keepItems.isEmpty()) {
try {
+ Writer writer = new BufferedWriter(new FileWriter(proguardCfg));
+ try {
+ Collections.sort(keepItems);
+ for (Item item : keepItems) {
+ writer.write(item.getKeepRule());
+ writer.write('\n');
+ }
+ } finally {
+ writer.close();
+ }
+ } catch (IOException ioe) {
+ error(ioe.toString());
+ return true;
+ }
+
+ // Now that we've handled these items, remove them from the list
+ // such that we don't accidentally also emit them into the annotations.zip
+ // file, where they are not needed
+ for (Item item : keepItems) {
+ removeItem(item.getQualifiedClassName(), item);
+ }
+ } else if (proguardCfg.exists()) {
+ //noinspection ResultOfMethodCallIgnored
+ proguardCfg.delete();
+ }
+ return false;
+ }
+
+ private boolean writeExternalAnnotations(@NonNull File annotationsZip) {
+ try {
+ FileOutputStream fileOutputStream = new FileOutputStream(annotationsZip);
+ JarOutputStream zos = new JarOutputStream(fileOutputStream);
+
+ try {
+ // TODO: Extract to share with keep rules
List<String> sortedPackages = new ArrayList<String>(itemMap.keySet());
Collections.sort(sortedPackages);
for (String pkg : sortedPackages) {
@@ -741,15 +828,15 @@
items.add(item);
}
- private void removeItem(@NonNull String fqn, @NonNull Item item) {
- String pkg = getPackage(fqn);
+ private void removeItem(@NonNull String classFqn, @NonNull Item item) {
+ String pkg = getPackage(classFqn);
Map<String, List<Item>> classMap = itemMap.get(pkg);
if (classMap != null) {
- List<Item> items = classMap.get(fqn);
+ List<Item> items = classMap.get(classFqn);
if (items != null) {
items.remove(item);
if (items.isEmpty()) {
- classMap.remove(fqn);
+ classMap.remove(classFqn);
if (classMap.isEmpty()) {
itemMap.remove(pkg);
}
@@ -849,7 +936,12 @@
Document document = XmlUtils.parseDocument(xml, false);
mergeDocument(document);
} catch (Exception e) {
- warning("Failed to merge " + path + ": " + e.toString());
+ String message = "Failed to merge " + path + ": " + e.toString();
+ if (e instanceof SAXParseException) {
+ SAXParseException spe = (SAXParseException)e;
+ message = "Line " + spe.getLineNumber() + ":" + spe.getColumnNumber() + ": " + message;
+ }
+ error(message);
if (!(e instanceof IOException)) {
e.printStackTrace();
}
@@ -857,6 +949,7 @@
}
private void mergeDocument(@NonNull Document document) {
+ @SuppressWarnings("SpellCheckingInspection")
final Pattern XML_SIGNATURE = Pattern.compile(
// Class (FieldName | Type? Name(ArgList) Argnum?)
//"(\\S+) (\\S+|(.*)\\s+(\\S+)\\((.*)\\)( \\d+)?)");
@@ -933,7 +1026,7 @@
}
filteredCount++;
} else {
- FieldItem fieldItem = new FieldItem(containingClass, fieldName);
+ FieldItem fieldItem = new FieldItem(containingClass, ClassKind.CLASS, fieldName, null);
Item existing = findItem(containingClass, fieldItem);
if (existing != null) {
mergedCount += mergeAnnotations(item, existing);
@@ -961,7 +1054,7 @@
String argNum = matcher.group(7);
if (argNum != null) {
argNum = argNum.trim();
- ParameterItem parameterItem = new ParameterItem(containingClass, type,
+ ParameterItem parameterItem = new ParameterItem(containingClass, ClassKind.CLASS, type,
methodName, parameters, constructor, argNum);
Item existing = findItem(containingClass, parameterItem);
@@ -979,8 +1072,8 @@
mergedCount += addAnnotations(item, parameterItem);
}
} else {
- MethodItem methodItem = new MethodItem(containingClass, type, methodName,
- parameters, constructor);
+ MethodItem methodItem = new MethodItem(containingClass, ClassKind.CLASS, type,
+ methodName, parameters, constructor);
Item existing = findItem(containingClass, methodItem);
if (existing != null) {
mergedCount += mergeAnnotations(item, existing);
@@ -1236,8 +1329,9 @@
annotation = new AnnotationData(
valName.equals("stringValues") ? STRING_DEF_ANNOTATION : INT_DEF_ANNOTATION,
- TYPE_DEF_VALUE_ATTRIBUTE, value,
- flag ? TYPE_DEF_FLAG_ATTRIBUTE : null, flag ? VALUE_TRUE : null);
+ new String[] {
+ TYPE_DEF_VALUE_ATTRIBUTE, value,
+ flag ? TYPE_DEF_FLAG_ATTRIBUTE : null, flag ? VALUE_TRUE : null });
} else if (STRING_DEF_ANNOTATION.equals(name) || ANDROID_STRING_DEF.equals(name) ||
INT_DEF_ANNOTATION.equals(name) || ANDROID_INT_DEF.equals(name)) {
List<Element> children = getChildren(annotationElement);
@@ -1254,14 +1348,14 @@
boolean intDef = INT_DEF_ANNOTATION.equals(name) || ANDROID_INT_DEF.equals(name);
annotation = new AnnotationData(
intDef ? INT_DEF_ANNOTATION : STRING_DEF_ANNOTATION,
- TYPE_DEF_VALUE_ATTRIBUTE, value,
- flag ? TYPE_DEF_FLAG_ATTRIBUTE : null, flag ? VALUE_TRUE : null);
+ new String[] { TYPE_DEF_VALUE_ATTRIBUTE, value,
+ flag ? TYPE_DEF_FLAG_ATTRIBUTE : null, flag ? VALUE_TRUE : null});
} else if (IDEA_CONTRACT.equals(name)) {
List<Element> children = getChildren(annotationElement);
assert children.size() == 1 : children.size();
Element valueElement = children.get(0);
String value = valueElement.getAttribute(ATTR_VAL);
- annotation = new AnnotationData(name, TYPE_DEF_VALUE_ATTRIBUTE, value, null, null);
+ annotation = new AnnotationData(name, new String[] { TYPE_DEF_VALUE_ATTRIBUTE, value });
} else if (isNonNull(name)) {
annotation = new AnnotationData(SUPPORT_NOTNULL);
} else if (isNullable(name)) {
@@ -1271,7 +1365,17 @@
}
annotation = new AnnotationData(SUPPORT_NULLABLE);
} else {
- annotation = new AnnotationData(name, null, null);
+ List<Element> children = getChildren(annotationElement);
+ if (children.isEmpty()) {
+ return new AnnotationData(name);
+ }
+ List<String> attributeStrings = Lists.newArrayList();
+ for (Element valueElement : children) {
+ attributeStrings.add(valueElement.getAttribute(ATTR_NAME));
+ attributeStrings.add(valueElement.getAttribute(ATTR_VAL));
+ }
+ annotation = new AnnotationData(name, attributeStrings.toArray(
+ new String[attributeStrings.size()]));
}
return annotation;
}
@@ -1354,43 +1458,25 @@
public final String name;
@Nullable
- public final String attributeName1;
-
- @Nullable
- public final String attributeValue1;
-
- @Nullable
- public final String attributeName2;
-
- @Nullable
- public final String attributeValue2;
+ public String[] attributeStrings;
@Nullable
public MemberValuePair[] attributes;
private AnnotationData(@NonNull String name) {
- this(name, null, null, null, null);
+ this.name = name;
}
private AnnotationData(@NonNull String name, @Nullable MemberValuePair[] pairs) {
- this(name, null, null, null, null);
+ this(name);
attributes = pairs;
assert attributes == null || attributes.length > 0;
}
- private AnnotationData(@NonNull String name, @Nullable String attributeName,
- @Nullable String attributeValue) {
- this(name, attributeName, attributeValue, null, null);
- }
-
- private AnnotationData(@NonNull String name,
- @Nullable String attributeName1, @Nullable String attributeValue1,
- @Nullable String attributeName2, @Nullable String attributeValue2) {
- this.name = name;
- this.attributeName1 = attributeName1;
- this.attributeValue1 = attributeValue1;
- this.attributeName2 = attributeName2;
- this.attributeValue2 = attributeValue2;
+ private AnnotationData(@NonNull String name, @Nullable String[] attributeStrings) {
+ this(name);
+ this.attributeStrings = attributeStrings;
+ assert attributeStrings != null && attributeStrings.length > 0;
}
void write(PrintWriter writer) {
@@ -1401,7 +1487,7 @@
writer.print("\">");
writer.println();
//noinspection PointlessBooleanExpression,ConstantConditions
- if (attributes.length > 1 && SORT_ANNOTATIONS) {
+ if (attributes.length > 1 && sortAnnotations) {
// Ensure that the value attribute is written first
Arrays.sort(attributes, new Comparator<MemberValuePair>() {
private String getName(MemberValuePair pair) {
@@ -1429,6 +1515,25 @@
});
}
+ MemberValuePair[] attributes = this.attributes;
+
+ if (attributes.length == 1
+ && name.startsWith(REQUIRES_PERMISSION)
+ && name.length() > REQUIRES_PERMISSION.length()
+ && attributes[0].value instanceof SingleMemberAnnotation) {
+ // The external annotations format does not allow for nested/complex annotations.
+ // However, these special annotations (@RequiresPermission.Read,
+ // @RequiresPermission.Write, etc) are known to only be simple containers with a
+ // single permission child, so instead we "inline" the content:
+ // @Read(@RequiresPermission(allOf={P1,P2},conditional=true)
+ // =>
+ // @RequiresPermission.Read(allOf({P1,P2},conditional=true)
+ // That's setting attributes that don't actually exist on the container permission,
+ // but we'll counteract that on the read-annotations side.
+ SingleMemberAnnotation annotation = (SingleMemberAnnotation)attributes[0].value;
+ attributes = annotation.memberValuePairs();
+ }
+
for (MemberValuePair pair : attributes) {
writer.print(" <val name=\"");
if (pair.name != null) {
@@ -1442,19 +1547,19 @@
}
writer.println(" </annotation>");
- } else if (attributeValue1 != null) {
+ } else if (attributeStrings != null) {
writer.print("\">");
writer.println();
- writer.print(" <val name=\"");
- writer.print(attributeName1);
- writer.print("\" val=\"");
- writer.print(escapeXml(attributeValue1));
- writer.println("\" />");
- if (attributeValue2 != null) {
+ for (int i = 0; i < attributeStrings.length; i += 2) {
+ String name = attributeStrings[i];
+ String value = attributeStrings[i + 1];
+ if (name == null) {
+ continue;
+ }
writer.print(" <val name=\"");
- writer.print(attributeName2);
+ writer.print(name);
writer.print("\" val=\"");
- writer.print(escapeXml(attributeValue2));
+ writer.print(escapeXml(value));
writer.println("\" />");
}
writer.println(" </annotation>");
@@ -1519,6 +1624,39 @@
if (reference.binding != null) {
if (reference.binding instanceof FieldBinding) {
FieldBinding fb = (FieldBinding)reference.binding;
+ Constant constant = fb.constant();
+ if (constant != null && constant != Constant.NotAConstant &&
+ !(name.equals(INT_DEF_ANNOTATION)) &&
+ !(name.equals(STRING_DEF_ANNOTATION))) {
+ if (constant instanceof StringConstant) {
+ sb.append('"').append(constant.stringValue()).append('"');
+ return true;
+ } else if (constant instanceof IntConstant) {
+ sb.append(Integer.toString(constant.intValue()));
+ return true;
+ } else if (constant instanceof BooleanConstant) {
+ sb.append(Boolean.toString(constant.booleanValue()));
+ return true;
+ } else if (constant instanceof LongConstant) {
+ sb.append(Long.toString(constant.longValue()));
+ return true;
+ } else if (constant instanceof DoubleConstant) {
+ sb.append(Double.toString(constant.doubleValue()));
+ return true;
+ } else if (constant instanceof CharConstant) {
+ sb.append('\'').append(Character.toString(constant.charValue())).append('\'');
+ return true;
+ } else if (constant instanceof FloatConstant) {
+ sb.append(Float.toString(constant.floatValue()));
+ return true;
+ } else if (constant instanceof ShortConstant) {
+ sb.append(Short.toString(constant.shortValue()));
+ return true;
+ } else if (constant instanceof ByteConstant) {
+ sb.append(Byte.toString(constant.byteValue()));
+ return true;
+ }
+ }
if (fb.declaringClass != null) {
if (apiFilter != null &&
!apiFilter.hasField(
@@ -1588,16 +1726,62 @@
}
}
+ public enum ClassKind {
+ CLASS,
+ INTERFACE,
+ ENUM,
+ ANNOTATION;
+
+ @NonNull
+ public static ClassKind forType(@Nullable TypeDeclaration declaration) {
+ if (declaration == null) {
+ return CLASS;
+ }
+ switch (TypeDeclaration.kind(declaration.modifiers)) {
+ case TypeDeclaration.INTERFACE_DECL:
+ return INTERFACE;
+ case TypeDeclaration.ANNOTATION_TYPE_DECL:
+ return ANNOTATION;
+ case TypeDeclaration.ENUM_DECL:
+ return ENUM;
+ default:
+ return CLASS;
+ }
+ }
+
+ public String getKeepType() {
+ // See http://proguard.sourceforge.net/manual/usage.html#classspecification
+ switch (this) {
+ case INTERFACE:
+ return "interface";
+ case ENUM:
+ return "enum";
+
+ case ANNOTATION:
+ case CLASS:
+ default:
+ return "class";
+ }
+ }
+ }
+
/**
* An item in the XML file: this corresponds to a method, a field, or a method parameter, and
* has an associated set of annotations
*/
private abstract static class Item implements Comparable<Item> {
+ @NonNull public final String containingClass;
+ @NonNull public final ClassKind classKind;
+
+ public Item(@NonNull String containingClass, @NonNull ClassKind classKind) {
+ this.containingClass = containingClass;
+ this.classKind = classKind;
+ }
public final List<AnnotationData> annotations = Lists.newArrayList();
void write(PrintWriter writer) {
- if (!isValid() || annotations.isEmpty()) {
+ if (annotations.isEmpty()) {
return;
}
writer.print(" <item name=\"");
@@ -1611,10 +1795,9 @@
writer.println();
}
- abstract boolean isValid();
-
abstract boolean isFiltered(@NonNull ApiDatabase database);
+ @NonNull
abstract String getSignature();
@Override
@@ -1631,41 +1814,52 @@
return signature1.compareTo(signature2);
}
+
+ @NonNull
+ public abstract String getKeepRule();
+
+ @NonNull
+ public abstract String getQualifiedClassName();
}
private static class ClassItem extends Item {
-
- @NonNull
- public final String className;
-
- private ClassItem(@NonNull String containingClass) {
- this.className = containingClass;
+ private ClassItem(@NonNull String containingClass, @NonNull ClassKind classKind) {
+ super(containingClass, classKind);
}
@NonNull
- static ClassItem create(@NonNull String classFqn) {
+ static ClassItem create(@NonNull String classFqn, @NonNull ClassKind kind) {
classFqn = ApiDatabase.getRawClass(classFqn);
- return new ClassItem(classFqn);
- }
-
- @Override
- boolean isValid() {
- return true;
+ return new ClassItem(classFqn, kind);
}
@Override
boolean isFiltered(@NonNull ApiDatabase database) {
- return !database.hasClass(className);
+ return !database.hasClass(containingClass);
}
+ @NonNull
@Override
String getSignature() {
- return escapeXml(className);
+ return escapeXml(containingClass);
+ }
+
+ @NonNull
+ @Override
+ public String getKeepRule() {
+ // See http://proguard.sourceforge.net/manual/usage.html#classspecification
+ return "-keep " + classKind.getKeepType() + " " + containingClass + "\n";
+ }
+
+ @NonNull
+ @Override
+ public String getQualifiedClassName() {
+ return containingClass;
}
@Override
public String toString() {
- return "Class " + className;
+ return "Class " + containingClass;
}
@Override
@@ -1679,12 +1873,12 @@
ClassItem that = (ClassItem) o;
- return className.equals(that.className);
+ return containingClass.equals(that.containingClass);
}
@Override
public int hashCode() {
- return className.hashCode();
+ return containingClass.hashCode();
}
}
@@ -1693,23 +1887,30 @@
@NonNull
public final String fieldName;
- @NonNull
- public final String containingClass;
+ @Nullable
+ public final String fieldType;
- private FieldItem(@NonNull String containingClass, @NonNull String fieldName) {
- this.containingClass = containingClass;
+ private FieldItem(@NonNull String containingClass, @NonNull ClassKind classKind,
+ @NonNull String fieldName, @Nullable String fieldType) {
+ super(containingClass, classKind);
this.fieldName = fieldName;
+ this.fieldType = fieldType;
}
@Nullable
- static FieldItem create(String classFqn, FieldBinding field) {
+ static FieldItem create(String classFqn, @NonNull ClassKind classKind, FieldBinding field) {
String name = new String(field.name);
- return classFqn != null ? new FieldItem(classFqn, name) : null;
+ String type = getFieldType(field);
+ return classFqn != null ? new FieldItem(classFqn, classKind, name, type) : null;
}
- @Override
- boolean isValid() {
- return true;
+ @Nullable
+ private static String getFieldType(FieldBinding binding) {
+ if (binding.type != null) {
+ return new String(binding.type.readableName());
+ }
+
+ return null;
}
@Override
@@ -1717,11 +1918,29 @@
return !database.hasField(containingClass, fieldName);
}
+ @NonNull
@Override
String getSignature() {
return escapeXml(containingClass) + ' ' + fieldName;
}
+ @NonNull
+ @Override
+ public String getKeepRule() {
+ if (fieldType == null) {
+ return ""; // imported item; these can't have keep rules
+ }
+ // See http://proguard.sourceforge.net/manual/usage.html#classspecification
+ return "-keep " + classKind.getKeepType() + " " + containingClass +
+ " {\n " + fieldType + " " + fieldName + "\n}\n";
+ }
+
+ @NonNull
+ @Override
+ public String getQualifiedClassName() {
+ return containingClass;
+ }
+
@Override
public String toString() {
return "Field " + containingClass + "#" + fieldName;
@@ -1756,9 +1975,6 @@
public final String methodName;
@NonNull
- public final String containingClass;
-
- @NonNull
public final String parameterList;
@Nullable
@@ -1766,9 +1982,14 @@
public final boolean isConstructor;
- private MethodItem(@NonNull String containingClass, @Nullable String returnType,
- @NonNull String methodName, @NonNull String parameterList, boolean isConstructor) {
- this.containingClass = containingClass;
+ private MethodItem(
+ @NonNull String containingClass,
+ @NonNull ClassKind classKind,
+ @Nullable String returnType,
+ @NonNull String methodName,
+ @NonNull String parameterList,
+ boolean isConstructor) {
+ super(containingClass, classKind);
this.returnType = returnType;
this.methodName = methodName;
this.parameterList = parameterList;
@@ -1782,6 +2003,7 @@
@Nullable
static MethodItem create(@Nullable String classFqn,
+ @NonNull ClassKind classKind,
@NonNull AbstractMethodDeclaration declaration,
@Nullable MethodBinding binding) {
if (classFqn == null || binding == null) {
@@ -1801,16 +2023,12 @@
classFqn = ApiDatabase.getRawClass(classFqn);
methodName = ApiDatabase.getRawMethod(methodName);
}
- return new MethodItem(classFqn, returnType,
+ return new MethodItem(classFqn, classKind, returnType,
methodName, parameterList,
binding.isConstructor());
}
- @Override
- boolean isValid() {
- return true;
- }
-
+ @NonNull
@Override
String getSignature() {
StringBuilder sb = new StringBuilder(100);
@@ -1893,6 +2111,38 @@
public String toString() {
return "Method " + containingClass + "#" + methodName;
}
+
+ @NonNull
+ @Override
+ public String getKeepRule() {
+ // See http://proguard.sourceforge.net/manual/usage.html#classspecification
+ StringBuilder sb = new StringBuilder();
+ sb.append("-keep ");
+ sb.append(classKind.getKeepType());
+ sb.append(" ");
+ sb.append(containingClass);
+ sb.append(" {\n");
+ sb.append(" ");
+ if (isConstructor) {
+ sb.append("<init>");
+ } else {
+ sb.append(returnType);
+ sb.append(" ");
+ sb.append(methodName);
+ }
+ sb.append("(");
+ sb.append(parameterList); // TODO: Strip generics?
+ sb.append(")\n");
+ sb.append("}\n");
+
+ return sb.toString();
+ }
+
+ @NonNull
+ @Override
+ public String getQualifiedClassName() {
+ return containingClass;
+ }
}
@Nullable
@@ -1948,18 +2198,27 @@
private static class ParameterItem extends MethodItem {
@NonNull
- public String argIndex;
+ public final String argIndex;
- private ParameterItem(@NonNull String containingClass, @Nullable String returnType,
- @NonNull String methodName, @NonNull String parameterList, boolean isConstructor,
+ private ParameterItem(
+ @NonNull String containingClass,
+ @NonNull ClassKind classKind,
+ @Nullable String returnType,
+ @NonNull String methodName,
+ @NonNull String parameterList,
+ boolean isConstructor,
@NonNull String argIndex) {
- super(containingClass, returnType, methodName, parameterList, isConstructor);
+ super(containingClass, classKind, returnType, methodName, parameterList, isConstructor);
this.argIndex = argIndex;
}
@Nullable
- static ParameterItem create(AbstractMethodDeclaration methodDeclaration, Argument argument,
- String classFqn, MethodBinding methodBinding,
+ static ParameterItem create(
+ AbstractMethodDeclaration methodDeclaration,
+ Argument argument,
+ String classFqn,
+ ClassKind classKind,
+ MethodBinding methodBinding,
LocalVariableBinding parameterBinding) {
if (classFqn == null || methodBinding == null || parameterBinding == null) {
return null;
@@ -1996,11 +2255,12 @@
classFqn = ApiDatabase.getRawClass(classFqn);
methodName = ApiDatabase.getRawMethod(methodName);
}
- return new ParameterItem(classFqn, returnType, methodName, parameterList,
+ return new ParameterItem(classFqn, classKind, returnType, methodName, parameterList,
methodBinding.isConstructor(), argNum);
}
+ @NonNull
@Override
String getSignature() {
return super.getSignature() + ' ' + argIndex;
@@ -2035,9 +2295,15 @@
public String toString() {
return "Parameter #" + argIndex + " in " + super.toString();
}
+
+ @NonNull
+ @Override
+ public String getKeepRule() {
+ return "";
+ }
}
- class AnnotationVisitor extends ASTVisitor {
+ private class AnnotationVisitor extends ASTVisitor {
@Override
public boolean visit(Argument argument, BlockScope scope) {
Annotation[] annotations = argument.annotations;
@@ -2045,9 +2311,14 @@
ReferenceContext referenceContext = scope.referenceContext();
if (referenceContext instanceof AbstractMethodDeclaration) {
MethodBinding binding = ((AbstractMethodDeclaration) referenceContext).binding;
- String fqn = getFqn(scope);
+ ClassScope classScope = findClassScope(scope);
+ if (classScope == null) {
+ return false;
+ }
+ String fqn = getFqn(classScope);
+ ClassKind kind = ClassKind.forType(classScope.referenceContext);
Item item = ParameterItem.create(
- (AbstractMethodDeclaration) referenceContext, argument, fqn,
+ (AbstractMethodDeclaration) referenceContext, argument, fqn, kind,
binding, argument.binding);
if (item != null) {
addItem(fqn, item);
@@ -2068,7 +2339,8 @@
}
String fqn = getFqn(scope);
- Item item = MethodItem.create(fqn, constructorDeclaration, constructorBinding);
+ ClassKind kind = ClassKind.forType(scope.referenceContext);
+ Item item = MethodItem.create(fqn, kind, constructorDeclaration, constructorBinding);
if (item != null) {
addItem(fqn, item);
addAnnotations(annotations, item);
@@ -2094,8 +2366,11 @@
}
String fqn = getFqn(scope);
- Item item = FieldItem.create(fqn, fieldBinding);
- if (item != null) {
+ ClassKind kind = scope.referenceContext instanceof TypeDeclaration ?
+ ClassKind.forType((TypeDeclaration)scope.referenceContext) :
+ ClassKind.CLASS;
+ Item item = FieldItem.create(fqn, kind, fieldBinding);
+ if (item != null && fqn != null) {
addItem(fqn, item);
addAnnotations(annotations, item);
}
@@ -2111,9 +2386,9 @@
if (methodBinding == null) {
return false;
}
-
String fqn = getFqn(scope);
- MethodItem item = MethodItem.create(fqn, methodDeclaration,
+ ClassKind kind = ClassKind.forType(scope.referenceContext);
+ MethodItem item = MethodItem.create(fqn, kind, methodDeclaration,
methodDeclaration.binding);
if (item != null) {
addItem(fqn, item);
@@ -2161,7 +2436,7 @@
if (fqn == null) {
fqn = new String(localTypeDeclaration.binding.readableName());
}
- Item item = ClassItem.create(fqn);
+ Item item = ClassItem.create(fqn, ClassKind.forType(localTypeDeclaration));
addItem(fqn, item);
addAnnotations(annotations, item);
@@ -2174,7 +2449,7 @@
Annotation[] annotations = memberTypeDeclaration.annotations;
if (hasRelevantAnnotations(annotations)) {
SourceTypeBinding binding = memberTypeDeclaration.binding;
- if (binding == null || !(binding instanceof MemberTypeBinding)) {
+ if (!(binding instanceof MemberTypeBinding)) {
return true;
}
if (binding.isAnnotationType() || binding.isAnonymousType()) {
@@ -2182,7 +2457,7 @@
}
String fqn = new String(memberTypeDeclaration.binding.readableName());
- Item item = ClassItem.create(fqn);
+ Item item = ClassItem.create(fqn, ClassKind.forType(memberTypeDeclaration));
addItem(fqn, item);
addAnnotations(annotations, item);
}
@@ -2198,7 +2473,7 @@
return true;
}
String fqn = new String(typeDeclaration.binding.readableName());
- Item item = ClassItem.create(fqn);
+ Item item = ClassItem.create(fqn, ClassKind.forType(typeDeclaration));
addItem(fqn, item);
addAnnotations(annotations, item);
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java
index 51e519c..5924f4c 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefCollector.java
@@ -16,10 +16,6 @@
package com.android.build.gradle.tasks.annotations;
-import static com.android.SdkConstants.DOT_CLASS;
-import static com.android.SdkConstants.INT_DEF_ANNOTATION;
-import static com.android.SdkConstants.STRING_DEF_ANNOTATION;
-
import com.android.annotations.NonNull;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -35,18 +31,19 @@
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import java.io.File;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/** Gathers information about typedefs (@IntDef and @StringDef */
public class TypedefCollector extends ASTVisitor {
- private Map<String,Annotation> mMap = Maps.newHashMap();
+ private Map<String,List<Annotation>> mMap = Maps.newHashMap();
- private boolean mRequireHide;
- private boolean mRequireSourceRetention;
+ private final boolean mRequireHide;
+ private final boolean mRequireSourceRetention;
private CompilationUnitDeclaration mCurrentUnit;
- private List<File> mClassFiles = Lists.newArrayList();
+ private List<String> mTypedefClasses = Lists.newArrayList();
public TypedefCollector(
@NonNull Collection<CompilationUnitDeclaration> units,
@@ -62,11 +59,11 @@
}
}
- public List<File> getNonPublicTypedefClassFiles() {
- return mClassFiles;
+ public List<String> getNonPublicTypedefClasses() {
+ return mTypedefClasses;
}
- public Map<String,Annotation> getTypedefs() {
+ public Map<String,List<Annotation>> getTypedefs() {
return mMap;
}
@@ -95,12 +92,16 @@
continue;
}
- if (typeName.equals(INT_DEF_ANNOTATION) ||
- typeName.equals(STRING_DEF_ANNOTATION) ||
- typeName.equals(Extractor.ANDROID_INT_DEF) ||
- typeName.equals(Extractor.ANDROID_STRING_DEF)) {
+ if (Extractor.isNestedAnnotation(typeName)) {
String fqn = new String(binding.readableName());
- mMap.put(fqn, annotation);
+
+ List<Annotation> list = mMap.get(fqn);
+ if (list == null) {
+ list = new ArrayList<Annotation>(2);
+ mMap.put(fqn, list);
+ }
+ list.add(annotation);
+
if (mRequireHide) {
Javadoc javadoc = declaration.javadoc;
if (javadoc != null) {
@@ -125,7 +126,7 @@
StringBuilder sb = new StringBuilder(100);
for (char c : declaration.binding.qualifiedPackageName()) {
if (c == '.') {
- sb.append(File.separatorChar);
+ sb.append('/');
} else {
sb.append(c);
}
@@ -138,9 +139,7 @@
sb.append(c);
}
}
- sb.append(DOT_CLASS);
- File file = new File(sb.toString());
- mClassFiles.add(file);
+ mTypedefClasses.add(sb.toString());
}
}
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefRemover.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefRemover.java
new file mode 100644
index 0000000..758f125
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/annotations/TypedefRemover.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.tasks.annotations;
+
+import static com.android.SdkConstants.DOT_CLASS;
+import static org.objectweb.asm.Opcodes.ASM5;
+
+import com.android.annotations.NonNull;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Finds and deletes typedef annotation classes (and also warns if their
+ * retention is wrong, such that usages of the annotation embeds data
+ * into the .class file.)
+ * <p>
+ * (Based on the similar class in {@code development/tools/rmtypedefs/})
+ */
+@SuppressWarnings("SpellCheckingInspection")
+public class TypedefRemover {
+ private final Extractor mExtractor;
+ private final boolean mQuiet;
+ private final boolean mVerbose;
+ private final boolean mDryRun;
+
+ public TypedefRemover(
+ @NonNull Extractor extractor,
+ boolean quiet,
+ boolean verbose,
+ boolean dryRun) {
+ mExtractor = extractor;
+ mQuiet = quiet;
+ mVerbose = verbose;
+ mDryRun = dryRun;
+ }
+
+ private Set<String> mAnnotationNames = Sets.newHashSet();
+ private List<File> mAnnotationClassFiles = Lists.newArrayList();
+ private Set<File> mAnnotationOuterClassFiles = Sets.newHashSet();
+
+ public void remove(@NonNull File classDir, @NonNull List<String> owners) {
+ if (!mQuiet) {
+ mExtractor.info("Deleting @IntDef and @StringDef annotation class files");
+ }
+
+ // Record typedef annotation names and files
+ for (String owner : owners) {
+ File file = new File(classDir, owner.replace('/', File.separatorChar) + DOT_CLASS);
+ addTypeDef(owner, file);
+ }
+
+ // Rewrite the .class files for any classes that *contain* typedefs as innerclasses
+ rewriteOuterClasses();
+
+ // Removes the actual .class files for the typedef annotations
+ deleteAnnotationClasses();
+ }
+
+ /**
+ * Records the given class name (internal name) and class file path as corresponding to a
+ * typedef annotation
+ * */
+ private void addTypeDef(String name, File file) {
+ mAnnotationClassFiles.add(file);
+ mAnnotationNames.add(name);
+
+ String fileName = file.getName();
+ int index = fileName.lastIndexOf('$');
+ if (index != -1) {
+ File parentFile = file.getParentFile();
+ assert parentFile != null : file;
+ File container = new File(parentFile, fileName.substring(0, index) + ".class");
+ if (container.exists()) {
+ mAnnotationOuterClassFiles.add(container);
+ } else {
+ Extractor.error("Warning: Could not find outer class " + container
+ + " for typedef " + file);
+ }
+ }
+ }
+
+ /**
+ * Rewrites the outer classes containing the typedefs such that they no longer refer to
+ * the (now removed) typedef annotation inner classes
+ */
+ private void rewriteOuterClasses() {
+ for (File file : mAnnotationOuterClassFiles) {
+ byte[] bytes;
+ try {
+ bytes = Files.toByteArray(file);
+ } catch (IOException e) {
+ Extractor.error("Could not read " + file + ": " + e.getLocalizedMessage());
+ continue;
+ }
+
+ ClassWriter classWriter = new ClassWriter(ASM5);
+ ClassVisitor classVisitor = new ClassVisitor(ASM5, classWriter) {
+ @Override
+ public void visitInnerClass(String name, String outerName, String innerName,
+ int access) {
+ if (!mAnnotationNames.contains(name)) {
+ super.visitInnerClass(name, outerName, innerName, access);
+ }
+ }
+ };
+ ClassReader reader = new ClassReader(bytes);
+ reader.accept(classVisitor, 0);
+ byte[] rewritten = classWriter.toByteArray();
+ try {
+ Files.write(rewritten, file);
+ } catch (IOException e) {
+ Extractor.error("Could not write " + file + ": " + e.getLocalizedMessage());
+ //noinspection UnnecessaryContinue
+ continue;
+ }
+ }
+ }
+
+ /**
+ * Performs the actual deletion (or display, if in dry-run mode) of the typedef annotation
+ * files
+ */
+ private void deleteAnnotationClasses() {
+ for (File mFile : mAnnotationClassFiles) {
+ if (mVerbose) {
+ if (mDryRun) {
+ mExtractor.info("Would delete " + mFile);
+ } else {
+ mExtractor.info("Deleting " + mFile);
+ }
+ }
+ if (!mDryRun) {
+ boolean deleted = mFile.delete();
+ if (!deleted) {
+ Extractor.warning("Could not delete " + mFile);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/AbstractCompilesUtil.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/AbstractCompilesUtil.java
new file mode 100644
index 0000000..1009f97
--- /dev/null
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/AbstractCompilesUtil.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.tasks.factory;
+
+import com.android.build.gradle.internal.CompileOptions;
+import com.android.build.gradle.internal.scope.ConventionMappingHelper;
+import com.android.sdklib.AndroidTargetHash;
+import com.android.sdklib.AndroidVersion;
+
+import org.gradle.api.JavaVersion;
+import org.gradle.api.tasks.compile.AbstractCompile;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Common code for configuring {@link AbstractCompile} instances.
+ */
+public class AbstractCompilesUtil {
+
+ /**
+ * Determines the java language level to use and sets it on the given task and
+ * {@link CompileOptions}. The latter is to propagate the information to Studio.
+ */
+ public static void configureLanguageLevel(
+ AbstractCompile compileTask,
+ final CompileOptions compileOptions,
+ String compileSdkVersion) {
+ final AndroidVersion hash = AndroidTargetHash.getVersionFromHash(compileSdkVersion);
+ Integer compileSdkLevel = (hash == null ? null : hash.getApiLevel());
+
+ JavaVersion javaVersionToUse;
+ if (compileSdkLevel == null || (0 <= compileSdkLevel && compileSdkLevel <= 20)) {
+ javaVersionToUse = JavaVersion.VERSION_1_6;
+ } else {
+ javaVersionToUse = JavaVersion.VERSION_1_7;
+ }
+
+ JavaVersion jdkVersion =
+ JavaVersion.toVersion(System.getProperty("java.specification.version"));
+ if (jdkVersion.compareTo(javaVersionToUse) < 0) {
+ compileTask.getLogger().warn(
+ "Default language level for compileSdkVersion '{}' is " +
+ "{}, but the JDK used is {}, so the JDK language level will be used.",
+ compileSdkVersion,
+ javaVersionToUse,
+ jdkVersion);
+ javaVersionToUse = jdkVersion;
+ }
+
+ compileOptions.setDefaultJavaVersion(javaVersionToUse);
+
+ compileTask.setSourceCompatibility(compileOptions.getSourceCompatibility().toString());
+ compileTask.setTargetCompatibility(compileOptions.getTargetCompatibility().toString());
+ }
+}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/JavaCompileConfigAction.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/JavaCompileConfigAction.java
index 502937b..69c93ca 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/JavaCompileConfigAction.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/JavaCompileConfigAction.java
@@ -2,27 +2,21 @@
import static com.android.builder.core.VariantType.LIBRARY;
import static com.android.builder.core.VariantType.UNIT_TEST;
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
import com.android.build.gradle.internal.CompileOptions;
import com.android.build.gradle.internal.scope.ConventionMappingHelper;
import com.android.build.gradle.internal.scope.TaskConfigAction;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.variant.BaseVariantData;
-import com.android.sdklib.AndroidTargetHash;
-import com.android.sdklib.AndroidVersion;
+import com.android.builder.dependency.LibraryDependency;
+import com.google.common.base.Joiner;
-import org.codehaus.groovy.runtime.DefaultGroovyMethods;
-import org.gradle.api.Action;
-import org.gradle.api.JavaVersion;
-import org.gradle.api.Task;
+import org.gradle.api.Project;
import org.gradle.api.file.FileCollection;
-import org.gradle.api.tasks.compile.AbstractCompile;
import org.gradle.api.tasks.compile.JavaCompile;
import java.io.File;
-
-import groovy.lang.Closure;
+import java.util.concurrent.Callable;
/**
* Configuration Action for a JavaCompile task.
@@ -37,7 +31,7 @@
@Override
public String getName() {
- return scope.getTaskName("compile", "Java");
+ return scope.getTaskName("compile", "JavaWithJavac");
}
@Override
@@ -46,146 +40,64 @@
}
@Override
- public void execute(final JavaCompile javaCompileTask) {
+ public void execute(final JavaCompile javacTask) {
final BaseVariantData testedVariantData = scope.getTestedVariantData();
- scope.getVariantData().javaCompileTask = javaCompileTask;
+ scope.getVariantData().javacTask = javacTask;
- javaCompileTask.setSource(scope.getVariantData().getJavaSources());
+ javacTask.setSource(scope.getVariantData().getJavaSources());
- ConventionMappingHelper
- .map(javaCompileTask, "classpath", new Closure<FileCollection>(this, this) {
- public FileCollection doCall(Object it) {
- FileCollection classpath = scope.getJavaClasspath();
+ ConventionMappingHelper.map(javacTask, "classpath", new Callable<FileCollection>() {
+ @Override
+ public FileCollection call() {
+ FileCollection classpath = scope.getJavaClasspath();
+ Project project = scope.getGlobalScope().getProject();
- if (testedVariantData != null) {
- // For libraries, the classpath from androidBuilder includes the library output
- // (bundle/classes.jar) as a normal dependency. In unit tests we don't want to package
- // the jar at every run, so we use the *.class files instead.
- if (!testedVariantData.getType().equals(LIBRARY) || scope
- .getVariantData().getType().equals(UNIT_TEST)) {
- classpath = classpath
- .plus(testedVariantData.getScope().getJavaClasspath())
- .plus(scope.getGlobalScope().getProject().files(
- testedVariantData.getScope().getJavaOutputDir(),
- testedVariantData.getScope().getJavaDependencyCache()));
- }
+ if (testedVariantData != null) {
+ // For libraries, the classpath from androidBuilder includes the library
+ // output (bundle/classes.jar) as a normal dependency. In unit tests we
+ // don't want to package the jar at every run, so we use the *.class
+ // files instead.
+ if (!testedVariantData.getType().equals(LIBRARY)
+ || scope.getVariantData().getType().equals(UNIT_TEST)) {
+ classpath = classpath.plus(project.files(
+ testedVariantData.getScope().getJavaClasspath(),
+ testedVariantData.getScope().getJavaOutputDir(),
+ testedVariantData.getScope().getJavaDependencyCache()));
+ }
- if (scope.getVariantData().getType().equals(UNIT_TEST)
- && testedVariantData.getType().equals(LIBRARY)) {
- // The bundled classes.jar may exist, but it's probably old. Don't use it, we
- // already have the *.class files in the classpath.
- classpath = classpath.minus(scope.getGlobalScope().getProject()
- .files(testedVariantData.getVariantConfiguration()
- .getOutput().getJarFile()));
- }
-
+ if (scope.getVariantData().getType().equals(UNIT_TEST)
+ && testedVariantData.getType().equals(LIBRARY)) {
+ // The bundled classes.jar may exist, but it's probably old. Don't
+ // use it, we already have the *.class files in the classpath.
+ LibraryDependency libraryDependency =
+ testedVariantData.getVariantConfiguration().getOutput();
+ if (libraryDependency != null) {
+ File jarFile = libraryDependency.getJarFile();
+ classpath = classpath.minus(project.files(jarFile));
}
-
- return classpath;
}
+ }
- public FileCollection doCall() {
- return doCall(null);
- }
-
- });
-
- ConventionMappingHelper
- .map(javaCompileTask, "destinationDir", new Closure<File>(this, this) {
- public File doCall(Object it) {
- return scope.getJavaOutputDir();
- }
-
- public File doCall() {
- return doCall(null);
- }
-
- });
- ConventionMappingHelper
- .map(javaCompileTask, "dependencyCacheDir", new Closure<File>(this, this) {
- public File doCall(Object it) {
- return scope.getJavaDependencyCache();
- }
-
- public File doCall() {
- return doCall(null);
- }
-
- });
-
- configureLanguageLevel(javaCompileTask);
- javaCompileTask.getOptions().setEncoding(
- scope.getGlobalScope().getExtension().getCompileOptions().getEncoding());
-
- // setup the boot classpath just before the task actually runs since this will
- // force the sdk to be parsed.
- javaCompileTask.doFirst(new Closure<String>(this, this) {
- public String doCall(Task it) {
- return setBootClasspath(javaCompileTask.getOptions(), DefaultGroovyMethods
- .join(scope.getGlobalScope().getAndroidBuilder()
- .getBootClasspathAsStrings(), File.pathSeparator));
+ return classpath;
}
-
- public String doCall() {
- return doCall(null);
- }
-
});
- }
- private void configureLanguageLevel(AbstractCompile compileTask) {
- final CompileOptions compileOptions = scope.getGlobalScope().getExtension()
- .getCompileOptions();
- JavaVersion javaVersionToUse;
+ javacTask.setDestinationDir(scope.getJavaOutputDir());
- final AndroidVersion hash = AndroidTargetHash
- .getVersionFromHash(scope.getGlobalScope().getExtension().getCompileSdkVersion());
- Integer compileSdkLevel = (hash == null ? null : hash.getApiLevel());
- if (compileSdkLevel == null || (0 <= compileSdkLevel && compileSdkLevel <= 20)) {
- javaVersionToUse = JavaVersion.VERSION_1_6;
- } else {
- javaVersionToUse = JavaVersion.VERSION_1_7;
- }
+ javacTask.setDependencyCacheDir(scope.getJavaDependencyCache());
- JavaVersion jdkVersion = JavaVersion
- .toVersion(System.getProperty("java.specification.version"));
- if (jdkVersion.compareTo(javaVersionToUse) < 0) {
-// logger.info(
-// "Default language level for 'compileSdkVersion $compileSdkLevel' is " +
-// "$javaVersionToUse, but the JDK used is $jdkVersion, so the JDK " +
-// "language level will be used.")
- javaVersionToUse = jdkVersion;
- }
+ CompileOptions compileOptions = scope.getGlobalScope().getExtension().getCompileOptions();
- compileOptions.setDefaultJavaVersion(javaVersionToUse);
+ AbstractCompilesUtil.configureLanguageLevel(
+ javacTask,
+ compileOptions,
+ scope.getGlobalScope().getExtension().getCompileSdkVersion()
+ );
- ConventionMappingHelper
- .map(compileTask, "sourceCompatibility", new Closure<String>(this, this) {
- public String doCall(Object it) {
- return compileOptions.getSourceCompatibility().toString();
- }
+ javacTask.getOptions().setEncoding(compileOptions.getEncoding());
- public String doCall() {
- return doCall(null);
- }
-
- });
- ConventionMappingHelper
- .map(compileTask, "targetCompatibility", new Closure<String>(this, this) {
- public String doCall(Object it) {
- return compileOptions.getTargetCompatibility().toString();
- }
-
- public String doCall() {
- return doCall(null);
- }
-
- });
- }
-
- private static <Value extends String> Value setBootClasspath(
- org.gradle.api.tasks.compile.CompileOptions propOwner, Value bootClasspath) {
- propOwner.setBootClasspath(bootClasspath);
- return bootClasspath;
+ javacTask.getOptions().setBootClasspath(
+ Joiner.on(File.pathSeparator).join(
+ scope.getGlobalScope().getAndroidBuilder().getBootClasspathAsStrings()));
}
}
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProGuardTaskConfigAction.java b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProGuardTaskConfigAction.java
index bb366d8..10a13b9 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProGuardTaskConfigAction.java
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProGuardTaskConfigAction.java
@@ -2,19 +2,16 @@
import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
-import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.PostCompilationData;
import com.android.build.gradle.internal.scope.TaskConfigAction;
import com.android.build.gradle.internal.scope.VariantScope;
-import com.android.utils.StringHelper;
import com.google.common.base.Preconditions;
-import org.codehaus.groovy.runtime.StringGroovyMethods;
-
import java.io.File;
import java.io.IOException;
import java.util.List;
+import java.util.concurrent.Callable;
-import groovy.lang.Closure;
import proguard.ParseException;
import proguard.gradle.ProGuardTask;
@@ -25,11 +22,11 @@
private VariantScope scope;
- private Closure<List<File>> inputFiles;
+ private Callable<List<File>> inputFiles;
- public ProGuardTaskConfigAction(VariantScope scope, TaskManager.PostCompilationData pcData) {
+ public ProGuardTaskConfigAction(VariantScope scope, PostCompilationData pcData) {
this.scope = scope;
- this.inputFiles = pcData.getInputFiles();
+ this.inputFiles = pcData.getInputFilesCallable();
}
@Override
@@ -53,8 +50,9 @@
try {
proguardComponentsTask.configuration(scope.getManifestKeepListFile());
- proguardComponentsTask.libraryjars(new Closure<File>(this, this) {
- public File doCall(Object it) {
+ proguardComponentsTask.libraryjars(new Callable<File>() {
+ @Override
+ public File call() throws Exception {
Preconditions.checkNotNull(
scope.getGlobalScope().getAndroidBuilder().getTargetInfo());
File shrinkedAndroid = new File(
@@ -74,14 +72,14 @@
return shrinkedAndroid;
}
-
- public File doCall() {
- return doCall(null);
- }
-
});
- proguardComponentsTask.injars(inputFiles.call().iterator().next());
+ proguardComponentsTask.injars(new Callable<File>() {
+ @Override
+ public File call() throws Exception {
+ return inputFiles.call().iterator().next();
+ }
+ });
proguardComponentsTask.outjars(scope.getProguardComponentsJarFile());
diff --git a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProcessJavaResConfigAction.groovy b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProcessJavaResConfigAction.groovy
index d6cbc0f..a3d5ae2 100644
--- a/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProcessJavaResConfigAction.groovy
+++ b/build-system/gradle-core/src/main/groovy/com/android/build/gradle/tasks/factory/ProcessJavaResConfigAction.groovy
@@ -20,19 +20,15 @@
import com.android.build.gradle.internal.scope.ConventionMappingHelper
import com.android.build.gradle.internal.scope.TaskConfigAction
import com.android.build.gradle.internal.scope.VariantScope
-import com.android.builder.core.BuilderConstants
-import com.android.builder.core.VariantType
-import com.android.builder.model.AndroidProject
import com.android.builder.model.SourceProvider
-import org.gradle.api.tasks.Copy
+import org.gradle.api.tasks.Sync
import static com.android.builder.core.VariantType.ANDROID_TEST
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
/**
* Configuration Action for a ProcessJavaRes task.
*/
-class ProcessJavaResConfigAction implements TaskConfigAction<Copy> {
+class ProcessJavaResConfigAction implements TaskConfigAction<Sync> {
VariantScope scope;
@@ -46,12 +42,12 @@
}
@Override
- Class<Copy> getType() {
- return Copy.class
+ Class<Sync> getType() {
+ return Sync.class
}
@Override
- void execute(Copy processResources) {
+ void execute(Sync processResources) {
scope.variantData.processJavaResourcesTask = processResources
// set the input
@@ -71,7 +67,7 @@
}
ConventionMappingHelper.map(processResources, "destinationDir") {
- scope.getJavaResourcesDestinationDir()
+ new File(scope.getSourceFoldersJavaResDestinationDir(), "src");
}
}
diff --git a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/CompileOptionsTest.java b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/CompileOptionsTest.java
new file mode 100644
index 0000000..37e7a86
--- /dev/null
+++ b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/CompileOptionsTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal;
+
+import static org.junit.Assert.assertEquals;
+
+import org.gradle.api.JavaVersion;
+import org.junit.Test;
+
+/**
+ * Tests for {@link CompileOptions}
+ */
+public class CompileOptionsTest {
+
+ @Test
+ public void sourceCompatibilityTest() {
+ CompileOptions options = new CompileOptions();
+
+ assertEquals(options.getDefaultJavaVersion(), options.getSourceCompatibility());
+
+ options.setSourceCompatibility("1.6");
+ assertEquals(JavaVersion.VERSION_1_6, options.getSourceCompatibility());
+
+ options.setSourceCompatibility(1.6);
+ assertEquals(JavaVersion.VERSION_1_6, options.getSourceCompatibility());
+
+ options.setSourceCompatibility(JavaVersion.VERSION_1_7);
+ assertEquals(JavaVersion.VERSION_1_7, options.getSourceCompatibility());
+
+ options.setSourceCompatibility("Version_1_7");
+ assertEquals(JavaVersion.VERSION_1_7, options.getSourceCompatibility());
+
+ options.setSourceCompatibility("VERSION_1_7");
+ assertEquals(JavaVersion.VERSION_1_7, options.getSourceCompatibility());
+ }
+
+ @Test
+ public void targetCompatibilityTest() {
+ CompileOptions options = new CompileOptions();
+
+ assertEquals(options.getDefaultJavaVersion(), options.getTargetCompatibility());
+
+ options.setTargetCompatibility("1.6");
+ assertEquals(JavaVersion.VERSION_1_6, options.getTargetCompatibility());
+
+ options.setTargetCompatibility(1.6);
+ assertEquals(JavaVersion.VERSION_1_6, options.getTargetCompatibility());
+
+ options.setTargetCompatibility(JavaVersion.VERSION_1_7);
+ assertEquals(JavaVersion.VERSION_1_7, options.getTargetCompatibility());
+
+ options.setTargetCompatibility("Version_1_7");
+ assertEquals(JavaVersion.VERSION_1_7, options.getTargetCompatibility());
+
+ options.setTargetCompatibility("VERSION_1_7");
+ assertEquals(JavaVersion.VERSION_1_7, options.getTargetCompatibility());
+ }
+}
diff --git a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/ProductFlavorComboTest.java b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/ProductFlavorComboTest.java
index f125bce..297779d 100644
--- a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/ProductFlavorComboTest.java
+++ b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/ProductFlavorComboTest.java
@@ -20,81 +20,86 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
+import com.android.builder.model.DimensionAware;
import com.google.common.collect.ImmutableList;
-import org.gradle.api.Project;
-import org.gradle.api.logging.Logger;
-import org.gradle.api.logging.Logging;
-import org.gradle.internal.reflect.DirectInstantiator;
-import org.gradle.internal.reflect.Instantiator;
-import org.gradle.testfixtures.ProjectBuilder;
+import org.gradle.api.Named;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
import java.util.Collections;
import java.util.List;
public class ProductFlavorComboTest {
- private Instantiator instantiator = new DirectInstantiator();
- private Project project = ProjectBuilder.builder().build();
- private Logger logger = Logging.getLogger(this.getClass());
private static final String DIMENSION1 = "dimension1";
private static final String DIMENSION2 = "dimension2";
- private GroupableProductFlavor f1;
- private GroupableProductFlavor f2;
- private GroupableProductFlavor f1d1;
- private GroupableProductFlavor f2d1;
- private GroupableProductFlavor f1d2;
- private GroupableProductFlavor f2d2;
+ @Mock
+ private CoreProductFlavor f1;
+ @Mock
+ private CoreProductFlavor f2;
+ @Mock
+ private CoreProductFlavor f1d1;
+ @Mock
+ private CoreProductFlavor f2d1;
+ @Mock
+ private CoreProductFlavor f1d2;
+ @Mock
+ private CoreProductFlavor f2d2;
@Before
public void setup() {
- f1 = new GroupableProductFlavor("flavor1", project, instantiator, logger);
- f2 = new GroupableProductFlavor("flavor2", project, instantiator, logger);
+ MockitoAnnotations.initMocks(this);
+ Mockito.when(f1.getName()).thenReturn("flavor1");
+ Mockito.when(f1.getDimension()).thenReturn(null);
+ Mockito.when(f2.getName()).thenReturn("flavor2");
+ Mockito.when(f2.getDimension()).thenReturn(null);
- f1d1 = new GroupableProductFlavor("flavor1", project, instantiator, logger);
- f1d1.setFlavorDimension(DIMENSION1);
- f2d1 = new GroupableProductFlavor("flavor2", project, instantiator, logger);
- f2d1.setFlavorDimension(DIMENSION1);
+ Mockito.when(f1d1.getName()).thenReturn("flavor1");
+ Mockito.when(f1d1.getDimension()).thenReturn(DIMENSION1);
+ Mockito.when(f2d1.getName()).thenReturn("flavor2");
+ Mockito.when(f2d1.getDimension()).thenReturn(DIMENSION1);
- f1d2 = new GroupableProductFlavor("flavor1", project, instantiator, logger);
- f1d2.setFlavorDimension(DIMENSION2);
- f2d2 = new GroupableProductFlavor("flavor2", project, instantiator, logger);
- f2d2.setFlavorDimension(DIMENSION2);
+ Mockito.when(f1d2.getName()).thenReturn("flavor1");
+ Mockito.when(f1d2.getDimension()).thenReturn(DIMENSION2);
+ Mockito.when(f2d2.getName()).thenReturn("flavor2");
+ Mockito.when(f2d2.getDimension()).thenReturn(DIMENSION2);
}
@Test
public void getNameEmpty() {
- assertEquals("", new ProductFlavorCombo().getName());
+ assertEquals("", new ProductFlavorCombo<CoreProductFlavor>().getName());
}
@Test
public void getNameSingleFlavor() {
- assertEquals("flavor1", new ProductFlavorCombo(f1).getName());
+ assertEquals("flavor1", new ProductFlavorCombo<CoreProductFlavor>(f1).getName());
}
@Test
public void getNameMultiFlavor() {
- assertEquals("flavor1Flavor2", new ProductFlavorCombo(f1, f2).getName());
+ assertEquals("flavor1Flavor2", new ProductFlavorCombo<CoreProductFlavor>(f1, f2).getName());
}
@Test
public void createGroupListEmpty() throws Exception {
assertEqualGroupList(
- ImmutableList.<ProductFlavorCombo>of(),
+ ImmutableList.<ProductFlavorCombo<CoreProductFlavor>>of(),
ProductFlavorCombo.createCombinations(
Collections.<String>emptyList(),
- Collections.<GroupableProductFlavor>emptyList()));
+ Collections.<CoreProductFlavor>emptyList()));
}
@Test
public void createGroupListNullDimension() throws Exception {
assertEqualGroupList(
ImmutableList.of(
- new ProductFlavorCombo(f1)
+ new ProductFlavorCombo<CoreProductFlavor>(f1)
),
ProductFlavorCombo.createCombinations(
null,
@@ -105,8 +110,8 @@
public void createGroupListEmptyDimension() throws Exception {
assertEqualGroupList(
ImmutableList.of(
- new ProductFlavorCombo(f1),
- new ProductFlavorCombo(f2)
+ new ProductFlavorCombo<CoreProductFlavor>(f1),
+ new ProductFlavorCombo<CoreProductFlavor>(f2)
),
ProductFlavorCombo.createCombinations(
ImmutableList.<String>of(),
@@ -116,7 +121,7 @@
@Test
public void createGroupListSingleDimension() throws Exception {
assertEqualGroupList(
- ImmutableList.of(new ProductFlavorCombo(f1d1)),
+ ImmutableList.of(new ProductFlavorCombo<CoreProductFlavor>(f1d1)),
ProductFlavorCombo.createCombinations(
ImmutableList.of(DIMENSION1),
ImmutableList.of(f1d1)));
@@ -126,10 +131,10 @@
public void createGroupListMultiDimensions() throws Exception {
assertEqualGroupList(
ImmutableList.of(
- new ProductFlavorCombo(f1d1, f1d2),
- new ProductFlavorCombo(f1d1, f2d2),
- new ProductFlavorCombo(f2d1, f1d2),
- new ProductFlavorCombo(f2d1, f2d2)),
+ new ProductFlavorCombo<CoreProductFlavor>(f1d1, f1d2),
+ new ProductFlavorCombo<CoreProductFlavor>(f1d1, f2d2),
+ new ProductFlavorCombo<CoreProductFlavor>(f2d1, f1d2),
+ new ProductFlavorCombo<CoreProductFlavor>(f2d1, f2d2)),
ProductFlavorCombo.createCombinations(
ImmutableList.of(DIMENSION1, DIMENSION2),
ImmutableList.of(f1d1, f1d2, f2d1, f2d2)));
@@ -168,7 +173,9 @@
}
}
- private static void assertEqualGroupList(List<ProductFlavorCombo> expected, List<ProductFlavorCombo> actual) {
+ private static <S extends DimensionAware & Named> void assertEqualGroupList(
+ List<ProductFlavorCombo<S>> expected,
+ List<ProductFlavorCombo<S>> actual) {
assertEquals(expected.size(), actual.size());
for (int i = 0; i < expected.size(); i++) {
assertArrayEquals(
diff --git a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptionsTest.java b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptionsTest.java
index 00147a2..a9636d7 100644
--- a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptionsTest.java
+++ b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/AbiSplitOptionsTest.java
@@ -20,9 +20,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import com.android.build.OutputFile;
-import com.android.resources.Density;
-
import org.junit.Test;
import java.util.Set;
@@ -35,29 +32,7 @@
Set<String> values = options.getApplicableFilters();
- assertEquals(1, values.size());
- assertTrue(values.contains(OutputFile.NO_FILTER));
- }
-
- @Test
- public void testNonUniversal() {
- AbiSplitOptions options = new AbiSplitOptions();
- options.setEnable(true);
-
- Set<String> values = options.getApplicableFilters();
-
- assertFalse(values.contains(OutputFile.NO_FILTER));
- }
-
- @Test
- public void testUniversal() {
- AbiSplitOptions options = new AbiSplitOptions();
- options.setEnable(true);
- options.setUniversalApk(true);
-
- Set<String> values = options.getApplicableFilters();
-
- assertTrue(values.contains(OutputFile.NO_FILTER));
+ assertEquals(0, values.size());
}
@Test
diff --git a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptionsTest.java b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptionsTest.java
index 25b921a..637d4e4 100644
--- a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptionsTest.java
+++ b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/dsl/DensitySplitOptionsTest.java
@@ -20,7 +20,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import com.android.build.OutputFile;
import com.android.resources.Density;
import org.junit.Test;
@@ -35,8 +34,7 @@
Set<String> values = options.getApplicableFilters();
- assertEquals(1, values.size());
- assertTrue(values.contains(OutputFile.NO_FILTER));
+ assertEquals(0, values.size());
}
@Test
@@ -45,9 +43,8 @@
options.setEnable(true);
Set<String> values = options.getApplicableFilters();
-
- // test first release is the universal filter
- assertEquals(OutputFile.NO_FILTER, values.iterator().next());
+ // at this time we have 6 densities, maybe more later.
+ assertTrue(values.size() >= 6);
}
@Test
diff --git a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/tasks/MergeJavaResourcesTaskTest.java b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/tasks/MergeJavaResourcesTaskTest.java
new file mode 100644
index 0000000..712c949
--- /dev/null
+++ b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/internal/tasks/MergeJavaResourcesTaskTest.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal.tasks;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.PackagingOptions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.Files;
+
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+/**
+ * Tests for {@link MergeJavaResourcesTaskTest}
+ */
+public class MergeJavaResourcesTaskTest {
+
+ @Mock
+ PackagingOptions packagingOptions;
+
+ @Mock
+ MergeJavaResourcesTask task;
+
+ MergeJavaResourcesTask.FileFilter fileFilter;
+
+ private static File sPackagedJarExpansionFolder, sMergedFolder;
+ private static File sExpandedJar1, sExpandedJar2, sExpandedJar3;
+
+ /**
+ * Create temporary folders to simulate expanded folders with java resources embedded
+ */
+ @BeforeClass
+ public static void prepareFolders() throws IOException {
+ sPackagedJarExpansionFolder = createTmpFolder(null /* parent */);
+
+ sExpandedJar1 = createTmpFolder(sPackagedJarExpansionFolder);
+ sExpandedJar2 = createTmpFolder(sPackagedJarExpansionFolder);
+ sExpandedJar3 = createTmpFolder(sPackagedJarExpansionFolder);
+
+ sMergedFolder = createTmpFolder(null /* parent */);
+ }
+
+ /**
+ * After each test, all folders are cleaned.
+ */
+ @After
+ public void cleanUp() {
+ cleanContents(sExpandedJar1);
+ cleanContents(sExpandedJar2);
+ cleanContents(sExpandedJar3);
+ cleanContents(sMergedFolder);
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(task.getExpandedFolders()).thenReturn(ImmutableList.of(sPackagedJarExpansionFolder));
+ assertTrue(listFiles(sMergedFolder).length == 0);
+ }
+
+ @Test
+ public void testSimpleCopy() throws IOException {
+
+ fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+ assertFalse(new File(sMergedFolder, "foo/text.properties").exists());
+ File changedFile = createFile(sExpandedJar1, "foo/text.properties");
+ fileFilter.handleChanged(sMergedFolder, changedFile);
+ assertTrue(new File(sMergedFolder, "foo/text.properties").exists());
+ }
+
+ @Test
+ public void testSimpleExclusion() throws IOException {
+
+ when(packagingOptions.getExcludes()).thenReturn(
+ ImmutableSet.of("foo/text.properties"));
+ fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+ fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar1, "foo/text.properties"));
+ assertTrue(listFiles(sMergedFolder).length == 0);
+ }
+
+ @Test
+ public void testExclusionFromMultipleFiles() throws IOException {
+ when(packagingOptions.getExcludes()).thenReturn(
+ ImmutableSet.of("foo/text.properties"));
+ fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+ fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar1, "foo/text.properties"));
+ fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar2, "foo/text.properties"));
+ assertTrue(listFiles(sMergedFolder).length == 0);
+ }
+
+ @Test
+ public void testMultipleExclusions() throws IOException {
+ when(packagingOptions.getExcludes()).thenReturn(
+ ImmutableSet.of("foo/text.properties", "bar/other.properties"));
+ fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+ fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar1, "foo/text.properties"));
+ fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar2, "bar/other.properties"));
+ assertTrue(listFiles(sMergedFolder).length == 0);
+ }
+
+ @Test
+ public void textNonExclusion() throws IOException {
+ when(packagingOptions.getExcludes()).thenReturn(
+ ImmutableSet.of("foo/text.properties", "bar/other.properties"));
+ fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+ fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar1, "foo/text.properties"));
+ fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar2, "bar/other.properties"));
+ // this one should be copied over.
+ fileFilter.handleChanged(sMergedFolder, createFile(sExpandedJar2, "bar/foo.properties"));
+ assertTrue(listFiles(sMergedFolder).length == 1);
+ assertTrue(new File(sMergedFolder, "bar/foo.properties").exists());
+ }
+
+ @Test
+ public void testSingleMerge() throws IOException {
+ when(packagingOptions.getMerges()).thenReturn(ImmutableSet.of("foo/text.properties"));
+ fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+ createFile(sExpandedJar1, "foo/text.properties", "one");
+ File secondFile = createFile(sExpandedJar2, "foo/text.properties", "two");
+
+ // one has changed...
+ fileFilter.handleChanged(sMergedFolder, secondFile);
+
+ File mergedFile = new File(sMergedFolder, "foo/text.properties");
+ assertTrue(mergedFile.exists());
+ assertContentInAnyOrder(
+ Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+ , ImmutableList.of("one", "two"));
+ }
+
+ @Test
+ public void testMultipleMerges() throws IOException {
+ when(packagingOptions.getMerges()).thenReturn(ImmutableSet.of("foo/text.properties"));
+ fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+ createFile(sExpandedJar1, "foo/text.properties", "one");
+ File secondFile = createFile(sExpandedJar2, "foo/text.properties", "two");
+ createFile(sExpandedJar3, "foo/text.properties", "three");
+
+ // one has changed...
+ fileFilter.handleChanged(sMergedFolder, secondFile);
+
+ File mergedFile = new File(sMergedFolder, "foo/text.properties");
+ assertTrue(mergedFile.exists());
+ assertContentInAnyOrder(
+ Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+ , ImmutableList.of("one", "two", "three"));
+ }
+
+ @Test
+ public void testMergeAddon() throws IOException {
+ when(packagingOptions.getMerges()).thenReturn(ImmutableSet.of("foo/text.properties"));
+ fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+ createFile(sExpandedJar1, "foo/text.properties", "one");
+ File secondFile = createFile(sExpandedJar2, "foo/text.properties", "two");
+
+ // simulate one has changed to create initial version
+ fileFilter.handleChanged(sMergedFolder, secondFile);
+
+ File mergedFile = new File(sMergedFolder, "foo/text.properties");
+ assertTrue(mergedFile.exists());
+ assertContentInAnyOrder(
+ Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+ , ImmutableList.of("one", "two"));
+
+ // add a new one.
+ File thirdFile = createFile(sExpandedJar3, "foo/text.properties", "three");
+ fileFilter.handleChanged(sMergedFolder, thirdFile);
+
+ assertTrue(mergedFile.exists());
+ assertContentInAnyOrder(
+ Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+ , ImmutableList.of("one", "two", "three"));
+ }
+
+ @Test
+ public void testMergeUpdate() throws IOException {
+ when(packagingOptions.getMerges()).thenReturn(ImmutableSet.of("foo/text.properties"));
+ fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+ createFile(sExpandedJar1, "foo/text.properties", "one");
+ File secondFile = createFile(sExpandedJar2, "foo/text.properties", "two");
+ createFile(sExpandedJar3, "foo/text.properties", "three");
+
+ // simulate one has changed to create initial version
+ fileFilter.handleChanged(sMergedFolder, secondFile);
+
+ File mergedFile = new File(sMergedFolder, "foo/text.properties");
+ assertTrue(mergedFile.exists());
+ assertContentInAnyOrder(
+ Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+ , ImmutableList.of("one", "two", "three"));
+
+ // change one...
+ assertTrue(secondFile.delete());
+ secondFile = createFile(sExpandedJar2, "foo/text.properties", "deux");
+
+ fileFilter.handleChanged(sMergedFolder, secondFile);
+
+ assertTrue(mergedFile.exists());
+ assertContentInAnyOrder(
+ Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+ , ImmutableList.of("one", "deux", "three"));
+ }
+
+ @Test
+ public void testMergeRemoval() throws IOException {
+ when(packagingOptions.getMerges()).thenReturn(ImmutableSet.of("foo/text.properties"));
+ fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+ createFile(sExpandedJar1, "foo/text.properties", "one");
+ File secondFile = createFile(sExpandedJar2, "foo/text.properties", "two");
+ createFile(sExpandedJar3, "foo/text.properties", "three");
+
+ // simulate one has changed to create initial version
+ fileFilter.handleChanged(sMergedFolder, secondFile);
+
+ File mergedFile = new File(sMergedFolder, "foo/text.properties");
+ assertTrue(mergedFile.exists());
+ assertContentInAnyOrder(
+ Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+ , ImmutableList.of("one", "two", "three"));
+
+ // remove one...
+ assertTrue(secondFile.delete());
+
+ fileFilter.handleRemoved(sMergedFolder, secondFile);
+
+ assertTrue(mergedFile.exists());
+ assertContentInAnyOrder(
+ Files.asCharSource(mergedFile, Charset.defaultCharset()).read()
+ , ImmutableList.of("one", "three"));
+ }
+
+ @Test
+ public void testPickFirst() throws IOException {
+ when(packagingOptions.getPickFirsts()).thenReturn(ImmutableSet.of("foo/text.properties"));
+ fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+ // simulate the three elements were added.
+ fileFilter.handleChanged(sMergedFolder,
+ createFile(sExpandedJar1, "foo/text.properties", "one"));
+ fileFilter.handleChanged(sMergedFolder,
+ createFile(sExpandedJar2, "foo/text.properties", "two"));
+ fileFilter.handleChanged(sMergedFolder,
+ createFile(sExpandedJar3, "foo/text.properties", "three"));
+
+ File mergedFile = new File(sMergedFolder, "foo/text.properties");
+ assertTrue(mergedFile.exists());
+ String mergedContent = Files.asCharSource(mergedFile, Charset.defaultCharset()).read();
+ assertTrue(mergedContent.equals("one")
+ || mergedContent.equals("two")
+ || mergedContent.equals("three"));
+ }
+
+ @Test
+ public void testPickFirstUpdate() throws IOException {
+ when(packagingOptions.getPickFirsts()).thenReturn(ImmutableSet.of("foo/text.properties"));
+ fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+ File firstFile = createFile(sExpandedJar1, "foo/text.properties", "one");
+ File secondFile = createFile(sExpandedJar2, "foo/text.properties", "two");
+ File thirdFile = createFile(sExpandedJar3, "foo/text.properties", "three");
+
+ // simulate the three elements were added.
+ fileFilter.handleChanged(sMergedFolder, firstFile);
+ fileFilter.handleChanged(sMergedFolder, secondFile);
+ fileFilter.handleChanged(sMergedFolder, thirdFile);
+
+ File mergedFile = new File(sMergedFolder, "foo/text.properties");
+ assertTrue(mergedFile.exists());
+ String mergedContent = Files.asCharSource(mergedFile, Charset.defaultCharset()).read();
+ if (mergedContent.equals("one")) {
+ assertTrue(firstFile.delete());
+ createFile(sExpandedJar1, "foo/text.properties", "un");
+ fileFilter.handleChanged(sMergedFolder, firstFile);
+ assertEquals("un", Files.asCharSource(mergedFile, Charset.defaultCharset()).read());
+ }
+ if (mergedContent.equals("two")) {
+ assertTrue(thirdFile.delete());
+ createFile(sExpandedJar2, "foo/text.properties", "deux");
+ fileFilter.handleChanged(sMergedFolder, secondFile);
+ assertEquals("deux", Files.asCharSource(mergedFile, Charset.defaultCharset()).read());
+ }
+ if (mergedContent.equals("three")) {
+ assertTrue(thirdFile.delete());
+ createFile(sExpandedJar3, "foo/text.properties", "trois");
+ fileFilter.handleChanged(sMergedFolder, thirdFile);
+ assertEquals("trois", Files.asCharSource(mergedFile, Charset.defaultCharset()).read());
+ }
+ }
+
+ @Test
+ public void testPickFirstRemoval() throws IOException {
+ when(packagingOptions.getPickFirsts()).thenReturn(ImmutableSet.of("foo/text.properties"));
+ fileFilter = new MergeJavaResourcesTask.FileFilter(task, packagingOptions);
+
+ File firstFile = createFile(sExpandedJar1, "foo/text.properties", "one");
+ File secondFile = createFile(sExpandedJar2, "foo/text.properties", "two");
+ File thirdFile = createFile(sExpandedJar3, "foo/text.properties", "three");
+
+ // simulate the three elements were added.
+ fileFilter.handleChanged(sMergedFolder, firstFile);
+ fileFilter.handleChanged(sMergedFolder, secondFile);
+ fileFilter.handleChanged(sMergedFolder, thirdFile);
+
+ File mergedFile = new File(sMergedFolder, "foo/text.properties");
+ assertTrue(mergedFile.exists());
+ String mergedContent = Files.asCharSource(mergedFile, Charset.defaultCharset()).read();
+ if (mergedContent.equals("one")) {
+ assertTrue(firstFile.delete());
+ fileFilter.handleRemoved(sMergedFolder, firstFile);
+ mergedContent = Files.asCharSource(mergedFile, Charset.defaultCharset()).read();
+ assertTrue(mergedContent.equals("two") || mergedContent.equals("three"));
+ }
+ if (mergedContent.equals("two")) {
+ assertTrue(thirdFile.delete());
+ fileFilter.handleRemoved(sMergedFolder, secondFile);
+ mergedContent = Files.asCharSource(mergedFile, Charset.defaultCharset()).read();
+ assertTrue(mergedContent.equals("one") || mergedContent.equals("three"));
+ }
+ if (mergedContent.equals("three")) {
+ assertTrue(thirdFile.delete());
+ fileFilter.handleRemoved(sMergedFolder, thirdFile);
+ mergedContent = Files.asCharSource(mergedFile, Charset.defaultCharset()).read();
+ assertTrue(mergedContent.equals("one") || mergedContent.equals("two"));
+ }
+ }
+
+ private static void assertContentInAnyOrder(String content, Iterable<String> subStrings) {
+ int length = 0;
+ for (String subString : subStrings) {
+ length += subString.length();
+ assertTrue(content.contains(subString));
+ }
+ assertEquals(length, content.length());
+ }
+
+ private static File createTmpFolder(@Nullable File parent) throws IOException {
+ File folder = File.createTempFile("tmp", "dir");
+ assertTrue(folder.delete());
+ if (parent != null) {
+ folder = new File(parent, folder.getName());
+ }
+ assertTrue(folder.mkdirs());
+ return folder;
+ }
+
+ @NonNull private static File[] listFiles(@NonNull File folder) {
+ File[] files = folder.listFiles();
+ if (files == null) {
+ return new File[0];
+ }
+ return files;
+ }
+
+ private static void cleanContents(File folder) {
+ for (File f : listFiles(folder)) {
+ if (f.isDirectory()) {
+ cleanContents(f);
+ }
+ assertTrue(f.delete());
+ }
+ }
+
+ @NonNull private static File createFile(
+ @NonNull File parent,
+ @NonNull String archivePath) throws IOException {
+ return createFile(parent, archivePath, null /* content */);
+ }
+
+ @NonNull private static File createFile(
+ @NonNull File parent,
+ @NonNull String archivePath,
+ @Nullable String content) throws IOException {
+
+ File newFile = new File(parent, archivePath);
+ if (!newFile.getParentFile().exists()) {
+ assertTrue(newFile.getParentFile().mkdirs());
+ }
+ String fileContent = content == null ? "test!" : content;
+ Files.append(fileContent, newFile, Charset.defaultCharset());
+ return newFile;
+ }
+}
diff --git a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/profiling/RecordingBuildListenerTest.groovy b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/profiling/RecordingBuildListenerTest.groovy
index 830bca1..b949f5c 100644
--- a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/profiling/RecordingBuildListenerTest.groovy
+++ b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/profiling/RecordingBuildListenerTest.groovy
@@ -74,6 +74,12 @@
}
@Override
+ def <T> T record(@NonNull ExecutionType executionType, @NonNull Recorder.Block<T> block,
+ @NonNull List<Recorder.Property> properties) {
+ throw new UnsupportedOperationException("record method was not supposed to be called.")
+ }
+
+ @Override
long allocationRecordId() {
return recordId.incrementAndGet()
}
diff --git a/build-system/gradle-core/src/test/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriverTest.java b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriverTest.java
new file mode 100644
index 0000000..f6340e3
--- /dev/null
+++ b/build-system/gradle-core/src/test/groovy/com/android/build/gradle/tasks/annotations/ExtractAnnotationsDriverTest.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.tasks.annotations;
+
+import static com.android.utils.SdkUtils.fileToUrlString;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.testutils.SdkTestCase;
+import com.google.common.base.Charsets;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closeables;
+import com.google.common.io.Files;
+
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.List;
+
+public class ExtractAnnotationsDriverTest extends SdkTestCase {
+ private static boolean isValidEcj() {
+ // When this test is run from within Gradle's testing infrastructure, e.g.
+ // via "./gradlew :base:gradle-core:test", Gradle's own *internal* dependencies
+ // somehow end up on the classpath, and in particular, an ancient version of
+ // (ECJ (3.x)) take priority over our explicit lint dependency which should
+ // bring in ECJ 4.
+ //
+ // This causes the test to fail. For now, make the test only run when a valid
+ // ECJ is present.
+ try {
+ CompilerOptions.class.getField("originalComplianceLevel");
+ return true;
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+
+ public void testProGuard() throws Exception {
+ if (!isValidEcj()) {
+ return;
+ }
+
+ File androidJar = findAndroidJar(false);
+ if (androidJar == null) {
+ System.err.println("Skipping test " + this.getClass() + "#" + getName()
+ + ": No android.jar found");
+ return;
+ }
+
+ File project = createProject(mKeepTest, mKeepAnnotation);
+
+ File output = File.createTempFile("proguard", ".cfg");
+ output.deleteOnExit();
+
+ List<String> list = java.util.Arrays.asList(
+ "--sources",
+ new File(project, "src").getPath(),
+ "--classpath",
+ androidJar.getPath(),
+
+ "--quiet",
+ "--language-level",
+ "1.6",
+ "--proguard",
+ output.getPath()
+ );
+ String[] args = list.toArray(new String[list.size()]);
+ assertNotNull(args);
+
+ new ExtractAnnotationsDriver().run(args);
+ assertEquals(""
+ + "-keep class test.pkg.KeepTest {\n"
+ + " java.lang.Object myField\n"
+ + "}\n"
+ + "\n"
+ + "-keep class test.pkg.KeepTest {\n"
+ + " void foo()\n"
+ + "}\n"
+ + "\n"
+ + "-keep class test.pkg.KeepTest.MyClass\n"
+ + "\n"
+ + "-keep enum test.pkg.KeepTest.MyEnum\n"
+ + "\n"
+ + "-keep interface test.pkg.KeepTest.MyInterface\n"
+ + "\n"
+ + "-keep interface test.pkg.KeepTest.MyInterface2 {\n"
+ + " void paint2()\n"
+ + "}\n"
+ + "\n",
+ Files.toString(output, Charsets.UTF_8));
+ deleteFile(project);
+ }
+
+ public void testIncludeClassRetention() throws Exception {
+ if (!isValidEcj()) {
+ return;
+ }
+
+ File androidJar = findAndroidJar(false);
+ if (androidJar == null) {
+ System.err.println("Skipping test " + this.getClass() + "#" + getName()
+ + ": No android.jar found");
+ return;
+ }
+
+ File project = createProject(
+ mIntDefTest,
+ mPermissionsTest,
+ mManifest,
+ mKeepAnnotation,
+ mIntDefAnnotation,
+ mIntRangeAnnotation,
+ mPermissionAnnotation);
+
+ File output = File.createTempFile("annotations", ".zip");
+ File proguard = File.createTempFile("proguard", ".cfg");
+ output.deleteOnExit();
+ proguard.deleteOnExit();
+
+ List<String> list = java.util.Arrays.asList(
+ "--sources",
+ new File(project, "src").getPath(),
+ "--classpath",
+ androidJar.getPath(),
+
+ "--quiet",
+ "--language-level",
+ "1.6",
+ "--output",
+ output.getPath(),
+ "--proguard",
+ proguard.getPath()
+ );
+ String[] args = list.toArray(new String[list.size()]);
+ assertNotNull(args);
+
+ new ExtractAnnotationsDriver().run(args);
+
+ // Check proguard rules
+ assertEquals(""
+ + "-keep class test.pkg.IntDefTest {\n"
+ + " void testIntDef(int)\n"
+ + "}\n"
+ + "\n",
+ Files.toString(proguard, Charsets.UTF_8));
+
+ // Check extracted annotations
+ checkPackageXml("test.pkg", output, ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<root>\n"
+ + " <item name=\"test.pkg.IntDefTest void setFlags(java.lang.Object, int) 1\">\n"
+ + " <annotation name=\"android.support.annotation.IntDef\">\n"
+ + " <val name=\"value\" val=\"{test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT}\" />\n"
+ + " <val name=\"flag\" val=\"true\" />\n"
+ + " </annotation>\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.IntDefTest void setStyle(int, int) 0\">\n"
+ + " <annotation name=\"android.support.annotation.IntDef\">\n"
+ + " <val name=\"value\" val=\"{test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT}\" />\n"
+ + " </annotation>\n"
+ + " <annotation name=\"android.support.annotation.IntRange\">\n"
+ + " <val name=\"from\" val=\"20\" />\n"
+ + " </annotation>\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.PermissionsTest CONTENT_URI\">\n"
+ + " <annotation name=\"android.support.annotation.RequiresPermission.Read\">\n"
+ + " <val name=\"value\" val=\""android.permission.MY_READ_PERMISSION_STRING"\" />\n"
+ + " </annotation>\n"
+ + " <annotation name=\"android.support.annotation.RequiresPermission.Write\">\n"
+ + " <val name=\"value\" val=\""android.permission.MY_WRITE_PERMISSION_STRING"\" />\n"
+ + " </annotation>\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.PermissionsTest void myMethod()\">\n"
+ + " <annotation name=\"android.support.annotation.RequiresPermission\">\n"
+ + " <val name=\"value\" val=\""android.permission.MY_PERMISSION_STRING"\" />\n"
+ + " </annotation>\n"
+ + " </item>\n"
+ + "</root>\n"
+ + "\n");
+
+ deleteFile(project);
+ }
+
+ public void testSkipClassRetention() throws Exception {
+ if (!isValidEcj()) {
+ return;
+ }
+
+ File androidJar = findAndroidJar(false);
+ if (androidJar == null) {
+ System.err.println("Skipping test " + this.getClass() + "#" + getName()
+ + ": No android.jar found");
+ return;
+ }
+
+ File project = createProject(
+ mIntDefTest,
+ mPermissionsTest,
+ mManifest,
+ mKeepAnnotation,
+ mIntDefAnnotation,
+ mIntRangeAnnotation,
+ mPermissionAnnotation);
+
+ File output = File.createTempFile("annotations", ".zip");
+ File proguard = File.createTempFile("proguard", ".cfg");
+ output.deleteOnExit();
+ proguard.deleteOnExit();
+
+ List<String> list = java.util.Arrays.asList(
+ "--sources",
+ new File(project, "src").getPath(),
+ "--classpath",
+ androidJar.getPath(),
+
+ "--quiet",
+ "--skip-class-retention",
+ "--language-level",
+ "1.6",
+ "--output",
+ output.getPath(),
+ "--proguard",
+ proguard.getPath()
+ );
+ String[] args = list.toArray(new String[list.size()]);
+ assertNotNull(args);
+
+ new ExtractAnnotationsDriver().run(args);
+
+ // Check external annotations
+ checkPackageXml("test.pkg", output, ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<root>\n"
+ + " <item name=\"test.pkg.IntDefTest void setFlags(java.lang.Object, int) 1\">\n"
+ + " <annotation name=\"android.support.annotation.IntDef\">\n"
+ + " <val name=\"value\" val=\"{test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT}\" />\n"
+ + " <val name=\"flag\" val=\"true\" />\n"
+ + " </annotation>\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.IntDefTest void setStyle(int, int) 0\">\n"
+ + " <annotation name=\"android.support.annotation.IntDef\">\n"
+ + " <val name=\"value\" val=\"{test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT}\" />\n"
+ + " </annotation>\n"
+ + " <annotation name=\"android.support.annotation.IntRange\">\n"
+ + " <val name=\"from\" val=\"20\" />\n"
+ + " </annotation>\n"
+ + " </item>\n"
+ + "</root>\n"
+ + "\n");
+
+
+ deleteFile(project);
+ }
+
+ @SuppressWarnings("SpellCheckingInspection")
+ @NonNull
+ private TestFile mksrc(@NonNull String to, @NonNull String source) {
+ return new TestFile().to(to).withSource(source);
+ }
+
+ private File createProject(TestFile... files) throws IOException {
+ File dir = Files.createTempDir();
+
+ for (TestFile fp : files) {
+ File file = fp.createFile(dir);
+ assertNotNull(file);
+ }
+
+ return dir;
+ }
+
+ private final TestFile mKeepAnnotation = mksrc("src/android/support/annotation/Keep.java", ""
+ + "package android.support.annotation;\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.Target;\n"
+ + "import static java.lang.annotation.ElementType.*;\n"
+ + "import static java.lang.annotation.RetentionPolicy.*;\n"
+ + "@Retention(CLASS)\n"
+ + "@Target({PACKAGE,TYPE,ANNOTATION_TYPE,CONSTRUCTOR,METHOD,FIELD})\n"
+ + "public @interface Keep {\n"
+ + "}\n");
+
+ private final TestFile mIntDefAnnotation = mksrc("src/android/support/annotation/IntDef.java", ""
+ + "package android.support.annotation;\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.RetentionPolicy;\n"
+ + "import java.lang.annotation.Target;\n"
+ + "import static java.lang.annotation.ElementType.*;\n"
+ + "import static java.lang.annotation.RetentionPolicy.SOURCE;\n"
+ + "@Retention(SOURCE)\n"
+ + "@Target({ANNOTATION_TYPE})\n"
+ + "public @interface IntDef {\n"
+ + " long[] value() default {};\n"
+ + " boolean flag() default false;\n"
+ + "}\n");
+
+ private final TestFile mIntRangeAnnotation = mksrc("src/android/support/annotation/IntRange.java", ""
+ + "package android.support.annotation;\n"
+ + "\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.Target;\n"
+ + "\n"
+ + "import static java.lang.annotation.ElementType.*;\n"
+ + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+ + "\n"
+ + "@Retention(CLASS)\n"
+ + "@Target({CONSTRUCTOR,METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE})\n"
+ + "public @interface IntRange {\n"
+ + " long from() default Long.MIN_VALUE;\n"
+ + " long to() default Long.MAX_VALUE;\n"
+ + "}\n");
+
+ private final TestFile mPermissionAnnotation = mksrc(
+ "src/android/support/annotation/RequiresPermission.java", ""
+ + "package android.support.annotation;\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.RetentionPolicy;\n"
+ + "import java.lang.annotation.Target;\n"
+ + "import static java.lang.annotation.ElementType.*;\n"
+ + "import static java.lang.annotation.RetentionPolicy.*;\n"
+ + "@Retention(CLASS)\n"
+ + "@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD})\n"
+ + "public @interface RequiresPermission {\n"
+ + " String value() default \"\";\n"
+ + " String[] allOf() default {};\n"
+ + " String[] anyOf() default {};\n"
+ + " boolean conditional() default false;\n"
+ + " @Target(FIELD)\n"
+ + " @interface Read {\n"
+ + " RequiresPermission value();\n"
+ + " }\n"
+ + " @Target(FIELD)\n"
+ + " @interface Write {\n"
+ + " RequiresPermission value();\n"
+ + " }\n"
+ + "}");
+
+ private final TestFile mIntDefTest = mksrc("src/test/pkg/IntDefTest.java", ""
+ + "package test.pkg;\n"
+ + "\n"
+ + "import android.content.Context;\n"
+ + "import android.support.annotation.IntDef;\n"
+ + "import android.support.annotation.IntRange;\n"
+ + "import android.support.annotation.Keep;\n"
+ + "import android.view.View;\n"
+ + "\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.RetentionPolicy;\n"
+ + "\n"
+ + "@SuppressWarnings(\"UnusedDeclaration\")\n"
+ + "public class IntDefTest {\n"
+ + " @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})\n"
+ + " @IntRange(from = 20)\n"
+ + " @Retention(RetentionPolicy.SOURCE)\n"
+ + " private @interface DialogStyle {}\n"
+ + "\n"
+ + " public static final int STYLE_NORMAL = 0;\n"
+ + " public static final int STYLE_NO_TITLE = 1;\n"
+ + " public static final int STYLE_NO_FRAME = 2;\n"
+ + " public static final int STYLE_NO_INPUT = 3;\n"
+ + " public static final int UNRELATED = 3;\n"
+ + "\n"
+ + " public void setStyle(@DialogStyle int style, int theme) {\n"
+ + " }\n"
+ + "\n"
+ + " @Keep"
+ + " public void testIntDef(int arg) {\n"
+ + " }\n"
+ + " @IntDef(value = {STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT}, flag=true)\n"
+ + " @Retention(RetentionPolicy.SOURCE)\n"
+ + " private @interface DialogFlags {}\n"
+ + "\n"
+ + " public void setFlags(Object first, @DialogFlags int flags) {\n"
+ + " }\n"
+ + "\n"
+ + " public static final String TYPE_1 = \"type1\";\n"
+ + " public static final String TYPE_2 = \"type2\";\n"
+ + " public static final String UNRELATED_TYPE = \"other\";\n"
+ + "}");
+
+ private final TestFile mPermissionsTest = mksrc("src/test/pkg/PermissionsTest.java", ""
+ + "package test.pkg;\n"
+ + "\n"
+ + "import android.support.annotation.RequiresPermission;\n"
+ + "\n"
+ + "public class PermissionsTest {\n"
+ + " @RequiresPermission(Manifest.permission.MY_PERMISSION)\n"
+ + " public void myMethod() {\n"
+ + " }\n"
+ + "\n"
+ + " @RequiresPermission.Read(@RequiresPermission(Manifest.permission.MY_READ_PERMISSION))\n"
+ + " @RequiresPermission.Write(@RequiresPermission(Manifest.permission.MY_WRITE_PERMISSION))\n"
+ + " public static final String CONTENT_URI = \"\";\n"
+ + "}\n");
+
+ private final TestFile mManifest = mksrc("src/test/pkg/Manifest.java", ""
+ + "package test.pkg;\n"
+ + "\n"
+ + "public class Manifest {\n"
+ + " public static final class permission {\n"
+ + " public static final String MY_PERMISSION = \"android.permission.MY_PERMISSION_STRING\";\n"
+ + " public static final String MY_READ_PERMISSION = \"android.permission.MY_READ_PERMISSION_STRING\";\n"
+ + " public static final String MY_WRITE_PERMISSION = \"android.permission.MY_WRITE_PERMISSION_STRING\";\n"
+ + " }\n"
+ + "}\n");
+
+ private final TestFile mKeepTest = mksrc("src/test/pkg/KeepTest.java", ""
+ + "package test.pkg;\n"
+ + "import android.support.annotation.Keep;\n"
+ + "\n"
+ + "public class KeepTest {\n"
+ + " @Keep\n"
+ + " public void foo() {\n"
+ + " }\n"
+ + "\n"
+ + " @Keep\n"
+ + " public static class MyClass {\n"
+ + " public void paint() {\n"
+ + " }\n"
+ + " }\n"
+ + "\n"
+ + " @Keep\n"
+ + " public static interface MyInterface {\n"
+ + " public void paint();\n"
+ + " }\n"
+ + "\n"
+ + " public static interface MyInterface2 {\n"
+ + " @Keep\n"
+ + " public void paint2();\n"
+ + " }\n"
+ + "\n"
+ + " @Keep\n"
+ + " public static enum MyEnum {\n"
+ + " TYPE1, TYPE2;\n"
+ + " }\n"
+ + "\n"
+ + " @Keep\n"
+ + " public static @interface MyAnnotation {\n"
+ + " }\n"
+ + "\n"
+ + " @Keep\n"
+ + " public Object myField = null;"
+ + "}\n");
+
+ private static void checkPackageXml(String pkg, File output, String expected)
+ throws IOException {
+ assertNotNull(output);
+ assertTrue(output.exists());
+ URL url = new URL("jar:" + fileToUrlString(output) + "!/" + pkg.replace('.','/') +
+ "/annotations.xml");
+ InputStream stream = url.openStream();
+ try {
+ byte[] bytes = ByteStreams.toByteArray(stream);
+ assertNotNull(bytes);
+ String xml = new String(bytes, Charsets.UTF_8);
+ assertEquals(expected, xml);
+ } finally {
+ Closeables.closeQuietly(stream);
+ }
+ }
+
+ @Nullable
+ private static File findAndroidJar(boolean requireExists) {
+ String androidHomePath = System.getenv("ANDROID_HOME");
+ assertNotNull("Must set $ANDROID_HOME to run this test", androidHomePath);
+ File androidHome = new File(androidHomePath);
+ if (!androidHome.exists()) {
+ if (!requireExists) {
+ return null;
+ }
+ fail(androidHomePath + " does not exist");
+ }
+ File androidJar = new File(androidHome, "platforms/android-22/android.jar");
+ assertTrue(
+ androidJar + " does not exist: make sure you have Lollipop installed in your SDK",
+ androidJar.exists());
+ return androidJar;
+ }
+
+ public void testGetRaw() throws Exception {
+ assertEquals("", ApiDatabase.getRawClass(""));
+ assertEquals("Foo", ApiDatabase.getRawClass("Foo"));
+ assertEquals("Foo", ApiDatabase.getRawClass("Foo<T>"));
+ assertEquals("Foo", ApiDatabase.getRawMethod("Foo<T>"));
+ assertEquals("Foo", ApiDatabase.getRawClass("Foo<A,B>"));
+ assertEquals("Foo", ApiDatabase.getRawParameterList("Foo<? extends java.util.List>"));
+ assertEquals("Object,java.util.List,List,int[],Object[]",
+ ApiDatabase.getRawParameterList("Object<? extends java.util.List>,java.util.List<String>,"
+ + "List<? super Number>,int[],Object..."));
+ }
+}
\ No newline at end of file
diff --git a/build-system/gradle-experimental/build.gradle b/build-system/gradle-experimental/build.gradle
index 7a31a62..9e9154a 100644
--- a/build-system/gradle-experimental/build.gradle
+++ b/build-system/gradle-experimental/build.gradle
@@ -1,20 +1,11 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'groovy'
apply plugin: 'clone-artifacts'
apply plugin: 'idea'
apply plugin: 'jacoco'
// Extract gradle libraries to ensure gradle-core is compatible with older version.
-String gradleVersion = "2.4-20150322230018+0000"
-File gradleBinary = file("$rootProject.projectDir/external/gradle/gradle-$gradleVersion-bin.zip")
+String gradleVersion = "2.5"
+File gradleBinary = file("$rootProject.projectDir/external/gradle/gradle-$gradleVersion-all.zip")
File gradleLib = file("$rootProject.ext.androidHostOut/alternate-gradle/gradle-$gradleVersion/lib")
task extractGradleLibs(type: Copy) {
@@ -38,12 +29,13 @@
group = 'com.android.tools.build'
archivesBaseName = 'gradle-experimental'
-version = rootProject.ext.buildVersion
+version = rootProject.ext.experimentalVersion
project.ext.pomName = 'Gradle Plug-in for Android Using Component Model'
project.ext.pomDesc = 'Gradle plug-in to build Android applications.'
apply from: "$rootDir/buildSrc/base/publish.gradle"
+apply from: "$rootDir/buildSrc/base/bintray.gradle"
jar.manifest.attributes("Plugin-Version": version)
jar.manifest.attributes("Inception-Date":"${Calendar.getInstance().get(Calendar.YEAR)}:" +
diff --git a/build-system/gradle-experimental/gradle-experimental.iml b/build-system/gradle-experimental/gradle-experimental.iml
index 2747ef5..9ab34f7 100644
--- a/build-system/gradle-experimental/gradle-experimental.iml
+++ b/build-system/gradle-experimental/gradle-experimental.iml
@@ -43,7 +43,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-core-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-core-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -52,7 +52,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-model-core-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-model-core-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -61,7 +61,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-model-groovy-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-model-groovy-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -70,7 +70,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/asm-all-5.0.3.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/asm-all-5.0.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -79,7 +79,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/ant-1.9.3.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/ant-1.9.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -88,7 +88,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/commons-collections-3.2.1.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/commons-collections-3.2.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -97,7 +97,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/commons-io-1.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/commons-io-1.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -106,7 +106,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/commons-lang-2.6.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/commons-lang-2.6.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -115,7 +115,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/logback-core-1.0.13.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/logback-core-1.0.13.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -124,7 +124,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/logback-classic-1.0.13.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/logback-classic-1.0.13.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -133,7 +133,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/guava-jdk5-17.0.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/guava-jdk5-17.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -142,7 +142,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/jcip-annotations-1.0.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/jcip-annotations-1.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -151,7 +151,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/jul-to-slf4j-1.7.7.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/jul-to-slf4j-1.7.7.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -160,7 +160,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/jarjar-1.3.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/jarjar-1.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -169,7 +169,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/javax.inject-1.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/javax.inject-1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -178,7 +178,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/slf4j-api-1.7.7.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/slf4j-api-1.7.7.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -187,7 +187,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/log4j-over-slf4j-1.7.7.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/log4j-over-slf4j-1.7.7.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -196,7 +196,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/jcl-over-slf4j-1.7.7.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/jcl-over-slf4j-1.7.7.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -205,7 +205,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/ant-launcher-1.9.3.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/ant-launcher-1.9.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -214,7 +214,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/commons-collections-3.2.1.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/commons-collections-3.2.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -223,7 +223,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/commons-io-1.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/commons-io-1.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -232,7 +232,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/commons-lang-2.6.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/commons-lang-2.6.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -241,7 +241,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-docs-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-docs-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -250,7 +250,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-launcher-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-launcher-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -259,7 +259,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-base-services-groovy-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-base-services-groovy-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -268,7 +268,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-base-services-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-base-services-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -277,7 +277,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-resources-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-resources-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -286,7 +286,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-cli-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-cli-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -295,7 +295,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-native-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-native-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -304,7 +304,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-open-api-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-open-api-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -313,7 +313,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-ui-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-ui-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -322,7 +322,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/jna-3.2.7.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/jna-3.2.7.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -331,7 +331,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/native-platform-0.10.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/native-platform-0.10.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -340,7 +340,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/jansi-1.2.1.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/jansi-1.2.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -349,7 +349,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/native-platform-osx-i386-0.10.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/native-platform-osx-i386-0.10.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -358,7 +358,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/native-platform-osx-amd64-0.10.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/native-platform-osx-amd64-0.10.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -367,7 +367,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/native-platform-linux-amd64-0.10.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/native-platform-linux-amd64-0.10.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -376,7 +376,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/native-platform-linux-i386-0.10.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/native-platform-linux-i386-0.10.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -385,7 +385,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/native-platform-windows-amd64-0.10.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/native-platform-windows-amd64-0.10.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -394,7 +394,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/native-platform-windows-i386-0.10.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/native-platform-windows-i386-0.10.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -403,7 +403,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-messaging-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-messaging-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -412,7 +412,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/kryo-2.20.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/kryo-2.20.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -421,7 +421,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/reflectasm-1.07-shaded.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/reflectasm-1.07-shaded.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -430,7 +430,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/minlog-1.2.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/minlog-1.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -439,7 +439,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/objenesis-1.2.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/objenesis-1.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -448,7 +448,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-settings-3.0.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-settings-3.0.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -457,7 +457,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-repository-metadata-3.0.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-repository-metadata-3.0.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -466,7 +466,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-plexus-container-default-1.5.5.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-plexus-container-default-1.5.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -475,7 +475,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-aether-provider-3.0.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-aether-provider-3.0.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -484,7 +484,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-wagon-provider-api-2.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-wagon-provider-api-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -493,7 +493,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-plexus-cipher-1.7.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-plexus-cipher-1.7.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -502,7 +502,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-plexus-interpolation-1.14.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-plexus-interpolation-1.14.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -511,7 +511,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-plexus-utils-2.0.6.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-plexus-utils-2.0.6.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -520,7 +520,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-plexus-classworlds-2.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-plexus-classworlds-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -529,7 +529,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-plugin-api-3.0.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-plugin-api-3.0.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -538,7 +538,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-model-builder-3.0.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-model-builder-3.0.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -547,7 +547,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-plexus-sec-dispatcher-1.3.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-plexus-sec-dispatcher-1.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -556,7 +556,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-plexus-component-annotations-1.5.5.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-plexus-component-annotations-1.5.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -565,7 +565,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-aether-connector-wagon-1.13.1.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-aether-connector-wagon-1.13.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -574,7 +574,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-compat-3.0.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-compat-3.0.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -583,7 +583,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-wagon-http-2.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-wagon-http-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -592,7 +592,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-aether-api-1.13.1.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-aether-api-1.13.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -601,7 +601,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-settings-builder-3.0.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-settings-builder-3.0.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -610,7 +610,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-aether-spi-1.13.1.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-aether-spi-1.13.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -619,7 +619,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-core-3.0.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-core-3.0.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -628,7 +628,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-wagon-http-shared4-2.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-wagon-http-shared4-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -637,7 +637,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-aether-util-1.13.1.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-aether-util-1.13.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -646,7 +646,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-artifact-3.0.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-artifact-3.0.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -655,7 +655,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-maven-model-3.0.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-maven-model-3.0.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -664,7 +664,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jarjar-aether-impl-1.13.1.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jarjar-aether-impl-1.13.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -673,7 +673,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/maven-ant-tasks-2.1.3.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/maven-ant-tasks-2.1.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -682,7 +682,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/nekohtml-1.9.14.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/nekohtml-1.9.14.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -691,7 +691,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/xbean-reflect-3.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/xbean-reflect-3.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -700,7 +700,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/xml-apis-1.3.04.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/xml-apis-1.3.04.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -709,7 +709,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/xercesImpl-2.9.1.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/xercesImpl-2.9.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -718,7 +718,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-tooling-api-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-tooling-api-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -727,7 +727,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-plugins-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-plugins-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -736,7 +736,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/junit-4.11.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/junit-4.11.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -745,7 +745,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/testng-6.3.1.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/testng-6.3.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -754,7 +754,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/commons-cli-1.2.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/commons-cli-1.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -763,7 +763,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/bsh-2.0b4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/bsh-2.0b4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -772,7 +772,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/snakeyaml-1.6.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/snakeyaml-1.6.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -781,7 +781,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/hamcrest-core-1.3.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/hamcrest-core-1.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -790,7 +790,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-code-quality-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-code-quality-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -799,7 +799,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-jetty-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-jetty-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -808,7 +808,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jetty-6.1.25.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jetty-6.1.25.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -817,7 +817,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jetty-util-6.1.25.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jetty-util-6.1.25.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -826,7 +826,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/servlet-api-2.5-20081211.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/servlet-api-2.5-20081211.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -835,7 +835,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jetty-plus-6.1.25.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jetty-plus-6.1.25.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -844,7 +844,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jsp-2.1-6.1.14.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jsp-2.1-6.1.14.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -853,7 +853,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jetty-annotations-6.1.25.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jetty-annotations-6.1.25.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -862,7 +862,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/geronimo-annotation_1.0_spec-1.0.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/geronimo-annotation_1.0_spec-1.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -871,7 +871,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jetty-naming-6.1.25.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jetty-naming-6.1.25.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -880,7 +880,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/core-3.1.1.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/core-3.1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -889,7 +889,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jsp-api-2.1-6.1.14.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jsp-api-2.1-6.1.14.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -898,7 +898,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-antlr-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-antlr-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -907,7 +907,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-dependency-management-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-dependency-management-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -916,7 +916,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-ide-native-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-ide-native-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -925,7 +925,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-language-groovy-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-language-groovy-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -934,7 +934,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-language-java-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-language-java-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -943,7 +943,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-language-jvm-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-language-jvm-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -952,7 +952,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-language-native-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-language-native-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -961,7 +961,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-platform-base-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-platform-base-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -970,7 +970,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-platform-jvm-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-platform-jvm-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -979,7 +979,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-platform-native-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-platform-native-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -988,7 +988,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-plugin-development-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-plugin-development-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -997,7 +997,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-plugin-use-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-plugin-use-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1006,7 +1006,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/gradle-wrapper-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/gradle-wrapper-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1015,7 +1015,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-osgi-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-osgi-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1024,7 +1024,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/bndlib-2.1.0.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/bndlib-2.1.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1033,7 +1033,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-maven-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-maven-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1042,7 +1042,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/pmaven-common-0.8-20100325.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/pmaven-common-0.8-20100325.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1051,7 +1051,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/pmaven-groovy-0.8-20100325.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/pmaven-groovy-0.8-20100325.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1060,7 +1060,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/plexus-component-annotations-1.5.2.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/plexus-component-annotations-1.5.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1069,7 +1069,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-ide-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-ide-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1078,7 +1078,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-announce-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-announce-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1087,7 +1087,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-scala-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-scala-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1096,7 +1096,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-sonar-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-sonar-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1105,7 +1105,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/sonar-batch-bootstrapper-2.9.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/sonar-batch-bootstrapper-2.9.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1114,7 +1114,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-signing-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-signing-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1123,7 +1123,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-ear-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-ear-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1132,7 +1132,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-javascript-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-javascript-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1141,7 +1141,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/rhino-1.7R3.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/rhino-1.7R3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1150,7 +1150,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gson-2.2.4.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gson-2.2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1159,7 +1159,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/simple-4.1.21.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/simple-4.1.21.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1168,7 +1168,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-build-comparison-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-build-comparison-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1177,7 +1177,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-diagnostics-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-diagnostics-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1186,7 +1186,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-reporting-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-reporting-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1195,7 +1195,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/jatl-0.2.2.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/jatl-0.2.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1204,7 +1204,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-publish-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-publish-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1213,7 +1213,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-ivy-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-ivy-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1222,7 +1222,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-jacoco-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-jacoco-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1231,7 +1231,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-build-init-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-build-init-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
@@ -1240,7 +1240,7 @@
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.4-20150322230018+0000/lib/plugins/gradle-language-jvm-2.4-20150322230018+0000.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../../out/alternate-gradle/gradle-2.5/lib/plugins/gradle-language-jvm-2.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/internal/AndroidConfigHelper.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/internal/AndroidConfigHelper.java
new file mode 100644
index 0000000..fa03d57
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/internal/AndroidConfigHelper.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.api.AndroidSourceSet;
+import com.android.build.gradle.internal.coverage.JacocoExtension;
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.dsl.AdbOptions;
+import com.android.build.gradle.internal.dsl.AndroidSourceSetFactory;
+import com.android.build.gradle.internal.dsl.DexOptions;
+import com.android.build.gradle.internal.dsl.LintOptions;
+import com.android.build.gradle.internal.dsl.PackagingOptions;
+import com.android.build.gradle.internal.dsl.PreprocessingOptions;
+import com.android.build.gradle.internal.dsl.Splits;
+import com.android.build.gradle.internal.dsl.TestOptions;
+import com.android.build.gradle.managed.AndroidConfig;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.core.LibraryRequest;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.builder.testing.api.TestServer;
+import com.google.common.collect.Lists;
+
+import org.gradle.api.Action;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.internal.reflect.Instantiator;
+
+/**
+ * Utility functions for initializing an AndroidConfig.
+ */
+public class AndroidConfigHelper {
+ public static void configure(
+ @NonNull AndroidConfig model,
+ @NonNull Instantiator instantiator) {
+ model.setDefaultPublishConfig(BuilderConstants.RELEASE);
+ model.setPublishNonDefault(false);
+ model.setGeneratePureSplits(false);
+ model.setPreProcessingOptions(instantiator.newInstance(PreprocessingOptions.class));
+ model.setDeviceProviders(Lists.<DeviceProvider>newArrayList());
+ model.setTestServers(Lists.<TestServer>newArrayList());
+ model.setAaptOptions(instantiator.newInstance(AaptOptions.class));
+ model.setDexOptions(instantiator.newInstance(DexOptions.class));
+ model.setLintOptions(instantiator.newInstance(LintOptions.class));
+ model.setTestOptions(instantiator.newInstance(TestOptions.class));
+ model.setCompileOptions(instantiator.newInstance(CompileOptions.class));
+ model.setPackagingOptions(instantiator.newInstance(PackagingOptions.class));
+ model.setJacoco(instantiator.newInstance(JacocoExtension.class));
+ model.setAdbOptions(instantiator.newInstance(AdbOptions.class));
+ model.setSplits(instantiator.newInstance(Splits.class, instantiator));
+ model.setLibraryRequests(Lists.<LibraryRequest>newArrayList());
+ }
+
+
+ public static NamedDomainObjectContainer<AndroidSourceSet> createSourceSetsContainer(
+ @NonNull final Project project,
+ @NonNull Instantiator instantiator,
+ final boolean isLibrary) {
+ NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer = project.container(
+ AndroidSourceSet.class,
+ new AndroidSourceSetFactory(instantiator, project, isLibrary));
+
+ sourceSetsContainer.whenObjectAdded(new Action<AndroidSourceSet>() {
+ @Override
+ public void execute(AndroidSourceSet sourceSet) {
+ ConfigurationContainer configurations = project.getConfigurations();
+
+ createConfiguration(
+ configurations,
+ sourceSet.getCompileConfigurationName(),
+ "Classpath for compiling the ${sourceSet.name} sources.");
+
+ String packageConfigDescription;
+ if (isLibrary) {
+ packageConfigDescription
+ = "Classpath only used when publishing '${sourceSet.name}'.";
+ } else {
+ packageConfigDescription
+ = "Classpath packaged with the compiled '${sourceSet.name}' classes.";
+ }
+ createConfiguration(
+ configurations,
+ sourceSet.getPackageConfigurationName(),
+ packageConfigDescription);
+
+ createConfiguration(
+ configurations,
+ sourceSet.getProvidedConfigurationName(),
+ "Classpath for only compiling the ${sourceSet.name} sources.");
+
+ createConfiguration(
+ configurations,
+ sourceSet.getWearAppConfigurationName(),
+ "Link to a wear app to embed for object '${sourceSet.name}'.");
+
+ sourceSet.setRoot(String.format("src/%s", sourceSet.getName()));
+
+ }
+ });
+ return sourceSetsContainer;
+ }
+
+ private static void createConfiguration(
+ @NonNull ConfigurationContainer configurations,
+ @NonNull String configurationName,
+ @NonNull String configurationDescription) {
+ Configuration configuration = configurations.findByName(configurationName);
+ if (configuration == null) {
+ configuration = configurations.create(configurationName);
+ }
+
+ // Disable modification to configurations as this causes issues when accessed through the
+ // tooling-api. Check that it works with Studio's ImportProjectAction before re-enabling
+ // them.
+ //configuration.setVisible(false);
+ //configuration.setDescription(configurationDescription);
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/internal/NdkOptionsHelper.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/internal/NdkOptionsHelper.java
new file mode 100644
index 0000000..ee22699
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/internal/NdkOptionsHelper.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal;
+
+import com.android.build.gradle.managed.NdkBuildType;
+import com.android.build.gradle.managed.NdkOptions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+/**
+ * Helper functions for configuring an NdkOptions.
+ */
+public class NdkOptionsHelper {
+
+ /**
+ * Initialize an NdkOptions.
+ *
+ * Should be unnecessary when Gradle supports managed List of String.
+ */
+ public static void init(NdkOptions ndk) {
+ ndk.setCFlags(Lists.<String>newArrayList());
+ ndk.setCppFlags(Lists.<String>newArrayList());
+ ndk.setLdFlags(Lists.<String>newArrayList());
+ ndk.setLdLibs(Lists.<String>newArrayList());
+ ndk.setAbiFilters(Sets.<String>newHashSet());
+ }
+
+ /**
+ * Merge one NdkOptions to another.
+ * @param base NdkOptions to merge to. base is mutated to contain the merged result.
+ * @param other NdkOptions to merge. Options in other has priority over base if both are set.
+ */
+ public static void merge(NdkOptions base, NdkOptions other) {
+ if (other.getModuleName() != null) {
+ base.setModuleName(other.getModuleName());
+ }
+
+ base.getAbiFilters().addAll(other.getAbiFilters());
+ base.getCFlags().addAll(other.getCFlags());
+ base.getCppFlags().addAll(other.getCppFlags());
+ base.getLdFlags().addAll(other.getLdFlags());
+ base.getLdLibs().addAll(other.getLdLibs());
+
+ if (other.getStl() != null) {
+ base.setStl(other.getStl());
+ }
+ if (other.getRenderscriptNdkMode() != null) {
+ base.setRenderscriptNdkMode(other.getRenderscriptNdkMode());
+ }
+ }
+
+ /**
+ * Merge one NdkBuildType to another
+ * @param base NdkBuildType to merge to. base is mutated to contain the merged result.
+ * @param other NdkBuildType to merge. Options in other has priority over base if both are set.
+ */
+ public static void merge(NdkBuildType base, NdkBuildType other) {
+ merge(base, (NdkOptions) other);
+ if (other.getDebuggable() != null) {
+ base.setDebuggable(other.getDebuggable());
+ }
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/AndroidConfig.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/AndroidConfig.java
new file mode 100644
index 0000000..8ec1eca
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/AndroidConfig.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed;
+
+import com.android.build.gradle.api.VariantFilter;
+import com.android.build.gradle.internal.CompileOptions;
+import com.android.build.gradle.internal.coverage.JacocoExtension;
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.dsl.AdbOptions;
+import com.android.build.gradle.internal.dsl.DexOptions;
+import com.android.build.gradle.internal.dsl.LintOptions;
+import com.android.build.gradle.internal.dsl.PackagingOptions;
+import com.android.build.gradle.internal.dsl.PreprocessingOptions;
+import com.android.build.gradle.internal.dsl.Splits;
+import com.android.build.gradle.internal.dsl.TestOptions;
+import com.android.build.gradle.model.AndroidComponentModelSourceSet;
+import com.android.builder.core.LibraryRequest;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.builder.testing.api.TestServer;
+import com.android.sdklib.repository.FullRevision;
+
+import org.gradle.api.Action;
+import org.gradle.model.Managed;
+import org.gradle.model.ModelMap;
+import org.gradle.model.Unmanaged;
+
+import java.util.Collection;
+import java.util.List;
+
+import groovy.lang.Closure;
+
+/**
+ * Component model for all Android plugin.
+ */
+@Managed
+public interface AndroidConfig {
+
+ /** Build tool version */
+ String getBuildToolsVersion();
+ void setBuildToolsVersion(String buildToolsVersion);
+
+ /** Compile SDK version */
+ String getCompileSdkVersion();
+ void setCompileSdkVersion(String compileSdkVersion);
+
+ /** Build tool revisions */
+ @Unmanaged
+ FullRevision getBuildToolsRevision();
+ void setBuildToolsRevision(FullRevision fullRevision);
+
+ /** Default config, shared by all flavors. */
+ ProductFlavor getDefaultConfig();
+
+ /** List of device providers */
+ @Unmanaged
+ List<DeviceProvider> getDeviceProviders();
+ void setDeviceProviders(List<DeviceProvider> providers);
+
+ /** List of remote CI servers */
+ @Unmanaged
+ List<TestServer> getTestServers();
+ void setTestServers(List<TestServer> providers);
+
+ /** Name of the variant to publish */
+ String getDefaultPublishConfig();
+ void setDefaultPublishConfig(String defaultPublishConfig);
+
+ /** Whether to also publish non-default variants */
+ Boolean getPublishNonDefault();
+ void setPublishNonDefault(Boolean publishNonDefault);
+
+ /** Filter to determine which variants to build */
+ @Unmanaged
+ Action<VariantFilter> getVariantFilter();
+ void setVariantFilter(Action<VariantFilter> filter);
+
+ /** A prefix to be used when creating new resources. Used by Studio */
+ String getResourcePrefix();
+ void setResourcePrefix(String resourcePrefix);
+
+ /** Whether to generate pure splits or multi apk */
+ Boolean getGeneratePureSplits();
+ void setGeneratePureSplits(Boolean generateSplits);
+
+ /** Whether to preprocess resources */
+ @Unmanaged
+ PreprocessingOptions getPreProcessingOptions();
+ void setPreProcessingOptions(PreprocessingOptions preprocessingOptions);
+
+ /** Build types used by this project. */
+ ModelMap<BuildType> getBuildTypes();
+
+ /** All product flavors used by this project. */
+ ModelMap<ProductFlavor> getProductFlavors();
+
+ /** Signing configs used by this project. */
+ ModelMap<SigningConfig> getSigningConfigs();
+
+ @Unmanaged
+ AndroidComponentModelSourceSet getSources();
+ void setSources(AndroidComponentModelSourceSet sources);
+
+ NdkConfig getNdk();
+
+ /** Adb options */
+ @Unmanaged
+ AdbOptions getAdbOptions();
+ void setAdbOptions(AdbOptions adbOptions);
+
+ /** Options for aapt, tool for packaging resources. */
+ @Unmanaged
+ AaptOptions getAaptOptions();
+ void setAaptOptions(AaptOptions aaptOptions);
+
+ /** Compile options */
+ @Unmanaged
+ CompileOptions getCompileOptions();
+ void setCompileOptions(CompileOptions compileOptions);
+
+ /** Dex options. */
+ @Unmanaged
+ DexOptions getDexOptions();
+ void setDexOptions(DexOptions dexOptions);
+
+ /** JaCoCo options. */
+ @Unmanaged
+ JacocoExtension getJacoco();
+ void setJacoco(JacocoExtension jacoco);
+
+ /** Lint options. */
+ @Unmanaged
+ LintOptions getLintOptions();
+ void setLintOptions(LintOptions lintOptions);
+
+ /** Packaging options. */
+ @Unmanaged
+ PackagingOptions getPackagingOptions();
+ void setPackagingOptions(PackagingOptions packagingOptions);
+
+ /** Options for running tests. */
+ @Unmanaged
+ TestOptions getTestOptions();
+ void setTestOptions(TestOptions testOptions);
+
+ /** APK splits */
+ @Unmanaged
+ Splits getSplits();
+ void setSplits(Splits splits);
+
+ @Unmanaged
+ Collection<LibraryRequest> getLibraryRequests();
+ void setLibraryRequests(Collection<LibraryRequest> libraryRequests);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ApiVersion.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ApiVersion.java
new file mode 100644
index 0000000..1a6ea0a
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ApiVersion.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed;
+
+import org.gradle.model.Managed;
+
+/**
+ * A Managed ApiVersion.
+ */
+@Managed
+public interface ApiVersion {
+
+ Integer getApiLevel();
+ void setApiLevel(Integer apiLevel);
+
+ String getCodename();
+ void setCodename(String codename);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/BuildType.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/BuildType.java
new file mode 100644
index 0000000..f1adf89
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/BuildType.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed;
+
+import com.android.builder.model.AndroidArtifact;
+
+import org.gradle.api.Named;
+import org.gradle.model.Managed;
+import org.gradle.model.ModelSet;
+import org.gradle.model.Unmanaged;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A Managed build type.
+ *
+ * TODO: Convert Unmanaged Collection to Managed type when Gradle provides ModelSet for basic class.
+ */
+@Managed
+public interface BuildType extends Named {
+
+ /**
+ * Map of Build Config Fields where the key is the field name.
+ *
+ * @return a non-null map of class fields (possibly empty).
+ */
+ ModelSet<ClassField> getBuildConfigFields();
+
+ /**
+ * Map of generated res values where the key is the res name.
+ *
+ * @return a non-null map of class fields (possibly empty).
+ */
+ ModelSet<ClassField> getResValues();
+
+ /**
+ * Returns the collection of proguard rule files.
+ *
+ * <p>These files are only applied to the production code.
+ *
+ * @return a non-null collection of files.
+ * @see #getTestProguardFiles()
+ */
+ @Unmanaged
+ Set<File> getProguardFiles();
+ void setProguardFiles(Set<File> files);
+
+ /**
+ * Returns the collection of proguard rule files for consumers of the library to use.
+ *
+ * @return a non-null collection of files.
+ */
+ @Unmanaged
+ Set<File> getConsumerProguardFiles();
+ void setConsumerProguardFiles(Set<File> files);
+
+ /**
+ * Returns the collection of proguard rule files to use for the test APK.
+ *
+ * @return a non-null collection of files.
+ */
+ @Unmanaged
+ Set<File> getTestProguardFiles();
+ void setTestProguardFiles(Set<File> files);
+
+ /**
+ * Returns the map of key value pairs for placeholder substitution in the android manifest file.
+ *
+ * This map will be used by the manifest merger.
+ * @return the map of key value pairs.
+ */
+ // TODO: Add the commented fields.
+ //Map<String, Object> getManifestPlaceholders();
+
+ /**
+ * Returns whether multi-dex is enabled.
+ *
+ * This can be null if the flag is not set, in which case the default value is used.
+ */
+ Boolean getMultiDexEnabled();
+ void setMultiDexEnabled(Boolean multiDexEnabled);
+
+ String getMultiDexKeepFile();
+ void setMultiDexKeepFile(String multiDexKeepFile);
+
+ String getMultiDexKeepProguard();
+ void setMultiDexKeepProguard(String multiDexKeepProguard);
+
+ /**
+ * Returns the optional jarjar rule files, or empty if jarjar should be skipped.
+ *
+ * <p>If more than one file is provided, the rule files will be merged in order with last one
+ * win in case of rule redefinition.
+ *
+ * <p>Can only be used with Jack toolchain.
+ *
+ * @return the optional jarjar rule file.
+ */
+ @Unmanaged
+ List<File> getJarJarRuleFiles();
+ void setJarJarRuleFiles(List<File> jarJarRuleFiles);
+
+ /**
+ * Returns whether the build type is configured to generate a debuggable apk.
+ *
+ * @return true if the apk is debuggable
+ */
+ Boolean getDebuggable();
+ void setDebuggable(Boolean isDebuggable);
+
+ /**
+ * Returns whether the build type is configured to be build with support for code coverage.
+ *
+ * @return true if code coverage is enabled.
+ */
+ Boolean getTestCoverageEnabled();
+ void setTestCoverageEnabled(Boolean isTestCoverageEnabled);
+
+ /**
+ * Returns whether the build type is configured to be build with support for pseudolocales.
+ *
+ * @return true if code coverage is enabled.
+ */
+ Boolean getPseudoLocalesEnabled();
+ void setPseudoLocalesEnabled(Boolean isPseudoLocalesEnabled);
+
+ /**
+ * Returns whether the build type is configured to generate an apk with debuggable
+ * renderscript code.
+ *
+ * @return true if the apk is debuggable
+ */
+ Boolean getRenderscriptDebuggable();
+ void setRenderscriptDebuggable(Boolean isRenderscriptDebuggable);
+
+ /**
+ * Returns the optimization level of the renderscript compilation.
+ *
+ * @return the optimization level.
+ */
+ Integer getRenderscriptOptimLevel();
+ void setRenderscriptOptimLevel(Integer renderscriptOptimLevel);
+
+ /**
+ * Returns the application id suffix applied to this build type.
+ * To get the final application id, use {@link AndroidArtifact#getApplicationId()}.
+ *
+ * @return the application id
+ */
+ String getApplicationIdSuffix();
+ void setApplicationIdSuffix(String applicationIdSuffix);
+
+ /**
+ * Returns the version name suffix.
+ *
+ * @return the version name suffix.
+ */
+ String getVersionNameSuffix();
+ void setVersionNameSuffix(String versionNameSuffix);
+
+ /**
+ * Returns whether minification is enabled for this build type.
+ *
+ * @return true if minification is enabled.
+ */
+ Boolean getMinifyEnabled();
+ void setMinifyEnabled(Boolean isMinifyEnabled);
+
+ /**
+ * Return whether zipalign is enabled for this build type.
+ *
+ * @return true if zipalign is enabled.
+ */
+ Boolean getZipAlignEnabled();
+ void setZipAlignEnabled(Boolean isZipAlignEnabled);
+
+ /**
+ * Returns whether the variant embeds the micro app.
+ */
+ Boolean getEmbedMicroApp();
+ void setEmbedMicroApp(Boolean isEmbedMicroApp);
+
+ /**
+ * Returns the associated signing config or null if none are set on the build type.
+ */
+ SigningConfig getSigningConfig();
+ void setSigningConfig(SigningConfig signingConfig);
+
+ Boolean getUseJack();
+ void setUseJack(Boolean useJack);
+
+ Boolean getShrinkResources();
+ void setShrinkResources(Boolean shrinkResources);
+
+ NdkBuildType getNdk();
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ClassField.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ClassField.java
new file mode 100644
index 0000000..f04492d
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ClassField.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed;
+
+import org.gradle.model.Managed;
+import org.gradle.model.Unmanaged;
+
+import java.util.Set;
+
+/**
+ * A Managed ClassField.
+ */
+@Managed
+public interface ClassField {
+
+ String getType();
+ void setType(String type);
+
+ String getName();
+ void setName(String name);
+
+ String getValue();
+ void setValue(String value);
+
+ String getDocumentation();
+ void setDocumentation(String documentation);
+
+ @Unmanaged
+ Set<String> getAnnotations();
+ void setAnnotations(Set<String> annotations);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/FilePattern.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/FilePattern.java
new file mode 100644
index 0000000..a710e27
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/FilePattern.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed;
+
+import org.gradle.model.Managed;
+import org.gradle.model.ModelSet;
+
+/**
+ * A Managed interface for FilterablePattern.
+ */
+@Managed
+public interface FilePattern {
+
+ ModelSet<ManagedString> getIncludes();
+
+ ModelSet<ManagedString> getExcludes();
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ManagedString.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ManagedString.java
new file mode 100644
index 0000000..0354dcd
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ManagedString.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed;
+
+import org.gradle.model.Managed;
+
+/**
+ * A Managed wrapper of String.
+ *
+ * This really should not be necessary, but at the moment, there is no way to create a ModelSet of
+ * String or equivalent.
+ */
+@Managed
+public interface ManagedString {
+ String getValue();
+ void setValue(String value);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkBuildType.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkBuildType.java
new file mode 100644
index 0000000..4da05e6
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkBuildType.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed;
+
+import org.gradle.model.Managed;
+
+/**
+ * NdkOptions with additional options specific to build types.
+ */
+@Managed
+public interface NdkBuildType extends NdkOptions {
+ /**
+ * Returns whether the resulting shared object is debuggable.
+ */
+ Boolean getDebuggable();
+ void setDebuggable(Boolean isDebuggable);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkConfig.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkConfig.java
new file mode 100644
index 0000000..5240e3e
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkConfig.java
@@ -0,0 +1,30 @@
+package com.android.build.gradle.managed;
+
+import com.android.annotations.NonNull;
+
+import org.gradle.model.Managed;
+import org.gradle.model.Unmanaged;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Root configuration model for android-ndk plugin.
+ */
+@Managed
+public interface NdkConfig extends NdkBuildType {
+
+ /**
+ * The toolchain version.
+ * Support "gcc" or "clang" (default: "gcc").
+ */
+ String getToolchain();
+ void setToolchain(@NonNull String toolchain);
+
+ /**
+ * The toolchain version.
+ * Set as empty to use the default version for the toolchain.
+ */
+ String getToolchainVersion();
+ void setToolchainVersion(@NonNull String toolchainVersion);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkOptions.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkOptions.java
new file mode 100644
index 0000000..344c3ca
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/NdkOptions.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed;
+
+import com.android.annotations.NonNull;
+
+import org.gradle.model.Managed;
+import org.gradle.model.Unmanaged;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * DSL object for variant specific NDK-related settings.
+ */
+@Managed
+public interface NdkOptions {
+
+ /**
+ * The module name.
+ * The resulting shared object will be named "lib${getModuleName()}.so".
+ */
+ String getModuleName();
+ void setModuleName(@NonNull String moduleName);
+
+ /**
+ * The ABI Filters. Leave empty to include all supported ABI.
+ */
+ @Unmanaged
+ Set<String> getAbiFilters();
+ void setAbiFilters(@NonNull Set<String> filters);
+
+ /**
+ * The C Flags
+ */
+ @Unmanaged
+ List<String> getCFlags();
+ void setCFlags(@NonNull List<String> cFlags);
+
+ /**
+ * The C++ Flags
+ */
+ @Unmanaged
+ List<String> getCppFlags();
+ void setCppFlags(@NonNull List<String> cppFlags);
+
+ /**
+ * The linker flags
+ */
+ @Unmanaged
+ List<String> getLdFlags();
+ void setLdFlags(@NonNull List<String> ldFlags);
+
+ /**
+ * The LD Libs
+ */
+ @Unmanaged
+ List<String> getLdLibs();
+ void setLdLibs(@NonNull List<String> ldLibs);
+
+ /**
+ * The STL.
+ *
+ * Supported values are:
+ * - system (default)
+ * - gabi++_static
+ * - gabi++_shared
+ * - stlport_static
+ * - stlport_shared
+ * - gnustl_static
+ * - gnustl_shared
+ */
+ String getStl();
+ void setStl(@NonNull String stl);
+
+ Boolean getRenderscriptNdkMode();
+ void setRenderscriptNdkMode(Boolean renderscriptNdkMode);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ProductFlavor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ProductFlavor.java
new file mode 100644
index 0000000..b3d15ef
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/ProductFlavor.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidArtifact;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.DimensionAware;
+import com.android.builder.model.Variant;
+
+import org.gradle.api.Named;
+import org.gradle.model.Managed;
+import org.gradle.model.ModelSet;
+import org.gradle.model.Unmanaged;
+
+import java.util.List;
+import java.util.Set;
+
+import java.io.File;
+
+/**
+ * A Managed product flavor.
+ *
+ * TODO: Convert Unmanaged Collection to Managed type when Gradle provides ModelSet for basic class.
+ */
+@Managed
+public interface ProductFlavor extends Named, DimensionAware {
+
+ /**
+ * Map of Build Config Fields where the key is the field name.
+ *
+ * @return a non-null map of class fields (possibly empty).
+ */
+ @NonNull
+ ModelSet<ClassField> getBuildConfigFields();
+
+ /**
+ * Map of generated res values where the key is the res name.
+ *
+ * @return a non-null map of class fields (possibly empty).
+ */
+ @NonNull
+ ModelSet<ClassField> getResValues();
+
+ /**
+ * Returns the collection of proguard rule files.
+ *
+ * <p>These files are only applied to the production code.
+ *
+ * @return a non-null collection of files.
+ * @see #getTestProguardFiles()
+ */
+ @Unmanaged
+ Set<File> getProguardFiles();
+ void setProguardFiles(Set<File> files);
+
+ /**
+ * Returns the collection of proguard rule files for consumers of the library to use.
+ *
+ * @return a non-null collection of files.
+ */
+ @Unmanaged
+ Set<File> getConsumerProguardFiles();
+ void setConsumerProguardFiles(Set<File> files);
+
+ /**
+ * Returns the collection of proguard rule files to use for the test APK.
+ *
+ * @return a non-null collection of files.
+ */
+ @Unmanaged
+ Set<File> getTestProguardFiles();
+ void setTestProguardFiles(Set<File> files);
+
+ /**
+ * Returns the map of key value pairs for placeholder substitution in the android manifest file.
+ *
+ * This map will be used by the manifest merger.
+ * @return the map of key value pairs.
+ */
+ // TODO: Add the commented fields.
+ //Map<String, Object> getManifestPlaceholders();
+
+ /**
+ * Returns whether multi-dex is enabled.
+ *
+ * This can be null if the flag is not set, in which case the default value is used.
+ */
+ @Nullable
+ Boolean getMultiDexEnabled();
+ void setMultiDexEnabled(Boolean multiDexEnabled);
+
+ @Nullable
+ File getMultiDexKeepFile();
+ void setMultiDexKeepFile(File multiDexKeepFile);
+
+ @Nullable
+ File getMultiDexKeepProguard();
+ void setMultiDexKeepProguard(File multiDexKeepProguard);
+
+ /**
+ * Returns the optional jarjar rule files, or empty if jarjar should be skipped.
+ *
+ * <p>If more than one file is provided, the rule files will be merged in order with last one
+ * win in case of rule redefinition.
+ *
+ * <p>Can only be used with Jack toolchain.
+ *
+ * @return the optional jarjar rule file.
+ */
+ @Unmanaged
+ List<File> getJarJarRuleFiles();
+ void setJarJarRuleFiles(List<File> jarJarRuleFiles);
+
+ /**
+ * Returns the flavor dimension or null if not applicable.
+ */
+ @Override
+ @Nullable
+ String getDimension();
+ void setDimension(String dimension);
+
+ /**
+ * Returns the name of the product flavor. This is only the value set on this product flavor.
+ * To get the final application id name, use {@link AndroidArtifact#getApplicationId()}.
+ *
+ * @return the application id.
+ */
+ @Nullable
+ String getApplicationId();
+ void setApplicationId(String applicationId);
+
+ /**
+ * Returns the version code associated with this flavor or null if none have been set.
+ * This is only the value set on this product flavor, not necessarily the actual
+ * version code used.
+ *
+ * @return the version code, or null if not specified
+ */
+ @Nullable
+ Integer getVersionCode();
+ void setVersionCode(Integer versionCode);
+
+ /**
+ * Returns the version name. This is only the value set on this product flavor.
+ * To get the final value, use {@link Variant#getMergedFlavor()} as well as
+ * {@link BuildType#getVersionNameSuffix()}
+ *
+ * @return the version name.
+ */
+ @Nullable
+ String getVersionName();
+ void setVersionName(String versionName);
+
+ /**
+ * Returns the minSdkVersion. This is only the value set on this product flavor.
+ *
+ * @return the minSdkVersion, or null if not specified
+ */
+ @Nullable
+ ApiVersion getMinSdkVersion();
+
+ /**
+ * Returns the targetSdkVersion. This is only the value set on this product flavor.
+ *
+ * @return the targetSdkVersion, or null if not specified
+ */
+ @Nullable
+ ApiVersion getTargetSdkVersion();
+
+ /**
+ * Returns the maxSdkVersion. This is only the value set on this produce flavor.
+ *
+ * @return the maxSdkVersion, or null if not specified
+ */
+ @Nullable
+ Integer getMaxSdkVersion();
+ void setMaxSdkVersion(Integer maxSdkVersion);
+
+ /**
+ * Returns the renderscript target api. This is only the value set on this product flavor.
+ * TODO: make final renderscript target api available through the model
+ *
+ * @return the renderscript target api, or null if not specified
+ */
+ @Nullable
+ Integer getRenderscriptTargetApi();
+ void setRenderscriptTargetApi(Integer renderscriptTargetApi);
+
+ /**
+ * Returns whether the renderscript code should be compiled in support mode to
+ * make it compatible with older versions of Android.
+ *
+ * @return true if support mode is enabled, false if not, and null if not specified.
+ */
+ @Nullable
+ Boolean getRenderscriptSupportModeEnabled();
+ void setRenderscriptSupportModeEnabled(Boolean renderscriptSupportModeEnabled);
+
+ /**
+ * Returns whether the renderscript code should be compiled to generate C/C++ bindings.
+ * @return true for C/C++ generation, false for Java, null if not specified.
+ */
+ @Nullable
+ Boolean getRenderscriptNdkModeEnabled();
+ void setRenderscriptNdkModeEnabled(Boolean renderscriptNdkModeEnabled);
+
+ /**
+ * Returns the test application id. This is only the value set on this product flavor.
+ * To get the final value, use {@link Variant#getExtraAndroidArtifacts()} with
+ * {@link AndroidProject#ARTIFACT_ANDROID_TEST} and then
+ * {@link AndroidArtifact#getApplicationId()}
+ *
+ * @return the test package name.
+ */
+ @Nullable
+ String getTestApplicationId();
+ void setTestApplicationId(String testApplicationId);
+
+ /**
+ * Returns the test instrumentation runner. This is only the value set on this product flavor.
+ * TODO: make test instrumentation runner available through the model.
+ *
+ * @return the test package name.
+ */
+ @Nullable
+ String getTestInstrumentationRunner();
+ void setTestInstrumentationRunner(String testInstrumentationRunner);
+
+ /**
+ * Returns the handlingProfile value. This is only the value set on this product flavor.
+ *
+ * @return the handlingProfile value.
+ */
+ @Nullable
+ Boolean getTestHandleProfiling();
+ void setTestHandleProfiling(Boolean testHandleProfiling);
+
+ /**
+ * Returns the functionalTest value. This is only the value set on this product flavor.
+ *
+ * @return the functionalTest value.
+ */
+ @Nullable
+ Boolean getTestFunctionalTest();
+ void setTestFunctionalTest(Boolean testFunctionalTest);
+
+ /**
+ * Returns the resource configuration for this variant.
+ *
+ * This is the list of -c parameters for aapt.
+ *
+ * @return the resource configuration options.
+ */
+ @Unmanaged
+ @Nullable
+ Set<String> getResourceConfigurations();
+ void setResourceConfigurations(Set<String> resourceConfigurations);
+
+ /**
+ * Returns the associated signing config or null if none are set on the product flavor.
+ */
+ SigningConfig getSigningConfig();
+ void setSigningConfig(SigningConfig signingConfig);
+
+ Boolean getUseJack();
+ void setUseJack(Boolean useJack);
+
+ NdkOptions getNdk();
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/SigningConfig.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/SigningConfig.java
new file mode 100644
index 0000000..627f5b6
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/SigningConfig.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed;
+
+import org.gradle.api.Named;
+import org.gradle.model.Managed;
+
+/**
+ * A Managed SigningConfig.
+ */
+@Managed
+public interface SigningConfig extends Named {
+
+ String getStoreFile();
+ void setStoreFile(String storeFile);
+
+ String getStorePassword();
+ void setStorePassword(String storePassword);
+
+ String getKeyAlias();
+ void setKeyAlias(String keyAlias);
+
+ String getKeyPassword();
+ void setKeyPassword(String keyPassword);
+
+ String getStoreType();
+ void setStoreType(String storeType);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/AndroidConfigAdaptor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/AndroidConfigAdaptor.java
new file mode 100644
index 0000000..f2f118e
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/AndroidConfigAdaptor.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed.adaptor;
+
+import static com.android.builder.core.VariantType.ANDROID_TEST;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.api.AndroidSourceDirectorySet;
+import com.android.build.gradle.api.AndroidSourceFile;
+import com.android.build.gradle.api.AndroidSourceSet;
+import com.android.build.gradle.api.VariantFilter;
+import com.android.build.gradle.internal.BuildTypeData;
+import com.android.build.gradle.internal.CompileOptions;
+import com.android.build.gradle.internal.ProductFlavorData;
+import com.android.build.gradle.internal.VariantManager;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.internal.coverage.JacocoExtension;
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.dsl.AdbOptions;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
+import com.android.build.gradle.internal.dsl.DexOptions;
+import com.android.build.gradle.internal.dsl.LintOptions;
+import com.android.build.gradle.internal.dsl.PackagingOptions;
+import com.android.build.gradle.internal.dsl.PreprocessingOptions;
+import com.android.build.gradle.internal.dsl.Splits;
+import com.android.build.gradle.internal.dsl.TestOptions;
+import com.android.build.gradle.managed.BuildType;
+import com.android.build.gradle.managed.ProductFlavor;
+import com.android.build.gradle.managed.SigningConfig;
+import com.android.build.gradle.model.AndroidComponentModelSourceSet;
+import com.android.build.gradle.managed.AndroidConfig;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.core.LibraryRequest;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.builder.testing.api.TestServer;
+import com.android.ide.common.rendering.api.ActionBarCallback;
+import com.android.sdklib.repository.FullRevision;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import org.gradle.api.Action;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.file.SourceDirectorySet;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import groovy.lang.Closure;
+
+/**
+ * An adaptor to convert a managed.AndroidConfig to an model.AndroidConfig.
+ */
+public class AndroidConfigAdaptor implements com.android.build.gradle.AndroidConfig {
+
+ private final AndroidConfig model;
+ private NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer;
+
+ public AndroidConfigAdaptor(
+ AndroidConfig model,
+ NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer) {
+ this.model = model;
+ this.sourceSetsContainer = sourceSetsContainer;
+ applyProjectSourceSet();
+ }
+
+ @Override
+ public String getBuildToolsVersion() {
+ return model.getBuildToolsVersion();
+ }
+
+ @Override
+ public String getCompileSdkVersion() {
+ return model.getCompileSdkVersion();
+ }
+
+ @Override
+ public FullRevision getBuildToolsRevision() {
+ return model.getBuildToolsRevision();
+ }
+
+ @Override
+ public boolean getEnforceUniquePackageName() {
+ return false;
+ }
+
+ @Override
+ public CoreProductFlavor getDefaultConfig() {
+ return new ProductFlavorAdaptor(model.getDefaultConfig());
+ }
+
+ @Override
+ @NonNull
+ public List<DeviceProvider> getDeviceProviders() {
+ return model.getDeviceProviders() == null ?
+ Lists.<DeviceProvider>newArrayList() :
+ model.getDeviceProviders();
+ }
+
+ @Override
+ @NonNull
+ public List<TestServer> getTestServers() {
+ return model.getTestServers();
+ }
+
+ @Override
+ public String getDefaultPublishConfig() {
+ return model.getDefaultPublishConfig();
+ }
+
+ @Override
+ public boolean getPublishNonDefault() {
+ return model.getPublishNonDefault();
+ }
+
+ @Override
+ public Action<VariantFilter> getVariantFilter() {
+ return model.getVariantFilter();
+ }
+
+ @Override
+ public String getResourcePrefix() {
+ return model.getResourcePrefix();
+ }
+
+ @Override
+ public List<String> getFlavorDimensionList() {
+ return null;
+ }
+
+ @Override
+ public boolean getGeneratePureSplits() {
+ return model.getGeneratePureSplits();
+ }
+
+ @Override
+ public PreprocessingOptions getPreprocessingOptions() {
+ return model.getPreProcessingOptions();
+ }
+
+ @Override
+ public Collection<CoreBuildType> getBuildTypes() {
+ return ImmutableList.copyOf(Iterables.transform(model.getBuildTypes().values(),
+ new Function<BuildType, CoreBuildType>() {
+ @Override
+ public CoreBuildType apply(BuildType buildType) {
+ return new BuildTypeAdaptor(buildType);
+ }
+ }));
+ }
+
+ @Override
+ public Collection<CoreProductFlavor> getProductFlavors() {
+ return ImmutableList.copyOf(Iterables.transform(model.getProductFlavors().values(),
+ new Function<ProductFlavor, CoreProductFlavor>() {
+ @Override
+ public CoreProductFlavor apply(ProductFlavor flavor) {
+ return new ProductFlavorAdaptor(flavor);
+ }
+ }));
+ }
+
+ @Override
+ public Collection<com.android.builder.model.SigningConfig> getSigningConfigs() {
+ return ImmutableList.copyOf(Iterables.transform(model.getSigningConfigs().values(),
+ new Function<SigningConfig, com.android.builder.model.SigningConfig>() {
+ @Override
+ public com.android.builder.model.SigningConfig apply(SigningConfig signingConfig) {
+ return new SigningConfigAdaptor(signingConfig);
+ }
+ }));
+ }
+
+ @Override
+ public NamedDomainObjectContainer<AndroidSourceSet> getSourceSets() {
+ return sourceSetsContainer;
+ }
+
+ @Override
+ public Boolean getPackageBuildConfig() {
+ return true;
+ }
+
+ public AndroidComponentModelSourceSet getSources() {
+ return model.getSources();
+ }
+
+ public void setSources(AndroidComponentModelSourceSet sources) {
+ model.setSources(sources);
+ }
+
+ public CoreNdkOptions getNdk() {
+ return new NdkOptionsAdaptor(model.getNdk());
+ }
+
+ @Override
+ public AdbOptions getAdbOptions() {
+ return model.getAdbOptions();
+ }
+
+ @Override
+ public AaptOptions getAaptOptions() {
+ return model.getAaptOptions();
+ }
+
+ @Override
+ public CompileOptions getCompileOptions() {
+ return model.getCompileOptions();
+ }
+
+ @Override
+ public DexOptions getDexOptions() {
+ return model.getDexOptions();
+ }
+
+ @Override
+ public JacocoExtension getJacoco() {
+ return model.getJacoco();
+ }
+
+ @Override
+ public LintOptions getLintOptions() {
+ return model.getLintOptions();
+ }
+
+ @Override
+ public PackagingOptions getPackagingOptions() {
+ return model.getPackagingOptions();
+ }
+
+
+ @Override
+ public TestOptions getTestOptions() {
+ return model.getTestOptions();
+ }
+
+ @Override
+ public Splits getSplits() {
+ return model.getSplits();
+ }
+
+ @Override
+ public Collection<LibraryRequest> getLibraryRequests() {
+ return model.getLibraryRequests();
+ }
+
+ private void applyProjectSourceSet() {
+ for (FunctionalSourceSet source : getSources()) {
+ String name = source.getName();
+ AndroidSourceSet androidSource = name.equals(BuilderConstants.MAIN) ?
+ sourceSetsContainer.maybeCreate(getDefaultConfig().getName()) :
+ sourceSetsContainer.maybeCreate(name);
+
+ convertSourceFile(androidSource.getManifest(), source, "manifest");
+ convertSourceSet(androidSource.getResources(), source, "resource");
+ convertSourceSet(androidSource.getJava(), source, "java");
+ convertSourceSet(androidSource.getRes(), source, "res");
+ convertSourceSet(androidSource.getAssets(), source, "assets");
+ convertSourceSet(androidSource.getAidl(), source, "aidl");
+ convertSourceSet(androidSource.getRenderscript(), source, "renderscript");
+ convertSourceSet(androidSource.getJni(), source, "jni");
+ convertSourceSet(androidSource.getJniLibs(), source, "jniLibs");
+ }
+ }
+
+ @Nullable
+ private static AndroidSourceSet findAndroidSourceSet(
+ VariantManager variantManager,
+ String name) {
+ BuildTypeData buildTypeData = variantManager.getBuildTypes().get(name);
+ if (buildTypeData != null) {
+ return buildTypeData.getSourceSet();
+ }
+
+ boolean isTest = name.startsWith(ANDROID_TEST.getPrefix());
+ name = name.replaceFirst(ANDROID_TEST.getPrefix(), "");
+ ProductFlavorData productFlavorData = variantManager.getProductFlavors().get(name);
+ if (productFlavorData != null) {
+ return isTest ? productFlavorData.getTestSourceSet(ANDROID_TEST) : productFlavorData.getSourceSet();
+ }
+ return null;
+ }
+
+ /**
+ * Convert a FunctionalSourceSet to an AndroidSourceFile.
+ */
+ private static void convertSourceFile(
+ AndroidSourceFile androidFile,
+ FunctionalSourceSet source,
+ String sourceName) {
+ LanguageSourceSet languageSourceSet = source.findByName(sourceName);
+ if (languageSourceSet == null) {
+ return;
+ }
+ SourceDirectorySet dir = languageSourceSet.getSource();
+ if (dir == null) {
+ return;
+ }
+ // We use the first file in the file tree until Gradle has a way to specify one source file
+ // instead of an entire source set.
+ Set<File> files = dir.getAsFileTree().getFiles();
+ if (!files.isEmpty()) {
+ androidFile.srcFile(Iterables.getOnlyElement(files));
+ }
+ }
+
+ /**
+ * Convert a FunctionalSourceSet to an AndroidSourceDirectorySet.
+ */
+ private static void convertSourceSet(
+ AndroidSourceDirectorySet androidDir,
+ FunctionalSourceSet source,
+ String sourceName) {
+ LanguageSourceSet languageSourceSet = source.findByName(sourceName);
+ if (languageSourceSet == null) {
+ return;
+ }
+ SourceDirectorySet dir = languageSourceSet.getSource();
+ if (dir == null) {
+ return;
+ }
+ androidDir.setSrcDirs(dir.getSrcDirs());
+ androidDir.include(dir.getIncludes());
+ androidDir.exclude(dir.getExcludes());
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/ApiVersionAdaptor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/ApiVersionAdaptor.java
new file mode 100644
index 0000000..9a17558
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/ApiVersionAdaptor.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed.adaptor;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.managed.ApiVersion;
+
+/**
+ * An adaptor to convert a managed.ApiVersion to an model.ApiVersion.
+ */
+public class ApiVersionAdaptor implements com.android.builder.model.ApiVersion {
+
+ private final ApiVersion apiVersion;
+
+ public static boolean isEmpty(ApiVersion apiVersion) {
+ return apiVersion.getApiLevel() == null &&
+ apiVersion.getCodename() == null;
+ }
+
+ public ApiVersionAdaptor(ApiVersion apiVersion) {
+ this.apiVersion = apiVersion;
+ }
+
+ @Override
+ public int getApiLevel() {
+ return apiVersion.getApiLevel() == null ? 0 : apiVersion.getApiLevel();
+ }
+
+ @Nullable
+ @Override
+ public String getCodename() {
+ return apiVersion.getCodename();
+ }
+
+ @NonNull
+ @Override
+ public String getApiString() {
+ return getCodename() != null ? getCodename() : Integer.toString(getApiLevel());
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/BuildTypeAdaptor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/BuildTypeAdaptor.java
new file mode 100644
index 0000000..b6f364d
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/BuildTypeAdaptor.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed.adaptor;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.managed.BuildType;
+import com.android.builder.internal.ClassFieldImpl;
+import com.android.builder.model.ClassField;
+import com.android.builder.model.SigningConfig;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An adaptor to convert a BuildType to a CoreBuildType.
+ */
+public class BuildTypeAdaptor implements CoreBuildType {
+ @NonNull
+ private final BuildType buildType;
+
+ public BuildTypeAdaptor(@NonNull BuildType buildType) {
+ this.buildType = buildType;
+ }
+
+ @NonNull
+ @Override
+ public String getName() {
+ return buildType.getName();
+ }
+
+ @NonNull
+ @Override
+ public Map<String, ClassField> getBuildConfigFields() {
+ ImmutableMap.Builder<String, ClassField> builder = ImmutableMap.builder();
+ for (com.android.build.gradle.managed.ClassField cf : buildType.getBuildConfigFields()) {
+ builder.put(
+ cf.getName(),
+ new ClassFieldImpl(
+ cf.getType(),
+ cf.getName(),
+ cf.getValue(),
+ Objects.firstNonNull(cf.getAnnotations(), ImmutableSet.<String>of()),
+ Objects.firstNonNull(cf.getDocumentation(), "")));
+ }
+ return builder.build();
+ }
+
+ @NonNull
+ @Override
+ public Map<String, ClassField> getResValues() {
+ ImmutableMap.Builder<String, ClassField> builder = ImmutableMap.builder();
+ for (com.android.build.gradle.managed.ClassField cf : buildType.getResValues()) {
+ builder.put(
+ cf.getName(),
+ new ClassFieldImpl(
+ cf.getType(),
+ cf.getName(),
+ cf.getValue(),
+ Objects.firstNonNull(cf.getAnnotations(), ImmutableSet.<String>of()),
+ Objects.firstNonNull(cf.getDocumentation(), "")));
+ }
+ return builder.build();
+ }
+
+ @NonNull
+ @Override
+ public Collection<File> getProguardFiles() {
+ return buildType.getProguardFiles();
+ }
+
+ @NonNull
+ @Override
+ public Collection<File> getConsumerProguardFiles() {
+ return buildType.getConsumerProguardFiles();
+ }
+
+ @NonNull
+ @Override
+ public Collection<File> getTestProguardFiles() {
+ return buildType.getTestProguardFiles();
+ }
+
+ @NonNull
+ @Override
+ public Map<String, Object> getManifestPlaceholders() {
+ // TODO: To be implemented
+ return Maps.newHashMap();
+ }
+
+ @Nullable
+ @Override
+ public Boolean getMultiDexEnabled() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public File getMultiDexKeepFile() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public File getMultiDexKeepProguard() {
+ return null;
+ }
+
+ @Override
+ public boolean isDebuggable() {
+ return buildType.getDebuggable();
+ }
+
+ @Override
+ public boolean isTestCoverageEnabled() {
+ return buildType.getTestCoverageEnabled();
+ }
+
+ @Override
+ public boolean isJniDebuggable() {
+ return Objects.firstNonNull(buildType.getNdk().getDebuggable(), false);
+ }
+
+ @Override
+ public boolean isPseudoLocalesEnabled() {
+ return buildType.getPseudoLocalesEnabled();
+ }
+
+ @Override
+ public boolean isRenderscriptDebuggable() {
+ return buildType.getRenderscriptDebuggable();
+ }
+
+ @Override
+ public int getRenderscriptOptimLevel() {
+ return buildType.getRenderscriptOptimLevel();
+ }
+
+ @Nullable
+ @Override
+ public String getApplicationIdSuffix() {
+ return buildType.getApplicationIdSuffix();
+ }
+
+ @Nullable
+ @Override
+ public String getVersionNameSuffix() {
+ return buildType.getVersionNameSuffix();
+ }
+
+ @Override
+ public boolean isMinifyEnabled() {
+ return buildType.getMinifyEnabled();
+ }
+
+ @Override
+ public boolean isZipAlignEnabled() {
+ return buildType.getZipAlignEnabled();
+ }
+
+ @Override
+ public boolean isEmbedMicroApp() {
+ return buildType.getEmbedMicroApp();
+ }
+
+ @Nullable
+ @Override
+ public SigningConfig getSigningConfig() {
+ return buildType.getSigningConfig() == null ? null : new SigningConfigAdaptor(buildType.getSigningConfig());
+ }
+
+ @Override
+ public CoreNdkOptions getNdkConfig() {
+ return new NdkOptionsAdaptor(buildType.getNdk());
+ }
+
+ @Override
+ public Boolean getUseJack() {
+ return buildType.getUseJack();
+ }
+
+ @Override
+ public boolean isShrinkResources() {
+ return buildType.getShrinkResources();
+ }
+
+ @NonNull
+ @Override
+ public List<File> getJarJarRuleFiles() {
+ return buildType.getJarJarRuleFiles();
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/NdkOptionsAdaptor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/NdkOptionsAdaptor.java
new file mode 100644
index 0000000..4643423
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/NdkOptionsAdaptor.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed.adaptor;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.managed.NdkOptions;
+import com.google.common.base.Joiner;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * An adaptor to convert a NdkConfig to NdkConfig.
+ */
+public class NdkOptionsAdaptor implements CoreNdkOptions {
+
+ NdkOptions ndkOptions;
+
+ public NdkOptionsAdaptor(@NonNull NdkOptions ndkOptions) {
+ this.ndkOptions = ndkOptions;
+ }
+
+ @Nullable
+ @Override
+ public String getModuleName() {
+ return ndkOptions.getModuleName();
+ }
+
+ @Nullable
+ @Override
+ public String getcFlags() {
+ return Joiner.on(' ').join(ndkOptions.getCFlags());
+ }
+
+ @Nullable
+ @Override
+ public List<String> getLdLibs() {
+ return ndkOptions.getLdLibs();
+ }
+
+ @Nullable
+ @Override
+ public Set<String> getAbiFilters() {
+ return ndkOptions.getAbiFilters();
+ }
+
+ @Nullable
+ @Override
+ public String getStl() {
+ return ndkOptions.getStl();
+ }
+
+ @Nullable
+ @Override
+ public Integer getJobs() {
+ return null;
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/ProductFlavorAdaptor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/ProductFlavorAdaptor.java
new file mode 100644
index 0000000..68639f7
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/ProductFlavorAdaptor.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed.adaptor;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
+import com.android.build.gradle.managed.ProductFlavor;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.internal.ClassFieldImpl;
+import com.android.builder.model.ApiVersion;
+import com.android.builder.model.ClassField;
+import com.android.builder.model.SigningConfig;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An adaptor to convert a ProductFlavor to CoreProductFlavor.
+ */
+public class ProductFlavorAdaptor implements CoreProductFlavor {
+
+ @NonNull
+ protected final ProductFlavor productFlavor;
+
+ public ProductFlavorAdaptor(@NonNull ProductFlavor productFlavor) {
+ this.productFlavor = productFlavor;
+ }
+
+ @NonNull
+ @Override
+ public String getName() {
+ return productFlavor.getName().equals("defaultConfig") ? BuilderConstants.MAIN : productFlavor.getName();
+ }
+
+ @Nullable
+ @Override
+ public String getDimension() {
+ return productFlavor.getDimension();
+ }
+
+ @NonNull
+ @Override
+ public Map<String, ClassField> getBuildConfigFields() {
+ ImmutableMap.Builder<String, ClassField> builder = ImmutableMap.builder();
+ for (com.android.build.gradle.managed.ClassField cf : productFlavor.getBuildConfigFields()) {
+ builder.put(
+ cf.getName(),
+ new ClassFieldImpl(
+ cf.getType(),
+ cf.getName(),
+ cf.getValue(),
+ Objects.firstNonNull(cf.getAnnotations(), ImmutableSet.<String>of()),
+ Objects.firstNonNull(cf.getDocumentation(), "")));
+ }
+ return builder.build();
+ }
+
+ @NonNull
+ @Override
+ public Map<String, ClassField> getResValues() {
+ ImmutableMap.Builder<String, ClassField> builder = ImmutableMap.builder();
+ for (com.android.build.gradle.managed.ClassField cf : productFlavor.getResValues()) {
+ builder.put(
+ cf.getName(),
+ new ClassFieldImpl(
+ cf.getType(),
+ cf.getName(),
+ cf.getValue(),
+ Objects.firstNonNull(cf.getAnnotations(), ImmutableSet.<String>of()),
+ Objects.firstNonNull(cf.getDocumentation(), "")));
+ }
+ return builder.build();
+ }
+
+ @NonNull
+ @Override
+ public Collection<File> getProguardFiles() {
+ return productFlavor.getProguardFiles();
+ }
+
+ @NonNull
+ @Override
+ public Collection<File> getConsumerProguardFiles() {
+ return productFlavor.getConsumerProguardFiles();
+ }
+
+ @NonNull
+ @Override
+ public Collection<File> getTestProguardFiles() {
+ return productFlavor.getTestProguardFiles();
+ }
+
+ @NonNull
+ @Override
+ public Map<String, Object> getManifestPlaceholders() {
+ // TODO: To be implemented
+ return Maps.newHashMap();
+ }
+
+ @Nullable
+ @Override
+ public Boolean getMultiDexEnabled() {
+ return productFlavor.getMultiDexEnabled();
+ }
+
+ @Nullable
+ @Override
+ public File getMultiDexKeepFile() {
+ return productFlavor.getMultiDexKeepFile();
+ }
+
+ @Nullable
+ @Override
+ public File getMultiDexKeepProguard() {
+ return productFlavor.getMultiDexKeepProguard();
+ }
+
+ @Nullable
+ @Override
+ public String getApplicationId() {
+ return productFlavor.getApplicationId();
+ }
+
+ @Nullable
+ @Override
+ public Integer getVersionCode() {
+ return productFlavor.getVersionCode();
+ }
+
+ @Nullable
+ @Override
+ public String getVersionName() {
+ return productFlavor.getVersionName();
+ }
+
+ @Nullable
+ @Override
+ public ApiVersion getMinSdkVersion() {
+ return ApiVersionAdaptor.isEmpty(productFlavor.getMinSdkVersion()) ?
+ null :
+ new ApiVersionAdaptor(productFlavor.getMinSdkVersion());
+ }
+
+ @Nullable
+ @Override
+ public ApiVersion getTargetSdkVersion() {
+ return ApiVersionAdaptor.isEmpty(productFlavor.getTargetSdkVersion()) ?
+ null :
+ new ApiVersionAdaptor(productFlavor.getTargetSdkVersion());
+ }
+
+ @Nullable
+ @Override
+ public Integer getMaxSdkVersion() {
+ return productFlavor.getMaxSdkVersion();
+ }
+
+ @Nullable
+ @Override
+ public Integer getRenderscriptTargetApi() {
+ return productFlavor.getRenderscriptTargetApi();
+ }
+
+ @Nullable
+ @Override
+ public Boolean getRenderscriptSupportModeEnabled() {
+ return productFlavor.getRenderscriptSupportModeEnabled();
+ }
+
+ @Nullable
+ @Override
+ public Boolean getRenderscriptNdkModeEnabled() {
+ return productFlavor.getRenderscriptNdkModeEnabled();
+ }
+
+ @Nullable
+ @Override
+ public String getTestApplicationId() {
+ return productFlavor.getTestApplicationId();
+ }
+
+ @Nullable
+ @Override
+ public String getTestInstrumentationRunner() {
+ return productFlavor.getTestInstrumentationRunner();
+ }
+
+ @NonNull
+ @Override
+ public Map<String, String> getTestInstrumentationRunnerArguments() {
+ // TODO: To be implemented.
+ return Maps.newHashMap();
+ }
+
+ @Nullable
+ @Override
+ public Boolean getTestHandleProfiling() {
+ return productFlavor.getTestHandleProfiling();
+ }
+
+ @Nullable
+ @Override
+ public Boolean getTestFunctionalTest() {
+ return productFlavor.getTestFunctionalTest();
+ }
+
+ @NonNull
+ @Override
+ public Collection<String> getResourceConfigurations() {
+ return productFlavor.getResourceConfigurations();
+ }
+
+ @Nullable
+ @Override
+ public SigningConfig getSigningConfig() {
+ return productFlavor.getSigningConfig() == null ?
+ null :
+ new SigningConfigAdaptor(productFlavor.getSigningConfig());
+ }
+
+ @Override
+ public CoreNdkOptions getNdkConfig() {
+ return new NdkOptionsAdaptor(productFlavor.getNdk());
+ }
+
+ @Override
+ public Boolean getUseJack() {
+ return productFlavor.getUseJack();
+ }
+
+ @NonNull
+ @Override
+ public List<File> getJarJarRuleFiles() {
+ return productFlavor.getJarJarRuleFiles();
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/SigningConfigAdaptor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/SigningConfigAdaptor.java
new file mode 100644
index 0000000..ddf199c
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/managed/adaptor/SigningConfigAdaptor.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.managed.adaptor;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.dsl.CoreSigningConfig;
+import com.android.build.gradle.managed.SigningConfig;
+
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Optional;
+
+import java.io.File;
+
+/**
+ * An adaptor to convert a managed.SigningConfig to a model.SigningConfig.
+ */
+public class SigningConfigAdaptor implements CoreSigningConfig {
+
+ @NonNull
+ private final SigningConfig signingConfig;
+
+ public SigningConfigAdaptor(@NonNull SigningConfig signingConfig) {
+ this.signingConfig = signingConfig;
+ }
+
+ @NonNull
+ @Override
+ public String getName() {
+ return signingConfig.getName();
+ }
+
+ @Nullable
+ @Override
+ @InputFile @Optional
+ public File getStoreFile() {
+ return signingConfig.getStoreFile() == null ? null : new File(signingConfig.getStoreFile());
+ }
+
+ @Nullable
+ @Override
+ @Input
+ public String getStorePassword() {
+ return signingConfig.getStorePassword();
+ }
+
+ @Nullable
+ @Override
+ @Input
+ public String getKeyAlias() {
+ return signingConfig.getKeyAlias();
+ }
+
+ @Nullable
+ @Override
+ public String getKeyPassword() {
+ return signingConfig.getKeyPassword();
+ }
+
+ @Nullable
+ @Override
+ @Input
+ public String getStoreType() {
+ return signingConfig.getStoreType();
+ }
+
+ @Override
+ public boolean isSigningReady() {
+ return signingConfig.getStoreFile() != null &&
+ signingConfig.getStorePassword() != null &&
+ signingConfig.getKeyAlias() != null &&
+ signingConfig.getKeyPassword() != null;
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidBinary.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidBinary.java
index 5218247..2fede34 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidBinary.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidBinary.java
@@ -16,9 +16,8 @@
package com.android.build.gradle.model;
-import com.android.build.gradle.api.GroupableProductFlavor;
-import com.android.builder.model.BuildType;
-import com.android.builder.model.ProductFlavor;
+import com.android.build.gradle.managed.BuildType;
+import com.android.build.gradle.managed.ProductFlavor;
import org.gradle.platform.base.BinarySpec;
@@ -30,5 +29,5 @@
public interface AndroidBinary extends BinarySpec {
BuildType getBuildType();
- List<? extends ProductFlavor> getProductFlavors();
+ List<ProductFlavor> getProductFlavors();
}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelPlugin.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelPlugin.groovy
deleted file mode 100644
index db13b2e..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelPlugin.groovy
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.model
-
-import com.android.build.gradle.internal.ProductFlavorCombo
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.BuildTypeFactory
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.GroupableProductFlavorFactory
-import com.android.builder.core.BuilderConstants
-import groovy.transform.CompileStatic
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.language.base.ProjectSourceSet
-import org.gradle.language.base.internal.registry.LanguageRegistry
-import org.gradle.language.base.plugins.ComponentModelBasePlugin
-import org.gradle.model.Defaults
-import org.gradle.model.Finalize
-import org.gradle.model.Model
-import org.gradle.model.Mutate
-import org.gradle.model.Path
-import org.gradle.model.RuleSource
-import org.gradle.model.collection.CollectionBuilder
-import org.gradle.model.internal.core.ModelCreators
-import org.gradle.model.internal.core.ModelPath
-import org.gradle.model.internal.core.ModelReference
-import org.gradle.model.internal.registry.ModelRegistry
-import org.gradle.platform.base.Binary
-import org.gradle.platform.base.BinaryContainer
-import org.gradle.platform.base.BinaryType
-import org.gradle.platform.base.BinaryTypeBuilder
-import org.gradle.platform.base.ComponentBinaries
-import org.gradle.platform.base.ComponentSpecContainer
-import org.gradle.platform.base.ComponentType
-import org.gradle.platform.base.ComponentTypeBuilder
-import org.gradle.platform.base.LanguageType
-import org.gradle.platform.base.LanguageTypeBuilder
-
-import javax.inject.Inject
-
-/**
- * Plugin to set up infrastructure for other android plugins.
- */
-@CompileStatic
-public class AndroidComponentModelPlugin implements Plugin<Project> {
- /**
- * The name of ComponentSpec created with android component model plugin.
- */
- public static final String COMPONENT_NAME = "android";
-
- private final ModelRegistry modelRegistry
-
- @Inject
- public AndroidComponentModelPlugin(ModelRegistry modelRegistry) {
- this.modelRegistry = modelRegistry
- }
-
- @Override
- public void apply(Project project) {
- project.apply plugin: ComponentModelBasePlugin
-
- // Remove this when our models no longer depends on Project.
- modelRegistry.create(
- ModelCreators.bridgedInstance(
- ModelReference.of("projectModel", Project), project)
- .descriptor("Model of project.")
- .build())
- }
-
- static class Rules extends RuleSource {
- @LanguageType
- void registerLanguage(LanguageTypeBuilder<AndroidLanguageSourceSet> builder) {
- builder.setLanguageName("android")
- builder.defaultImplementation(AndroidLanguageSourceSet)
- }
-
- @Model("android")
- void android(AndroidModel androidModel) {
- }
-
- // Initialize each component separately to ensure correct ordering.
- @Defaults
- void androidModelBuildTypes (
- AndroidModel androidModel,
- @Path("androidBuildTypes") NamedDomainObjectContainer<BuildType> buildTypes) {
- androidModel.buildTypes = buildTypes
- }
-
- @Defaults
- void androidModelProductFlavors (
- AndroidModel androidModel,
- @Path("androidProductFlavors") NamedDomainObjectContainer<GroupableProductFlavor> productFlavors) {
- androidModel.productFlavors = productFlavors
- }
-
- @Defaults
- void androidModelSources (
- AndroidModel androidModel,
- @Path("androidSources") AndroidComponentModelSourceSet sources) {
- androidModel.sources = sources
- }
-
- @Model
- NamedDomainObjectContainer<BuildType> androidBuildTypes(
- ServiceRegistry serviceRegistry,
- Project project) {
- Instantiator instantiator = serviceRegistry.get(Instantiator.class)
- def buildTypeContainer = project.container(BuildType,
- new BuildTypeFactory(instantiator, project, project.getLogger()))
-
- // create default Objects, signingConfig first as its used by the BuildTypes.
- buildTypeContainer.create(BuilderConstants.DEBUG)
- buildTypeContainer.create(BuilderConstants.RELEASE)
-
- buildTypeContainer.whenObjectRemoved {
- throw new UnsupportedOperationException("Removing build types is not supported.")
- }
- return buildTypeContainer
- }
-
- @Model
- NamedDomainObjectContainer<GroupableProductFlavor> androidProductFlavors(
- ServiceRegistry serviceRegistry,
- Project project) {
- Instantiator instantiator = serviceRegistry.get(Instantiator.class)
- def productFlavorContainer = project.container(GroupableProductFlavor,
- new GroupableProductFlavorFactory(instantiator, project, project.getLogger()))
-
- productFlavorContainer.whenObjectRemoved {
- throw new UnsupportedOperationException(
- "Removing product flavors is not supported.")
- }
-
- return productFlavorContainer
- }
-
- @Model
- List<ProductFlavorCombo> createProductFlavorCombo (
- @Path("android.productFlavors") NamedDomainObjectContainer<GroupableProductFlavor> productFlavors) {
- // TODO: Create custom product flavor container to manually configure flavor dimensions.
- List<String> flavorDimensionList = productFlavors*.dimension.unique().asList()
- flavorDimensionList.removeAll([null])
-
- return ProductFlavorCombo.createCombinations(flavorDimensionList, productFlavors)
- }
-
- @ComponentType
- void defineComponentType(ComponentTypeBuilder<AndroidComponentSpec> builder) {
- builder.defaultImplementation(DefaultAndroidComponentSpec)
- }
-
- @Mutate
- void createAndroidComponents(CollectionBuilder<AndroidComponentSpec> androidComponents) {
- androidComponents.create(COMPONENT_NAME)
- }
-
- @Model
- AndroidComponentSpec androidComponentSpec(ComponentSpecContainer specs) {
- return (AndroidComponentSpec) specs.getByName(COMPONENT_NAME)
- }
-
- @Model
- AndroidComponentModelSourceSet androidSources (
- ServiceRegistry serviceRegistry,
- ProjectSourceSet projectSourceSet,
- LanguageRegistry languageRegistry) {
- def instantiator = serviceRegistry.get(Instantiator.class)
- def sources = new AndroidComponentModelSourceSet(instantiator, projectSourceSet)
-
- languageRegistry.each { languageRegistration ->
- sources.registerLanguage(languageRegistration)
- }
-
- // Create main source set.
- sources.create("main")
-
- return sources
- }
-
- /**
- * Create all source sets for each AndroidBinary.
- *
- * Need to ensure this is done before model mutation in build.gradle.
- */
- @Mutate
- void createVariantSourceSet(
- @Path("android.sources") AndroidComponentModelSourceSet sources,
- @Path("android.buildTypes") NamedDomainObjectContainer<BuildType> buildTypes,
- @Path("android.productFlavors") NamedDomainObjectContainer<GroupableProductFlavor> flavors,
- List<ProductFlavorCombo> flavorGroups) {
- buildTypes.each { buildType ->
- sources.maybeCreate(buildType.name)
- }
- flavorGroups.each { group ->
- sources.maybeCreate(group.name)
- if (!group.flavorList.isEmpty()) {
- buildTypes.each { buildType ->
- sources.maybeCreate(group.name + buildType.name.capitalize())
- }
- }
- }
- if (flavorGroups.size() != flavors.size()) {
- // If flavorGroups and flavors are the same size, there is at most 1 flavor
- // dimension. So we don't need to reconfigure the source sets for flavorGroups.
- flavors.each { flavor ->
- sources.maybeCreate(flavor.name)
- }
- }
- }
-
- @Finalize
- void setDefaultSrcDir(@Path("android.sources") AndroidComponentModelSourceSet sourceSet) {
- sourceSet.setDefaultSrcDir()
- }
-
- @BinaryType
- void defineBinaryType(BinaryTypeBuilder<AndroidBinary> builder) {
- builder.defaultImplementation(DefaultAndroidBinary)
- }
-
- @ComponentBinaries
- void createBinaries(
- CollectionBuilder<AndroidBinary> binaries,
- @Path("android.buildTypes") NamedDomainObjectContainer<BuildType> buildTypes,
- List<ProductFlavorCombo> flavorCombos,
- AndroidComponentSpec spec) {
- if (flavorCombos.isEmpty()) {
- flavorCombos.add(new ProductFlavorCombo());
- }
-
- buildTypes.each { BuildType buildType ->
- flavorCombos.each { ProductFlavorCombo flavorCombo ->
- binaries.create(getBinaryName(buildType, flavorCombo)) {
- def binary = it as DefaultAndroidBinary
- binary.buildType = buildType
- binary.productFlavors = flavorCombo.flavorList
- }
- }
- }
- }
-
- private static String getBinaryName(BuildType buildType, ProductFlavorCombo flavorCombo) {
- if (flavorCombo.flavorList.isEmpty()) {
- return buildType.name
- } else {
- return flavorCombo.name + buildType.name.capitalize()
- }
- }
- }
-
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelPlugin.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelPlugin.java
new file mode 100644
index 0000000..e680dad
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelPlugin.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.model;
+
+import static com.android.builder.core.VariantType.ANDROID_TEST;
+import static com.android.builder.core.VariantType.UNIT_TEST;
+
+import com.android.build.gradle.internal.ProductFlavorCombo;
+import com.android.build.gradle.managed.AndroidConfig;
+import com.android.build.gradle.managed.BuildType;
+import com.android.build.gradle.managed.ProductFlavor;
+import com.android.builder.core.BuilderConstants;
+import com.android.sdklib.repository.FullRevision;
+import com.android.utils.StringHelper;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.primitives.Ints;
+
+import org.gradle.api.Action;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.ProjectSourceSet;
+import org.gradle.language.base.internal.registry.LanguageRegistration;
+import org.gradle.language.base.internal.registry.LanguageRegistry;
+import org.gradle.language.base.plugins.ComponentModelBasePlugin;
+import org.gradle.model.Defaults;
+import org.gradle.model.Finalize;
+import org.gradle.model.Model;
+import org.gradle.model.ModelMap;
+import org.gradle.model.Mutate;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+import org.gradle.platform.base.BinaryType;
+import org.gradle.platform.base.BinaryTypeBuilder;
+import org.gradle.platform.base.ComponentBinaries;
+import org.gradle.platform.base.ComponentType;
+import org.gradle.platform.base.ComponentTypeBuilder;
+import org.gradle.platform.base.LanguageType;
+import org.gradle.platform.base.LanguageTypeBuilder;
+import org.gradle.tooling.BuildException;
+
+import java.io.File;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Plugin to set up infrastructure for other android plugins.
+ */
+public class AndroidComponentModelPlugin implements Plugin<Project> {
+
+ /**
+ * The name of ComponentSpec created with android component model plugin.
+ */
+ public static final String COMPONENT_NAME = "android";
+
+ //public static final Pattern GRADLE_ACCEPTABLE_VERSIONS = Pattern.compile("2\\.5.*");
+ public static final String GRADLE_ACCEPTABLE_VERSION = "2.5";
+
+ private static final String GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY =
+ "com.android.build.gradle.overrideVersionCheck";
+
+ @Override
+ public void apply(Project project) {
+ checkGradleVersion(project);
+ project.getPlugins().apply(ComponentModelBasePlugin.class);
+ }
+
+ private static void checkGradleVersion(Project project) {
+ String gradleVersion = project.getGradle().getGradleVersion();
+ if (!gradleVersion.startsWith(GRADLE_ACCEPTABLE_VERSION)) {
+ boolean allowNonMatching = Boolean.getBoolean(GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY);
+ File file = new File("gradle" + File.separator + "wrapper" + File.separator +
+ "gradle-wrapper.properties");
+ String errorMessage = String.format(
+ "Gradle version %s is required. Current version is %s. " +
+ "If using the gradle wrapper, try editing the distributionUrl in %s " +
+ "to gradle-%s-all.zip",
+ GRADLE_ACCEPTABLE_VERSION, gradleVersion, file.getAbsolutePath(),
+ GRADLE_ACCEPTABLE_VERSION);
+ if (allowNonMatching) {
+ project.getLogger().warn(errorMessage);
+ project.getLogger().warn("As %s is set, continuing anyways.",
+ GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY);
+ } else {
+ throw new BuildException(errorMessage, null);
+ }
+ }
+ }
+
+ @SuppressWarnings("MethodMayBeStatic")
+ public static class Rules extends RuleSource {
+
+ @LanguageType
+ public void registerLanguage(LanguageTypeBuilder<AndroidLanguageSourceSet> builder) {
+ builder.setLanguageName("android");
+ builder.defaultImplementation(AndroidLanguageSourceSet.class);
+ }
+
+ /**
+ * Create "android" model block.
+ */
+ @Model("android")
+ public void android(AndroidConfig androidModel) {
+ }
+
+ @Defaults
+ public void androidModelSources(AndroidConfig androidModel,
+ @Path("androidSources") AndroidComponentModelSourceSet sources) {
+ androidModel.setSources(sources);
+ }
+
+ @Finalize
+ public void finalizeAndroidModel(AndroidConfig androidModel) {
+ if (androidModel.getBuildToolsRevision() == null
+ && androidModel.getBuildToolsVersion() != null) {
+ androidModel.setBuildToolsRevision(
+ FullRevision.parseRevision(androidModel.getBuildToolsVersion()));
+ }
+
+ if (androidModel.getCompileSdkVersion() != null
+ && !androidModel.getCompileSdkVersion().startsWith("android-")
+ && Ints.tryParse(androidModel.getCompileSdkVersion()) != null) {
+ androidModel.setCompileSdkVersion("android-" + androidModel.getCompileSdkVersion());
+ }
+
+ }
+
+ @Defaults
+ public void createDefaultBuildTypes(
+ @Path("android.buildTypes") ModelMap<BuildType> buildTypes) {
+ buildTypes.create(BuilderConstants.DEBUG, new Action<BuildType>() {
+ @Override
+ public void execute(BuildType buildType) {
+ buildType.setDebuggable(true);
+ buildType.setEmbedMicroApp(false);
+ }
+ });
+ buildTypes.create(BuilderConstants.RELEASE);
+ }
+
+ @Model
+ public List<ProductFlavorCombo<ProductFlavor>> createProductFlavorCombo(
+ @Path("android.productFlavors") ModelMap<ProductFlavor> productFlavors) {
+ // TODO: Create custom product flavor container to manually configure flavor dimensions.
+ Set<String> flavorDimensionList = Sets.newHashSet();
+ for (ProductFlavor flavor : productFlavors.values()) {
+ if (flavor.getDimension() != null) {
+ flavorDimensionList.add(flavor.getDimension());
+ }
+ }
+
+ return ProductFlavorCombo.createCombinations(
+ Lists.newArrayList(flavorDimensionList),
+ productFlavors.values());
+ }
+
+ @ComponentType
+ public void defineComponentType(ComponentTypeBuilder<AndroidComponentSpec> builder) {
+ builder.defaultImplementation(DefaultAndroidComponentSpec.class);
+ }
+
+ @Mutate
+ public void createAndroidComponents(ModelMap<AndroidComponentSpec> androidComponents) {
+ androidComponents.create(COMPONENT_NAME);
+ }
+
+ @Model
+ public AndroidComponentModelSourceSet androidSources(ServiceRegistry serviceRegistry) {
+ Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+ return new AndroidComponentModelSourceSet(instantiator);
+ }
+
+ /**
+ * Create all source sets for each AndroidBinary.
+ */
+ @Mutate
+ public void createVariantSourceSet(
+ @Path("android.sources") final AndroidComponentModelSourceSet sources,
+ @Path("android.buildTypes") final ModelMap<BuildType> buildTypes,
+ @Path("android.productFlavors") ModelMap<ProductFlavor> flavors,
+ List<ProductFlavorCombo<ProductFlavor>> flavorGroups, ProjectSourceSet projectSourceSet,
+ LanguageRegistry languageRegistry) {
+ sources.setProjectSourceSet(projectSourceSet);
+ for (LanguageRegistration languageRegistration : languageRegistry) {
+ sources.registerLanguage(languageRegistration);
+ }
+
+ // Create main source set.
+ sources.create("main");
+ sources.create(ANDROID_TEST.getPrefix());
+ sources.create(UNIT_TEST.getPrefix());
+
+ for (BuildType buildType : buildTypes.values()) {
+ sources.maybeCreate(buildType.getName());
+
+ for (ProductFlavorCombo group: flavorGroups) {
+ sources.maybeCreate(group.getName());
+ if (!group.getFlavorList().isEmpty()) {
+ sources.maybeCreate(
+ group.getName() + StringHelper.capitalize(buildType.getName()));
+ }
+
+ }
+
+ }
+ if (flavorGroups.size() != flavors.size()) {
+ // If flavorGroups and flavors are the same size, there is at most 1 flavor
+ // dimension. So we don't need to reconfigure the source sets for flavorGroups.
+ for (ProductFlavor flavor: flavors.values()) {
+ sources.maybeCreate(flavor.getName());
+ }
+ }
+ }
+
+ @Finalize
+ public void setDefaultSrcDir(
+ @Path("android.sources") AndroidComponentModelSourceSet sourceSet) {
+ sourceSet.setDefaultSrcDir();
+ }
+
+ @BinaryType
+ public void defineBinaryType(BinaryTypeBuilder<AndroidBinary> builder) {
+ builder.defaultImplementation(DefaultAndroidBinary.class);
+ }
+
+ @ComponentBinaries
+ public void createBinaries(
+ final ModelMap<AndroidBinary> binaries,
+ @Path("android") final AndroidConfig androidConfig,
+ @Path("android.buildTypes") final ModelMap<BuildType> buildTypes,
+ final List<ProductFlavorCombo<ProductFlavor>> flavorCombos,
+ final AndroidComponentSpec spec) {
+ if (flavorCombos.isEmpty()) {
+ flavorCombos.add(new ProductFlavorCombo<ProductFlavor>());
+ }
+
+ for (final BuildType buildType : buildTypes.values()) {
+ for (final ProductFlavorCombo<ProductFlavor> flavorCombo : flavorCombos) {
+ binaries.create(getBinaryName(buildType, flavorCombo),
+ new Action<AndroidBinary>() {
+ @Override
+ public void execute(AndroidBinary androidBinary) {
+ DefaultAndroidBinary binary = (DefaultAndroidBinary) androidBinary;
+ binary.setBuildType(buildType);
+ binary.setProductFlavors(flavorCombo.getFlavorList());
+ }
+ });
+ }
+ }
+ }
+
+ private static String getBinaryName(BuildType buildType, ProductFlavorCombo flavorCombo) {
+ if (flavorCombo.getFlavorList().isEmpty()) {
+ return buildType.getName();
+ } else {
+ return flavorCombo.getName() + StringHelper.capitalize(buildType.getName());
+ }
+
+ }
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelSourceSet.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelSourceSet.java
index af341ea..7e555d9 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelSourceSet.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelSourceSet.java
@@ -39,11 +39,8 @@
implements NamedDomainObjectContainer<FunctionalSourceSet> {
ProjectSourceSet projectSourceSet;
- public AndroidComponentModelSourceSet (
- Instantiator instantiator,
- ProjectSourceSet projectSourceSet) {
+ public AndroidComponentModelSourceSet (Instantiator instantiator) {
super(FunctionalSourceSet.class, instantiator);
- this.projectSourceSet = projectSourceSet;
}
public <T extends LanguageSourceSet> void registerLanguage(final LanguageRegistration<T> languageRegistration) {
@@ -56,7 +53,15 @@
languageRegistration.getSourceSetFactory(functionalSourceSet.getName()));
}
});
+ }
+ /**
+ * Setter for projectSourceSet.
+ * Having a setter avoid the need for ProjectSourceSet to be part of the constructor, which can cause
+ * cyclic rule dependenecy issues.
+ */
+ public void setProjectSourceSet(ProjectSourceSet projectSourceSet) {
+ this.projectSourceSet = projectSourceSet;
}
@Override
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelTestPlugin.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelTestPlugin.groovy
deleted file mode 100644
index ae964e7..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelTestPlugin.groovy
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.model
-
-import com.android.build.gradle.TestedExtension
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.VariantManager
-import com.android.build.gradle.internal.variant.BaseVariantData
-import com.android.build.gradle.internal.variant.TestVariantData
-import groovy.transform.CompileStatic
-import org.gradle.api.Task
-import org.gradle.model.Mutate
-import org.gradle.model.RuleSource
-import org.gradle.model.collection.CollectionBuilder
-import org.gradle.platform.base.BinaryContainer
-
-import static com.android.builder.core.VariantType.ANDROID_TEST
-
-/**
- * Plugin for creating test tasks for AndroidBinary.
- */
-@SuppressWarnings("GrMethodMayBeStatic")
-@CompileStatic
-class AndroidComponentModelTestPlugin extends RuleSource {
-
- @Mutate
- void createConnectedTestTasks(
- CollectionBuilder<Task> tasks,
- BinaryContainer binaries,
- TaskManager taskManager,
- AndroidComponentSpec spec) {
- TestedExtension extension
- if (spec.extension instanceof TestedExtension) {
- extension = spec.extension as TestedExtension
- } else {
- return
- }
-
- VariantManager variantManager = (spec as DefaultAndroidComponentSpec).variantManager
- binaries.withType(AndroidBinary) { androidBinary->
- DefaultAndroidBinary binary = androidBinary as DefaultAndroidBinary
-
- if (binary.buildType.name != extension.testBuildType) {
- return
- }
-
- // Create test tasks.
- BaseVariantData testedVariantData = binary.variantData
-
- assert testedVariantData != null,
- "Internal error: tested variant must be created before test variant."
-
- TestVariantData testVariantData =
- variantManager.createTestVariantData(testedVariantData, ANDROID_TEST)
- variantManager.getVariantDataList().add(testVariantData);
- variantManager.createTasksForVariantData(
- new TaskCollectionBuilderAdaptor(tasks),
- testVariantData)
- }
- }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelTestPlugin.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelTestPlugin.java
new file mode 100644
index 0000000..187c0a0
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentModelTestPlugin.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.model;
+
+import static com.android.build.gradle.model.AndroidComponentModelPlugin.COMPONENT_NAME;
+import static com.android.builder.core.VariantType.ANDROID_TEST;
+
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.VariantManager;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.TestVariantData;
+import com.android.builder.core.BuilderConstants;
+import com.google.common.base.Preconditions;
+
+import org.gradle.api.Action;
+import org.gradle.api.Task;
+import org.gradle.model.ModelMap;
+import org.gradle.model.Mutate;
+import org.gradle.model.RuleSource;
+import org.gradle.platform.base.BinaryContainer;
+
+/**
+ * Plugin for creating test tasks for AndroidBinary.
+ */
+@SuppressWarnings("MethodMayBeStatic")
+public class AndroidComponentModelTestPlugin extends RuleSource {
+
+ @Mutate
+ public void createConnectedTestTasks(
+ final ModelMap<Task> tasks,
+ BinaryContainer binaries,
+ TaskManager taskManager,
+ ModelMap<AndroidComponentSpec> specs) {
+ final VariantManager variantManager =
+ ((DefaultAndroidComponentSpec) specs.get(COMPONENT_NAME)).getVariantManager();
+ binaries.withType(AndroidBinary.class, new Action<AndroidBinary>() {
+ @Override
+ public void execute(AndroidBinary androidBinary) {
+ DefaultAndroidBinary binary = (DefaultAndroidBinary) androidBinary;
+
+ // TODO: compare against testBuildType instead of BuilderConstants.DEBUG.
+ if (!binary.getBuildType().getName().equals(BuilderConstants.DEBUG)) {
+ return;
+
+ }
+
+ // Create test tasks.
+ BaseVariantData testedVariantData = binary.getVariantData();
+
+ Preconditions.checkState(testedVariantData != null,
+ "Internal error: tested variant must be created before test variant.");
+
+ TestVariantData testVariantData =
+ variantManager.createTestVariantData(testedVariantData, ANDROID_TEST);
+ variantManager.getVariantDataList().add(testVariantData);
+ variantManager.createTasksForVariantData(
+ new TaskModelMapAdaptor(tasks),
+ testVariantData);
+ }
+ });
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentSpec.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentSpec.java
index 83895c7..210b45a 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentSpec.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidComponentSpec.java
@@ -16,7 +16,7 @@
package com.android.build.gradle.model;
-import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.managed.AndroidConfig;
import org.gradle.platform.base.ComponentSpec;
@@ -24,5 +24,5 @@
* Android ComponentSpec.
*/
public interface AndroidComponentSpec extends ComponentSpec{
- BaseExtension getExtension();
+ AndroidConfig getExtension();
}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidModel.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidModel.java
deleted file mode 100644
index d86d097..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AndroidModel.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.model;
-
-import com.android.build.gradle.BaseExtension;
-import com.android.build.gradle.internal.dsl.BuildType;
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor;
-import com.android.build.gradle.internal.dsl.SigningConfig;
-import com.android.build.gradle.ndk.NdkExtension;
-
-import org.gradle.api.NamedDomainObjectContainer;
-import org.gradle.model.Managed;
-import org.gradle.model.Unmanaged;
-
-/**
- * Component model for all Android plugin.
- */
-@Managed
-public interface AndroidModel {
- @Unmanaged
- NamedDomainObjectContainer<BuildType> getBuildTypes();
-
- void setBuildTypes(NamedDomainObjectContainer<BuildType> buildTypes);
-
- @Unmanaged
- NamedDomainObjectContainer<GroupableProductFlavor> getProductFlavors();
-
- void setProductFlavors(NamedDomainObjectContainer<GroupableProductFlavor> productFlavors);
-
- @Unmanaged
- NamedDomainObjectContainer<SigningConfig> getSigningConfigs();
-
- void setSigningConfigs(NamedDomainObjectContainer<SigningConfig> signingConfigs);
-
- @Unmanaged
- AndroidComponentModelSourceSet getSources();
-
- void setSources(AndroidComponentModelSourceSet sources);
-
- @Unmanaged
- NdkExtension getNdk();
-
- void setNdk(NdkExtension ndk);
-
- @Unmanaged
- BaseExtension getConfig();
-
- void setConfig(BaseExtension config);
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AppComponentModelPlugin.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AppComponentModelPlugin.groovy
deleted file mode 100644
index b4b8896..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AppComponentModelPlugin.groovy
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.model
-
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.internal.DependencyManager
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.variant.ApplicationVariantFactory
-import com.android.build.gradle.internal.variant.VariantFactory
-import com.android.builder.core.AndroidBuilder
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.model.Model
-import org.gradle.model.RuleSource
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-
-/**
- * Gradle component model plugin class for 'application' projects.
- */
-public class AppComponentModelPlugin implements Plugin<Project> {
-
- @Override
- void apply(Project project) {
- project.plugins.apply(BaseComponentModelPlugin)
- project.plugins.apply(AndroidComponentModelTestPlugin)
- }
-
- static class Rules extends RuleSource {
-
- @Model
- Boolean isApplication() {
- return true
- }
-
- @Model
- TaskManager createTaskManager(
- BaseExtension androidExtension,
- Project project,
- AndroidBuilder androidBuilder,
- SdkHandler sdkHandler,
- ExtraModelInfo extraModelInfo,
- ToolingModelBuilderRegistry toolingRegistry) {
- DependencyManager dependencyManager = new DependencyManager(project, extraModelInfo)
-
- return new ApplicationComponentTaskManager(
- project,
- androidBuilder,
- androidExtension,
- sdkHandler,
- dependencyManager,
- toolingRegistry);
- }
-
-
- @Model
- VariantFactory createVariantFactory(
- ServiceRegistry serviceRegistry,
- AndroidBuilder androidBuilder,
- BaseExtension extension) {
- Instantiator instantiator = serviceRegistry.get(Instantiator.class);
- return new ApplicationVariantFactory(
- instantiator,
- androidBuilder,
- extension)
- }
- }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AppComponentModelPlugin.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AppComponentModelPlugin.java
new file mode 100644
index 0000000..0fd6ac7
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/AppComponentModelPlugin.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.model;
+
+import static com.android.build.gradle.model.ModelConstants.IS_APPLICATION;
+import static com.android.build.gradle.model.ModelConstants.TASK_MANAGER;
+
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.internal.DependencyManager;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.variant.ApplicationVariantFactory;
+import com.android.build.gradle.internal.variant.VariantFactory;
+import com.android.builder.core.AndroidBuilder;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.model.Model;
+import org.gradle.model.RuleSource;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+/**
+ * Gradle component model plugin class for 'application' projects.
+ */
+public class AppComponentModelPlugin implements Plugin<Project> {
+
+ @Override
+ public void apply(Project project) {
+ project.getPluginManager().apply(BaseComponentModelPlugin.class);
+ project.getPluginManager().apply(AndroidComponentModelTestPlugin.class);
+ }
+
+ @SuppressWarnings("MethodMayBeStatic")
+ public static class Rules extends RuleSource {
+ @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+ @Model(IS_APPLICATION)
+ public Boolean isApplication() {
+ return true;
+ }
+
+ @Model(TASK_MANAGER)
+ public TaskManager createTaskManager(
+ AndroidConfig androidExtension,
+ Project project,
+ AndroidBuilder androidBuilder,
+ SdkHandler sdkHandler,
+ ExtraModelInfo extraModelInfo,
+ ToolingModelBuilderRegistry toolingRegistry) {
+ DependencyManager dependencyManager = new DependencyManager(project, extraModelInfo);
+
+ return new ApplicationComponentTaskManager(
+ project,
+ androidBuilder,
+ androidExtension,
+ sdkHandler,
+ dependencyManager,
+ toolingRegistry);
+ }
+
+ @Model
+ public VariantFactory createVariantFactory(
+ ServiceRegistry serviceRegistry,
+ AndroidBuilder androidBuilder,
+ AndroidConfig extension) {
+ Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+ return new ApplicationVariantFactory(instantiator, androidBuilder, extension);
+ }
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ApplicationComponentTaskManager.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ApplicationComponentTaskManager.java
index c91ac2f..d191c7a 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ApplicationComponentTaskManager.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ApplicationComponentTaskManager.java
@@ -16,19 +16,19 @@
package com.android.build.gradle.model;
-import com.android.build.gradle.BaseExtension;
+import com.android.annotations.NonNull;
+import com.android.build.gradle.AndroidConfig;
import com.android.build.gradle.internal.ApplicationTaskManager;
import com.android.build.gradle.internal.DependencyManager;
import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.variant.BaseVariantData;
import com.android.builder.core.AndroidBuilder;
import com.google.common.collect.ImmutableList;
import org.gradle.api.Project;
-import org.gradle.api.tasks.TaskContainer;
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
-import java.io.File;
import java.util.Collection;
/**
@@ -39,7 +39,7 @@
public ApplicationComponentTaskManager (
Project project,
AndroidBuilder androidBuilder,
- BaseExtension extension,
+ AndroidConfig extension,
SdkHandler sdkHandler,
DependencyManager dependencyManager,
ToolingModelBuilderRegistry toolingRegistry) {
@@ -54,9 +54,7 @@
}
@Override
- protected Collection<File> getNdkOutputDirectories(BaseVariantData variantData) {
- NdkComponentModelPlugin plugin = project.getPlugins().getPlugin(
- NdkComponentModelPlugin.class);
- return plugin.getOutputDirectories(variantData.getVariantConfiguration());
+ public void configureScopeForNdk(@NonNull VariantScope scope) {
+ NdkComponentModelPlugin.configureScopeForNdk(scope);
}
}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/BaseComponentModelPlugin.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/BaseComponentModelPlugin.groovy
deleted file mode 100644
index ef9857b..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/BaseComponentModelPlugin.groovy
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.model
-
-import com.android.annotations.NonNull
-import com.android.annotations.Nullable
-import com.android.build.gradle.AppExtension
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.LibraryExtension
-import com.android.build.gradle.api.AndroidSourceDirectorySet
-import com.android.build.gradle.api.AndroidSourceSet
-import com.android.build.gradle.internal.BuildTypeData
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.LibraryCache
-import com.android.build.gradle.internal.LoggerWrapper
-import com.android.build.gradle.internal.ProductFlavorData
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.VariantManager
-import com.android.build.gradle.internal.coverage.JacocoPlugin
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.build.gradle.internal.dsl.SigningConfigFactory
-import com.android.build.gradle.internal.model.ModelBuilder
-import com.android.build.gradle.internal.process.GradleJavaProcessExecutor
-import com.android.build.gradle.internal.process.GradleProcessExecutor
-import com.android.build.gradle.internal.profile.RecordingBuildListener
-import com.android.build.gradle.internal.tasks.DependencyReportTask
-import com.android.build.gradle.internal.tasks.SigningReportTask
-import com.android.build.gradle.internal.variant.VariantFactory
-import com.android.build.gradle.ndk.NdkExtension
-import com.android.build.gradle.tasks.JillTask
-import com.android.build.gradle.tasks.PreDex
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.BuilderConstants
-import com.android.builder.internal.compiler.JackConversionCache
-import com.android.builder.internal.compiler.PreDexCache
-import com.android.builder.profile.ExecutionType
-import com.android.builder.profile.ProcessRecorderFactory
-import com.android.builder.profile.Recorder
-import com.android.builder.profile.ThreadRecorder
-import com.android.builder.sdk.TargetInfo
-import com.android.ide.common.internal.ExecutorSingleton
-import com.android.ide.common.process.LoggedProcessOutputHandler
-import com.android.utils.ILogger
-import groovy.transform.CompileStatic
-import org.gradle.api.DefaultTask
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.api.artifacts.Configuration
-import org.gradle.api.artifacts.ConfigurationContainer
-import org.gradle.api.artifacts.repositories.MavenArtifactRepository
-import org.gradle.api.execution.TaskExecutionGraph
-import org.gradle.api.file.SourceDirectorySet
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.logging.LogLevel
-import org.gradle.api.plugins.JavaBasePlugin
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.language.base.FunctionalSourceSet
-import org.gradle.language.base.LanguageSourceSet
-import org.gradle.language.base.internal.SourceTransformTaskConfig
-import org.gradle.language.base.internal.registry.LanguageTransform
-import org.gradle.model.Model
-import org.gradle.model.Mutate
-import org.gradle.model.Path
-import org.gradle.model.RuleSource
-import org.gradle.model.collection.CollectionBuilder
-import org.gradle.model.internal.core.ModelCreators
-import org.gradle.model.internal.core.ModelReference
-import org.gradle.model.internal.registry.ModelRegistry
-import org.gradle.platform.base.BinaryContainer
-import org.gradle.platform.base.BinarySpec
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-
-import javax.inject.Inject
-
-import static com.android.builder.core.BuilderConstants.DEBUG
-import static com.android.builder.core.VariantType.ANDROID_TEST
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-
-@CompileStatic
-public class BaseComponentModelPlugin implements Plugin<Project> {
- ToolingModelBuilderRegistry toolingRegistry
- ModelRegistry modelRegistry
-
- @Inject
- protected BaseComponentModelPlugin(
- ToolingModelBuilderRegistry toolingRegistry,
- ModelRegistry modelRegistry) {
- this.toolingRegistry = toolingRegistry
- this.modelRegistry = modelRegistry
- }
-
- /**
- * Replace BasePlugin's apply method for component model.
- */
- @Override
- public void apply(Project project) {
- ProcessRecorderFactory.initialize(new LoggerWrapper(project.logger), project.rootProject.
- file("profiler" + System.currentTimeMillis() + ".json"))
- project.gradle.addListener(new RecordingBuildListener(ThreadRecorder.get()));
-
- project.apply plugin: AndroidComponentModelPlugin
-
- project.apply plugin: JavaBasePlugin
- project.apply plugin: JacocoPlugin
-
- // TODO: Create configurations for build types and flavors, or migrate to new dependency
- // management if it's ready.
- ConfigurationContainer configurations = project.getConfigurations()
- createConfiguration(
- configurations,
- "compile",
- "Classpath for default sources.")
-
- createConfiguration(
- configurations,
- "default-metadata",
- "Metadata for published APKs")
-
- createConfiguration(
- configurations,
- "default-mapping",
- "Metadata for published APKs")
-
- project.tasks.getByName("assemble").description =
- "Assembles all variants of all applications and secondary packages."
-
- project.apply plugin: NdkComponentModelPlugin
-
- modelRegistry.create(
- ModelCreators.bridgedInstance(
- ModelReference.of("toolingRegistry", ToolingModelBuilderRegistry), toolingRegistry)
- .descriptor("Tooling model builder model registry.")
- .build())
- }
-
- @SuppressWarnings("GrMethodMayBeStatic")
- static class Rules extends RuleSource {
-
- @Mutate
- void configureAndroidModel(
- AndroidModel androidModel,
- @Path("androidConfig") BaseExtension config,
- @Path("androidSigningConfigs") NamedDomainObjectContainer<SigningConfig> signingConfigs) {
- androidModel.config = config
- androidModel.signingConfigs = signingConfigs
- }
-
- // TODO: Remove code duplicated from BasePlugin.
- @Model
- ExtraModelInfo createExtraModelInfo(Project project) {
- return new ExtraModelInfo(project)
- }
-
- @Model
- SdkHandler createSdkHandler(Project project) {
- ILogger logger = new LoggerWrapper(project.logger)
- SdkHandler sdkHandler = new SdkHandler(project, logger)
-
- // call back on execution. This is called after the whole build is done (not
- // after the current project is done).
- // This is will be called for each (android) projects though, so this should support
- // being called 2+ times.
- project.gradle.buildFinished {
- ExecutorSingleton.shutdown()
- sdkHandler.unload()
- PreDexCache.getCache().clear(
- project.rootProject.file(
- "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/dex-cache/cache.xml"),
- logger)
- JackConversionCache.getCache().clear(
- project.rootProject.file(
- "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/jack-cache/cache.xml"),
- logger)
- LibraryCache.getCache().unload()
- }
-
- project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
- for (Task task : taskGraph.allTasks) {
- if (task instanceof PreDex) {
- PreDexCache.getCache().load(
- project.rootProject.file(
- "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/dex-cache/cache.xml"))
- break;
- } else if (task instanceof JillTask) {
- JackConversionCache.getCache().load(
- project.rootProject.file(
- "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/jack-cache/cache.xml"))
- break;
- }
- }
- }
- return sdkHandler
- }
-
- @Model
- AndroidBuilder createAndroidBuilder(Project project) {
- String creator = "Android Gradle"
- ILogger logger = new LoggerWrapper(project.logger)
-
- return new AndroidBuilder(
- project == project.rootProject ? project.name : project.path,
- creator,
- new GradleProcessExecutor(project),
- new GradleJavaProcessExecutor(project),
- new LoggedProcessOutputHandler(logger),
- logger,
- project.logger.isEnabled(LogLevel.INFO))
-
- }
-
- @Model("androidConfig")
- BaseExtension androidConfig(
- ServiceRegistry serviceRegistry,
- @Path("androidBuildTypes") NamedDomainObjectContainer<BuildType> buildTypeContainer,
- @Path("androidProductFlavors") NamedDomainObjectContainer<GroupableProductFlavor> productFlavorContainer,
- @Path("androidSigningConfigs") NamedDomainObjectContainer<SigningConfig> signingConfigContainer,
- @Path("isApplication") Boolean isApplication,
- AndroidBuilder androidBuilder,
- SdkHandler sdkHandler,
- ExtraModelInfo extraModelInfo,
- Project project) {
- Instantiator instantiator = serviceRegistry.get(Instantiator.class);
-
- Class extensionClass = isApplication ? AppExtension : LibraryExtension
-
- BaseExtension extension = (BaseExtension) instantiator.newInstance(extensionClass,
- (ProjectInternal) project, instantiator, androidBuilder,
- sdkHandler, buildTypeContainer, productFlavorContainer, signingConfigContainer,
- extraModelInfo, !isApplication)
-
- return extension
- }
-
- @Mutate
- void addDefaultAndroidSourceSet(AndroidComponentModelSourceSet sources) {
- sources.addDefaultSourceSet("resources", AndroidLanguageSourceSet.class);
- sources.addDefaultSourceSet("java", AndroidLanguageSourceSet.class);
- sources.addDefaultSourceSet("manifest", AndroidLanguageSourceSet.class);
- sources.addDefaultSourceSet("res", AndroidLanguageSourceSet.class);
- sources.addDefaultSourceSet("assets", AndroidLanguageSourceSet.class);
- sources.addDefaultSourceSet("aidl", AndroidLanguageSourceSet.class);
- sources.addDefaultSourceSet("renderscript", AndroidLanguageSourceSet.class);
- sources.addDefaultSourceSet("jniLibs", AndroidLanguageSourceSet.class);
- }
-
- @Mutate
- void forwardCompileSdkVersion(
- @Path("android.ndk") NdkExtension ndkExtension,
- @Path("android.config") BaseExtension baseExtension) {
- if (ndkExtension.compileSdkVersion.isEmpty() && baseExtension.compileSdkVersion != null) {
- ndkExtension.compileSdkVersion(baseExtension.compileSdkVersion);
- }
- }
-
- @Model("androidSigningConfigs")
- NamedDomainObjectContainer<SigningConfig> signingConfig(ServiceRegistry serviceRegistry,
- Project project) {
- Instantiator instantiator = serviceRegistry.get(Instantiator.class);
- def signingConfigContainer =
- project.container(SigningConfig, new SigningConfigFactory(instantiator))
- signingConfigContainer.create(DEBUG)
- signingConfigContainer.whenObjectRemoved {
- throw new UnsupportedOperationException("Removing signingConfigs is not supported.")
- }
- return signingConfigContainer
- }
-
- @Mutate
- void closeProjectSourceSet(AndroidComponentModelSourceSet sources) {
- }
-
- @Mutate
- void createAndroidComponents(
- AndroidComponentSpec androidSpec,
- ServiceRegistry serviceRegistry,
- @Path("android.config") BaseExtension androidExtension,
- @Path("android.buildTypes") NamedDomainObjectContainer<BuildType> buildTypeContainer,
- @Path("android.productFlavors") NamedDomainObjectContainer<GroupableProductFlavor> productFlavorContainer,
- @Path("android.signingConfigs") NamedDomainObjectContainer<SigningConfig> signingConfigContainer,
- VariantFactory variantFactory,
- TaskManager taskManager,
- Project project,
- AndroidBuilder androidBuilder,
- SdkHandler sdkHandler,
- ExtraModelInfo extraModelInfo,
- ToolingModelBuilderRegistry toolingRegistry,
- @Path("isApplication") Boolean isApplication) {
- Instantiator instantiator = serviceRegistry.get(Instantiator.class);
-
- // check if the target has been set.
- TargetInfo targetInfo = androidBuilder.getTargetInfo()
- if (targetInfo == null) {
- sdkHandler.initTarget(
- androidExtension.getCompileSdkVersion(),
- androidExtension.buildToolsRevision,
- androidBuilder)
- }
-
- VariantManager variantManager = new VariantManager(
- project,
- androidBuilder,
- androidExtension,
- variantFactory,
- taskManager,
- instantiator)
-
- signingConfigContainer.all { SigningConfig signingConfig ->
- variantManager.addSigningConfig(signingConfig)
- }
- buildTypeContainer.all { BuildType buildType ->
- variantManager.addBuildType(buildType)
- }
- productFlavorContainer.all { GroupableProductFlavor productFlavor ->
- variantManager.addProductFlavor(productFlavor)
- }
-
- ModelBuilder modelBuilder = new ModelBuilder(
- androidBuilder, variantManager, taskManager,
- androidExtension, extraModelInfo, !isApplication);
- toolingRegistry.register(modelBuilder);
-
-
- def spec = androidSpec as DefaultAndroidComponentSpec
- spec.extension = androidExtension
- spec.variantManager = variantManager
- }
-
- @Mutate
- void createVariantData(
- CollectionBuilder<AndroidBinary> binaries,
- AndroidComponentSpec spec) {
- VariantManager variantManager = (spec as DefaultAndroidComponentSpec).variantManager
- binaries.all {
- DefaultAndroidBinary binary = it as DefaultAndroidBinary
- binary.variantData =
- variantManager.createVariantData(binary.buildType, binary.productFlavors)
- variantManager.getVariantDataList().add(binary.variantData);
- }
- }
-
- @Mutate
- void createLifeCycleTasks(
- CollectionBuilder<Task> tasks,
- TaskManager taskManager) {
- taskManager.createTasksBeforeEvaluate(new TaskCollectionBuilderAdaptor(tasks))
- }
-
- @Mutate
- void createAndroidTasks(
- CollectionBuilder<Task> tasks,
- AndroidComponentSpec androidSpec,
- TaskManager taskManager,
- SdkHandler sdkHandler,
- Project project,
- AndroidComponentModelSourceSet androidSources) {
- DefaultAndroidComponentSpec spec = (DefaultAndroidComponentSpec) androidSpec
-
- applyProjectSourceSet(spec, androidSources, spec.extension)
-
- // setup SDK repositories.
- for (File file : sdkHandler.sdkLoader.repositories) {
- project.repositories.maven { MavenArtifactRepository repo ->
- repo.url = file.toURI()
- }
- }
-
- // TODO: determine how to provide functionalities of variant API objects.
- }
-
- // TODO: Use @BinaryTasks after figuring how to configure non-binary specific tasks.
- @Mutate
- void createBinaryTasks(
- CollectionBuilder<Task> tasks,
- BinaryContainer binaries,
- AndroidComponentSpec spec,
- TaskManager taskManager) {
-
- VariantManager variantManager = (spec as DefaultAndroidComponentSpec).variantManager
- binaries.withType(AndroidBinary) { androidBinary ->
- ThreadRecorder.get().record(ExecutionType.VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT,
- new Recorder.Block<Void>() {
- @Override
- public Void call() throws Exception {
- DefaultAndroidBinary binary = androidBinary as DefaultAndroidBinary
- variantManager.createTasksForVariantData(
- new TaskCollectionBuilderAdaptor(tasks),
- binary.variantData)
- return null;
- }
- });
- }
- }
-
- /**
- * Create tasks that must be created after other tasks for variants are created.
- */
- @Mutate
- void createRemainingTasks(
- CollectionBuilder<Task> tasks,
- TaskManager taskManager,
- AndroidComponentSpec spec) {
- VariantManager variantManager = (spec as DefaultAndroidComponentSpec).variantManager
-
- // create the test tasks.
- taskManager.createTopLevelTestTasks (
- new TaskCollectionBuilderAdaptor(tasks),
- !variantManager.productFlavors.isEmpty() /*hasFlavors*/);
- }
-
- @Mutate
- void createReportTasks(CollectionBuilder<Task> tasks, AndroidComponentSpec spec) {
- VariantManager variantManager = (spec as DefaultAndroidComponentSpec).variantManager
-
- tasks.create("androidDependencies", DependencyReportTask) { DependencyReportTask dependencyReportTask ->
- dependencyReportTask.setDescription("Displays the Android dependencies of the project")
- dependencyReportTask.setVariants(variantManager.variantDataList)
- dependencyReportTask.setGroup("Android")
- }
-
- tasks.create("signingReport", SigningReportTask) { SigningReportTask signingReportTask ->
- signingReportTask.setDescription("Displays the signing info for each variant")
- signingReportTask.setVariants(variantManager.variantDataList)
- signingReportTask.setGroup("Android")
- }
- }
-
- private static void applyProjectSourceSet(
- AndroidComponentSpec androidSpec,
- AndroidComponentModelSourceSet sources,
- BaseExtension baseExtension) {
- DefaultAndroidComponentSpec spec = (DefaultAndroidComponentSpec)androidSpec
- VariantManager variantManager = spec.variantManager
- for (FunctionalSourceSet source : sources) {
- String name = source.getName();
- AndroidSourceSet androidSource = (
- name.equals(BuilderConstants.MAIN)
- ? baseExtension.sourceSets.getByName(baseExtension.getDefaultConfig().getName())
- : (name.equals(ANDROID_TEST.prefix)
- ? baseExtension.sourceSets.getByName(ANDROID_TEST.getPrefix())
- : findAndroidSourceSet(variantManager, name)))
-
- if (androidSource == null) {
- continue;
- }
-
- convertSourceSet(androidSource.getResources(), source.findByName("resource")?.getSource())
- convertSourceSet(androidSource.getJava(), source.findByName("java")?.getSource())
- convertSourceSet(androidSource.getRes(), source.findByName("res")?.getSource())
- convertSourceSet(androidSource.getAssets(), source.findByName("assets")?.getSource())
- convertSourceSet(androidSource.getAidl(), source.findByName("aidl")?.getSource())
- convertSourceSet(androidSource.getRenderscript(), source.findByName("renderscript")?.getSource())
- convertSourceSet(androidSource.getJni(), source.findByName("jni")?.getSource())
- convertSourceSet(androidSource.getJniLibs(), source.findByName("jniLibs")?.getSource())
- }
- }
-
- private static convertSourceSet(
- AndroidSourceDirectorySet androidDir,
- @Nullable SourceDirectorySet dir) {
- if (dir == null) {
- return
- }
- androidDir.setSrcDirs(dir.getSrcDirs())
- androidDir.include(dir.getIncludes())
- androidDir.exclude(dir.getExcludes())
- }
-
- @Nullable
- private static AndroidSourceSet findAndroidSourceSet(
- VariantManager variantManager,
- String name) {
- BuildTypeData buildTypeData = variantManager.getBuildTypes().get(name)
- if (buildTypeData != null) {
- return buildTypeData.getSourceSet();
- }
-
- boolean isTest = name.startsWith(ANDROID_TEST.prefix)
- name = name.replaceFirst(ANDROID_TEST.prefix, "")
- ProductFlavorData productFlavorData = variantManager.getProductFlavors().get(name)
- if (productFlavorData != null) {
- return isTest ? productFlavorData.getTestSourceSet(ANDROID_TEST) : productFlavorData.getSourceSet();
- }
- return null;
- }
- }
-
- /**
- * Default Android LanguageRegistration.
- *
- * Allows default LanguageSourceSet to be create until specialized LanguageRegistration is
- * created.
- */
- private static class AndroidSource implements LanguageTransform<AndroidLanguageSourceSet, AndroidObject> {
- public String getName() {
- return "main";
- }
-
- public Class<AndroidLanguageSourceSet> getSourceSetType() {
- return AndroidLanguageSourceSet.class;
- }
-
- public Class<? extends AndroidLanguageSourceSet> getSourceSetImplementation() {
- return AndroidLanguageSourceSet.class;
- }
-
- public Map<String, Class<?>> getBinaryTools() {
- return Collections.emptyMap();
- }
-
- public Class<AndroidObject> getOutputType() {
- return null;
- }
-
- public SourceTransformTaskConfig getTransformTask() {
- return new SourceTransformTaskConfig() {
- public String getTaskPrefix() {
- return "process";
- }
-
- public Class<? extends DefaultTask> getTaskType() {
- return DefaultTask;
- }
-
- public void configureTask(Task task, BinarySpec binary, LanguageSourceSet sourceSet) {
- }
- };
- }
-
- public boolean applyToBinary(BinarySpec binary) {
- return false
- }
- }
-
- private void createConfiguration(
- @NonNull ConfigurationContainer configurations,
- @NonNull String configurationName,
- @NonNull String configurationDescription) {
- Configuration configuration = configurations.findByName(configurationName)
- if (configuration == null) {
- configuration = configurations.create(configurationName)
- }
- configuration.setVisible(false);
- configuration.setDescription(configurationDescription)
- }
-
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/BaseComponentModelPlugin.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/BaseComponentModelPlugin.java
new file mode 100644
index 0000000..2144cd7
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/BaseComponentModelPlugin.java
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.model;
+
+import static com.android.build.gradle.model.AndroidComponentModelPlugin.COMPONENT_NAME;
+import static com.android.build.gradle.model.ModelConstants.ANDROID_BUILDER;
+import static com.android.build.gradle.model.ModelConstants.ANDROID_CONFIG_ADAPTOR;
+import static com.android.build.gradle.model.ModelConstants.EXTRA_MODEL_INFO;
+import static com.android.builder.core.BuilderConstants.DEBUG;
+import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.AndroidGradleOptions;
+import com.android.build.gradle.internal.AndroidConfigHelper;
+import com.android.build.gradle.internal.ExecutionConfigurationUtil;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.LibraryCache;
+import com.android.build.gradle.internal.LoggerWrapper;
+import com.android.build.gradle.internal.NdkOptionsHelper;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.VariantManager;
+import com.android.build.gradle.internal.coverage.JacocoPlugin;
+import com.android.build.gradle.internal.process.GradleJavaProcessExecutor;
+import com.android.build.gradle.internal.process.GradleProcessExecutor;
+import com.android.build.gradle.internal.profile.RecordingBuildListener;
+import com.android.build.gradle.internal.tasks.DependencyReportTask;
+import com.android.build.gradle.internal.tasks.SigningReportTask;
+import com.android.build.gradle.internal.variant.VariantFactory;
+import com.android.build.gradle.managed.AndroidConfig;
+import com.android.build.gradle.managed.BuildType;
+import com.android.build.gradle.managed.ClassField;
+import com.android.build.gradle.managed.NdkConfig;
+import com.android.build.gradle.managed.NdkOptions;
+import com.android.build.gradle.managed.ProductFlavor;
+import com.android.build.gradle.managed.SigningConfig;
+import com.android.build.gradle.managed.adaptor.AndroidConfigAdaptor;
+import com.android.build.gradle.managed.adaptor.BuildTypeAdaptor;
+import com.android.build.gradle.managed.adaptor.ProductFlavorAdaptor;
+import com.android.build.gradle.tasks.JillTask;
+import com.android.build.gradle.tasks.PreDex;
+import com.android.builder.Version;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.internal.compiler.JackConversionCache;
+import com.android.builder.internal.compiler.PreDexCache;
+import com.android.builder.profile.ProcessRecorderFactory;
+import com.android.builder.profile.Recorder;
+import com.android.builder.profile.ThreadRecorder;
+import com.android.builder.sdk.TargetInfo;
+import com.android.builder.signing.DefaultSigningConfig;
+import com.android.ide.common.internal.ExecutorSingleton;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.ide.common.signing.KeystoreHelper;
+import com.android.prefs.AndroidLocation;
+import com.android.utils.ILogger;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import org.gradle.api.Action;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
+import org.gradle.api.execution.TaskExecutionGraph;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.plugins.JavaBasePlugin;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.model.Defaults;
+import org.gradle.model.Model;
+import org.gradle.model.ModelMap;
+import org.gradle.model.Mutate;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+import org.gradle.model.internal.core.ModelCreators;
+import org.gradle.model.internal.core.ModelReference;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.platform.base.ComponentSpecContainer;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.KeyStore;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import groovy.lang.Closure;
+
+public class BaseComponentModelPlugin implements Plugin<Project> {
+
+ private ToolingModelBuilderRegistry toolingRegistry;
+
+ private ModelRegistry modelRegistry;
+
+ @Inject
+ protected BaseComponentModelPlugin(ToolingModelBuilderRegistry toolingRegistry,
+ ModelRegistry modelRegistry) {
+ this.toolingRegistry = toolingRegistry;
+ this.modelRegistry = modelRegistry;
+ }
+
+ /**
+ * Replace BasePlugin's apply method for component model.
+ */
+ @Override
+ public void apply(Project project) {
+ ExecutionConfigurationUtil.setThreadPoolSize(project);
+ try {
+ List<Recorder.Property> propertyList = Lists.newArrayList(
+ new Recorder.Property("plugin_version", Version.ANDROID_GRADLE_PLUGIN_VERSION),
+ new Recorder.Property("next_gen_plugin", "true"),
+ new Recorder.Property("gradle_version", project.getGradle().getGradleVersion())
+ );
+ String benchmarkName = AndroidGradleOptions.getBenchmarkName(project);
+ if (benchmarkName != null) {
+ propertyList.add(new Recorder.Property("benchmark_name", benchmarkName));
+ }
+ String benchmarkMode = AndroidGradleOptions.getBenchmarkMode(project);
+ if (benchmarkMode != null) {
+ propertyList.add(new Recorder.Property("benchmark_mode", benchmarkMode));
+ }
+
+ ProcessRecorderFactory.initialize(
+ new LoggerWrapper(project.getLogger()),
+ project.getRootProject()
+ .file("profiler" + System.currentTimeMillis() + ".json"),
+ propertyList);
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to initialize ProcessRecorderFactory");
+ }
+ project.getGradle().addListener(new RecordingBuildListener(ThreadRecorder.get()));
+
+ project.getPlugins().apply(AndroidComponentModelPlugin.class);
+ project.getPlugins().apply(JavaBasePlugin.class);
+ project.getPlugins().apply(JacocoPlugin.class);
+
+ // TODO: Create configurations for build types and flavors, or migrate to new dependency
+ // management if it's ready.
+ ConfigurationContainer configurations = project.getConfigurations();
+ createConfiguration(configurations, "compile", "Classpath for default sources.");
+ createConfiguration(configurations, "default-metadata", "Metadata for published APKs");
+ createConfiguration(configurations, "default-mapping", "Metadata for published APKs");
+
+ project.getPlugins().apply(NdkComponentModelPlugin.class);
+
+ // Remove this when our models no longer depends on Project.
+ modelRegistry.create(ModelCreators
+ .bridgedInstance(ModelReference.of("projectModel", Project.class), project)
+ .descriptor("Model of project.").build());
+
+ toolingRegistry.register(new ComponentModelBuilder(modelRegistry));
+
+ // Inserting the ToolingModelBuilderRegistry into the model so that it can be use to create
+ // TaskManager in child classes.
+ modelRegistry.create(ModelCreators.bridgedInstance(
+ ModelReference.of("toolingRegistry", ToolingModelBuilderRegistry.class),
+ toolingRegistry).descriptor("Tooling model builder model registry.").build());
+ }
+
+ private static void createConfiguration(@NonNull ConfigurationContainer configurations,
+ @NonNull String configurationName, @NonNull String configurationDescription) {
+ Configuration configuration = configurations.findByName(configurationName);
+ if (configuration == null) {
+ configuration = configurations.create(configurationName);
+ }
+
+ configuration.setVisible(false);
+ configuration.setDescription(configurationDescription);
+ }
+
+ @SuppressWarnings("MethodMayBeStatic")
+ public static class Rules extends RuleSource {
+
+ @Defaults
+ public void configureAndroidModel(
+ AndroidConfig androidModel,
+ ServiceRegistry serviceRegistry) {
+ Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+ AndroidConfigHelper.configure(androidModel, instantiator);
+
+ androidModel.getSigningConfigs().create(DEBUG, new Action<SigningConfig>() {
+ @Override
+ public void execute(SigningConfig signingConfig) {
+ try {
+ signingConfig.setStoreFile(KeystoreHelper.defaultDebugKeystoreLocation());
+ signingConfig.setStorePassword(DefaultSigningConfig.DEFAULT_PASSWORD);
+ signingConfig.setKeyAlias(DefaultSigningConfig.DEFAULT_ALIAS);
+ signingConfig.setKeyPassword(DefaultSigningConfig.DEFAULT_PASSWORD);
+ signingConfig.setStoreType(KeyStore.getDefaultType());
+ } catch (AndroidLocation.AndroidLocationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ }
+
+ // com.android.build.gradle.AndroidConfig do not contain an NdkConfig. Copy it to the
+ // defaultConfig for now.
+ @Defaults
+ public void copyNdkConfig(
+ @Path("android.defaultConfig.ndk") NdkOptions defaultNdkConfig,
+ @Path("android.ndk") NdkConfig pluginNdkConfig) {
+ NdkOptionsHelper.init(defaultNdkConfig);
+ NdkOptionsHelper.merge(defaultNdkConfig, pluginNdkConfig);
+ }
+
+ // TODO: Remove code duplicated from BasePlugin.
+ @Model(EXTRA_MODEL_INFO)
+ public ExtraModelInfo createExtraModelInfo(
+ Project project,
+ @NonNull @Path("isApplication") Boolean isApplication) {
+ return new ExtraModelInfo(project, isApplication);
+ }
+
+ @Model
+ public SdkHandler createSdkHandler(final Project project) {
+ final ILogger logger = new LoggerWrapper(project.getLogger());
+ final SdkHandler sdkHandler = new SdkHandler(project, logger);
+
+ // call back on execution. This is called after the whole build is done (not
+ // after the current project is done).
+ // This is will be called for each (android) projects though, so this should support
+ // being called 2+ times.
+ project.getGradle().buildFinished(new Closure<Object>(this, this) {
+ public void doCall(Object it) {
+ ExecutorSingleton.shutdown();
+ sdkHandler.unload();
+ try {
+ PreDexCache.getCache().clear(project.getRootProject()
+ .file(String.valueOf(project.getRootProject().getBuildDir()) + "/"
+ + FD_INTERMEDIATES + "/dex-cache/cache.xml"), logger);
+ JackConversionCache.getCache().clear(project.getRootProject()
+ .file(String.valueOf(project.getRootProject().getBuildDir()) + "/"
+ + FD_INTERMEDIATES + "/jack-cache/cache.xml"), logger);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ LibraryCache.getCache().unload();
+ }
+
+ public void doCall() {
+ doCall(null);
+ }
+ });
+
+ project.getGradle().getTaskGraph().whenReady(new Closure<Void>(this, this) {
+ public void doCall(TaskExecutionGraph taskGraph) {
+ for (Task task : taskGraph.getAllTasks()) {
+ if (task instanceof PreDex) {
+ PreDexCache.getCache().load(project.getRootProject()
+ .file(String.valueOf(project.getRootProject().getBuildDir())
+ + "/" + FD_INTERMEDIATES + "/dex-cache/cache.xml"));
+ break;
+ } else if (task instanceof JillTask) {
+ JackConversionCache.getCache().load(project.getRootProject()
+ .file(String.valueOf(project.getRootProject().getBuildDir())
+ + "/" + FD_INTERMEDIATES + "/jack-cache/cache.xml"));
+ break;
+ }
+ }
+ }
+ });
+
+ // setup SDK repositories.
+ for (final File file : sdkHandler.getSdkLoader().getRepositories()) {
+ project.getRepositories().maven(new Action<MavenArtifactRepository>() {
+ @Override
+ public void execute(MavenArtifactRepository repo) {
+ repo.setUrl(file.toURI());
+
+ }
+ });
+ }
+ return sdkHandler;
+ }
+
+ @Model(ANDROID_BUILDER)
+ public AndroidBuilder createAndroidBuilder(Project project, ExtraModelInfo extraModelInfo) {
+ String creator = "Android Gradle";
+ ILogger logger = new LoggerWrapper(project.getLogger());
+
+ return new AndroidBuilder(project.equals(project.getRootProject()) ? project.getName()
+ : project.getPath(), creator, new GradleProcessExecutor(project),
+ new GradleJavaProcessExecutor(project),
+ extraModelInfo, logger, project.getLogger().isEnabled(LogLevel.INFO));
+
+ }
+
+ @Mutate
+ public void initDebugBuildTypes(
+ @Path("android.buildTypes") ModelMap<BuildType> buildTypes,
+ @Path("android.signingConfigs") final ModelMap<SigningConfig> signingConfigs) {
+ buildTypes.beforeEach(new Action<BuildType>() {
+ @Override
+ public void execute(BuildType buildType) {
+ initBuildType(buildType);
+ }
+ });
+
+ buildTypes.named(DEBUG, new Action<BuildType>() {
+ @Override
+ public void execute(BuildType buildType) {
+ buildType.setSigningConfig(signingConfigs.get(DEBUG));
+ }
+ });
+ }
+
+ private static void initBuildType(@NonNull BuildType buildType) {
+ buildType.setDebuggable(false);
+ buildType.setTestCoverageEnabled(false);
+ buildType.setPseudoLocalesEnabled(false);
+ buildType.setRenderscriptDebuggable(false);
+ buildType.setRenderscriptOptimLevel(3);
+ buildType.setMinifyEnabled(false);
+ buildType.setZipAlignEnabled(true);
+ buildType.setEmbedMicroApp(true);
+ buildType.setUseJack(false);
+ buildType.setShrinkResources(false);
+ buildType.setProguardFiles(Sets.<File>newHashSet());
+ buildType.setConsumerProguardFiles(Sets.<File>newHashSet());
+ buildType.setTestProguardFiles(Sets.<File>newHashSet());
+ }
+
+ @Mutate
+ public void initDefaultConfig(@Path("android.defaultConfig") ProductFlavor defaultConfig) {
+ initProductFlavor(defaultConfig);
+ }
+
+ @Mutate
+ public void initProductFlavors(
+ @Path("android.productFlavors") final ModelMap<ProductFlavor> productFlavors) {
+ productFlavors.beforeEach(new Action<ProductFlavor>() {
+ @Override
+ public void execute(ProductFlavor productFlavor) {
+ initProductFlavor(productFlavor);
+ }
+ });
+ }
+
+ private void initProductFlavor(ProductFlavor productFlavor) {
+ productFlavor.setProguardFiles(Sets.<File>newHashSet());
+ productFlavor.setConsumerProguardFiles(Sets.<File>newHashSet());
+ productFlavor.setTestProguardFiles(Sets.<File>newHashSet());
+ productFlavor.setResourceConfigurations(Sets.<String>newHashSet());
+ productFlavor.setJarJarRuleFiles(Lists.<File>newArrayList());
+ productFlavor.getBuildConfigFields().beforeEach(new Action<ClassField>() {
+ @Override
+ public void execute(ClassField classField) {
+ classField.setAnnotations(Sets.<String>newHashSet());
+ }
+ });
+ productFlavor.getResValues().beforeEach(new Action<ClassField>() {
+ @Override
+ public void execute(ClassField classField) {
+ classField.setAnnotations(Sets.<String>newHashSet());
+ }
+ });
+ }
+
+ @Mutate
+ public void addDefaultAndroidSourceSet(
+ @Path("android.sources") AndroidComponentModelSourceSet sources) {
+ sources.addDefaultSourceSet("resources", AndroidLanguageSourceSet.class);
+ sources.addDefaultSourceSet("java", AndroidLanguageSourceSet.class);
+ sources.addDefaultSourceSet("manifest", AndroidLanguageSourceSet.class);
+ sources.addDefaultSourceSet("res", AndroidLanguageSourceSet.class);
+ sources.addDefaultSourceSet("assets", AndroidLanguageSourceSet.class);
+ sources.addDefaultSourceSet("aidl", AndroidLanguageSourceSet.class);
+ sources.addDefaultSourceSet("renderscript", AndroidLanguageSourceSet.class);
+ sources.addDefaultSourceSet("jniLibs", AndroidLanguageSourceSet.class);
+
+ sources.all(new Action<FunctionalSourceSet>() {
+ @Override
+ public void execute(FunctionalSourceSet functionalSourceSet) {
+ LanguageSourceSet manifest = functionalSourceSet.getByName("manifest");
+ manifest.getSource().setIncludes(ImmutableList.of("AndroidManifest.xml"));
+ }
+ });
+ }
+
+ @Model(ANDROID_CONFIG_ADAPTOR)
+ public com.android.build.gradle.AndroidConfig createModelAdaptor(
+ ServiceRegistry serviceRegistry,
+ AndroidConfig androidExtension,
+ Project project,
+ @Path("isApplication") Boolean isApplication) {
+ Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+ return new AndroidConfigAdaptor(androidExtension, AndroidConfigHelper
+ .createSourceSetsContainer(project, instantiator, !isApplication));
+ }
+
+ @Mutate
+ public void createAndroidComponents(
+ ComponentSpecContainer androidSpecs,
+ ServiceRegistry serviceRegistry, AndroidConfig androidExtension,
+ com.android.build.gradle.AndroidConfig adaptedModel,
+ @Path("android.buildTypes") ModelMap<BuildType> buildTypes,
+ @Path("android.productFlavors") ModelMap<ProductFlavor> productFlavors,
+ @Path("android.signingConfigs") ModelMap<SigningConfig> signingConfigs,
+ VariantFactory variantFactory,
+ TaskManager taskManager,
+ Project project,
+ AndroidBuilder androidBuilder,
+ SdkHandler sdkHandler,
+ ExtraModelInfo extraModelInfo,
+ @Path("isApplication") Boolean isApplication) {
+ Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+
+ // check if the target has been set.
+ TargetInfo targetInfo = androidBuilder.getTargetInfo();
+ if (targetInfo == null) {
+ sdkHandler.initTarget(androidExtension.getCompileSdkVersion(),
+ androidExtension.getBuildToolsRevision(),
+ androidExtension.getLibraryRequests(), androidBuilder);
+ }
+
+ VariantManager variantManager = new VariantManager(project, androidBuilder,
+ adaptedModel, variantFactory, taskManager, instantiator);
+
+ for (BuildType buildType : buildTypes.values()) {
+ variantManager.addBuildType(new BuildTypeAdaptor(buildType));
+ }
+
+ for (ProductFlavor productFlavor : productFlavors.values()) {
+ variantManager.addProductFlavor(new ProductFlavorAdaptor(productFlavor));
+ }
+
+ DefaultAndroidComponentSpec spec =
+ (DefaultAndroidComponentSpec) androidSpecs.get(COMPONENT_NAME);
+ spec.setExtension(androidExtension);
+ spec.setVariantManager(variantManager);
+ }
+
+ @Mutate
+ public void createVariantData(
+ ModelMap<AndroidBinary> binaries,
+ ModelMap<AndroidComponentSpec> specs,
+ TaskManager taskManager) {
+ final VariantManager variantManager =
+ ((DefaultAndroidComponentSpec) specs.get(COMPONENT_NAME)).getVariantManager();
+ binaries.afterEach(new Action<AndroidBinary>() {
+ @Override
+ public void execute(AndroidBinary androidBinary) {
+ DefaultAndroidBinary binary = (DefaultAndroidBinary) androidBinary;
+ List<ProductFlavorAdaptor> adaptedFlavors = Lists.newArrayList();
+ for (ProductFlavor flavor : binary.getProductFlavors()) {
+ adaptedFlavors.add(new ProductFlavorAdaptor(flavor));
+ }
+ binary.setVariantData(
+ variantManager.createVariantData(
+ new BuildTypeAdaptor(binary.getBuildType()),
+ adaptedFlavors));
+ variantManager.getVariantDataList().add(binary.getVariantData());
+ }
+ });
+ }
+
+ @Mutate
+ public void createLifeCycleTasks(ModelMap<Task> tasks, TaskManager taskManager) {
+ taskManager.createTasksBeforeEvaluate(new TaskModelMapAdaptor(tasks));
+ }
+
+ @Mutate
+ public void createAndroidTasks(
+ ModelMap<Task> tasks,
+ ModelMap<AndroidComponentSpec> androidSpecs,
+ TaskManager taskManager,
+ SdkHandler sdkHandler,
+ Project project, AndroidComponentModelSourceSet androidSources) {
+ // setup SDK repositories.
+ for (final File file : sdkHandler.getSdkLoader().getRepositories()) {
+ project.getRepositories().maven(new Action<MavenArtifactRepository>() {
+ @Override
+ public void execute(MavenArtifactRepository repo) {
+ repo.setUrl(file.toURI());
+ }
+ });
+ }
+ // TODO: determine how to provide functionalities of variant API objects.
+ }
+
+ // TODO: Use @BinaryTasks after figuring how to configure non-binary specific tasks.
+ @Mutate
+ public void createBinaryTasks(
+ final ModelMap<Task> tasks,
+ BinaryContainer binaries,
+ ModelMap<AndroidComponentSpec> specs,
+ TaskManager taskManager) {
+ final VariantManager variantManager =
+ ((DefaultAndroidComponentSpec) specs.get(COMPONENT_NAME)).getVariantManager();
+ binaries.withType(AndroidBinary.class, new Action<AndroidBinary>() {
+ @Override
+ public void execute(AndroidBinary androidBinary) {
+ DefaultAndroidBinary binary = (DefaultAndroidBinary) androidBinary;
+ variantManager.createTasksForVariantData(
+ new TaskModelMapAdaptor(tasks),
+ binary.getVariantData());
+ }
+ });
+ }
+
+ /**
+ * Create tasks that must be created after other tasks for variants are created.
+ */
+ @Mutate
+ public void createRemainingTasks(
+ ModelMap<Task> tasks,
+ TaskManager taskManager,
+ ModelMap<AndroidComponentSpec> spec) {
+ VariantManager variantManager =
+ ((DefaultAndroidComponentSpec)spec.get(COMPONENT_NAME)).getVariantManager();
+
+ // create the test tasks.
+ taskManager.createTopLevelTestTasks(new TaskModelMapAdaptor(tasks),
+ !variantManager.getProductFlavors().isEmpty());
+ }
+
+ @Mutate
+ public void createReportTasks(
+ ModelMap<Task> tasks,
+ ModelMap<AndroidComponentSpec> specs) {
+ final VariantManager variantManager =
+ ((DefaultAndroidComponentSpec)specs.get(COMPONENT_NAME)).getVariantManager();
+
+ tasks.create("androidDependencies", DependencyReportTask.class,
+ new Action<DependencyReportTask>() {
+ @Override
+ public void execute(DependencyReportTask dependencyReportTask) {
+ dependencyReportTask.setDescription(
+ "Displays the Android dependencies of the project");
+ dependencyReportTask.setVariants(variantManager.getVariantDataList());
+ dependencyReportTask.setGroup("Android");
+ }
+ });
+
+ tasks.create("signingReport", SigningReportTask.class,
+ new Action<SigningReportTask>() {
+ @Override
+ public void execute(SigningReportTask signingReportTask) {
+ signingReportTask
+ .setDescription("Displays the signing info for each variant");
+ signingReportTask.setVariants(variantManager.getVariantDataList());
+ signingReportTask.setGroup("Android");
+
+ }
+ });
+ }
+
+ @Mutate
+ public void modifyAssembleTaskDescription(@Path("tasks.assemble") Task assembleTask) {
+ assembleTask.setDescription(
+ "Assembles all variants of all applications and secondary packages.");
+ }
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ComponentModelBuilder.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ComponentModelBuilder.java
new file mode 100644
index 0000000..81256fd
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ComponentModelBuilder.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.model;
+
+import static com.android.build.gradle.model.ModelConstants.ANDROID_BUILDER;
+import static com.android.build.gradle.model.ModelConstants.ANDROID_CONFIG_ADAPTOR;
+import static com.android.build.gradle.model.ModelConstants.BINARIES;
+import static com.android.build.gradle.model.ModelConstants.COMPONENTS;
+import static com.android.build.gradle.model.ModelConstants.EXTRA_MODEL_INFO;
+import static com.android.build.gradle.model.ModelConstants.IS_APPLICATION;
+import static com.android.build.gradle.model.ModelConstants.NDK_HANDLER;
+import static com.android.build.gradle.model.ModelConstants.TASK_MANAGER;
+
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.VariantManager;
+import com.android.build.gradle.internal.model.ModelBuilder;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.model.AndroidProject;
+
+import org.gradle.api.Project;
+import org.gradle.model.internal.core.ModelPath;
+import org.gradle.model.internal.registry.ModelRegistry;
+import org.gradle.model.internal.type.ModelType;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.platform.base.ComponentSpecContainer;
+import org.gradle.tooling.provider.model.ToolingModelBuilder;
+
+/**
+ * A ToolingModelBuilder for creating AndroidProject in the component model plugin.
+ *
+ * It retrieves models from ModelRegistry and uses ModelBuilder to create AndroidProject.
+ *
+ * This model builder uses Gradle's internal API as a public API for create tooling model from
+ * component model is not yet available.
+ */
+public class ComponentModelBuilder implements ToolingModelBuilder {
+
+ ModelBuilder modelBuilder;
+ ModelRegistry registry;
+
+
+ public ComponentModelBuilder(ModelRegistry registry) {
+ this.registry = registry;
+ }
+
+ @Override
+ public boolean canBuild(String modelName) {
+ return modelName.equals(AndroidProject.class.getName());
+ }
+
+ @Override
+ public Object buildAll(String modelName, Project project) {
+ if (modelBuilder == null) {
+ modelBuilder = createModelBuilder();
+ }
+ return modelBuilder.buildAll(modelName, project);
+ }
+
+ private ModelBuilder createModelBuilder() {
+ AndroidBuilder androidBuilder = registry.realize(
+ new ModelPath(ANDROID_BUILDER),
+ ModelType.of(AndroidBuilder.class));
+ DefaultAndroidComponentSpec componentSpec = (DefaultAndroidComponentSpec) registry.realize(
+ new ModelPath(COMPONENTS),
+ ModelType.of(ComponentSpecContainer.class))
+ .get(AndroidComponentModelPlugin.COMPONENT_NAME);
+ VariantManager variantManager = componentSpec.getVariantManager();
+ TaskManager taskManager = registry.realize(
+ new ModelPath(TASK_MANAGER),
+ ModelType.of(TaskManager.class));
+ AndroidConfig extension = registry.realize(
+ new ModelPath(ANDROID_CONFIG_ADAPTOR),
+ ModelType.of(AndroidConfig.class));
+ ExtraModelInfo extraModelInfo = registry.realize(
+ new ModelPath(EXTRA_MODEL_INFO),
+ ModelType.of(ExtraModelInfo.class));
+ Boolean isApplication = registry.realize(
+ new ModelPath(IS_APPLICATION),
+ ModelType.of(Boolean.class));
+ NdkHandler ndkHandler = registry.realize(
+ new ModelPath(NDK_HANDLER),
+ ModelType.of(NdkHandler.class));
+ BinaryContainer binaries = registry.realize(
+ new ModelPath(BINARIES),
+ ModelType.of(BinaryContainer.class));
+
+ return new ModelBuilder(
+ androidBuilder, variantManager, taskManager,
+ extension, extraModelInfo, ndkHandler,
+ new ComponentNativeLibraryFactory(binaries, ndkHandler),
+ !isApplication);
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ComponentNativeLibraryFactory.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ComponentNativeLibraryFactory.java
new file mode 100644
index 0000000..5497b62
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ComponentNativeLibraryFactory.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.model;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.internal.model.NativeLibraryFactory;
+import com.android.build.gradle.internal.model.NativeLibraryImpl;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.build.gradle.ndk.internal.BinaryToolHelper;
+import com.android.builder.model.NativeLibrary;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+import org.gradle.nativeplatform.NativeLibraryBinarySpec;
+import org.gradle.platform.base.BinaryContainer;
+
+import java.io.File;
+import java.util.Collections;
+
+/**
+ * Implementation of NativeLibraryFactory from in the component model plugin.
+ *
+ * The library extract information directly from the binaries.
+ */
+public class ComponentNativeLibraryFactory implements NativeLibraryFactory {
+
+ BinaryContainer binaries;
+
+ NdkHandler ndkHandler;
+
+ public ComponentNativeLibraryFactory(BinaryContainer binaries,
+ NdkHandler ndkHandler) {
+ this.binaries = binaries;
+ this.ndkHandler = ndkHandler;
+ }
+
+ @NonNull
+ @Override
+ public Optional<NativeLibrary> create(
+ @NonNull VariantScope scope,
+ @NonNull String toolchainName,
+ @NonNull final Abi abi) {
+ BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+
+ DefaultAndroidBinary androidBinary =
+ (DefaultAndroidBinary) binaries.findByName(variantData.getName());
+
+ if (androidBinary == null) {
+ // Binaries are not created for test variants.
+ return Optional.absent();
+ }
+
+ @SuppressWarnings("ConstantConditions")
+ Optional<NativeLibraryBinarySpec> nativeBinary =
+ Iterables.tryFind(androidBinary.getNativeBinaries(),
+ new Predicate<NativeLibraryBinarySpec>() {
+ @Override
+ public boolean apply(NativeLibraryBinarySpec binary) {
+ return binary.getTargetPlatform().getName().equals(abi.getName());
+ }
+ });
+
+ if (!nativeBinary.isPresent()) {
+ // We don't have native binaries.
+ return Optional.absent();
+ }
+
+ CoreNdkOptions ndkConfig = variantData.getVariantConfiguration().getNdkConfig();
+ // The DSL currently do not support all options available in the model such as the
+ // include dirs and the defines. Therefore, just pass an empty collection for now.
+ return Optional.<NativeLibrary>of(new NativeLibraryImpl(
+ ndkConfig.getModuleName(),
+ toolchainName,
+ abi.getName(),
+ Collections.<File>emptyList(), /*cIncludeDirs*/
+ Collections.<File>emptyList(), /*cppIncludeDirs*/
+ Collections.<File>emptyList(), /*cSystemIncludeDirs*/
+ ndkHandler.getStlIncludes(ndkConfig.getStl(), abi),
+ Collections.<String>emptyList(), /*cDefines*/
+ Collections.<String>emptyList(), /*cppDefines*/
+ BinaryToolHelper.getCCompiler(nativeBinary.get()).getArgs(),
+ BinaryToolHelper.getCppCompiler(nativeBinary.get()).getArgs(),
+ ImmutableList.of(variantData.getScope().getNdkDebuggableLibraryFolders(abi))));
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidBinary.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidBinary.java
index ce7531f..5f61693 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidBinary.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidBinary.java
@@ -16,9 +16,12 @@
package com.android.build.gradle.model;
-import com.android.build.gradle.api.GroupableProductFlavor;
+import com.android.build.gradle.internal.NdkOptionsHelper;
import com.android.build.gradle.internal.variant.BaseVariantData;
-import com.android.builder.model.BuildType;
+import com.android.build.gradle.managed.BuildType;
+import com.android.build.gradle.managed.NdkConfig;
+import com.android.build.gradle.managed.NdkOptions;
+import com.android.build.gradle.managed.ProductFlavor;
import com.google.common.collect.Lists;
import org.gradle.nativeplatform.NativeLibraryBinarySpec;
@@ -33,7 +36,9 @@
private BuildType buildType;
- private List<? extends GroupableProductFlavor> productFlavors;
+ private List<ProductFlavor> productFlavors;
+
+ private NdkConfig mergedNdkConfig = new NdkConfigImpl();
private BaseVariantData variantData;
@@ -51,14 +56,18 @@
}
@Override
- public List<? extends GroupableProductFlavor> getProductFlavors() {
+ public List<ProductFlavor> getProductFlavors() {
return productFlavors;
}
- public void setProductFlavors(List<? extends GroupableProductFlavor> productFlavors) {
+ public void setProductFlavors(List<ProductFlavor> productFlavors) {
this.productFlavors = productFlavors;
}
+ public NdkConfig getMergedNdkConfig() {
+ return mergedNdkConfig;
+ }
+
public BaseVariantData getVariantData() {
return variantData;
}
@@ -67,11 +76,33 @@
this.variantData = variantData;
}
- public List<? extends NativeLibraryBinarySpec> getNativeBinaries() {
+ public List<NativeLibraryBinarySpec> getNativeBinaries() {
return nativeBinaries;
}
public List<String> getTargetAbi() {
return targetAbi;
}
+
+ public void computeMergedNdk(
+ NdkConfig ndkConfig,
+ List<com.android.build.gradle.managed.ProductFlavor> flavors,
+ com.android.build.gradle.managed.BuildType buildType) {
+
+
+ if (ndkConfig != null) {
+ NdkOptionsHelper.merge(mergedNdkConfig, ndkConfig);
+ }
+
+ for (int i = flavors.size() - 1 ; i >= 0 ; i--) {
+ NdkOptions ndkOptions = flavors.get(i).getNdk();
+ if (ndkOptions != null) {
+ NdkOptionsHelper.merge(mergedNdkConfig, ndkOptions);
+ }
+ }
+
+ if (buildType.getNdk() != null) {
+ NdkOptionsHelper.merge(mergedNdkConfig, buildType.getNdk());
+ }
+ }
}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidComponentSpec.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidComponentSpec.java
index d67a7e2..a9c198f 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidComponentSpec.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/DefaultAndroidComponentSpec.java
@@ -16,11 +16,10 @@
package com.android.build.gradle.model;
-import com.android.build.gradle.BaseExtension;
import com.android.build.gradle.internal.VariantManager;
+import com.android.build.gradle.managed.AndroidConfig;
import com.android.builder.model.SigningConfig;
-import org.gradle.nativeplatform.NativeLibrary;
import org.gradle.nativeplatform.NativeLibrarySpec;
import org.gradle.platform.base.component.BaseComponentSpec;
@@ -28,7 +27,7 @@
* Implementation for Android component spec.
*/
public class DefaultAndroidComponentSpec extends BaseComponentSpec implements AndroidComponentSpec{
- BaseExtension extension;
+ AndroidConfig extension;
VariantManager variantManager;
@@ -37,11 +36,11 @@
NativeLibrarySpec nativeLibrary;
@Override
- public BaseExtension getExtension() {
+ public AndroidConfig getExtension() {
return extension;
}
- public void setExtension(BaseExtension extension) {
+ public void setExtension(AndroidConfig extension) {
this.extension = extension;
}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentModelPlugin.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentModelPlugin.groovy
deleted file mode 100644
index b24a025..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentModelPlugin.groovy
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.model
-
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.LibraryExtension
-import com.android.build.gradle.internal.DependencyManager
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.variant.LibraryVariantFactory
-import com.android.build.gradle.internal.variant.VariantFactory
-import com.android.builder.core.AndroidBuilder
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.model.Model
-import org.gradle.model.RuleSource
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-
-/**
- * Gradle component model plugin class for 'application' projects.
- */
-public class LibraryComponentModelPlugin implements Plugin<Project> {
- @Override
- void apply(Project project) {
- project.plugins.apply(BaseComponentModelPlugin)
- project.tasks.create("assembleDefault")
- project.plugins.apply(AndroidComponentModelTestPlugin)
- }
-
- static class Rules extends RuleSource{
-
- @Model
- Boolean isApplication() {
- return false
- }
-
- @Model
- TaskManager createTaskManager(
- BaseExtension androidExtension,
- Project project,
- AndroidBuilder androidBuilder,
- SdkHandler sdkHandler,
- ExtraModelInfo extraModelInfo,
- ToolingModelBuilderRegistry toolingRegistry) {
- DependencyManager dependencyManager = new DependencyManager(project, extraModelInfo)
-
- return new LibraryComponentTaskManager(
- project,
- androidBuilder,
- androidExtension,
- sdkHandler,
- dependencyManager,
- toolingRegistry)
- }
-
- @Model
- VariantFactory createVariantFactory(
- ServiceRegistry serviceRegistry,
- AndroidBuilder androidBuilder,
- BaseExtension extension) {
- Instantiator instantiator = serviceRegistry.get(Instantiator.class)
- return new LibraryVariantFactory(
- instantiator,
- androidBuilder,
- (LibraryExtension) extension)
- }
- }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentModelPlugin.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentModelPlugin.java
new file mode 100644
index 0000000..af05716
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentModelPlugin.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.model;
+
+import static com.android.build.gradle.model.ModelConstants.IS_APPLICATION;
+import static com.android.build.gradle.model.ModelConstants.TASK_MANAGER;
+
+import com.android.build.gradle.AndroidConfig;
+import com.android.build.gradle.internal.DependencyManager;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.variant.LibraryVariantFactory;
+import com.android.build.gradle.internal.variant.VariantFactory;
+import com.android.builder.core.AndroidBuilder;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.internal.service.ServiceRegistry;
+import org.gradle.model.Model;
+import org.gradle.model.RuleSource;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+/**
+ * Gradle component model plugin class for 'application' projects.
+ */
+public class LibraryComponentModelPlugin implements Plugin<Project> {
+ @Override
+ public void apply(Project project) {
+ project.getPluginManager().apply(BaseComponentModelPlugin.class);
+ project.getTasks().create("assembleDefault");
+ project.getPluginManager().apply(AndroidComponentModelTestPlugin.class);
+ }
+
+ @SuppressWarnings("MethodMayBeStatic")
+ public static class Rules extends RuleSource {
+
+ @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+ @Model(IS_APPLICATION)
+ public Boolean isApplication() {
+ return false;
+ }
+
+ @Model(TASK_MANAGER)
+ public TaskManager createTaskManager(
+ AndroidConfig androidExtension,
+ Project project,
+ AndroidBuilder androidBuilder,
+ SdkHandler sdkHandler,
+ ExtraModelInfo extraModelInfo,
+ ToolingModelBuilderRegistry toolingRegistry) {
+ DependencyManager dependencyManager = new DependencyManager(project, extraModelInfo);
+
+ return new LibraryComponentTaskManager(
+ project,
+ androidBuilder,
+ androidExtension,
+ sdkHandler,
+ dependencyManager,
+ toolingRegistry);
+ }
+
+ @Model
+ public VariantFactory createVariantFactory(
+ ServiceRegistry serviceRegistry,
+ AndroidBuilder androidBuilder,
+ AndroidConfig extension) {
+ Instantiator instantiator = serviceRegistry.get(Instantiator.class);
+ return new LibraryVariantFactory(instantiator, androidBuilder, extension);
+ }
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentTaskManager.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentTaskManager.java
index 8a7f3e9..f1b79ec 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentTaskManager.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/LibraryComponentTaskManager.java
@@ -16,19 +16,19 @@
package com.android.build.gradle.model;
-import com.android.build.gradle.BaseExtension;
+import com.android.annotations.NonNull;
+import com.android.build.gradle.AndroidConfig;
import com.android.build.gradle.internal.DependencyManager;
import com.android.build.gradle.internal.LibraryTaskManager;
import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.variant.BaseVariantData;
import com.android.builder.core.AndroidBuilder;
import com.google.common.collect.ImmutableList;
import org.gradle.api.Project;
-import org.gradle.api.tasks.TaskContainer;
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
-import java.io.File;
import java.util.Collection;
/**
@@ -39,7 +39,7 @@
public LibraryComponentTaskManager(
Project project,
AndroidBuilder androidBuilder,
- BaseExtension extension,
+ AndroidConfig extension,
SdkHandler sdkHandler,
DependencyManager dependencyManager,
ToolingModelBuilderRegistry toolingRegistry) {
@@ -54,9 +54,7 @@
}
@Override
- protected Collection<File> getNdkOutputDirectories(BaseVariantData variantData) {
- NdkComponentModelPlugin plugin = project.getPlugins().getPlugin(
- NdkComponentModelPlugin.class);
- return plugin.getOutputDirectories(variantData.getVariantConfiguration());
+ public void configureScopeForNdk(@NonNull VariantScope scope) {
+ NdkComponentModelPlugin.configureScopeForNdk(scope);
}
}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ModelConstants.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ModelConstants.java
new file mode 100644
index 0000000..039c3c6
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/ModelConstants.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.model;
+
+/**
+ * Component model path names.
+ */
+public class ModelConstants {
+
+ public static final String ANDROID_BUILDER = "androidBuilder";
+
+ public static final String ANDROID_CONFIG_ADAPTOR = "androidConfigAdaptor";
+
+ public static final String BINARIES = "binaries";
+
+ public static final String COMPONENTS = "components";
+
+ public static final String EXTRA_MODEL_INFO = "extraModelInfo";
+
+ public static final String IS_APPLICATION = "isApplication";
+
+ public static final String NDK_HANDLER = "ndkHandler";
+
+ public static final String TASK_MANAGER = "taskManager";
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkComponentModelPlugin.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkComponentModelPlugin.groovy
deleted file mode 100644
index 409b8b1..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkComponentModelPlugin.groovy
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package com.android.build.gradle.model
-
-import com.android.build.gradle.BaseExtension
-import com.android.build.gradle.internal.ProductFlavorCombo
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.ndk.NdkExtension
-import com.android.build.gradle.ndk.internal.NdkConfiguration
-import com.android.build.gradle.ndk.internal.NdkExtensionConvention
-import com.android.build.gradle.ndk.internal.NdkHandler
-import com.android.build.gradle.ndk.internal.ToolchainConfiguration
-import com.android.builder.core.VariantConfiguration
-import org.gradle.api.NamedDomainObjectContainer
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.api.internal.project.ProjectIdentifier
-import org.gradle.api.tasks.TaskContainer
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.internal.service.ServiceRegistry
-import org.gradle.language.c.CSourceSet
-import org.gradle.language.cpp.CppSourceSet
-import org.gradle.model.Finalize
-import org.gradle.model.Model
-import org.gradle.model.Mutate
-import org.gradle.model.Path
-import org.gradle.model.RuleSource
-import org.gradle.nativeplatform.BuildTypeContainer
-import org.gradle.nativeplatform.FlavorContainer
-import org.gradle.nativeplatform.NativeLibraryBinarySpec
-import org.gradle.nativeplatform.NativeLibrarySpec
-import org.gradle.nativeplatform.SharedLibraryBinarySpec
-import org.gradle.nativeplatform.StaticLibraryBinary
-import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry
-import org.gradle.platform.base.BinaryContainer
-import org.gradle.platform.base.BinarySpec
-import org.gradle.platform.base.ComponentSpecContainer
-import org.gradle.platform.base.PlatformContainer
-
-/**
- * Plugin for Android NDK applications.
- */
-class NdkComponentModelPlugin implements Plugin<Project> {
- private Project project
-
- void apply(Project project) {
- this.project = project
-
- project.apply plugin: AndroidComponentModelPlugin
-
- project.apply plugin: 'c'
- project.apply plugin: 'cpp'
-
- // Remove static library tasks from assemble
- project.binaries.withType(StaticLibraryBinary) {
- it.buildable = false
- }
- }
-
- static class Rules extends RuleSource {
- @Mutate
- void configureAndroidModel(
- AndroidModel androidModel,
- @Path("androidNdk") NdkExtension ndk) {
- androidModel.ndk = ndk
- }
-
- @Model("androidNdk")
- NdkExtension createAndroidNdk(ServiceRegistry serviceRegistry) {
- Instantiator instantiator = serviceRegistry.get(Instantiator.class)
- return instantiator.newInstance(NdkExtension)
- }
-
- @Mutate
- void addDefaultNativeSourceSet(AndroidComponentModelSourceSet sources) {
- sources.addDefaultSourceSet("c", CSourceSet.class);
- sources.addDefaultSourceSet("cpp", CppSourceSet.class);
- }
-
- @Model
- NdkHandler ndkHandler(ProjectIdentifier projectId, NdkExtension extension) {
- while (projectId.parentIdentifier != null) {
- projectId = projectId.parentIdentifier
- }
- return new NdkHandler(projectId.projectDir, extension)
- }
-
- @Finalize
- void setDefaultNdkExtensionValue(NdkExtension extension) {
- NdkExtensionConvention.setExtensionDefault(extension)
- }
-
- @Mutate
- void createAndroidPlatforms(PlatformContainer platforms, NdkHandler ndkHandler) {
- // Create android platforms.
- ToolchainConfiguration.configurePlatforms(platforms, ndkHandler)
- }
-
- @Mutate
- void createToolchains(
- NativeToolChainRegistry toolchains,
- NdkExtension ndkExtension,
- NdkHandler ndkHandler) {
- // Create toolchain for each ABI.
- ToolchainConfiguration.configureToolchain(
- toolchains,
- ndkExtension.getToolchain(),
- ndkExtension.getToolchainVersion(),
- ndkHandler)
- }
-
- @Mutate
- void createNativeBuildTypes(
- BuildTypeContainer nativeBuildTypes,
- @Path("android.buildTypes") NamedDomainObjectContainer<BuildType> androidBuildTypes) {
- for (def buildType : androidBuildTypes) {
- nativeBuildTypes.maybeCreate(buildType.name)
- }
- }
-
- @Mutate
- void createNativeFlavors(
- FlavorContainer nativeFlavors,
- List<ProductFlavorCombo> androidFlavorGroups) {
- for (def group : androidFlavorGroups) {
- nativeFlavors.maybeCreate(group.name)
- }
- }
-
- @Mutate
- void createNativeLibrary(
- ComponentSpecContainer specs,
- @Path("android.ndk") NdkExtension extension,
- NdkHandler ndkHandler,
- @Path("android.sources") AndroidComponentModelSourceSet sources,
- @Path("buildDir") File buildDir) {
- if (!extension.moduleName.isEmpty()) {
- NativeLibrarySpec library = specs.create(extension.moduleName, NativeLibrarySpec)
- specs.withType(DefaultAndroidComponentSpec) { androidSpec ->
- androidSpec.nativeLibrary = library
- NdkConfiguration.configureProperties(
- library, sources, buildDir, extension, ndkHandler)
- }
- }
- }
- @Mutate
- void createAdditionalTasksForNatives(
- TaskContainer tasks,
- ComponentSpecContainer specs,
- NdkExtension extension,
- NdkHandler ndkHandler,
- @Path("buildDir") File buildDir) {
- specs.withType(DefaultAndroidComponentSpec) { androidSpec ->
- if (androidSpec.nativeLibrary != null) {
- androidSpec.nativeLibrary.binaries.withType(SharedLibraryBinarySpec) { binary ->
- NdkConfiguration.createTasks(
- tasks, binary, buildDir, extension, ndkHandler)
- }
- }
- }
- }
-
- @Mutate
- void attachNativeBinaryToAndroid(
- BinaryContainer binaries,
- ComponentSpecContainer specs,
- AndroidComponentSpec androidSpec,
- @Path("android.ndk") NdkExtension extension) {
- if (!extension.moduleName.isEmpty()) {
- NativeLibrarySpec library =
- specs.withType(NativeLibrarySpec).getByName(extension.moduleName);
- binaries.withType(DefaultAndroidBinary).each { binary ->
- def nativeBinaries = getNativeBinaries(library, binary.buildType, binary.productFlavors)
- binary.getNativeBinaries().addAll(nativeBinaries)
- }
- }
- }
-
- @Finalize
- void attachNativeTasksToAssembleTasks(BinaryContainer binaries) {
- binaries.withType(DefaultAndroidBinary) { binary ->
- if (binary.targetAbi.isEmpty()) {
- binary.builtBy(binary.nativeBinaries)
- } else {
- for (NativeLibraryBinarySpec nativeBinary : binary.nativeBinaries) {
- if (binary.targetAbi.contains(nativeBinary.targetPlatform.name)) {
- binary.builtBy(nativeBinary)
- }
- }
- }
- }
- }
-
- /**
- * Remove unintended tasks created by Gradle native plugin from task list.
- *
- * Gradle native plugins creates static library tasks automatically. This method removes them
- * to avoid cluttering the task list.
- */
- @Mutate
- void hideNativeTasks(TaskContainer tasks, BinaryContainer binaries) {
- // Gradle do not support a way to remove created tasks. The best workaround is to clear the
- // group of the task and have another task depends on it. Therefore, we have to create
- // a dummy task to depend on all the tasks that we do not want to show up on the task
- // list. The dummy task dependsOn itself, effectively making it non-executable and
- // invisible unless the --all option is use.
- Task nonExecutableTask = tasks.create("nonExecutableTask")
- nonExecutableTask.dependsOn nonExecutableTask
- nonExecutableTask.description =
- "Dummy task to hide other unwanted tasks in the task list."
-
- binaries.withType(NativeLibraryBinarySpec) { binary ->
- Task buildTask = binary.getBuildTask()
- nonExecutableTask.dependsOn buildTask
- buildTask.group = null
- }
- }
- }
-
- private static Collection<SharedLibraryBinarySpec> getNativeBinaries(
- NativeLibrarySpec library,
- com.android.builder.model.BuildType buildType,
- List<? extends com.android.build.gradle.api.GroupableProductFlavor> productFlavors) {
- ProductFlavorCombo flavorGroup = new ProductFlavorCombo(productFlavors);
- library.binaries.withType(SharedLibraryBinarySpec).matching { binary ->
- (binary.buildType.name.equals(buildType.name)
- && ((productFlavors.isEmpty() && binary.flavor.name.equals("default"))
- || binary.flavor.name.equals(flavorGroup.name)))
- }
- }
-
- /**
- * Return library binaries for a VariantConfiguration.
- */
- public Collection<BinarySpec> getBinaries(VariantConfiguration variantConfig) {
- if (variantConfig.getType().isForTesting()) {
- // Do not return binaries for test variants as test source set is not supported at the
- // moment.
- return []
- }
-
- project.binaries.withType(SharedLibraryBinarySpec).matching { binary ->
- (binary.buildType.name.equals(variantConfig.getBuildType().getName())
- && (binary.flavor.name.equals(variantConfig.getFlavorName())
- || (binary.flavor.name.equals("default")
- && variantConfig.getFlavorName().isEmpty()))
- && (variantConfig.getNdkConfig().getAbiFilters() == null
- || variantConfig.getNdkConfig().getAbiFilters().contains(
- binary.targetPlatform.name)))
- }
- }
-
- /**
- * Return the output directory of the native binary tasks for a VariantConfiguration.
- */
- public Collection<File> getOutputDirectories(VariantConfiguration variantConfig) {
- // Return the parent's parent directory of the binaries' output.
- // A binary's output file is set to something in the form of
- // "/path/to/lib/platformName/libmodulename.so". We want to return "/path/to/lib".
- (getBinaries(variantConfig)
- *.getPrimaryOutput()
- *.getParentFile()
- *.getParentFile()
- .unique())
- }
-}
-
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkComponentModelPlugin.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkComponentModelPlugin.java
new file mode 100644
index 0000000..a727529
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkComponentModelPlugin.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.model;
+
+import static com.android.build.gradle.model.AndroidComponentModelPlugin.COMPONENT_NAME;
+
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.NdkOptionsHelper;
+import com.android.build.gradle.internal.ProductFlavorCombo;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.managed.BuildType;
+import com.android.build.gradle.managed.NdkConfig;
+import com.android.build.gradle.managed.ProductFlavor;
+import com.android.build.gradle.ndk.internal.NdkConfiguration;
+import com.android.build.gradle.ndk.internal.NdkExtensionConvention;
+import com.android.build.gradle.ndk.internal.NdkNamingScheme;
+import com.android.build.gradle.ndk.internal.ToolchainConfiguration;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.core.VariantConfiguration;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
+import org.gradle.api.Action;
+import org.gradle.api.BuildableModelElement;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.internal.project.ProjectIdentifier;
+import org.gradle.api.specs.Spec;
+import org.gradle.api.tasks.TaskContainer;
+import org.gradle.language.c.plugins.CPlugin;
+import org.gradle.language.cpp.plugins.CppPlugin;
+import org.gradle.model.Defaults;
+import org.gradle.model.Finalize;
+import org.gradle.model.Model;
+import org.gradle.model.ModelMap;
+import org.gradle.model.Mutate;
+import org.gradle.model.Path;
+import org.gradle.model.RuleSource;
+import org.gradle.model.Validate;
+import org.gradle.nativeplatform.BuildTypeContainer;
+import org.gradle.nativeplatform.FlavorContainer;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.nativeplatform.NativeLibraryBinarySpec;
+import org.gradle.nativeplatform.NativeLibrarySpec;
+import org.gradle.nativeplatform.SharedLibraryBinarySpec;
+import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry;
+import org.gradle.platform.base.BinaryContainer;
+import org.gradle.platform.base.BinarySpec;
+import org.gradle.platform.base.ComponentSpecContainer;
+import org.gradle.platform.base.PlatformContainer;
+import org.gradle.platform.base.binary.BaseBinarySpec;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Plugin for Android NDK applications.
+ */
+public class NdkComponentModelPlugin implements Plugin<Project> {
+ private Project project;
+
+ @Override
+ public void apply(Project project) {
+ this.project = project;
+
+ project.getPluginManager().apply(AndroidComponentModelPlugin.class);
+ project.getPluginManager().apply(CPlugin.class);
+ project.getPluginManager().apply(CppPlugin.class);
+ }
+
+ @SuppressWarnings({"MethodMayBeStatic", "unused"})
+ public static class Rules extends RuleSource {
+
+ @Mutate
+ public void initializeNdkConfig(@Path("android.ndk") NdkConfig ndk) {
+ NdkOptionsHelper.init(ndk);
+ ndk.setModuleName("");
+ ndk.setToolchain("");
+ ndk.setToolchainVersion("");
+ ndk.setStl("");
+ ndk.setRenderscriptNdkMode(false);
+ }
+
+ @Finalize
+ public void setDefaultNdkExtensionValue(@Path("android.ndk") NdkConfig ndkConfig) {
+ NdkExtensionConvention.setExtensionDefault(ndkConfig);
+ }
+
+ @Validate
+ public void checkNdkDir(NdkHandler ndkHandler, @Path("android.ndk") NdkConfig ndkConfig) {
+ if (!ndkConfig.getModuleName().isEmpty() && !ndkHandler.isNdkDirConfigured()) {
+ throw new InvalidUserDataException(
+ "NDK location not found. Define location with ndk.dir in the "
+ + "local.properties file or with an ANDROID_NDK_HOME environment "
+ + "variable.");
+ }
+ if (ndkHandler.isNdkDirConfigured()) {
+ if (!ndkHandler.getNdkDirectory().exists()) {
+ throw new InvalidUserDataException(
+ "Specified NDK location does not exists. Please ensure ndk.dir in "
+ + "local.properties file or ANDROID_NDK_HOME is configured "
+ + "correctly.");
+
+ }
+ }
+ }
+
+ @Mutate
+ public void addDefaultNativeSourceSet(
+ @Path("android.sources") AndroidComponentModelSourceSet sources) {
+ sources.addDefaultSourceSet("jni", AndroidLanguageSourceSet.class);
+ }
+
+ @Model(ModelConstants.NDK_HANDLER)
+ public NdkHandler ndkHandler(
+ ProjectIdentifier projectId,
+ @Path("android.compileSdkVersion") String compileSdkVersion,
+ @Path("android.ndk") NdkConfig ndkConfig) {
+ while (projectId.getParentIdentifier() != null) {
+ projectId = projectId.getParentIdentifier();
+ }
+
+ return new NdkHandler(projectId.getProjectDir(), compileSdkVersion,
+ ndkConfig.getToolchain(), ndkConfig.getToolchainVersion());
+ }
+
+ @Defaults
+ public void initBuildTypeNdk(@Path("android.buildTypes") ModelMap<BuildType> buildTypes) {
+ buildTypes.beforeEach(new Action<BuildType>() {
+ @Override
+ public void execute(BuildType buildType) {
+ NdkOptionsHelper.init(buildType.getNdk());
+ }
+ });
+
+ buildTypes.named(
+ BuilderConstants.DEBUG,
+ new Action<BuildType>() {
+ @Override
+ public void execute(BuildType buildType) {
+ if (buildType.getNdk().getDebuggable() == null) {
+ buildType.getNdk().setDebuggable(true);
+ }
+ }
+ });
+ }
+
+ @Defaults
+ public void initProductFlavorNdk(
+ @Path("android.productFlavors") ModelMap<ProductFlavor> productFlavors) {
+ productFlavors.beforeEach(new Action<ProductFlavor>() {
+ @Override
+ public void execute(ProductFlavor productFlavor) {
+ NdkOptionsHelper.init(productFlavor.getNdk());
+ }
+ });
+ }
+
+ @Mutate
+ public void createAndroidPlatforms(PlatformContainer platforms, NdkHandler ndkHandler) {
+ if (!ndkHandler.isNdkDirConfigured()) {
+ return;
+ }
+ // Create android platforms.
+ ToolchainConfiguration.configurePlatforms(platforms, ndkHandler);
+ }
+
+ @Mutate
+ public void createToolchains(
+ NativeToolChainRegistry toolchainRegistry,
+ @Path("android.ndk") NdkConfig ndkConfig,
+ NdkHandler ndkHandler) {
+ if (!ndkHandler.isNdkDirConfigured()) {
+ return;
+ }
+ // Create toolchain for each ABI.
+ ToolchainConfiguration.configureToolchain(
+ toolchainRegistry,
+ ndkConfig.getToolchain(),
+ ndkHandler);
+ }
+
+ @Mutate
+ public void createNativeBuildTypes(BuildTypeContainer nativeBuildTypes,
+ @Path("android.buildTypes") ModelMap<BuildType> androidBuildTypes) {
+ for (BuildType buildType : androidBuildTypes.values()) {
+ nativeBuildTypes.maybeCreate(buildType.getName());
+ }
+ }
+
+ @Mutate
+ public void createNativeFlavors(FlavorContainer nativeFlavors,
+ List<ProductFlavorCombo<ProductFlavor>> androidFlavorGroups) {
+ if (androidFlavorGroups.isEmpty()) {
+ // Create empty native flavor to override Gradle's default name.
+ nativeFlavors.maybeCreate("");
+ } else {
+ for (ProductFlavorCombo group : androidFlavorGroups) {
+ nativeFlavors.maybeCreate(group.getName());
+ }
+ }
+ }
+
+ @Mutate
+ public void createNativeLibrary(
+ final ComponentSpecContainer specs,
+ @Path("android.ndk") final NdkConfig ndkConfig,
+ final NdkHandler ndkHandler,
+ @Path("android.sources") final AndroidComponentModelSourceSet sources,
+ @Path("buildDir") final File buildDir) {
+ if (!ndkHandler.isNdkDirConfigured()) {
+ return;
+ }
+ if (!ndkConfig.getModuleName().isEmpty()) {
+ specs.create(
+ ndkConfig.getModuleName(),
+ NativeLibrarySpec.class,
+ new Action<NativeLibrarySpec>() {
+ @Override
+ public void execute(final NativeLibrarySpec nativeLib) {
+ ((DefaultAndroidComponentSpec) specs.get(COMPONENT_NAME))
+ .setNativeLibrary(nativeLib);
+ NdkConfiguration.configureProperties(
+ nativeLib,
+ sources,
+ buildDir,
+ ndkHandler);
+ }
+ });
+ DefaultAndroidComponentSpec androidSpecs =
+ (DefaultAndroidComponentSpec) specs.get(COMPONENT_NAME);
+ androidSpecs.setNativeLibrary(
+ (NativeLibrarySpec) specs.get(ndkConfig.getModuleName()));
+ }
+ }
+
+ @Mutate
+ public void createAdditionalTasksForNatives(
+ final ModelMap<Task> tasks,
+ ModelMap<AndroidComponentSpec> specs,
+ @Path("android.ndk") final NdkConfig ndkConfig,
+ final NdkHandler ndkHandler,
+ BinaryContainer binaries,
+ @Path("buildDir") final File buildDir) {
+ if (!ndkHandler.isNdkDirConfigured()) {
+ return;
+ }
+ final DefaultAndroidComponentSpec androidSpec =
+ (DefaultAndroidComponentSpec) specs.get(COMPONENT_NAME);
+ if (androidSpec.getNativeLibrary() != null) {
+ binaries.withType(DefaultAndroidBinary.class, new Action<DefaultAndroidBinary>() {
+ @Override
+ public void execute(DefaultAndroidBinary binary) {
+ for (NativeBinarySpec nativeBinary : binary.getNativeBinaries()) {
+ NdkConfiguration.createTasks(
+ tasks,
+ (SharedLibraryBinarySpec) nativeBinary,
+ buildDir,
+ binary.getMergedNdkConfig(),
+ ndkHandler);
+ }
+ }
+ });
+ }
+ }
+
+ @Mutate
+ public void configureNativeBinary(
+ BinaryContainer binaries,
+ ComponentSpecContainer specs,
+ @Path("android.ndk") final NdkConfig ndkConfig,
+ @Path("buildDir") final File buildDir,
+ final NdkHandler ndkHandler) {
+ if (!ndkConfig.getModuleName().isEmpty()) {
+ final NativeLibrarySpec library = specs.withType(NativeLibrarySpec.class)
+ .get(ndkConfig.getModuleName());
+ binaries.withType(
+ DefaultAndroidBinary.class,
+ new Action<DefaultAndroidBinary>() {
+ @Override
+ public void execute(DefaultAndroidBinary binary) {
+ binary.computeMergedNdk(
+ ndkConfig,
+ binary.getProductFlavors(),
+ binary.getBuildType());
+
+ Collection<SharedLibraryBinarySpec> nativeBinaries =
+ getNativeBinaries(
+ library,
+ binary.getBuildType(),
+ binary.getProductFlavors());
+ for (SharedLibraryBinarySpec nativeBin : nativeBinaries) {
+ if (binary.getMergedNdkConfig().getAbiFilters().isEmpty() ||
+ binary.getMergedNdkConfig().getAbiFilters().contains(
+ nativeBin.getTargetPlatform().getName())) {
+ NdkConfiguration.configureBinary(
+ nativeBin,
+ buildDir,
+ binary.getMergedNdkConfig(),
+ ndkHandler);
+ binary.getNativeBinaries().add(nativeBin);
+ }
+ }
+ }
+ });
+ }
+ }
+
+ @Finalize
+ public void attachNativeTasksToAndroidBinary(ModelMap<AndroidBinary> binaries) {
+ binaries.afterEach(new Action<AndroidBinary>() {
+ @Override
+ public void execute(AndroidBinary androidBinary) {
+ DefaultAndroidBinary binary = (DefaultAndroidBinary) androidBinary;
+ for (NativeLibraryBinarySpec nativeBinary : binary.getNativeBinaries()) {
+ if (binary.getTargetAbi().isEmpty() || binary.getTargetAbi().contains(
+ nativeBinary.getTargetPlatform().getName())) {
+ binary.getBuildTask().dependsOn(NdkNamingScheme.getNdkBuildTaskName(nativeBinary));
+ }
+ }
+ }
+ });
+ }
+
+ @Mutate
+ public void removeNativeBinaryFromAssembleTask(ModelMap<AndroidComponentSpec> components) {
+ // Setting each native binary to not buildable to prevent the native tasks to be
+ // automatically added to the "assemble" task.
+ components.afterEach(new Action<AndroidComponentSpec>() {
+ @Override
+ public void execute(AndroidComponentSpec spec) {
+ NativeLibrarySpec nativeLibrary =
+ ((DefaultAndroidComponentSpec)spec).getNativeLibrary();
+ if (nativeLibrary != null) {
+ nativeLibrary.getBinaries().afterEach(
+ new Action<BinarySpec>() {
+ @Override
+ public void execute(BinarySpec binary) {
+ ((BaseBinarySpec) binary).setBuildable(false);
+ }
+ });
+ }
+ }
+ });
+ }
+
+ /**
+ * Remove unintended tasks created by Gradle native plugin from task list.
+ *
+ * Gradle native plugins creates static library tasks automatically. This method removes
+ * them to avoid cluttering the task list.
+ */
+ @Mutate
+ public void hideNativeTasks(TaskContainer tasks, BinaryContainer binaries) {
+ // Gradle do not support a way to remove created tasks. The best workaround is to clear the
+ // group of the task and have another task depends on it. Therefore, we have to create
+ // a dummy task to depend on all the tasks that we do not want to show up on the task
+ // list. The dummy task dependsOn itself, effectively making it non-executable and
+ // invisible unless the --all option is use.
+ final Task nonExecutableTask = tasks.create("nonExecutableTask");
+ nonExecutableTask.dependsOn(nonExecutableTask);
+ nonExecutableTask
+ .setDescription("Dummy task to hide other unwanted tasks in the task list.");
+
+ binaries.withType(NativeLibraryBinarySpec.class, new Action<NativeLibraryBinarySpec>() {
+ @Override
+ public void execute(NativeLibraryBinarySpec binary) {
+ Task buildTask = binary.getBuildTask();
+ nonExecutableTask.dependsOn(buildTask);
+ buildTask.setGroup(null);
+ }
+ });
+ }
+ }
+
+
+ public static void configureScopeForNdk(VariantScope scope) {
+ VariantConfiguration config = scope.getVariantConfiguration();
+ ImmutableSet.Builder<File> builder = ImmutableSet.builder();
+ for (Abi abi : NdkHandler.getAbiList()) {
+ scope.addNdkDebuggableLibraryFolders(
+ abi,
+ new File(
+ scope.getGlobalScope().getBuildDir(),
+ NdkNamingScheme.getDebugLibraryDirectoryName(
+ config.getBuildType().getName(),
+ config.getFlavorName(),
+ abi.getName())));
+
+ // Return the parent directory of the binaries' output.
+ // If output directory is "/path/to/lib/platformName". We want to return
+ // "/path/to/lib".
+ builder.add(new File(
+ scope.getGlobalScope().getBuildDir(),
+ NdkNamingScheme.getOutputDirectoryName(
+ config.getBuildType().getName(),
+ config.getFlavorName(),
+ abi.getName())).getParentFile());
+ }
+ scope.setNdkSoFolder(builder.build());
+ }
+
+
+ private static Collection<SharedLibraryBinarySpec> getNativeBinaries(
+ NativeLibrarySpec library,
+ final BuildType buildType,
+ final List<ProductFlavor> productFlavors) {
+ final ProductFlavorCombo<ProductFlavor> flavorGroup =
+ new ProductFlavorCombo<ProductFlavor>(productFlavors);
+ return ImmutableList.copyOf(Iterables.filter(
+ library.getBinaries().withType(SharedLibraryBinarySpec.class).values(),
+ new Predicate<SharedLibraryBinarySpec>() {
+ @Override
+ public boolean apply(SharedLibraryBinarySpec binary) {
+ return binary.getBuildType().getName().equals(buildType.getName())
+ && (binary.getFlavor().getName().equals(flavorGroup.getName())
+ || (productFlavors.isEmpty()
+ && binary.getFlavor().getName().equals("default")));
+ }
+ }));
+ }
+
+ /**
+ * Return library binaries for a VariantConfiguration.
+ */
+ public Collection<? extends BuildableModelElement> getBinaries(final VariantConfiguration variantConfig) {
+ if (variantConfig.getType().isForTesting()) {
+ // Do not return binaries for test variants as test source set is not supported at the
+ // moment.
+ return Collections.emptyList();
+ }
+ BinaryContainer binaries = (BinaryContainer) project.getExtensions().getByName("binaries");
+ return binaries.withType(AndroidBinary.class).matching(
+ new Spec<AndroidBinary>() {
+ @Override
+ public boolean isSatisfiedBy(AndroidBinary binary) {
+ return (binary.getName().equals(variantConfig.getFullName()));
+ }
+ }
+ );
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkConfigImpl.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkConfigImpl.java
new file mode 100644
index 0000000..1d37a60
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/NdkConfigImpl.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.model;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.managed.NdkConfig;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Implementation of NdkConfig.
+ * Used in AndroidBinary, which is currently not a Managed type.
+ */
+public class NdkConfigImpl implements NdkConfig {
+
+ String moduleName;
+
+ String toolchain;
+
+ String toolchainVersion;
+
+ Set<String> abiFilters = Sets.newHashSet();
+
+ List<String> cFlags = Lists.newArrayList();
+
+ List<String> cppFlags = Lists.newArrayList();
+
+ List<String> ldFlags = Lists.newArrayList();
+
+ List<String> ldLibs = Lists.newArrayList();
+
+ String stl;
+
+ Boolean isDebuggable;
+
+ Boolean renderscriptNdkMode;
+
+ @Override
+ public String getModuleName() {
+ return moduleName;
+ }
+
+ @Override
+ public void setModuleName(@NonNull String moduleName) {
+ this.moduleName = moduleName;
+ }
+
+ @Override
+ public String getToolchain() {
+ return toolchain;
+ }
+
+ @Override
+ public void setToolchain(@NonNull String toolchain) {
+ this.toolchain = toolchain;
+ }
+
+ @Override
+ public String getToolchainVersion() {
+ return toolchainVersion;
+ }
+
+ @Override
+ public void setToolchainVersion(@NonNull String toolchainVersion) {
+ this.toolchainVersion = toolchainVersion;
+ }
+
+ @Override
+ public Set<String> getAbiFilters() {
+ return abiFilters;
+ }
+
+ @Override
+ public void setAbiFilters(@NonNull Set<String> abiFilters) {
+ throw new UnsupportedOperationException("Field should not be set.");
+ }
+
+ @Override
+ public List<String> getCFlags() {
+ return cFlags;
+ }
+
+ @Override
+ public void setCFlags(@NonNull List<String> cFlags) {
+ throw new UnsupportedOperationException("Field should not be set.");
+ }
+
+ @Override
+ public List<String> getCppFlags() {
+ return cppFlags;
+ }
+
+ @Override
+ public void setCppFlags(@NonNull List<String> cppFlags) {
+ throw new UnsupportedOperationException("Field should not be set.");
+ }
+
+ @Override
+ public List<String> getLdFlags() {
+ return ldFlags;
+ }
+
+ @Override
+ public void setLdFlags(@NonNull List<String> ldFlags) {
+ throw new UnsupportedOperationException("Field should not be set.");
+ }
+
+ @Override
+ public List<String> getLdLibs() {
+ return ldLibs;
+ }
+
+ @Override
+ public void setLdLibs(@NonNull List<String> ldLibs) {
+ throw new UnsupportedOperationException("Field should not be set.");
+ }
+
+ @Override
+ public String getStl() {
+ return stl;
+ }
+
+ @Override
+ public void setStl(@NonNull String stl) {
+ this.stl = stl;
+ }
+
+ @Override
+ public Boolean getDebuggable() {
+ return isDebuggable;
+ }
+
+ @Override
+ public void setDebuggable(Boolean isDebuggable) {
+ this.isDebuggable = isDebuggable;
+ }
+
+ @Override
+ public Boolean getRenderscriptNdkMode() {
+ return renderscriptNdkMode;
+ }
+
+ @Override
+ public void setRenderscriptNdkMode(Boolean renderscriptNdkMode) {
+ this.renderscriptNdkMode = renderscriptNdkMode;
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/TaskCollectionBuilderAdaptor.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/TaskCollectionBuilderAdaptor.groovy
deleted file mode 100644
index 7650947..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/TaskCollectionBuilderAdaptor.groovy
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.model
-
-import com.android.annotations.Nullable
-import com.android.build.gradle.internal.TaskFactory
-import org.gradle.api.Action
-import org.gradle.api.Task
-import org.gradle.model.collection.CollectionBuilder
-
-/**
- * Adaptor to transform CollectionBuilder<Task> into TaskFactory.
- */
-class TaskCollectionBuilderAdaptor implements TaskFactory {
-
- private final CollectionBuilder<Task> tasks
-
- TaskCollectionBuilderAdaptor(CollectionBuilder<Task> tasks) {
- this.tasks = tasks
- }
-
- @Override
- boolean containsKey(String name) {
- return tasks.containsKey(name)
- }
-
- @Override
- void create(String name) {
- tasks.create(name)
- }
-
- @Override
- void create(String name, Action<? super Task> configAction) {
- tasks.create(name, configAction)
- }
-
- @Override
- def <S extends Task> void create(String name, Class<S> type) {
- tasks.create(name, type)
- }
-
- @Override
- def <S extends Task> void create(String name, Class<S> type, Action<? super S> configAction) {
- tasks.create(name, type, configAction)
- }
-
- @Override
- void named(String name, Action<? super Task> configAction) {
- tasks.named(name, configAction)
- }
-
- @Nullable
- @Override
- Task named(String name) {
- return tasks.get(name);
- }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/TaskModelMapAdaptor.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/TaskModelMapAdaptor.java
new file mode 100644
index 0000000..3a62c05
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/model/TaskModelMapAdaptor.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.model;
+
+import com.android.annotations.Nullable;
+import com.android.build.gradle.internal.TaskFactory;
+
+import org.gradle.api.Action;
+import org.gradle.api.Task;
+import org.gradle.model.ModelMap;
+
+/**
+ * Adaptor to transform ModelMap<Task> into TaskFactory.
+ */
+public class TaskModelMapAdaptor implements TaskFactory {
+
+ private final ModelMap<Task> tasks;
+
+ public TaskModelMapAdaptor(ModelMap<Task> tasks) {
+ this.tasks = tasks;
+ }
+
+ @Override
+ public boolean containsKey(String name) {
+ return tasks.containsKey(name);
+ }
+
+ @Override
+ public void create(String name) {
+ tasks.create(name);
+ }
+
+ @Override
+ public void create(String name, Action<? super Task> configAction) {
+ tasks.create(name, configAction);
+ }
+
+ @Override
+ public <S extends Task> void create(String name, Class<S> type) {
+ tasks.create(name, type);
+ }
+
+ @Override
+ public <S extends Task> void create(String name, Class<S> type,
+ Action<? super S> configAction) {
+ tasks.create(name, type, configAction);
+ }
+
+ @Override
+ public void named(String name, Action<? super Task> configAction) {
+ tasks.named(name, configAction);
+ }
+
+ @Nullable
+ @Override
+ public Task named(String name) {
+ return tasks.get(name);
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/NdkExtension.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/NdkExtension.java
deleted file mode 100644
index dc7a5c2..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/NdkExtension.java
+++ /dev/null
@@ -1,193 +0,0 @@
-package com.android.build.gradle.ndk;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.google.common.collect.Sets;
-
-import org.gradle.api.Action;
-import org.gradle.api.tasks.util.PatternFilterable;
-import org.gradle.api.tasks.util.PatternSet;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * Extension for android-ndk plugin.
- */
-public class NdkExtension {
-
- @NonNull
- private String moduleName = "";
-
- @NonNull
- private String target = "";
-
- @NonNull
- private String cFlags = "";
-
- @NonNull
- private String cppFlags = "";
-
- @NonNull
- private Set<String> ldLibs;
-
- @NonNull
- private String toolchain = "";
-
- @NonNull
- private String toolchainVersion = "";
-
- @NonNull
- private String stl = "";
-
- private boolean renderscriptNdkMode = false;
-
- @NonNull
- private PatternSet cFilePattern;
-
- @NonNull
- private PatternSet cppFilePattern;
-
- public NdkExtension() {
- cFilePattern = new PatternSet();
- cppFilePattern = new PatternSet();
- ldLibs = Sets.newHashSet();
- }
-
- @NonNull
- public String getModuleName() {
- return moduleName;
- }
-
- public void setModuleName(@NonNull String moduleName) {
- this.moduleName = moduleName;
- }
-
- @NonNull
- public String getCompileSdkVersion() {
- return target;
- }
-
- public void compileSdkVersion(@NonNull String target) {
- this.target = target;
- }
-
- public void compileSdkVersion(int apiLevel) {
- compileSdkVersion("android-" + apiLevel);
- }
-
- public void setCompileSdkVersion(int apiLevel) {
- compileSdkVersion(apiLevel);
- }
-
- public void setCompileSdkVersion(@NonNull String target) {
- compileSdkVersion(target);
- }
-
- @NonNull
- public String getToolchain() {
- return toolchain;
- }
-
- public void setToolchain(@NonNull String toolchain) {
- this.toolchain = toolchain;
- }
-
- /**
- * The toolchain version.
- */
- @NonNull
- public String getToolchainVersion() {
- return toolchainVersion;
- }
-
- public void setToolchainVersion(@NonNull String toolchainVersion) {
- this.toolchainVersion = toolchainVersion;
- }
-
- @NonNull
- public String getcFlags() {
- return cFlags;
- }
-
- public void setcFlags(@NonNull String cFlags) {
- this.cFlags = cFlags;
- }
-
- @NonNull
- public String getCppFlags() {
- return cppFlags;
- }
-
- public void setCppFlags(@NonNull String cppFlags) {
- this.cppFlags = cppFlags;
- }
-
- @NonNull
- public Set<String> getLdLibs() {
- return ldLibs;
- }
-
- @NonNull
- public NdkExtension ldLibs(String lib) {
- ldLibs.add(lib);
- return this;
- }
-
- @NonNull
- public NdkExtension ldLibs(String... libs) {
- Collections.addAll(ldLibs, libs);
- return this;
- }
-
- @NonNull
- public NdkExtension setLdLibs(@NonNull Collection<String> libs) {
- ldLibs.clear();
- ldLibs.addAll(libs);
- return this;
- }
-
- @NonNull
- public String getStl() {
- return stl;
- }
-
- public void setStl(@NonNull String stl) {
- this.stl = stl;
- }
-
- public boolean getRenderscriptNdkMode() {
- return renderscriptNdkMode;
- }
-
- public void setRenderscriptNdkMode(boolean renderscriptNdkMode) {
- this.renderscriptNdkMode = renderscriptNdkMode;
- }
-
- public void cFilePattern(Action<PatternFilterable> action) {
- action.execute(cFilePattern);
- }
-
- @NonNull
- public PatternFilterable getCFilePattern() {
- return cFilePattern;
- }
-
- public void setCFilePattern(@NonNull PatternFilterable pattern) {
- cFilePattern.copyFrom(pattern);
- }
-
- public void cppFilePattern(Action<PatternFilterable> action) {
- action.execute(cppFilePattern);
- }
-
- @NonNull
- public PatternFilterable getCppFilePattern() {
- return cppFilePattern;
- }
-
- public void setCppFilePattern(@NonNull PatternFilterable pattern) {
- cppFilePattern.copyFrom(pattern);
- }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/AbstractNativeToolSpecification.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/AbstractNativeToolSpecification.groovy
deleted file mode 100644
index 64ca127..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/AbstractNativeToolSpecification.groovy
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.ndk.internal;
-
-import org.gradle.nativeplatform.NativeBinarySpec;
-
-/**
- * An abstract class for NativeToolSpecification.
- */
-public abstract class AbstractNativeToolSpecification implements NativeToolSpecification {
- /**
- * Configure a native binary with this specification.
- *
- * @param binary The binary to be configured. It is assumed the 'c' and 'cpp' plugin is applied
- * such that the binary contains the cCompiler and cppCompiler extensions.
- */
- void apply(NativeBinarySpec binary) {
- for (String arg : getCFlags()) {
- binary.cCompiler.args(arg);
- }
- for (String arg : getCppFlags()) {
- binary.cppCompiler.args(arg);
- }
- for (String arg : getLdFlags()) {
- binary.linker.args(arg);
- }
- }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/AbstractNativeToolSpecification.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/AbstractNativeToolSpecification.java
new file mode 100644
index 0000000..47a037b
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/AbstractNativeToolSpecification.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.ndk.internal;
+
+import static com.android.build.gradle.ndk.internal.BinaryToolHelper.getCCompiler;
+import static com.android.build.gradle.ndk.internal.BinaryToolHelper.getCppCompiler;
+
+import org.gradle.nativeplatform.NativeBinarySpec;
+
+/**
+ * An abstract class for NativeToolSpecification.
+ */
+public abstract class AbstractNativeToolSpecification implements NativeToolSpecification {
+ /**
+ * Configure a native binary with this specification.
+ *
+ * @param binary The binary to be configured. It is assumed the 'c' and 'cpp' plugin is applied
+ * such that the binary contains the cCompiler and cppCompiler extensions.
+ */
+ @Override
+ public void apply(NativeBinarySpec binary) {
+ for (String arg : getCFlags()) {
+ getCCompiler(binary).args(arg);
+ }
+
+ for (String arg : getCppFlags()) {
+ getCppCompiler(binary).args(arg);
+ }
+
+ for (String arg : getLdFlags()) {
+ binary.getLinker().args(arg);
+ }
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/BinaryToolHelper.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/BinaryToolHelper.java
new file mode 100644
index 0000000..82aa470
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/BinaryToolHelper.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.ndk.internal;
+
+import org.gradle.api.plugins.ExtensionAware;
+import org.gradle.language.nativeplatform.internal.DefaultPreprocessingTool;
+import org.gradle.platform.base.BinarySpec;
+
+/**
+ * Gradle's LanguageRegistration dynamically add the cCompiler and cppCompiler tool to the library.
+ *
+ * In Java, we can't call those functions dynamically, but can cast the binaries to ExternsionAware
+ * and access those tools through the extension container.
+ *
+ * This helper class hide the details for accessing the tools.
+ */
+public class BinaryToolHelper {
+ public static DefaultPreprocessingTool getCCompiler(BinarySpec binary) {
+ return (DefaultPreprocessingTool) ((ExtensionAware) binary).getExtensions().getByName("cCompiler");
+ }
+
+ public static DefaultPreprocessingTool getCppCompiler(BinarySpec binary) {
+ return (DefaultPreprocessingTool) ((ExtensionAware) binary).getExtensions().getByName("cppCompiler");
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ClangNativeToolSpecification.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ClangNativeToolSpecification.groovy
deleted file mode 100644
index fd12d54..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ClangNativeToolSpecification.groovy
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.ndk.internal
-
-import com.android.SdkConstants
-import com.android.builder.core.BuilderConstants
-import org.gradle.nativeplatform.BuildType
-import org.gradle.nativeplatform.platform.NativePlatform
-
-/**
- * Flag configuration for Clang toolchain.
- */
-class ClangNativeToolSpecification extends AbstractNativeToolSpecification {
-
- private NdkHandler ndkHandler
-
- private NativePlatform platform
-
- private boolean isDebugBuild
-
- private static final def TARGET_TRIPLE = [
- (SdkConstants.ABI_INTEL_ATOM) : "i686-none-linux-android",
- (SdkConstants.ABI_INTEL_ATOM64) : "x86_64-none-linux-android",
- (SdkConstants.ABI_ARMEABI) : "armv5-none-linux-android",
- (SdkConstants.ABI_ARMEABI_V7A) : "armv7-none-linux-android",
- (SdkConstants.ABI_ARM64_V8A) : "aarch64-none-linux-android",
- (SdkConstants.ABI_MIPS) : "mipsel-none-linux-android",
- (SdkConstants.ABI_MIPS64) : "mips64el-none-linux-android",
- ]
-
- private static final def RELEASE_CFLAGS = [
- (SdkConstants.ABI_ARMEABI) : [
- "-fpic",
- "-ffunction-sections",
- "-funwind-tables",
- "-fstack-protector",
- "-no-canonical-prefixes",
- "-march=armv5te",
- "-mtune=xscale",
- "-msoft-float",
- "-mthumb",
- "-Os",
- "-DNDEBUG",
- "-fomit-frame-pointer",
- "-fstrict-aliasing",
- ],
- (SdkConstants.ABI_ARMEABI_V7A) : [
- "-fpic",
- "-ffunction-sections",
- "-funwind-tables",
- "-fstack-protector",
- "-no-canonical-prefixes",
- "-march=armv7-a",
- "-mfloat-abi=softfp",
- "-mfpu=vfpv3-d16",
- "-mthumb",
- "-Os",
- "-DNDEBUG",
- "-fomit-frame-pointer",
- "-fstrict-aliasing",
- ],
- (SdkConstants.ABI_ARM64_V8A) : [
- "-fpic",
- "-ffunction-sections",
- "-funwind-tables",
- "-fstack-protector",
- "-no-canonical-prefixes",
- "-O2",
- "-DNDEBUG",
- "-fomit-frame-pointer",
- "-fstrict-aliasing",
- ],
- (SdkConstants.ABI_INTEL_ATOM) : [
- "-ffunction-sections",
- "-funwind-tables",
- "-fstack-protector",
- "-fPIC",
- "-no-canonical-prefixes",
- "-O2",
- "-DNDEBUG",
- "-fomit-frame-pointer",
- "-fstrict-aliasing",
- ],
- (SdkConstants.ABI_INTEL_ATOM64) : [
- "-ffunction-sections",
- "-funwind-tables",
- "-fstack-protector",
- "-fPIC",
- "-no-canonical-prefixes",
- "-O2",
- "-DNDEBUG",
- "-fomit-frame-pointer",
- "-fstrict-aliasing",
- ],
- (SdkConstants.ABI_MIPS) : [
- "-fpic",
- "-fno-strict-aliasing",
- "-finline-functions",
- "-ffunction-sections",
- "-funwind-tables",
- "-fmessage-length=0",
- "-no-canonical-prefixes",
- "-O2",
- "-g",
- "-DNDEBUG",
- "-fomit-frame-pointer",
- ],
- (SdkConstants.ABI_MIPS64) : [
- "-fpic",
- "-fno-strict-aliasing",
- "-finline-functions",
- "-ffunction-sections",
- "-funwind-tables",
- "-fmessage-length=0",
- "-no-canonical-prefixes",
- "-O2",
- "-g",
- "-DNDEBUG",
- "-fomit-frame-pointer",
- ]
- ]
-
- private static final def DEBUG_CFLAGS = [
- (SdkConstants.ABI_ARMEABI) : [
- "-O0",
- "-UNDEBUG",
- "-marm",
- "-fno-strict-aliasing",
- ],
- (SdkConstants.ABI_ARMEABI_V7A) : [
- "-O0",
- "-UNDEBUG",
- "-marm",
- "-fno-strict-aliasing",
- ],
- (SdkConstants.ABI_ARM64_V8A) : [
- "-O0",
- "-UNDEBUG",
- "-fno-omit-frame-pointer",
- "-fno-strict-aliasing",
- ],
- (SdkConstants.ABI_INTEL_ATOM) : [
- "-O0",
- "-UNDEBUG",
- "-fno-omit-frame-pointer",
- "-fno-strict-aliasing",
- ],
- (SdkConstants.ABI_INTEL_ATOM64) : [
- "-O0",
- "-UNDEBUG",
- "-fno-omit-frame-pointer",
- "-fno-strict-aliasing",
- ],
- (SdkConstants.ABI_MIPS) : [
- "-O0",
- "-UNDEBUG",
- "-fno-omit-frame-pointer",
- ],
- (SdkConstants.ABI_MIPS64) : [
- "-O0",
- "-UNDEBUG",
- "-fno-omit-frame-pointer",
- ]
- ]
-
- public ClangNativeToolSpecification(
- NdkHandler ndkHandler,
- BuildType buildType,
- NativePlatform platform) {
- this.ndkHandler = ndkHandler
- this.isDebugBuild = (buildType.name.equals(BuilderConstants.DEBUG))
- this.platform = platform
- }
-
- @Override
- public Iterable<String> getCFlags() {
- getTargetFlags() + RELEASE_CFLAGS[platform.name] + DEBUG_CFLAGS[platform.name]
- }
-
- @Override
- public Iterable<String> getCppFlags() {
- getCFlags()
- }
-
- @Override
- public Iterable<String> getLdFlags() {
- getTargetFlags() +
- (platform.name.equals(SdkConstants.ABI_ARMEABI_V7A) ? ["-Wl,--fix-cortex-a8"] : [])
- }
-
- private Iterable<String> getTargetFlags() {
- [
- "-gcc-toolchain",
- ndkHandler.getToolchainPath(
- "gcc",
- ndkHandler.getGccToolchainVersion(platform.name),
- platform.name),
- "-target",
- TARGET_TRIPLE[platform.name]
- ]
- }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ClangNativeToolSpecification.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ClangNativeToolSpecification.java
new file mode 100644
index 0000000..6b7b1e5
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ClangNativeToolSpecification.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.ndk.internal;
+
+import com.android.SdkConstants;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.builder.core.BuilderConstants;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.ListMultimap;
+
+import org.gradle.nativeplatform.BuildType;
+import org.gradle.nativeplatform.platform.NativePlatform;
+
+import java.util.Map;
+
+/**
+ * Flag configuration for Clang toolchain.
+ */
+public class ClangNativeToolSpecification extends AbstractNativeToolSpecification {
+
+ private NdkHandler ndkHandler;
+
+ private NativePlatform platform;
+
+ private boolean isDebugBuild;
+
+ private static final Map<String, String> TARGET_TRIPLE = ImmutableMap.<String, String>builder()
+ .put(SdkConstants.ABI_INTEL_ATOM, "i686-none-linux-android")
+ .put(SdkConstants.ABI_INTEL_ATOM64, "x86_64-none-linux-android")
+ .put(SdkConstants.ABI_ARMEABI, "armv5-none-linux-android")
+ .put(SdkConstants.ABI_ARMEABI_V7A, "armv7-none-linux-android")
+ .put(SdkConstants.ABI_ARM64_V8A, "aarch64-none-linux-android")
+ .put(SdkConstants.ABI_MIPS, "mipsel-none-linux-android")
+ .put(SdkConstants.ABI_MIPS64, "mips64el-none-linux-android")
+ .build();
+
+
+ private static final ListMultimap<String, String> RELEASE_CFLAGS =
+ ImmutableListMultimap.<String, String>builder()
+ .putAll(SdkConstants.ABI_ARMEABI, ImmutableList.of(
+ "-fpic",
+ "-ffunction-sections",
+ "-funwind-tables",
+ "-fstack-protector",
+ "-no-canonical-prefixes",
+ "-march=armv5te",
+ "-mtune=xscale",
+ "-msoft-float",
+ "-mthumb",
+ "-Os",
+ "-DNDEBUG",
+ "-fomit-frame-pointer",
+ "-fstrict-aliasing"))
+ .putAll(SdkConstants.ABI_ARMEABI_V7A, ImmutableList.of(
+ "-fpic",
+ "-ffunction-sections",
+ "-funwind-tables",
+ "-fstack-protector",
+ "-no-canonical-prefixes",
+ "-march=armv7-a",
+ "-mfloat-abi=softfp",
+ "-mfpu=vfpv3-d16",
+ "-mthumb",
+ "-Os",
+ "-DNDEBUG",
+ "-fomit-frame-pointer",
+ "-fstrict-aliasing"))
+ .putAll(SdkConstants.ABI_ARM64_V8A, ImmutableList.of(
+ "-fpic",
+ "-ffunction-sections",
+ "-funwind-tables",
+ "-fstack-protector",
+ "-no-canonical-prefixes",
+ "-O2",
+ "-DNDEBUG",
+ "-fomit-frame-pointer",
+ "-fstrict-aliasing"))
+ .putAll(SdkConstants.ABI_INTEL_ATOM, ImmutableList.of(
+ "-ffunction-sections",
+ "-funwind-tables",
+ "-fstack-protector",
+ "-fPIC",
+ "-no-canonical-prefixes",
+ "-O2",
+ "-DNDEBUG",
+ "-fomit-frame-pointer",
+ "-fstrict-aliasing"))
+ .putAll(SdkConstants.ABI_INTEL_ATOM64, ImmutableList.of(
+ "-ffunction-sections",
+ "-funwind-tables",
+ "-fstack-protector",
+ "-fPIC",
+ "-no-canonical-prefixes",
+ "-O2",
+ "-DNDEBUG",
+ "-fomit-frame-pointer",
+ "-fstrict-aliasing"))
+ .putAll(SdkConstants.ABI_MIPS, ImmutableList.of(
+ "-fpic",
+ "-fno-strict-aliasing",
+ "-finline-functions",
+ "-ffunction-sections",
+ "-funwind-tables",
+ "-fmessage-length=0",
+ "-no-canonical-prefixes",
+ "-O2",
+ "-g",
+ "-DNDEBUG",
+ "-fomit-frame-pointer"))
+ .putAll(SdkConstants.ABI_MIPS64, ImmutableList.of(
+ "-fpic",
+ "-fno-strict-aliasing",
+ "-finline-functions",
+ "-ffunction-sections",
+ "-funwind-tables",
+ "-fmessage-length=0",
+ "-no-canonical-prefixes",
+ "-O2",
+ "-g",
+ "-DNDEBUG",
+ "-fomit-frame-pointer"))
+ .build();
+
+ private static final ListMultimap<String, String> DEBUG_CFLAGS =
+ ImmutableListMultimap.<String, String>builder()
+ .putAll(SdkConstants.ABI_ARMEABI, ImmutableList.of(
+ "-O0",
+ "-UNDEBUG",
+ "-marm",
+ "-fno-strict-aliasing",
+ "-fno-limit-debug-info"))
+ .putAll(SdkConstants.ABI_ARMEABI_V7A, ImmutableList.of(
+ "-O0",
+ "-UNDEBUG",
+ "-marm",
+ "-fno-strict-aliasing",
+ "-fno-limit-debug-info"))
+ .putAll(SdkConstants.ABI_ARM64_V8A, ImmutableList.of(
+ "-O0",
+ "-UNDEBUG",
+ "-fno-omit-frame-pointer",
+ "-fno-strict-aliasing",
+ "-fno-limit-debug-info"))
+ .putAll(SdkConstants.ABI_INTEL_ATOM, ImmutableList.of(
+ "-O0",
+ "-UNDEBUG",
+ "-fno-omit-frame-pointer",
+ "-fno-strict-aliasing",
+ "-fno-limit-debug-info"))
+ .putAll(SdkConstants.ABI_INTEL_ATOM64, ImmutableList.of(
+ "-O0",
+ "-UNDEBUG",
+ "-fno-omit-frame-pointer",
+ "-fno-strict-aliasing",
+ "-fno-limit-debug-info"))
+ .putAll(SdkConstants.ABI_MIPS, ImmutableList.of(
+ "-O0",
+ "-UNDEBUG",
+ "-fno-omit-frame-pointer",
+ "-fno-limit-debug-info"))
+ .putAll(SdkConstants.ABI_MIPS64, ImmutableList.of(
+ "-O0",
+ "-UNDEBUG",
+ "-fno-omit-frame-pointer",
+ "-fno-limit-debug-info"))
+ .build();
+
+ public ClangNativeToolSpecification(
+ NdkHandler ndkHandler,
+ NativePlatform platform,
+ boolean isDebugBuild) {
+ this.ndkHandler = ndkHandler;
+ this.isDebugBuild = isDebugBuild;
+ this.platform = platform;
+ }
+
+ @Override
+ public Iterable<String> getCFlags() {
+ return Iterables.concat(
+ getTargetFlags(),
+ RELEASE_CFLAGS.get(platform.getName()),
+ isDebugBuild ? DEBUG_CFLAGS.get(platform.getName()) : ImmutableList.<String>of());
+ }
+
+ @Override
+ public Iterable<String> getCppFlags() {
+ return getCFlags();
+ }
+
+ @Override
+ public Iterable<String> getLdFlags() {
+ return Iterables.concat(
+ getTargetFlags(),
+ platform.getName().equals(SdkConstants.ABI_ARMEABI_V7A)
+ ? ImmutableList.of("-Wl,--fix-cortex-a8")
+ : ImmutableList.<String>of());
+ }
+
+ private Iterable<String> getTargetFlags() {
+ return ImmutableList.of(
+ "-gcc-toolchain",
+ ndkHandler.getDefaultGccToolchainPath(Abi.getByName(platform.getName())).toString(),
+ "-target",
+ TARGET_TRIPLE.get(platform.getName()));
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/DefaultNativeToolSpecification.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/DefaultNativeToolSpecification.java
new file mode 100644
index 0000000..14e64e7
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/DefaultNativeToolSpecification.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.ndk.internal;
+
+import com.google.common.collect.ImmutableList;
+
+import org.gradle.nativeplatform.NativeBinarySpec;
+
+import java.util.List;
+
+/**
+ * Default flags that applies to all toolchains.
+ */
+public class DefaultNativeToolSpecification extends AbstractNativeToolSpecification
+ implements NativeToolSpecification {
+
+ private static final List<String> CPP_FLAGS = ImmutableList.of("-fno-rtti", "-fno-exceptions");
+
+ private static final List<String> LD_FLAGS = ImmutableList.of(
+ "-Wl,--no-undefined",
+ "-Wl,-z,noexecstack",
+ "-Wl,-z,relro",
+ "-Wl,-z,now");
+
+ @Override
+ public Iterable<String> getCFlags() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public Iterable<String> getCppFlags() {
+ return CPP_FLAGS;
+ }
+
+ @Override
+ public Iterable<String> getLdFlags() {
+ return LD_FLAGS;
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/GccNativeToolSpecification.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/GccNativeToolSpecification.groovy
deleted file mode 100644
index 281f67e..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/GccNativeToolSpecification.groovy
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.ndk.internal
-
-import com.android.SdkConstants
-import com.android.builder.core.BuilderConstants
-import org.gradle.nativeplatform.BuildType
-import org.gradle.nativeplatform.platform.NativePlatform
-
-/**
- * Flag configuration for GCC toolchain.
- */
-
-class GccNativeToolSpecification extends AbstractNativeToolSpecification {
-
- private static final def RELEASE_CFLAGS = [
- (SdkConstants.ABI_ARMEABI) : [
- "-fpic",
- "-ffunction-sections",
- "-funwind-tables",
- "-fstack-protector",
- "-no-canonical-prefixes",
- "-march=armv5te",
- "-mtune=xscale",
- "-msoft-float",
- "-mthumb",
- "-Os",
- "-g",
- "-DNDEBUG",
- "-fomit-frame-pointer",
- "-fno-strict-aliasing",
- "-finline-limit=64",
- ],
- (SdkConstants.ABI_ARMEABI_V7A) : [
- "-fpic",
- "-ffunction-sections",
- "-funwind-tables",
- "-fstack-protector",
- "-no-canonical-prefixes",
- "-march=armv7-a",
- "-mfpu=vfpv3-d16",
- "-mfloat-abi=softfp",
- "-mthumb",
- "-Os",
- "-g",
- "-DNDEBUG",
- "-fomit-frame-pointer",
- "-fno-strict-aliasing",
- "-finline-limit=64",
- ],
- (SdkConstants.ABI_ARM64_V8A) : [
- "-fpic",
- "-ffunction-sections",
- "-funwind-tables",
- "-fstack-protector",
- "-no-canonical-prefixes",
- "-O2",
- "-g",
- "-DNDEBUG",
- "-fomit-frame-pointer",
- "-fstrict-aliasing",
- "-funswitch-loops",
- "-finline-limit=300",
- ],
- (SdkConstants.ABI_INTEL_ATOM) : [
- "-ffunction-sections",
- "-funwind-tables",
- "-no-canonical-prefixes",
- "-fstack-protector",
- "-O2",
- "-g",
- "-DNDEBUG",
- "-fomit-frame-pointer",
- "-fstrict-aliasing",
- "-funswitch-loops",
- "-finline-limit=300",
- ],
- (SdkConstants.ABI_INTEL_ATOM64) : [
- "-ffunction-sections",
- "-funwind-tables",
- "-no-canonical-prefixes",
- "-fstack-protector",
- "-O2",
- "-g",
- "-DNDEBUG",
- "-fomit-frame-pointer",
- "-fstrict-aliasing",
- "-funswitch-loops",
- "-finline-limit=300",
- ],
- (SdkConstants.ABI_MIPS) : [
- "-fpic",
- "-fno-strict-aliasing",
- "-finline-functions",
- "-ffunction-sections",
- "-funwind-tables",
- "-fmessage-length=0",
- "-fno-inline-functions-called-once",
- "-fgcse-after-reload",
- "-frerun-cse-after-loop",
- "-frename-registers",
- "-no-canonical-prefixes",
- "-O2",
- "-g",
- "-DNDEBUG",
- "-fomit-frame-pointer",
- "-funswitch-loops",
- "-finline-limit=300",
- ],
- (SdkConstants.ABI_MIPS64) : [
- "-fpic",
- "-fno-strict-aliasing",
- "-finline-functions",
- "-ffunction-sections",
- "-funwind-tables",
- "-fmessage-length=0",
- "-fno-inline-functions-called-once",
- "-fgcse-after-reload",
- "-frerun-cse-after-loop",
- "-frename-registers",
- "-no-canonical-prefixes",
- "-O2",
- "-g",
- "-DNDEBUG",
- "-fomit-frame-pointer",
- "-funswitch-loops",
- "-finline-limit=300",
- ],
- ]
-
- private static final def DEBUG_CFLAGS = [
- (SdkConstants.ABI_ARMEABI) : [
- "-O0",
- "-UNDEBUG",
- "-fno-omit-frame-pointer",
- "-fno-strict-aliasing",
- ],
- (SdkConstants.ABI_ARMEABI_V7A) : [
- "-O0",
- "-UNDEBUG",
- "-fno-omit-frame-pointer",
- "-fno-strict-aliasing",
- ],
- (SdkConstants.ABI_ARM64_V8A) : [
- "-O0",
- "-UNDEBUG",
- "-fno-omit-frame-pointer",
- "-fno-strict-aliasing",
- ],
- (SdkConstants.ABI_INTEL_ATOM) : [
- "-O0",
- "-UNDEBUG",
- "-fno-omit-frame-pointer",
- "-fno-strict-aliasing",
- ],
- (SdkConstants.ABI_INTEL_ATOM64) : [
- "-O0",
- "-UNDEBUG",
- "-fno-omit-frame-pointer",
- "-fno-strict-aliasing",
- ],
- (SdkConstants.ABI_MIPS) : [
- "-O0",
- "-UNDEBUG",
- "-fno-omit-frame-pointer",
- "-fno-unswitch-loops",
- ],
- (SdkConstants.ABI_MIPS64) : [
- "-O0",
- "-UNDEBUG",
- "-fno-omit-frame-pointer",
- ]
- ]
-
- private static final Iterable<String> LDFLAGS = [
- "-no-canonical-prefixes",
- ]
-
- private NativePlatform platform
-
- private boolean isDebugBuild
-
- GccNativeToolSpecification(BuildType buildType, NativePlatform platform) {
- this.isDebugBuild = (buildType.name.equals(BuilderConstants.DEBUG))
- this.platform = platform
- }
-
- @Override
- public Iterable<String> getCFlags() {
- RELEASE_CFLAGS[platform.name] + (isDebugBuild ? DEBUG_CFLAGS[platform.name] : [])
- }
-
- @Override
- public Iterable<String> getCppFlags() {
- getCFlags()
- }
-
- @Override
- public Iterable<String> getLdFlags() {
- LDFLAGS
- }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/GccNativeToolSpecification.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/GccNativeToolSpecification.java
new file mode 100644
index 0000000..d1b7bf6
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/GccNativeToolSpecification.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.ndk.internal;
+
+import com.android.SdkConstants;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.ListMultimap;
+
+import org.gradle.nativeplatform.platform.NativePlatform;
+
+/**
+ * Flag configuration for GCC toolchain.
+ */
+public class GccNativeToolSpecification extends AbstractNativeToolSpecification {
+
+ private static final ListMultimap<String, String> RELEASE_CFLAGS =
+ ImmutableListMultimap.<String, String>builder()
+ .putAll(SdkConstants.ABI_ARMEABI, ImmutableList.of(
+ "-fpic",
+ "-ffunction-sections",
+ "-funwind-tables",
+ "-fstack-protector",
+ "-no-canonical-prefixes",
+ "-march=armv5te",
+ "-mtune=xscale",
+ "-msoft-float",
+ "-mthumb",
+ "-Os",
+ "-g",
+ "-DNDEBUG",
+ "-fomit-frame-pointer",
+ "-fno-strict-aliasing",
+ "-finline-limit=64"))
+ .putAll(SdkConstants.ABI_ARMEABI_V7A, ImmutableList.of(
+ "-fpic",
+ "-ffunction-sections",
+ "-funwind-tables",
+ "-fstack-protector",
+ "-no-canonical-prefixes",
+ "-march=armv7-a",
+ "-mfpu=vfpv3-d16",
+ "-mfloat-abi=softfp",
+ "-mthumb",
+ "-Os",
+ "-g",
+ "-DNDEBUG",
+ "-fomit-frame-pointer",
+ "-fno-strict-aliasing",
+ "-finline-limit=64"))
+ .putAll(SdkConstants.ABI_ARM64_V8A, ImmutableList.of(
+ "-fpic",
+ "-ffunction-sections",
+ "-funwind-tables",
+ "-fstack-protector",
+ "-no-canonical-prefixes",
+ "-O2",
+ "-g",
+ "-DNDEBUG",
+ "-fomit-frame-pointer",
+ "-fstrict-aliasing",
+ "-funswitch-loops",
+ "-finline-limit=300"))
+ .putAll(SdkConstants.ABI_INTEL_ATOM, ImmutableList.of(
+ "-ffunction-sections",
+ "-funwind-tables",
+ "-no-canonical-prefixes",
+ "-fstack-protector",
+ "-O2",
+ "-g",
+ "-DNDEBUG",
+ "-fomit-frame-pointer",
+ "-fstrict-aliasing",
+ "-funswitch-loops",
+ "-finline-limit=300"))
+ .putAll(SdkConstants.ABI_INTEL_ATOM64, ImmutableList.of(
+ "-ffunction-sections",
+ "-funwind-tables",
+ "-no-canonical-prefixes",
+ "-fstack-protector",
+ "-O2",
+ "-g",
+ "-DNDEBUG",
+ "-fomit-frame-pointer",
+ "-fstrict-aliasing",
+ "-funswitch-loops",
+ "-finline-limit=300"))
+ .putAll(SdkConstants.ABI_MIPS, ImmutableList.of(
+ "-fpic",
+ "-fno-strict-aliasing",
+ "-finline-functions",
+ "-ffunction-sections",
+ "-funwind-tables",
+ "-fmessage-length=0",
+ "-fno-inline-functions-called-once",
+ "-fgcse-after-reload",
+ "-frerun-cse-after-loop",
+ "-frename-registers",
+ "-no-canonical-prefixes",
+ "-O2",
+ "-g",
+ "-DNDEBUG",
+ "-fomit-frame-pointer",
+ "-funswitch-loops",
+ "-finline-limit=300"))
+ .putAll(SdkConstants.ABI_MIPS64, ImmutableList.of(
+ "-fpic",
+ "-fno-strict-aliasing",
+ "-finline-functions",
+ "-ffunction-sections",
+ "-funwind-tables",
+ "-fmessage-length=0",
+ "-fno-inline-functions-called-once",
+ "-fgcse-after-reload",
+ "-frerun-cse-after-loop",
+ "-frename-registers",
+ "-no-canonical-prefixes",
+ "-O2",
+ "-g",
+ "-DNDEBUG",
+ "-fomit-frame-pointer",
+ "-funswitch-loops",
+ "-finline-limit=300"))
+ .build();
+
+ private static final ListMultimap<String, String> DEBUG_CFLAGS =
+ ImmutableListMultimap.<String, String>builder()
+ .putAll(SdkConstants.ABI_ARMEABI, ImmutableList.of(
+ "-O0",
+ "-UNDEBUG",
+ "-fno-omit-frame-pointer",
+ "-fno-strict-aliasing"))
+ .putAll(SdkConstants.ABI_ARMEABI_V7A, ImmutableList.of(
+ "-O0",
+ "-UNDEBUG",
+ "-fno-omit-frame-pointer",
+ "-fno-strict-aliasing"))
+ .putAll(SdkConstants.ABI_ARM64_V8A, ImmutableList.of(
+ "-O0",
+ "-UNDEBUG",
+ "-fno-omit-frame-pointer",
+ "-fno-strict-aliasing"))
+ .putAll(SdkConstants.ABI_INTEL_ATOM, ImmutableList.of(
+ "-O0",
+ "-UNDEBUG",
+ "-fno-omit-frame-pointer",
+ "-fno-strict-aliasing"))
+ .putAll(SdkConstants.ABI_INTEL_ATOM64, ImmutableList.of(
+ "-O0",
+ "-UNDEBUG",
+ "-fno-omit-frame-pointer",
+ "-fno-strict-aliasing"))
+ .putAll(SdkConstants.ABI_MIPS, ImmutableList.of(
+ "-O0",
+ "-UNDEBUG",
+ "-fno-omit-frame-pointer",
+ "-fno-unswitch-loops"))
+ .putAll(SdkConstants.ABI_MIPS64, ImmutableList.of(
+ "-O0",
+ "-UNDEBUG",
+ "-fno-omit-frame-pointer"))
+ .build();
+
+ private static final Iterable<String> LDFLAGS = ImmutableList.of("-no-canonical-prefixes");
+
+ private NativePlatform platform;
+
+ private boolean isDebugBuild;
+
+
+ public GccNativeToolSpecification(NativePlatform platform, boolean isDebugBuild) {
+ this.isDebugBuild = isDebugBuild;
+ this.platform = platform;
+ }
+
+ @Override
+ public Iterable<String> getCFlags() {
+ return Iterables.concat(
+ RELEASE_CFLAGS.get(platform.getName()),
+ (isDebugBuild ? DEBUG_CFLAGS.get(platform.getName()) : ImmutableList.<String>of()));
+ }
+
+ @Override
+ public Iterable<String> getCppFlags() {
+ return getCFlags();
+ }
+
+ @Override
+ public Iterable<String> getLdFlags() {
+ return LDFLAGS;
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecification.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecification.groovy
deleted file mode 100644
index 073a83d..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecification.groovy
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.ndk.internal
-
-import org.gradle.nativeplatform.NativeBinarySpec
-
-/**
- * Interface for native binaries flag configurations.
- */
-interface NativeToolSpecification {
-
- public Iterable<String> getCFlags()
-
- public Iterable<String> getCppFlags()
-
- public Iterable<String> getLdFlags()
-
- public void apply(NativeBinarySpec binary);
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecification.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecification.java
new file mode 100644
index 0000000..cb0555b
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecification.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.ndk.internal;
+
+import org.gradle.nativeplatform.NativeBinarySpec;
+
+/**
+ * Interface for native binaries flag configurations.
+ */
+public interface NativeToolSpecification {
+
+ Iterable<String> getCFlags();
+
+ Iterable<String> getCppFlags();
+
+ Iterable<String> getLdFlags();
+
+ void apply(NativeBinarySpec binary);
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecificationFactory.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecificationFactory.groovy
deleted file mode 100644
index a9e3a47..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecificationFactory.groovy
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.ndk.internal
-
-import org.gradle.nativeplatform.BuildType
-import org.gradle.nativeplatform.platform.NativePlatform
-
-/**
- * Factory to create a NativeToolSpecification.
- */
-class NativeToolSpecificationFactory {
- /**
- * Returns a NativeToolSpecification.
- *
- * @param buildType Build type of the native binary.
- * @param platform Target platform of the native binary.
- * @return A NativeToolSpecification for the targeted native binary.
- */
- public static NativeToolSpecification create(
- NdkHandler ndkHandler,
- BuildType buildType,
- NativePlatform platform) {
- String toolchain = ndkHandler.getNdkExtension().getToolchain()
- return (toolchain == null || toolchain.equals("gcc")
- ? new GccNativeToolSpecification(buildType, platform)
- : new ClangNativeToolSpecification(ndkHandler, buildType, platform))
- }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecificationFactory.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecificationFactory.java
new file mode 100644
index 0000000..cb91e06
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NativeToolSpecificationFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.ndk.internal;
+
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Toolchain;
+
+import org.gradle.nativeplatform.BuildType;
+import org.gradle.nativeplatform.platform.NativePlatform;
+
+/**
+ * Factory to create a NativeToolSpecification.
+ */
+public class NativeToolSpecificationFactory {
+
+ /**
+ * Returns a NativeToolSpecification.
+ *
+ * @param platform Target platform of the native binary.
+ * @param isDebugBuild Is the build debuggable.
+ * @return A NativeToolSpecification for the targeted native binary.
+ */
+ public static NativeToolSpecification create(
+ NdkHandler ndkHandler,
+ NativePlatform platform,
+ boolean isDebugBuild) {
+ return (ndkHandler.getToolchain().equals(Toolchain.GCC)
+ ? new GccNativeToolSpecification(platform, isDebugBuild)
+ : new ClangNativeToolSpecification(ndkHandler, platform, isDebugBuild));
+ }
+
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkConfiguration.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkConfiguration.groovy
deleted file mode 100644
index b78036c..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkConfiguration.groovy
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.ndk.internal
-
-import com.android.build.gradle.model.AndroidComponentModelSourceSet
-import com.android.build.gradle.ndk.NdkExtension
-import com.android.build.gradle.tasks.GdbSetupTask
-import com.android.builder.core.BuilderConstants
-import org.gradle.api.tasks.TaskContainer
-import org.gradle.language.base.FunctionalSourceSet
-import org.gradle.language.c.CSourceSet
-import org.gradle.language.cpp.CppSourceSet
-import org.gradle.api.Task
-import org.gradle.api.tasks.Copy
-import org.gradle.nativeplatform.NativeBinarySpec
-import org.gradle.nativeplatform.NativeLibrarySpec
-import org.gradle.nativeplatform.SharedLibraryBinarySpec
-import org.gradle.language.c.tasks.CCompile
-import org.gradle.language.cpp.tasks.CppCompile
-import org.gradle.platform.base.BinarySpec
-
-/**
- * Configure settings used by the native binaries.
- */
-class NdkConfiguration {
- public static void configureProperties(
- NativeLibrarySpec library,
- AndroidComponentModelSourceSet sources,
- File buildDir,
- NdkExtension ndkExtension,
- NdkHandler ndkHandler) {
- Collection<String> abiList = ndkHandler.getSupportedAbis()
- abiList.each {
- library.targetPlatform(it)
- }
-
- library.binaries.withType(SharedLibraryBinarySpec) { binary ->
- sourceIfExist(binary, sources, "main")
- sourceIfExist(binary, sources, binary.flavor.name)
- sourceIfExist(binary, sources, binary.buildType.name)
- sourceIfExist(binary, sources, binary.flavor.name + binary.buildType.name.capitalize())
-
- cCompiler.define "ANDROID"
- cppCompiler.define "ANDROID"
- cCompiler.define "ANDROID_NDK"
- cppCompiler.define "ANDROID_NDK"
-
- // Set output library filename.
- binary.sharedLibraryFile =
- new File(buildDir, NdkNamingScheme.getOutputDirectoryName(binary) + "/" +
- NdkNamingScheme.getSharedLibraryFileName(ndkExtension.getModuleName()))
-
- // Replace output directory of compile tasks.
- binary.tasks.withType(CCompile) { task ->
- String sourceSetName = task.objectFileDir.name
- task.objectFileDir = NdkNamingScheme.getObjectFilesOutputDirectory(
- binary, buildDir, sourceSetName)
- }
- binary.tasks.withType(CppCompile) { task ->
- String sourceSetName = task.objectFileDir.name
- task.objectFileDir = NdkNamingScheme.getObjectFilesOutputDirectory(
- binary, buildDir, sourceSetName)
- }
-
- String sysroot = ndkHandler.getSysroot(binary.targetPlatform)
- cCompiler.args "--sysroot=$sysroot"
- cppCompiler.args "--sysroot=$sysroot"
- linker.args "--sysroot=$sysroot"
-
- if (ndkExtension.getRenderscriptNdkMode()) {
- cCompiler.args "-I$sysroot/usr/include/rs"
- cCompiler.args "-I$sysroot/usr/include/rs/cpp"
- cppCompiler.args "-I$sysroot/usr/include/rs"
- cppCompiler.args "-I$sysroot/usr/include/rs/cpp"
- linker.args "-L$sysroot/usr/lib/rs"
- }
-
- NativeToolSpecificationFactory.create(ndkHandler, binary.buildType, binary.targetPlatform).apply(binary)
-
- // Add flags defined in NdkExtension
- if (ndkExtension.getcFlags() != null) {
- cCompiler.args ndkExtension.getcFlags()
- }
- if (ndkExtension.getCppFlags() != null) {
- cppCompiler.args ndkExtension.getCppFlags()
- }
- for (String ldLibs : ndkExtension.getLdLibs()) {
- linker.args "-l$ldLibs"
- }
- }
- }
-
- public static void createTasks(
- TaskContainer tasks,
- SharedLibraryBinarySpec binary,
- File buildDir,
- NdkExtension ndkExtension,
- NdkHandler ndkHandler) {
- StlConfiguration.apply(ndkHandler, ndkExtension.getStl(), tasks, buildDir, binary)
-
- if (binary.buildType.name.equals(BuilderConstants.DEBUG)) {
- setupNdkGdbDebug(tasks, binary, buildDir, ndkExtension, ndkHandler)
- }
- }
-
- /**
- * Add the sourceSet with the specified name to the binary if such sourceSet is defined.
- */
- private static void sourceIfExist(
- BinarySpec binary,
- AndroidComponentModelSourceSet projectSourceSet,
- String sourceSetName) {
- FunctionalSourceSet sourceSet = projectSourceSet.findByName(sourceSetName)
- if (sourceSet != null) {
- binary.source(sourceSet)
- }
- }
-
- /**
- * Setup tasks to create gdb.setup and copy gdbserver for NDK debugging.
- */
- private static void setupNdkGdbDebug(
- TaskContainer tasks,
- NativeBinarySpec binary,
- File buildDir,
- NdkExtension ndkExtension,
- NdkHandler handler) {
- Task copyGdbServerTask = tasks.create(
- name: NdkNamingScheme.getTaskName(binary, "copy", "GdbServer"),
- type: Copy) {
- from(new File(
- handler.getPrebuiltDirectory(binary.targetPlatform),
- "gdbserver/gdbserver"))
- into(new File(buildDir, NdkNamingScheme.getOutputDirectoryName(binary)))
- }
- binary.builtBy copyGdbServerTask
-
- Task createGdbSetupTask = tasks.create(
- name: NdkNamingScheme.getTaskName(binary, "create", "Gdbsetup"),
- type: GdbSetupTask) { def task ->
- task.ndkHandler = handler
- task.extension = ndkExtension
- task.binary = binary
- task.outputDir = new File(buildDir, NdkNamingScheme.getOutputDirectoryName(binary))
- }
- binary.builtBy createGdbSetupTask
- }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkConfiguration.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkConfiguration.java
new file mode 100644
index 0000000..13d907c
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkConfiguration.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.ndk.internal;
+
+import static com.android.build.gradle.ndk.internal.BinaryToolHelper.getCCompiler;
+import static com.android.build.gradle.ndk.internal.BinaryToolHelper.getCppCompiler;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.managed.NdkConfig;
+import com.android.build.gradle.model.AndroidComponentModelSourceSet;
+import com.android.build.gradle.tasks.GdbSetupTask;
+import com.android.build.gradle.tasks.StripDebugSymbolTask;
+import com.android.utils.StringHelper;
+import com.google.common.base.Objects;
+
+import org.gradle.api.Action;
+import org.gradle.api.PolymorphicDomainObjectContainer;
+import org.gradle.api.Task;
+import org.gradle.api.tasks.Copy;
+import org.gradle.language.base.FunctionalSourceSet;
+import org.gradle.language.base.LanguageSourceSet;
+import org.gradle.language.c.CSourceSet;
+import org.gradle.language.c.tasks.CCompile;
+import org.gradle.language.cpp.CppSourceSet;
+import org.gradle.language.cpp.tasks.CppCompile;
+import org.gradle.model.ModelMap;
+import org.gradle.nativeplatform.NativeBinarySpec;
+import org.gradle.nativeplatform.NativeLibrarySpec;
+import org.gradle.nativeplatform.SharedLibraryBinarySpec;
+import org.gradle.platform.base.BinarySpec;
+
+import java.io.File;
+
+/**
+ * Configure settings used by the native binaries.
+ */
+public class NdkConfiguration {
+
+ public static void configureProperties(
+ NativeLibrarySpec library,
+ final AndroidComponentModelSourceSet sources,
+ final File buildDir,
+ final NdkHandler ndkHandler) {
+ for (Abi abi : ndkHandler.getSupportedAbis()) {
+ library.targetPlatform(abi.getName());
+ }
+
+ library.getBinaries()
+ .withType(SharedLibraryBinarySpec.class, new Action<SharedLibraryBinarySpec>() {
+ @Override
+ public void execute(final SharedLibraryBinarySpec binary) {
+ sourceIfExist(binary, sources, "main");
+ sourceIfExist(binary, sources, binary.getFlavor().getName());
+ sourceIfExist(binary, sources, binary.getBuildType().getName());
+ sourceIfExist(binary, sources,
+ binary.getFlavor().getName()
+ + StringHelper.capitalize(binary.getBuildType().getName()));
+
+ getCCompiler(binary).define("ANDROID");
+ getCppCompiler(binary).define("ANDROID");
+ getCCompiler(binary).define("ANDROID_NDK");
+ getCppCompiler(binary).define("ANDROID_NDK");
+
+ // Replace output directory of compile tasks.
+ binary.getTasks().withType(CCompile.class, new Action<CCompile>() {
+ @Override
+ public void execute(CCompile task) {
+ String sourceSetName = task.getObjectFileDir().getName();
+ task.setObjectFileDir(
+ NdkNamingScheme.getObjectFilesOutputDirectory(
+ binary,
+ buildDir,
+ sourceSetName));
+ }
+ });
+ binary.getTasks().withType(CppCompile.class, new Action<CppCompile>() {
+ @Override
+ public void execute(CppCompile task) {
+ String sourceSetName = task.getObjectFileDir().getName();
+ task.setObjectFileDir(
+ NdkNamingScheme.getObjectFilesOutputDirectory(
+ binary,
+ buildDir,
+ sourceSetName));
+ }
+ });
+
+ new DefaultNativeToolSpecification().apply(binary);
+
+ String sysroot = ndkHandler.getSysroot(
+ Abi.getByName(binary.getTargetPlatform().getName()));
+
+ getCCompiler(binary).args("--sysroot=" + sysroot);
+ getCppCompiler(binary).args("--sysroot=" + sysroot);
+ binary.getLinker().args("--sysroot=" + sysroot);
+ binary.getLinker().args("-Wl,--build-id");
+
+ }
+
+ });
+ }
+
+ /**
+ * Configure native binary with variant specific options.
+ */
+ public static void configureBinary(
+ SharedLibraryBinarySpec binary,
+ final File buildDir,
+ final NdkConfig ndkConfig,
+ final NdkHandler ndkHandler) {
+ // Set output library filename.
+ binary.setSharedLibraryFile(
+ new File(
+ buildDir,
+ NdkNamingScheme.getDebugLibraryDirectoryName(binary)
+ + "/"
+ + NdkNamingScheme.getSharedLibraryFileName(
+ ndkConfig.getModuleName())));
+
+ String sysroot = ndkHandler.getSysroot(
+ Abi.getByName(binary.getTargetPlatform().getName()));
+
+ if (ndkConfig.getRenderscriptNdkMode()) {
+ getCCompiler(binary).args("-I" + sysroot + "/usr/include/rs");
+ getCCompiler(binary).args("-I" + sysroot + "/usr/include/rs/cpp");
+ getCppCompiler(binary).args("-I" + sysroot + "/usr/include/rs");
+ getCppCompiler(binary).args("-I" + sysroot + "/usr/include/rs/cpp");
+ binary.getLinker().args("-L" + sysroot + "/usr/lib/rs");
+ }
+
+ // STL flags must be applied before user defined flags to resolve possible undefined symbols
+ // in the STL library.
+ StlNativeToolSpecification stlConfig = new StlNativeToolSpecification(
+ ndkHandler,
+ ndkConfig.getStl(),
+ binary.getTargetPlatform());
+ stlConfig.apply(binary);
+
+ NativeToolSpecificationFactory.create(
+ ndkHandler,
+ binary.getTargetPlatform(),
+ Objects.firstNonNull(ndkConfig.getDebuggable(), false)).apply(
+ binary);
+
+ // Add flags defined in NdkConfig
+ for (String flag : ndkConfig.getCFlags()) {
+ getCCompiler(binary).args(flag.trim());
+ }
+
+ for (String flag : ndkConfig.getCppFlags()) {
+ getCppCompiler(binary).args(flag.trim());
+ }
+
+ for (String flag : ndkConfig.getLdFlags()) {
+ binary.getLinker().args(flag.trim());
+ }
+
+ for (String ldLib : ndkConfig.getLdLibs()) {
+ binary.getLinker().args("-l" + ldLib.trim());
+ }
+ }
+
+ public static void createTasks(
+ @NonNull ModelMap<Task> tasks,
+ @NonNull SharedLibraryBinarySpec binary,
+ @NonNull File buildDir,
+ @NonNull NdkConfig ndkConfig,
+ @NonNull NdkHandler ndkHandler) {
+ String compileNdkTaskName = NdkNamingScheme.getNdkBuildTaskName(binary);
+ tasks.create(compileNdkTaskName);
+
+ StlConfiguration.createStlCopyTask(tasks, binary, buildDir, ndkHandler,
+ ndkConfig.getStl(), compileNdkTaskName);
+
+ if (Boolean.TRUE.equals(ndkConfig.getDebuggable())) {
+ // TODO: Use AndroidTaskRegistry and scopes to create tasks in experimental plugin.
+ setupNdkGdbDebug(tasks, binary, buildDir, ndkConfig, ndkHandler, compileNdkTaskName);
+ }
+ createStripDebugTask(tasks, binary, buildDir, ndkHandler, compileNdkTaskName);
+ }
+
+ /**
+ * Add the sourceSet with the specified name to the binary if such sourceSet is defined.
+ */
+ private static void sourceIfExist(
+ BinarySpec binary,
+ AndroidComponentModelSourceSet projectSourceSet,
+ final String sourceSetName) {
+ FunctionalSourceSet sourceSet = projectSourceSet.findByName(sourceSetName);
+ if (sourceSet != null) {
+ final LanguageSourceSet jni = sourceSet.getByName("jni");
+ binary.sources(new Action<PolymorphicDomainObjectContainer<LanguageSourceSet>>() {
+ @Override
+ public void execute(
+ PolymorphicDomainObjectContainer<LanguageSourceSet> languageSourceSets) {
+ // Hardcode the acceptable extension until we find a suitable DSL for user to
+ // modify.
+ languageSourceSets.create(
+ sourceSetName + "C",
+ CSourceSet.class,
+ new Action<LanguageSourceSet>() {
+ @Override
+ public void execute(LanguageSourceSet source) {
+ source.getSource().setSrcDirs(jni.getSource().getSrcDirs());
+ source.getSource().include("**/*.c");
+ source.getSource().exclude(jni.getSource().getExcludes());
+ }
+ });
+ languageSourceSets.create(
+ sourceSetName + "Cpp",
+ CppSourceSet.class,
+ new Action<LanguageSourceSet>() {
+ @Override
+ public void execute(LanguageSourceSet source) {
+ source.getSource().setSrcDirs(jni.getSource().getSrcDirs());
+ source.getSource().include("**/*.C");
+ source.getSource().include("**/*.CPP");
+ source.getSource().include("**/*.c++");
+ source.getSource().include("**/*.cc");
+ source.getSource().include("**/*.cp");
+ source.getSource().include("**/*.cpp");
+ source.getSource().include("**/*.cxx");
+ source.getSource().exclude(jni.getSource().getExcludes());
+ }
+ });
+ }
+ });
+ }
+ }
+
+ /**
+ * Setup tasks to create gdb.setup and copy gdbserver for NDK debugging.
+ */
+ private static void setupNdkGdbDebug(
+ @NonNull ModelMap<Task> tasks,
+ @NonNull final NativeBinarySpec binary,
+ @NonNull final File buildDir,
+ @NonNull final NdkConfig ndkConfig,
+ @NonNull final NdkHandler handler,
+ @NonNull String buildTaskName) {
+ final String copyGdbServerTaskName = NdkNamingScheme.getTaskName(binary, "copy", "GdbServer");
+ tasks.create(copyGdbServerTaskName, Copy.class, new Action<Copy>() {
+ @Override
+ public void execute(Copy task) {
+ task.from(new File(handler.getPrebuiltDirectory(
+ Abi.getByName(binary.getTargetPlatform().getName())),
+ "gdbserver/gdbserver"));
+ task.into(new File(buildDir, NdkNamingScheme.getOutputDirectoryName(binary)));
+ }
+ });
+
+ final String createGdbSetupTaskName = NdkNamingScheme.getTaskName(binary, "create", "Gdbsetup");
+ tasks.create(createGdbSetupTaskName, GdbSetupTask.class, new Action<GdbSetupTask>() {
+ @Override
+ public void execute(GdbSetupTask task) {
+ task.setNdkHandler(handler);
+ task.setExtension(ndkConfig);
+ task.setBinary(binary);
+ task.setOutputDir(
+ new File(buildDir, NdkNamingScheme.getOutputDirectoryName(binary)));
+ }
+ });
+
+ tasks.named(buildTaskName, new Action<Task>() {
+ @Override
+ public void execute(Task task) {
+ task.dependsOn(copyGdbServerTaskName);
+ task.dependsOn(createGdbSetupTaskName);
+ }
+ });
+ }
+
+ private static void createStripDebugTask(
+ ModelMap<Task> tasks,
+ final SharedLibraryBinarySpec binary,
+ final File buildDir,
+ final NdkHandler handler,
+ String buildTaskName) {
+
+ final String taskName = NdkNamingScheme.getTaskName(binary, "stripSymbols");
+ tasks.create(
+ taskName,
+ StripDebugSymbolTask.class,
+ new StripDebugSymbolTask.ConfigAction(binary, buildDir, handler));
+ tasks.named(buildTaskName, new Action<Task>() {
+ @Override
+ public void execute(Task task) {
+ task.dependsOn(taskName);
+ }
+ });
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkExtensionConvention.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkExtensionConvention.java
index 4621bdd..90b691f 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkExtensionConvention.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkExtensionConvention.java
@@ -1,7 +1,7 @@
package com.android.build.gradle.ndk.internal;
-
-import com.android.build.gradle.ndk.NdkExtension;
+import com.android.build.gradle.internal.core.Toolchain;
+import com.android.build.gradle.managed.NdkConfig;
import org.gradle.api.InvalidUserDataException;
@@ -10,46 +10,27 @@
*/
public class NdkExtensionConvention {
- public static final String DEFAULT_TOOLCHAIN = "gcc";
-
- // Default toolchain version depends on the target ABI. Setting it to "default" to allow
- // the version to be determined later.
- public static final String DEFAULT_TOOLCHAIN_VERSION = "default";
-
public static final String DEFAULT_STL = "system";
/**
* Validate the NdkExtension and provide default values.
*/
- public static void setExtensionDefault(NdkExtension extension) {
- if (extension.getToolchain().isEmpty()) {
- extension.setToolchain(DEFAULT_TOOLCHAIN);
+ public static void setExtensionDefault(NdkConfig ndkConfig) {
+ if (ndkConfig.getToolchain().isEmpty()) {
+ ndkConfig.setToolchain(Toolchain.getDefault().getName());
} else {
- if (!extension.getToolchain().equals("gcc") &&
- !extension.getToolchain().equals("clang")) {
+ if (!ndkConfig.getToolchain().equals("gcc") &&
+ !ndkConfig.getToolchain().equals("clang")) {
throw new InvalidUserDataException(String.format(
"Invalid toolchain '%s'. Supported toolchains are 'gcc' and 'clang'.",
- extension.getToolchain()));
+ ndkConfig.getToolchain()));
}
}
- if (extension.getToolchainVersion().isEmpty()) {
- extension.setToolchainVersion(DEFAULT_TOOLCHAIN_VERSION);
- }
-
- if (extension.getCFilePattern().getIncludes().isEmpty()) {
- extension.getCFilePattern().include("**/*.c");
- }
-
- if (extension.getCppFilePattern().getIncludes().isEmpty()) {
- extension.getCppFilePattern().include("**/*.cpp");
- extension.getCppFilePattern().include("**/*.cc");
- }
-
- if (extension.getStl().isEmpty()) {
- extension.setStl(DEFAULT_STL);
+ if (ndkConfig.getStl().isEmpty()) {
+ ndkConfig.setStl(DEFAULT_STL);
} else {
- StlConfiguration.checkStl(extension.getStl());
+ StlConfiguration.checkStl(ndkConfig.getStl());
}
}
}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkHandler.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkHandler.java
deleted file mode 100644
index 4723d00..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkHandler.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.ndk.internal;
-
-import static com.android.SdkConstants.FN_LOCAL_PROPERTIES;
-
-import com.android.SdkConstants;
-import com.android.annotations.Nullable;
-import com.android.build.gradle.ndk.NdkExtension;
-import com.google.common.base.Charsets;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.io.Closeables;
-
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.Project;
-import org.gradle.nativeplatform.platform.NativePlatform;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.Collection;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * Handles NDK related information.
- */
-public class NdkHandler {
-
- // Map of ABI to toolchain platform string.
- private static final Map<String, String> PLATFORM_STRING;
-
- // Map of ABI to target architecture
- private static final Map<String, String> ARCHITECTURE_STRING;
-
- // Map of toolchain names to the subdirectory name containing the toolchain.
- private static final Map<String, String> TOOLCHAIN_STRING;
-
- private static final List<String> ABI32 = ImmutableList.of(
- SdkConstants.ABI_INTEL_ATOM,
- SdkConstants.ABI_ARMEABI_V7A,
- SdkConstants.ABI_ARMEABI,
- SdkConstants.ABI_MIPS);
-
- private static final List<String> ALL_ABI = ImmutableList.of(
- SdkConstants.ABI_INTEL_ATOM,
- SdkConstants.ABI_INTEL_ATOM64,
- SdkConstants.ABI_ARMEABI_V7A,
- SdkConstants.ABI_ARMEABI,
- SdkConstants.ABI_ARM64_V8A,
- SdkConstants.ABI_MIPS,
- SdkConstants.ABI_MIPS64);
-
- private static final String DEFAULT_LLVM_GCC32_VERSION="4.8";
- private static final String DEFAULT_LLVM_GCC64_VERSION="4.9";
-
- private NdkExtension ndkExtension;
-
- private File ndkDirectory;
-
- static {
- // Initialize static maps.
- PLATFORM_STRING = ImmutableMap.<String, String>builder()
- .put(SdkConstants.ABI_INTEL_ATOM, "x86")
- .put(SdkConstants.ABI_INTEL_ATOM64, "x86_64")
- .put(SdkConstants.ABI_ARMEABI_V7A, "arm-linux-androideabi")
- .put(SdkConstants.ABI_ARMEABI, "arm-linux-androideabi")
- .put(SdkConstants.ABI_ARM64_V8A, "aarch64-linux-android")
- .put(SdkConstants.ABI_MIPS, "mipsel-linux-android")
- .put(SdkConstants.ABI_MIPS64, "mips64el-linux-android")
- .build();
-
- ARCHITECTURE_STRING = ImmutableMap.<String, String>builder()
- .put(SdkConstants.ABI_INTEL_ATOM, SdkConstants.CPU_ARCH_INTEL_ATOM)
- .put(SdkConstants.ABI_INTEL_ATOM64, SdkConstants.CPU_ARCH_INTEL_ATOM64)
- .put(SdkConstants.ABI_ARMEABI_V7A, SdkConstants.CPU_ARCH_ARM)
- .put(SdkConstants.ABI_ARMEABI, SdkConstants.CPU_ARCH_ARM)
- .put(SdkConstants.ABI_ARM64_V8A, SdkConstants.CPU_ARCH_ARM64)
- .put(SdkConstants.ABI_MIPS, SdkConstants.CPU_ARCH_MIPS)
- .put(SdkConstants.ABI_MIPS64, SdkConstants.CPU_ARCH_MIPS64)
- .build();
-
- TOOLCHAIN_STRING = ImmutableMap.<String, String>builder()
- .put("gcc", "")
- .put("clang", "clang")
- .build();
- }
-
- public NdkHandler(File projectDir, NdkExtension ndkExtension) {
- this.ndkExtension = ndkExtension;
- ndkDirectory = findNdkDirectory(projectDir);
- }
-
- /**
- * Toolchain name used by the NDK.
- *
- * This is the name of the folder containing the toolchain under $ANDROID_NDK_HOME/toolchain.
- * e.g. for gcc targetting arm64_v8a, this method returns "aarch64-linux-android-4.9".
- */
- private static String getToolchainName(
- String toolchain,
- String toolchainVersion,
- String platform) {
- return PLATFORM_STRING.get(platform) + "-" + TOOLCHAIN_STRING.get(toolchain)
- + toolchainVersion;
- }
-
- /**
- * Determine the location of the NDK directory.
- *
- * The NDK directory can be set in the local.properties file or using the ANDROID_NDK_HOME
- * environment variable.
- */
- private static File findNdkDirectory(File projectDir) {
- File localProperties = new File(projectDir, FN_LOCAL_PROPERTIES);
-
- if (localProperties.isFile()) {
-
- Properties properties = new Properties();
- InputStreamReader reader = null;
- try {
- //noinspection IOResourceOpenedButNotSafelyClosed
- FileInputStream fis = new FileInputStream(localProperties);
- reader = new InputStreamReader(fis, Charsets.UTF_8);
- properties.load(reader);
- } catch (FileNotFoundException ignored) {
- // ignore since we check up front and we don't want to fail on it anyway
- // in case there's an env var.
- } catch (IOException e) {
- throw new RuntimeException(String.format("Unable to read %1$s.", localProperties), e);
- } finally {
- try {
- Closeables.close(reader, true /* swallowIOException */);
- } catch (IOException e) {
- // ignore.
- }
- }
-
- String ndkDirProp = properties.getProperty("ndk.dir");
- if (ndkDirProp != null) {
- return new File(ndkDirProp);
- }
-
- } else {
- String envVar = System.getenv("ANDROID_NDK_HOME");
- if (envVar != null) {
- return new File(envVar);
- }
- }
- return null;
- }
-
- /**
- * Returns the directory of the NDK.
- */
- @Nullable
- public File getNdkDirectory() {
- return ndkDirectory;
- }
-
- NdkExtension getNdkExtension() {
- return ndkExtension;
- }
-
- /**
- * Return the path containing the prebuilt toolchain.
- *
- * @param toolchain Name of the toolchain ["gcc", "clang"].
- * @param toolchainVersion Version of the toolchain.
- * @param platform Target platform supported by the NDK.
- * @return Directory containing the prebuilt toolchain.
- */
-
- public File getToolchainPath(String toolchain, String toolchainVersion, String platform) {
- File prebuiltFolder;
- if (toolchain.equals("gcc")) {
- prebuiltFolder = new File(
- getNdkDirectory(),
- "toolchains/" + getToolchainName(toolchain, toolchainVersion, platform)
- + "/prebuilt");
-
- } else if (toolchain.equals("clang")) {
- prebuiltFolder = new File(
- getNdkDirectory(),
- "toolchains/llvm-" + toolchainVersion + "/prebuilt");
- } else {
- throw new InvalidUserDataException("Unrecognized toolchain: " + toolchain);
- }
-
- String osName = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
- String hostOs;
- if (osName.contains("windows")) {
- hostOs = "windows";
- } else if (osName.contains("mac")) {
- hostOs = "darwin";
- } else {
- hostOs = "linux";
- }
-
- // There should only be one directory in the prebuilt folder. If there are more than one
- // attempt to determine the right one based on the operating system.
- File[] toolchainPaths = prebuiltFolder.listFiles(
- new FileFilter() {
- @Override
- public boolean accept(File file) {
- return file.isDirectory();
- }
- });
-
- if (toolchainPaths == null) {
- throw new InvalidUserDataException("Unable to find toolchain: "
- + prebuiltFolder);
- }
- if (toolchainPaths.length == 1) {
- return toolchainPaths[0];
- }
-
- // Use 64-bit toolchain if available.
- File toolchainPath = new File(prebuiltFolder, hostOs + "-x86_64");
- if (toolchainPath.isDirectory()) {
- return toolchainPath;
- }
-
- // Fallback to 32-bit if we can't find the 64-bit toolchain.
- String osString = (osName.equals("windows")) ? hostOs : hostOs + "-x86";
- toolchainPath = new File(prebuiltFolder, osString);
- if (toolchainPath.isDirectory()) {
- return toolchainPath;
- } else {
- throw new InvalidUserDataException("Unable to find toolchain prebuilt folder in: "
- + prebuiltFolder);
- }
- }
-
- /**
- * Returns the sysroot directory for the toolchain.
- */
- public String getSysroot(NativePlatform platform) {
- return ndkDirectory + "/platforms/" + ndkExtension.getCompileSdkVersion()
- + "/arch-" + ARCHITECTURE_STRING.get(platform.getName());
- }
-
- /**
- * Return the directory containing prebuilt binaries such as gdbserver.
- */
- public File getPrebuiltDirectory(NativePlatform platform) {
- return new File(
- ndkDirectory, "prebuilt/android-" + ARCHITECTURE_STRING.get(platform.getName()));
- }
-
- /**
- * Return true if compiledSdkVersion supports 64 bits ABI.
- */
- public boolean supports64Bits() {
- String targetString = getNdkExtension().getCompileSdkVersion().replace("android-", "");
- try {
- return Integer.parseInt(targetString) >= 20;
- } catch (NumberFormatException ignored) {
- // "android-L" supports 64-bits.
- return true;
- }
- }
-
- /**
- * Return the gcc version that will be used by the NDK.
- *
- * If the gcc toolchain is used, then it's simply the toolchain version requested by the user.
- * If clang is used, then it depends the target abi.
- */
- public String getGccToolchainVersion(String abi) {
- String toolchain = ndkExtension.getToolchain();
- if (toolchain.equals("gcc")) {
- return (toolchain.equals(NdkExtensionConvention.DEFAULT_TOOLCHAIN))
- ? ToolchainConfiguration.getDefaultToolchainVersion(toolchain, abi)
- : ndkExtension.getToolchainVersion();
- } else {
- return is64Bits(abi) ? DEFAULT_LLVM_GCC64_VERSION : DEFAULT_LLVM_GCC32_VERSION;
- }
- }
-
- /**
- * Returns a list of supported ABI.
- */
- public Collection<String> getSupportedAbis() {
- return supports64Bits() ? ALL_ABI : ABI32;
- }
-
- /**
- * Return whether the specified abi is 64 bits.
- */
- public static boolean is64Bits(String abi) {
- return !ABI32.contains(abi);
- }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkNamingScheme.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkNamingScheme.java
index a83954e..63127c5 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkNamingScheme.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/NdkNamingScheme.java
@@ -16,8 +16,11 @@
package com.android.build.gradle.ndk.internal;
+import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
+import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
+
+import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.builder.model.AndroidProject;
import com.android.utils.StringHelper;
import com.google.common.base.Joiner;
@@ -38,7 +41,7 @@
buildDir,
String.format(
"%s/objectFiles/%s/%s",
- AndroidProject.FD_INTERMEDIATES ,
+ FD_INTERMEDIATES ,
binary.getName(),
sourceSetName));
}
@@ -68,14 +71,47 @@
}
}
- public static String getOutputDirectoryName(NativeBinarySpec binary) {
+ public static String getNdkBuildTaskName(@NonNull NativeBinarySpec binary) {
+ return getTaskName(binary, "ndkBuild");
+ }
+
+ /**
+ * Return the name of the directory that will contain the final output of the native binary.
+ */
+ public static String getOutputDirectoryName(String buildType, String productFlavor, String abi) {
return Joiner.on(File.separator).join(
- AndroidProject.FD_INTERMEDIATES,
+ FD_INTERMEDIATES,
"binaries",
- binary.getName(),
+ buildType,
+ productFlavor,
+ "lib",
+ abi);
+ }
+
+ public static String getOutputDirectoryName(NativeBinarySpec binary) {
+ return getOutputDirectoryName(
binary.getBuildType().getName(),
binary.getFlavor().getName(),
- "lib",
+ binary.getTargetPlatform().getName());
+ }
+
+ /**
+ * Return the name of the directory that will contain the native library with debug symbols.
+ */
+ public static String getDebugLibraryDirectoryName(String buildType, String productFlavor, String abi) {
+ return Joiner.on(File.separator).join(
+ FD_INTERMEDIATES,
+ "binaries",
+ buildType,
+ productFlavor,
+ "obj",
+ abi);
+ }
+
+ public static String getDebugLibraryDirectoryName(NativeBinarySpec binary) {
+ return getDebugLibraryDirectoryName(
+ binary.getBuildType().getName(),
+ binary.getFlavor().getName(),
binary.getTargetPlatform().getName());
}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlConfiguration.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlConfiguration.groovy
deleted file mode 100644
index 53a456c..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlConfiguration.groovy
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.ndk.internal
-
-import org.gradle.api.InvalidUserDataException;
-import org.gradle.api.Task;
-import org.gradle.api.tasks.Copy
-import org.gradle.api.tasks.TaskContainer
-import org.gradle.nativeplatform.SharedLibraryBinarySpec;
-
-/**
- * Configuration to setup STL for NDK.
- */
-public class StlConfiguration {
- static final String DEFAULT_STL = "system"
- static final String[] VALID_STL = [
- "system",
- "stlport_static",
- "stlport_shared",
- "gnustl_static",
- "gnustl_shared",
- "gabi++_static",
- "gabi++_shared",
- "c++_static",
- "c++_shared",
- ]
-
- static final Map<String, Collection<String>> STL_SOURCES = [
- "system" : [
- "system/include"
- ],
- "stlport" : [
- "stlport/stlport",
- "gabi++/include",
- ],
- "gnustl" : [
- "gnu-libstdc++",
- "gnu-libstdc++/4.6/include",
- "gnu-libstdc++/4.6/libs/armeabi-v7a/include",
- "gnu-libstdc++/4.6/include/backward",
- ],
- "gabi++" : [
- "gabi++",
- "gabi++/include",
- ],
- "c++" : [
- "../android/support/include",
- "llvm-libc++",
- "../android/compiler-rt",
- "llvm-libc++/libcxx/include",
- "gabi++/include",
- "../android/support/include",
- ],
- ]
-
- public static File getStlBaseDirectory(NdkHandler ndkHandler) {
- return new File(ndkHandler.getNdkDirectory(), "sources/cxx-stl/");
- }
-
- public static Collection<String> getStlSources(NdkHandler ndkHandler, String stl) {
- String stlBase = getStlBaseDirectory(ndkHandler);
- String stlName = stl.equals("system") ? "system" : stl.substring(0, stl.indexOf('_'));
- return STL_SOURCES[stlName].collect { String sourceDir ->
- stlBase.toString() + "/" + sourceDir
- }
- }
-
-
- public static void checkStl(String stl) {
- if (!VALID_STL.contains(stl)) {
- throw new InvalidUserDataException("Invalid STL: $stl")
- }
- }
-
- public static void apply(
- NdkHandler ndkHandler,
- String stl,
- TaskContainer tasks,
- File buildDir,
- SharedLibraryBinarySpec binary) {
- StlNativeToolSpecification stlConfig =
- new StlNativeToolSpecification(ndkHandler, stl, binary.targetPlatform)
- stlConfig.apply(binary)
-
- if (stl.endsWith("_shared")) {
- Task copySharedLib = tasks.create(
- name: NdkNamingScheme.getTaskName(binary, "copy", "StlSo"),
- type: Copy) {
- from(stlConfig.getStlLib(binary.targetPlatform.name))
- into(new File(buildDir, NdkNamingScheme.getOutputDirectoryName(binary)))
- }
- binary.builtBy copySharedLib
- }
- }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlConfiguration.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlConfiguration.java
new file mode 100644
index 0000000..d9da1ab
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlConfiguration.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.ndk.internal;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.NdkHandler;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ListMultimap;
+
+import org.gradle.api.Action;
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.Task;
+import org.gradle.api.tasks.Copy;
+import org.gradle.model.ModelMap;
+import org.gradle.nativeplatform.NativeBinarySpec;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Configuration to setup STL for NDK.
+ */
+public class StlConfiguration {
+
+ private static final String DEFAULT_STL = "system";
+
+ private static final List<String> VALID_STL = ImmutableList.of(
+ "system",
+ "stlport_static",
+ "stlport_shared",
+ "gnustl_static",
+ "gnustl_shared",
+ "gabi++_static",
+ "gabi++_shared",
+ "c++_static",
+ "c++_shared");
+
+ private static final ListMultimap<String, String> STL_SOURCES =
+ ImmutableListMultimap.<String, String>builder()
+ .putAll("system", ImmutableList.of(
+ "system/include"))
+ .putAll("stlport", ImmutableList.of(
+ "stlport/stlport",
+ "gabi++/include"))
+ .putAll("gnustl", ImmutableList.of(
+ "gnu-libstdc++",
+ "gnu-libstdc++/4.6/include",
+ "gnu-libstdc++/4.6/libs/armeabi-v7a/include",
+ "gnu-libstdc++/4.6/include/backward"))
+ .putAll("gabi++", ImmutableList.of(
+ "gabi++",
+ "gabi++/include"))
+ .putAll("c++", ImmutableList.of(
+ "../android/support/include",
+ "llvm-libc++",
+ "../android/compiler-rt",
+ "llvm-libc++/libcxx/include",
+ "gabi++/include",
+ "../android/support/include"))
+ .build();
+
+ public static File getStlBaseDirectory(NdkHandler ndkHandler) {
+ return new File(ndkHandler.getNdkDirectory(), "sources/cxx-stl/");
+ }
+
+ public static Collection<String> getStlSources(NdkHandler ndkHandler, String stl) {
+ final File stlBase = getStlBaseDirectory(ndkHandler);
+ String stlName = stl.equals("system") ? "system" : stl.substring(0, stl.indexOf('_'));
+
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+ for (String sourceDir : STL_SOURCES.get(stlName)) {
+ builder.add(stlBase.toString() + "/" + sourceDir);
+ }
+ return builder.build();
+ }
+
+ public static void checkStl(String stl) {
+ if (!VALID_STL.contains(stl)) {
+ throw new InvalidUserDataException("Invalid STL: " + stl);
+ }
+ }
+
+ public static void createStlCopyTask(
+ @NonNull ModelMap<Task> tasks,
+ @NonNull final NativeBinarySpec binary,
+ @NonNull final File buildDir,
+ @NonNull NdkHandler ndkHandler,
+ @NonNull String stl,
+ @NonNull String buildTaskName) {
+ if (stl.endsWith("_shared")) {
+ final StlNativeToolSpecification stlConfig = new StlNativeToolSpecification(ndkHandler,
+ stl, binary.getTargetPlatform());
+
+ final String copyTaskName = NdkNamingScheme.getTaskName(binary, "copy", "StlSo");
+ tasks.create(copyTaskName, Copy.class, new Action<Copy>() {
+ @Override
+ public void execute(Copy copy) {
+ copy.from(stlConfig.getStlLib(binary.getTargetPlatform().getName()));
+ copy.into(new File(buildDir, NdkNamingScheme.getOutputDirectoryName(binary)));
+
+ }
+ });
+ tasks.named(buildTaskName, new Action<Task>() {
+ @Override
+ public void execute(Task task) {
+ task.dependsOn(copyTaskName);
+ }
+ });
+ }
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlNativeToolSpecification.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlNativeToolSpecification.java
index b401374..758b0e2 100644
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlNativeToolSpecification.java
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/StlNativeToolSpecification.java
@@ -16,6 +16,8 @@
package com.android.build.gradle.ndk.internal;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
import com.google.common.collect.Lists;
import org.gradle.nativeplatform.platform.NativePlatform;
@@ -53,31 +55,14 @@
List<String> cppFlags = Lists.newArrayList();
- List<String> includeDirs = Lists.newArrayList();
- if (stlName.equals("system")) {
- includeDirs.add("system/include");
- } else if (stlName.equals("stlport")) {
- includeDirs.add("stlport/stlport");
- includeDirs.add("gabi++/include");
- } else if (stlName.equals("gnustl")) {
- String gccToolchainVersion = ndkHandler.getGccToolchainVersion(platform.getName());
- includeDirs.add("gnu-libstdc++/" + gccToolchainVersion + "/include");
- includeDirs.add("gnu-libstdc++/" + gccToolchainVersion +
- "/libs/" + platform.getName() + "/include");
- includeDirs.add("gnu-libstdc++/" + gccToolchainVersion +
- "/include/backward");
- } else if (stlName.equals("gabi++")) {
- includeDirs.add("gabi++/include");
- } else if (stlName.equals("c++")) {
- includeDirs.add("llvm-libc++/libcxx/include");
- includeDirs.add("gabi++/include");
- includeDirs.add("../android/support/include");
+ if (stlName.equals("c++")) {
cppFlags.add("-std=c++11");
}
- for (String dir : includeDirs) {
- cppFlags.add("-I" +
- new File(StlConfiguration.getStlBaseDirectory(ndkHandler), dir).toString());
+ List<File> includeDirs = ndkHandler.getStlIncludes(stlName, Abi.getByName(
+ platform.getName()));
+ for (File dir : includeDirs) {
+ cppFlags.add("-I" + dir.toString());
}
return cppFlags;
}
@@ -97,7 +82,7 @@
if (stlName.equals("stlport")) {
stlLib = "stlport";
} else if (stlName.equals("gnustl")) {
- stlLib = "gnu-libstdc++/" + ndkHandler.getGccToolchainVersion(abi);
+ stlLib = "gnu-libstdc++/" + ndkHandler.getGccToolchainVersion(Abi.getByName(abi));
} else if (stlName.equals("gabi++")) {
stlLib = "gabi++";
} else if (stlName.equals("c++")) {
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ToolchainConfiguration.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ToolchainConfiguration.groovy
deleted file mode 100644
index 0dc78ce..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ToolchainConfiguration.groovy
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.ndk.internal
-
-import com.android.SdkConstants
-import org.gradle.nativeplatform.platform.NativePlatform
-import org.gradle.nativeplatform.toolchain.Clang
-import org.gradle.nativeplatform.toolchain.Gcc
-import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry
-import org.gradle.platform.base.PlatformContainer
-
-/**
- * Action to configure toolchain for native binaries.
- */
-class ToolchainConfiguration {
-
- private static final String DEFAULT_GCC32_VERSION="4.6"
- private static final String DEFAULT_GCC64_VERSION="4.9"
- private static final String DEFAULT_LLVM_VERSION="3.4"
-
- private static final GCC_PREFIX = [
- (SdkConstants.ABI_INTEL_ATOM) : "i686-linux-android",
- (SdkConstants.ABI_INTEL_ATOM64) : "x86_64-linux-android",
- (SdkConstants.ABI_ARMEABI_V7A) : "arm-linux-androideabi",
- (SdkConstants.ABI_ARMEABI) : "arm-linux-androideabi",
- (SdkConstants.ABI_ARM64_V8A) : "aarch64-linux-android",
- (SdkConstants.ABI_MIPS) : "mipsel-linux-android",
- (SdkConstants.ABI_MIPS64) : "mips64el-linux-android"
- ]
-
- public static void configurePlatforms(PlatformContainer platforms, NdkHandler ndkHandler) {
- List<String> abiList = ndkHandler.getSupportedAbis();
- for (String abi : abiList) {
- NativePlatform platform = platforms.maybeCreate(abi, NativePlatform)
-
- // All we care is the name of the platform. It doesn't matter what the
- // architecture is, but it must be set to non-x86 so that it does not match
- // the default supported platform.
- platform.architecture "ppc"
- platform.operatingSystem "linux"
- }
- }
-
- /**
- * Return the default version of the specified toolchain for a target abi.
- */
- public static String getDefaultToolchainVersion(String toolchain, String abi) {
- if (NdkHandler.is64Bits(abi)) {
- return (toolchain.equals("gcc")) ? DEFAULT_GCC64_VERSION : DEFAULT_LLVM_VERSION
- } else {
- return (toolchain.equals("gcc")) ? DEFAULT_GCC32_VERSION : DEFAULT_LLVM_VERSION
- }
- }
-
- /**
- * Configure toolchain for a platform.
- */
- public static void configureToolchain(
- NativeToolChainRegistry toolchains,
- String toolchainName,
- String toolchainVersion,
- NdkHandler ndkHandler) {
- toolchains.create("ndk-" + toolchainName, toolchainName.equals("gcc") ? Gcc : Clang) {
- // Configure each platform.
- List<String> abiList = ndkHandler.getSupportedAbis();
- for (String abi: abiList) {
- String platform = abi
- String localToolchainVersion = toolchainVersion
-
- if (localToolchainVersion.equals(NdkExtensionConvention.DEFAULT_TOOLCHAIN_VERSION)) {
- localToolchainVersion = getDefaultToolchainVersion(toolchainName, platform);
- }
-
- target(platform) {
- if (toolchainName.equals("gcc")) {
- cCompiler.setExecutable("${GCC_PREFIX[platform]}-gcc")
- cppCompiler.setExecutable("${GCC_PREFIX[platform]}-g++")
- linker.setExecutable("${GCC_PREFIX[platform]}-g++")
- assembler.setExecutable("${GCC_PREFIX[platform]}-as")
- staticLibArchiver.setExecutable("${GCC_PREFIX[platform]}-ar")
- }
-
- // By default, gradle will use -Xlinker to pass arguments to the linker.
- // Removing it as it prevents -sysroot from being properly set.
- linker.withArguments { List<String> args ->
- args.removeAll("-Xlinker")
- }
-
- String bin = (
- ndkHandler.getToolchainPath(toolchainName, localToolchainVersion, platform).
- toString()
- + "/bin")
- path bin
- }
- }
- }
- }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ToolchainConfiguration.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ToolchainConfiguration.java
new file mode 100644
index 0000000..b500610
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/ndk/internal/ToolchainConfiguration.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.ndk.internal;
+
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.core.Toolchain;
+
+import org.gradle.api.Action;
+import org.gradle.nativeplatform.platform.NativePlatform;
+import org.gradle.nativeplatform.toolchain.Clang;
+import org.gradle.nativeplatform.toolchain.Gcc;
+import org.gradle.nativeplatform.toolchain.GccCompatibleToolChain;
+import org.gradle.nativeplatform.toolchain.GccPlatformToolChain;
+import org.gradle.nativeplatform.toolchain.NativeToolChainRegistry;
+import org.gradle.platform.base.PlatformContainer;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Action to configure toolchain for native binaries.
+ */
+public class ToolchainConfiguration {
+
+ public static void configurePlatforms(PlatformContainer platforms, NdkHandler ndkHandler) {
+ for (Abi abi : ndkHandler.getSupportedAbis()) {
+ NativePlatform platform = platforms.maybeCreate(abi.getName(), NativePlatform.class);
+
+ // All we care is the name of the platform. It doesn't matter what the
+ // architecture is, but it must be set to non-x86 so that it does not match
+ // the default supported platform.
+ platform.architecture("ppc");
+ platform.operatingSystem("linux");
+ }
+
+ }
+
+ /**
+ * Configure toolchain for a platform.
+ */
+ public static void configureToolchain(
+ NativeToolChainRegistry toolchainRegistry,
+ final String toolchainName,
+ final NdkHandler ndkHandler) {
+ final Toolchain ndkToolchain = Toolchain.getByName(toolchainName);
+ toolchainRegistry.create("ndk-" + toolchainName,
+ toolchainName.equals("gcc") ? Gcc.class : Clang.class,
+ new Action<GccCompatibleToolChain>() {
+ @Override
+ public void execute(GccCompatibleToolChain toolchain) {
+ // Configure each platform.
+ for (Abi it : ndkHandler.getSupportedAbis()) {
+ final Abi abi = it;
+ toolchain.target(abi.getName(), new Action<GccPlatformToolChain>() {
+ @Override
+ public void execute(GccPlatformToolChain targetPlatform) {
+ if (Toolchain.GCC.equals(ndkToolchain)) {
+ String gccPrefix = abi.getGccExecutablePrefix();
+ targetPlatform.getcCompiler()
+ .setExecutable(gccPrefix + "-gcc");
+ targetPlatform.getCppCompiler()
+ .setExecutable(gccPrefix + "-g++");
+ targetPlatform.getLinker()
+ .setExecutable(gccPrefix + "-g++");
+ targetPlatform.getAssembler()
+ .setExecutable(gccPrefix + "-as");
+ targetPlatform.getStaticLibArchiver()
+ .setExecutable(gccPrefix + "-ar");
+ }
+
+ // By default, gradle will use -Xlinker to pass arguments to the linker.
+ // Removing it as it prevents -sysroot from being properly set.
+ targetPlatform.getLinker().withArguments(
+ new Action<List<String>>() {
+ @Override
+ public void execute(List<String> args) {
+ args.removeAll(Collections.singleton("-Xlinker"));
+ }
+ });
+ }
+
+ });
+ toolchain.path(ndkHandler.getCCompiler(abi).getParentFile());
+ }
+ }
+ });
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/GdbSetupTask.groovy b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/GdbSetupTask.groovy
deleted file mode 100644
index 4295259..0000000
--- a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/GdbSetupTask.groovy
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.tasks
-
-import com.android.build.gradle.internal.tasks.DefaultAndroidTask
-import com.android.build.gradle.ndk.NdkExtension
-import com.android.build.gradle.ndk.internal.NdkHandler
-import com.android.build.gradle.ndk.internal.StlConfiguration
-import com.google.common.base.Charsets
-import com.google.common.collect.Sets
-import com.google.common.io.Files
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.TaskAction
-import org.gradle.language.c.CSourceSet
-import org.gradle.language.cpp.CppSourceSet
-import org.gradle.nativeplatform.NativeBinarySpec
-
-/**
- * Task to create gdb.setup for native code debugging.
- */
-class GdbSetupTask extends DefaultAndroidTask {
- @Input
- NdkHandler ndkHandler
-
- @Input
- NdkExtension extension
-
- @Input
- NativeBinarySpec binary
-
- @Input
- File outputDir
-
- @TaskAction
- void taskAction() {
- File gdbSetupFile = new File(outputDir, "gdb.setup")
-
- StringBuilder sb = new StringBuilder()
-
- sb.append("set solib-search-path ${outputDir.toString()}\n")
- sb.append("directory ")
- sb.append("${ndkHandler.getSysroot(binary.targetPlatform)}/usr/include ")
-
- Set<String> sources = Sets.newHashSet();
- binary.getSource().withType(CSourceSet) { sourceSet ->
- sources.addAll(sourceSet.source.srcDirs*.toString())
- }
- binary.getSource().withType(CppSourceSet) { sourceSet ->
- sources.addAll(sourceSet.source.srcDirs*.toString())
- }
- sources.addAll(StlConfiguration.getStlSources(ndkHandler, extension.stl))
- sb.append(sources.join(" "))
-
- if (!outputDir.exists()) {
- outputDir.mkdirs()
- }
- Files.write(sb.toString(), gdbSetupFile, Charsets.UTF_8)
- }
-}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/GdbSetupTask.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/GdbSetupTask.java
new file mode 100644
index 0000000..3e3866e
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/GdbSetupTask.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.build.gradle.tasks;
+
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.managed.NdkConfig;
+import com.android.build.gradle.ndk.internal.StlConfiguration;
+import com.google.common.base.Charsets;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+import org.gradle.api.Action;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.language.c.CSourceSet;
+import org.gradle.language.cpp.CppSourceSet;
+import org.gradle.nativeplatform.NativeBinarySpec;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * Task to create gdb.setup for native code debugging.
+ */
+public class GdbSetupTask extends DefaultTask {
+
+ private NdkHandler ndkHandler;
+
+ private NdkConfig extension;
+
+ private NativeBinarySpec binary;
+
+ private File outputDir;
+
+
+ // ----- PUBLIC TASK API -----
+
+ @Input
+ public File getOutputDir() {
+ return outputDir;
+ }
+
+ public void setOutputDir(File outputDir) {
+ this.outputDir = outputDir;
+ }
+
+ // ----- PRIVATE TASK API -----
+
+ @Input
+ public NdkHandler getNdkHandler() {
+ return ndkHandler;
+ }
+
+ public void setNdkHandler(NdkHandler ndkHandler) {
+ this.ndkHandler = ndkHandler;
+ }
+
+ @Input
+ public NdkConfig getExtension() {
+ return extension;
+ }
+
+ public void setExtension(NdkConfig extension) {
+ this.extension = extension;
+ }
+
+ @Input
+ public NativeBinarySpec getBinary() {
+ return binary;
+ }
+
+ public void setBinary(NativeBinarySpec binary) {
+ this.binary = binary;
+ }
+
+ @TaskAction
+ public void taskAction() {
+ File gdbSetupFile = new File(outputDir, "gdb.setup");
+
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("set solib-search-path ")
+ .append(outputDir.toString())
+ .append("\n")
+ .append("directory ")
+ .append(ndkHandler.getSysroot(Abi.getByName(binary.getTargetPlatform().getName())))
+ .append("/usr/include ");
+
+ final Set<String> sources = Sets.newHashSet();
+ binary.getSource().withType(CSourceSet.class, new Action<CSourceSet>() {
+ @Override
+ public void execute(CSourceSet sourceSet) {
+ for (File src : sourceSet.getSource().getSrcDirs()) {
+ sources.add(src.toString());
+ }
+ }
+ });
+ binary.getSource().withType(CppSourceSet.class, new Action<CppSourceSet>() {
+ @Override
+ public void execute(CppSourceSet sourceSet) {
+ for (File src : sourceSet.getSource().getSrcDirs()) {
+ sources.add(src.toString());
+ }
+ }
+ });
+ sources.addAll(StlConfiguration.getStlSources(ndkHandler, extension.getStl()));
+ sb.append(Joiner.on(' ').join(sources));
+
+ if (!outputDir.exists()) {
+ outputDir.mkdirs();
+ }
+
+ try {
+ Files.write(sb.toString(), gdbSetupFile, Charsets.UTF_8);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/StripDebugSymbolTask.java b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/StripDebugSymbolTask.java
new file mode 100644
index 0000000..ff5b96b
--- /dev/null
+++ b/build-system/gradle-experimental/src/main/groovy/com/android/build/gradle/tasks/StripDebugSymbolTask.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.tasks;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.LoggerWrapper;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.process.GradleProcessExecutor;
+import com.android.build.gradle.ndk.internal.NdkNamingScheme;
+import com.android.ide.common.process.LoggedProcessOutputHandler;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessInfoBuilder;
+
+import org.gradle.api.Action;
+import org.gradle.api.DefaultTask;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFile;
+import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.nativeplatform.SharedLibraryBinarySpec;
+
+import java.io.File;
+
+/**
+ * Task to remove debug symbols from a native library.
+ */
+public class StripDebugSymbolTask extends DefaultTask {
+
+ private File stripCommand;
+
+ private File inputFile;
+
+ private File outputFile;
+
+ // ----- PUBLIC API -----
+
+ @Input
+ public File getStripCommand() {
+ return stripCommand;
+ }
+
+ public void setStripCommand(File stripCommand) {
+ this.stripCommand = stripCommand;
+ }
+
+ @Optional
+ @InputFile
+ public File getInputFile() {
+ // If source set is empty, the file debuggable library is not generated.
+ return inputFile.exists() ? inputFile : null;
+ }
+
+ public void setInputFile(File inputFile) {
+ this.inputFile = inputFile;
+ }
+
+ @OutputFile
+ public File getOutputFile() {
+ return outputFile;
+ }
+
+ public void setOutputFile(File outputFile) {
+ this.outputFile = outputFile;
+ }
+
+ // ----- PRIVATE API -----
+
+ @TaskAction
+ void taskAction() throws ProcessException {
+ if (getInputFile() == null) {
+ return;
+ }
+
+ if (!outputFile.getParentFile().exists()) {
+ outputFile.getParentFile().mkdirs();
+ }
+
+ ProcessInfoBuilder builder = new ProcessInfoBuilder();
+ builder.setExecutable(stripCommand);
+ builder.addArgs("--strip-unneeded");
+ builder.addArgs("-o");
+ builder.addArgs(outputFile.toString());
+ builder.addArgs(inputFile.toString());
+ new GradleProcessExecutor(getProject()).execute(
+ builder.createProcess(),
+ new LoggedProcessOutputHandler(new LoggerWrapper(getLogger())));
+ }
+
+ // ----- ConfigAction -----
+
+ public static class ConfigAction implements Action<StripDebugSymbolTask> {
+ @NonNull
+ private final SharedLibraryBinarySpec binary;
+ @NonNull
+ private final File buildDir;
+ @NonNull
+ private final NdkHandler handler;
+
+ public ConfigAction(
+ @NonNull SharedLibraryBinarySpec binary,
+ @NonNull File buildDir,
+ @NonNull NdkHandler handler) {
+ this.binary = binary;
+ this.buildDir = buildDir;
+ this.handler = handler;
+ }
+
+ @Override
+ public void execute(@NonNull StripDebugSymbolTask task) {
+ File debugLib = binary.getSharedLibraryFile();
+ task.setInputFile(debugLib);
+ task.setOutputFile(new File(
+ buildDir,
+ NdkNamingScheme.getOutputDirectoryName(binary) + "/"
+ + debugLib.getName()));
+ task.setStripCommand(handler.getStripCommand(
+ Abi.getByName(binary.getTargetPlatform().getName())));
+ task.dependsOn(binary);
+ }
+ }
+}
diff --git a/build-system/gradle/build.gradle b/build-system/gradle/build.gradle
index 23875d1..5bcf41f 100644
--- a/build-system/gradle/build.gradle
+++ b/build-system/gradle/build.gradle
@@ -1,16 +1,8 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'groovy'
apply plugin: 'clone-artifacts'
apply plugin: 'idea'
apply plugin: 'jacoco'
+apply plugin: 'license-report'
dependencies {
compile project(':base:gradle-core')
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/AppExtension.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/AppExtension.java
new file mode 100644
index 0000000..35bde14
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/AppExtension.java
@@ -0,0 +1,48 @@
+package com.android.build.gradle;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.api.ApplicationVariant;
+import com.android.build.gradle.api.BaseVariant;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.builder.core.AndroidBuilder;
+
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.internal.reflect.Instantiator;
+
+/**
+ * 'android' extension for 'com.android.application' project.
+ */
+public class AppExtension extends TestedExtension {
+
+ private final DefaultDomainObjectSet<ApplicationVariant> applicationVariantList
+ = new DefaultDomainObjectSet<ApplicationVariant>(ApplicationVariant.class);
+
+ public AppExtension(@NonNull ProjectInternal project, @NonNull Instantiator instantiator,
+ @NonNull AndroidBuilder androidBuilder, @NonNull SdkHandler sdkHandler,
+ @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
+ @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
+ @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
+ @NonNull ExtraModelInfo extraModelInfo, boolean isLibrary) {
+ super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
+ signingConfigs, extraModelInfo, isLibrary);
+ }
+
+ /**
+ * Returns the list of Application variants. Since the collections is built after evaluation, it
+ * should be used with Gradle's <code>all</code> iterator to process future items.
+ */
+ public DefaultDomainObjectSet<ApplicationVariant> getApplicationVariants() {
+ return applicationVariantList;
+ }
+
+ @Override
+ public void addVariant(BaseVariant variant) {
+ applicationVariantList.add((ApplicationVariant) variant);
+ }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy b/build-system/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy
index 9ef7d06..aba323e 100644
--- a/build-system/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/AppPlugin.groovy
@@ -25,7 +25,6 @@
import com.android.builder.core.AndroidBuilder
import org.gradle.api.Plugin
import org.gradle.api.Project
-import org.gradle.api.tasks.TaskContainer
import org.gradle.internal.reflect.Instantiator
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
@@ -49,7 +48,7 @@
protected TaskManager createTaskManager(
Project project,
AndroidBuilder androidBuilder,
- BaseExtension extension,
+ AndroidConfig extension,
SdkHandler sdkHandler,
DependencyManager dependencyManager,
ToolingModelBuilderRegistry toolingRegistry) {
@@ -68,7 +67,7 @@
}
@Override
- protected VariantFactory getVariantFactory() {
+ protected VariantFactory createVariantFactory() {
return new ApplicationVariantFactory(instantiator, androidBuilder, extension)
}
}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.java
new file mode 100644
index 0000000..b4f4bb0
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/BaseExtension.java
@@ -0,0 +1,799 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.gradle.api.AndroidSourceSet;
+import com.android.build.gradle.api.BaseVariant;
+import com.android.build.gradle.api.VariantFilter;
+import com.android.build.gradle.internal.CompileOptions;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.LoggingUtil;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.SourceSetSourceProviderWrapper;
+import com.android.build.gradle.internal.coverage.JacocoExtension;
+import com.android.build.gradle.internal.dsl.AaptOptions;
+import com.android.build.gradle.internal.dsl.AdbOptions;
+import com.android.build.gradle.internal.dsl.AndroidSourceSetFactory;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.CoreBuildType;
+import com.android.build.gradle.internal.dsl.CoreProductFlavor;
+import com.android.build.gradle.internal.dsl.DexOptions;
+import com.android.build.gradle.internal.dsl.LintOptions;
+import com.android.build.gradle.internal.dsl.PackagingOptions;
+import com.android.build.gradle.internal.dsl.PreprocessingOptions;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.build.gradle.internal.dsl.Splits;
+import com.android.build.gradle.internal.dsl.TestOptions;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.core.LibraryRequest;
+import com.android.builder.model.SourceProvider;
+import com.android.builder.sdk.TargetInfo;
+import com.android.builder.testing.api.DeviceProvider;
+import com.android.builder.testing.api.TestServer;
+import com.android.sdklib.repository.FullRevision;
+import com.google.common.annotations.Beta;
+import com.google.common.collect.Lists;
+
+import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Configuration;
+import org.gradle.api.artifacts.ConfigurationContainer;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.logging.Logger;
+import org.gradle.api.logging.Logging;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.internal.reflect.Instantiator;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Base 'android' extension for all android plugins.
+ *
+ * <p>This is never used directly. Instead,
+ *<ul>
+ * <li>Plugin <code>com.android.application</code> uses {@link AppExtension}</li>
+ * <li>Plugin <code>com.android.library</code> uses {@link LibraryExtension}</li>
+ * <li>Plugin <code>com.android.test</code> uses {@link TestedExtension}</li>
+ * </ul>
+ */
+@SuppressWarnings("UnnecessaryInheritDoc")
+public abstract class BaseExtension implements AndroidConfig {
+
+ private String target;
+ private FullRevision buildToolsRevision;
+ private List<LibraryRequest> libraryRequests = Lists.newArrayList();
+
+ /** Default config, shared by all flavors. */
+ final ProductFlavor defaultConfig;
+
+ /** Options for aapt, tool for packaging resources. */
+ final AaptOptions aaptOptions;
+
+ /** Lint options. */
+ final LintOptions lintOptions;
+
+ /** Dex options. */
+ final DexOptions dexOptions;
+
+ /** Options for running tests. */
+ final TestOptions testOptions;
+
+ /** Compile options */
+ final CompileOptions compileOptions;
+
+ /** Packaging options. */
+ final PackagingOptions packagingOptions;
+
+ /** Options to control resources preprocessing. Not finalized yet.*/
+ final PreprocessingOptions preprocessingOptions;
+
+ /** JaCoCo options. */
+ final JacocoExtension jacoco;
+
+ /**
+ * APK splits options.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits">APK Splits</a>.
+ */
+ final Splits splits;
+
+ /** All product flavors used by this project. */
+ final NamedDomainObjectContainer<CoreProductFlavor> productFlavors;
+
+ /** Build types used by this project. */
+ final NamedDomainObjectContainer<BuildType> buildTypes;
+
+ /** Signing configs used by this project. */
+ final NamedDomainObjectContainer<SigningConfig> signingConfigs;
+
+ private ExtraModelInfo extraModelInfo;
+
+ protected Project project;
+
+ /** Adb options */
+ final AdbOptions adbOptions;
+
+ /** A prefix to be used when creating new resources. Used by Studio */
+ String resourcePrefix;
+
+ List<String> flavorDimensionList;
+
+ private String defaultPublishConfig = "release";
+ private boolean publishNonDefault = false;
+
+ private Action<VariantFilter> variantFilter;
+
+ private final List<DeviceProvider> deviceProviderList = Lists.newArrayList();
+ private final List<TestServer> testServerList = Lists.newArrayList();
+
+ private final AndroidBuilder androidBuilder;
+
+ private final SdkHandler sdkHandler;
+
+ protected Logger logger;
+
+ private boolean isWritable = true;
+
+ /**
+ * The source sets container.
+ */
+ final NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer;
+
+ BaseExtension(
+ @NonNull final ProjectInternal project,
+ @NonNull Instantiator instantiator,
+ @NonNull AndroidBuilder androidBuilder,
+ @NonNull SdkHandler sdkHandler,
+ @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
+ @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
+ @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
+ @NonNull ExtraModelInfo extraModelInfo,
+ final boolean isLibrary) {
+ this.androidBuilder = androidBuilder;
+ this.sdkHandler = sdkHandler;
+ this.buildTypes = buildTypes;
+ //noinspection unchecked
+ this.productFlavors = (NamedDomainObjectContainer) productFlavors;
+ this.signingConfigs = signingConfigs;
+ this.extraModelInfo = extraModelInfo;
+ this.project = project;
+
+ logger = Logging.getLogger(this.getClass());
+
+ defaultConfig = instantiator.newInstance(ProductFlavor.class, BuilderConstants.MAIN,
+ project, instantiator, project.getLogger());
+
+ aaptOptions = instantiator.newInstance(AaptOptions.class);
+ dexOptions = instantiator.newInstance(DexOptions.class);
+ lintOptions = instantiator.newInstance(LintOptions.class);
+ testOptions = instantiator.newInstance(TestOptions.class);
+ compileOptions = instantiator.newInstance(CompileOptions.class);
+ packagingOptions = instantiator.newInstance(PackagingOptions.class);
+ preprocessingOptions = instantiator.newInstance(PreprocessingOptions.class);
+ jacoco = instantiator.newInstance(JacocoExtension.class);
+ adbOptions = instantiator.newInstance(AdbOptions.class);
+ splits = instantiator.newInstance(Splits.class, instantiator);
+
+ sourceSetsContainer = project.container(AndroidSourceSet.class,
+ new AndroidSourceSetFactory(instantiator, project, isLibrary));
+
+ sourceSetsContainer.whenObjectAdded(new Action<AndroidSourceSet>() {
+ @Override
+ public void execute(AndroidSourceSet sourceSet) {
+ ConfigurationContainer configurations = project.getConfigurations();
+
+ createConfiguration(
+ configurations,
+ sourceSet.getCompileConfigurationName(),
+ "Classpath for compiling the " + sourceSet.getName() + " sources.");
+
+ String packageConfigDescription;
+ if (isLibrary) {
+ packageConfigDescription
+ = "Classpath only used when publishing '" + sourceSet.getName() + "'.";
+ } else {
+ packageConfigDescription
+ = "Classpath packaged with the compiled '" + sourceSet.getName() + "' classes.";
+ }
+ createConfiguration(
+ configurations,
+ sourceSet.getPackageConfigurationName(),
+ packageConfigDescription);
+
+ createConfiguration(
+ configurations,
+ sourceSet.getProvidedConfigurationName(),
+ "Classpath for only compiling the " + sourceSet.getName() + " sources.");
+
+ createConfiguration(
+ configurations,
+ sourceSet.getWearAppConfigurationName(),
+ "Link to a wear app to embed for object '" + sourceSet.getName() + "'.");
+
+ sourceSet.setRoot(String.format("src/%s", sourceSet.getName()));
+ }
+ });
+
+ sourceSetsContainer.create(defaultConfig.getName());
+ }
+
+ /**
+ * Disallow further modification on the extension.
+ */
+ public void disableWrite() {
+ isWritable = false;
+ }
+
+ protected void checkWritability() {
+ if (!isWritable) {
+ throw new GradleException(
+ "Android tasks have already been created.\n" +
+ "This happens when calling android.applicationVariants,\n" +
+ "android.libraryVariants or android.testVariants.\n" +
+ "Once these methods are called, it is not possible to\n" +
+ "continue configuring the model.");
+ }
+ }
+
+ protected void createConfiguration(
+ @NonNull ConfigurationContainer configurations,
+ @NonNull String configurationName,
+ @NonNull String configurationDescription) {
+ logger.info("Creating configuration {}", configurationName);
+
+ Configuration configuration = configurations.findByName(configurationName);
+ if (configuration == null) {
+ configuration = configurations.create(configurationName);
+ }
+ configuration.setVisible(false);
+ configuration.setDescription(configurationDescription);
+ }
+
+ /**
+ * Sets the compile SDK version, based on full SDK version string, e.g.
+ * <code>android-21</code> for Lollipop.
+ */
+ public void compileSdkVersion(String version) {
+ checkWritability();
+ this.target = version;
+ }
+
+ /**
+ * Sets the compile SDK version, based on API level, e.g. 21 for Lollipop.
+ */
+ public void compileSdkVersion(int apiLevel) {
+ compileSdkVersion("android-" + apiLevel);
+ }
+
+ public void setCompileSdkVersion(int apiLevel) {
+ compileSdkVersion(apiLevel);
+ }
+
+ public void setCompileSdkVersion(String target) {
+ compileSdkVersion(target);
+ }
+
+ /**
+ * Request the use a of Library. The library is then added to the classpath.
+ * @param name the name of the library.
+ */
+ public void useLibrary(String name) {
+ useLibrary(name, true);
+ }
+
+ /**
+ * Request the use a of Library. The library is then added to the classpath.
+ * @param name the name of the library.
+ * @param required if using the library requires a manifest entry, the entry will
+ * indicate that the library is not required.
+ */
+ public void useLibrary(String name, boolean required) {
+ libraryRequests.add(new LibraryRequest(name, required));
+ }
+
+ public void buildToolsVersion(String version) {
+ checkWritability();
+ buildToolsRevision = FullRevision.parseRevision(version);
+ }
+
+ /**
+ * <strong>Required.</strong> Version of the build tools to use.
+ *
+ * <p>Value assigned to this property is parsed and stored in a normalized form, so reading it
+ * back may give a slightly different string.
+ */
+ @Override
+ public String getBuildToolsVersion() {
+ return buildToolsRevision.toString();
+ }
+
+ public void setBuildToolsVersion(String version) {
+ buildToolsVersion(version);
+ }
+
+ /**
+ * Configures the build types.
+ */
+ public void buildTypes(Action<? super NamedDomainObjectContainer<BuildType>> action) {
+ checkWritability();
+ action.execute(buildTypes);
+ }
+
+ /**
+ * Configures the product flavors.
+ */
+ public void productFlavors(Action<? super NamedDomainObjectContainer<CoreProductFlavor>> action) {
+ checkWritability();
+ action.execute(productFlavors);
+ }
+
+ /**
+ * Configures the signing configs.
+ */
+ public void signingConfigs(Action<? super NamedDomainObjectContainer<SigningConfig>> action) {
+ checkWritability();
+ action.execute(signingConfigs);
+ }
+
+ public void flavorDimensions(String... dimensions) {
+ checkWritability();
+ flavorDimensionList = Arrays.asList(dimensions);
+ }
+
+ /**
+ * Configures the source sets. Note that the Android plugin uses its own implementation of
+ * source sets, {@link AndroidSourceSet}.
+ */
+ public void sourceSets(Action<NamedDomainObjectContainer<AndroidSourceSet>> action) {
+ checkWritability();
+ action.execute(sourceSetsContainer);
+ }
+
+ /**
+ * All source sets. Note that the Android plugin uses its own implementation of
+ * source sets, {@link AndroidSourceSet}.
+ */
+ @Override
+ public NamedDomainObjectContainer<AndroidSourceSet> getSourceSets() {
+ return sourceSetsContainer;
+ }
+
+ /**
+ * The default configuration, inherited by all build flavors (if any are defined).
+ */
+ public void defaultConfig(Action<ProductFlavor> action) {
+ checkWritability();
+ action.execute(defaultConfig);
+ }
+
+ /**
+ * Configures aapt options.
+ */
+ public void aaptOptions(Action<AaptOptions> action) {
+ checkWritability();
+ action.execute(aaptOptions);
+ }
+
+ /**
+ * Configures dex options.
+ */
+ public void dexOptions(Action<DexOptions> action) {
+ checkWritability();
+ action.execute(dexOptions);
+ }
+
+ /**
+ * Configure lint options.
+ */
+ public void lintOptions(Action<LintOptions> action) {
+ checkWritability();
+ action.execute(lintOptions);
+ }
+
+ /** Configures the test options. */
+ public void testOptions(Action<TestOptions> action) {
+ checkWritability();
+ action.execute(testOptions);
+ }
+
+ /**
+ * Configures compile options.
+ */
+ public void compileOptions(Action<CompileOptions> action) {
+ checkWritability();
+ action.execute(compileOptions);
+ }
+
+ /**
+ * Configures packaging options.
+ */
+ public void packagingOptions(Action<PackagingOptions> action) {
+ checkWritability();
+ action.execute(packagingOptions);
+ }
+
+ /**
+ * Configures preprocessing options.
+ */
+ public void preprocessingOptions(Action<PreprocessingOptions> action) {
+ checkWritability();
+ action.execute(preprocessingOptions);
+ }
+
+ /**
+ * Configures JaCoCo options.
+ */
+ public void jacoco(Action<JacocoExtension> action) {
+ checkWritability();
+ action.execute(jacoco);
+ }
+
+ /**
+ * Configures adb options.
+ */
+ public void adbOptions(Action<AdbOptions> action) {
+ checkWritability();
+ action.execute(adbOptions);
+ }
+
+ /**
+ * Configures APK splits.
+ */
+ public void splits(Action<Splits> action) {
+ checkWritability();
+ action.execute(splits);
+ }
+
+ public void deviceProvider(DeviceProvider deviceProvider) {
+ checkWritability();
+ deviceProviderList.add(deviceProvider);
+ }
+
+ @Override
+ @NonNull
+ public List<DeviceProvider> getDeviceProviders() {
+ return deviceProviderList;
+ }
+
+ public void testServer(TestServer testServer) {
+ checkWritability();
+ testServerList.add(testServer);
+ }
+
+ @Override
+ @NonNull
+ public List<TestServer> getTestServers() {
+ return testServerList;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<? extends CoreProductFlavor> getProductFlavors() {
+ return productFlavors;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<? extends CoreBuildType> getBuildTypes() {
+ return buildTypes;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<? extends com.android.builder.model.SigningConfig> getSigningConfigs() {
+ return signingConfigs;
+ }
+
+ public void defaultPublishConfig(String value) {
+ setDefaultPublishConfig(value);
+ }
+
+ public void publishNonDefault(boolean value) {
+ publishNonDefault = value;
+ }
+
+ /**
+ * Name of the configuration used to build the default artifact of this project.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Referencing-a-Library">
+ * Referencing a Library</a>
+ */
+ @Override
+ public String getDefaultPublishConfig() {
+ return defaultPublishConfig;
+ }
+
+ public void setDefaultPublishConfig(String value) {
+ defaultPublishConfig = value;
+ }
+
+ /**
+ * Whether to publish artifacts for all configurations, not just the default one.
+ *
+ * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Referencing-a-Library">
+ * Referencing a Library</a>
+ */
+ @Override
+ public boolean getPublishNonDefault() {
+ return publishNonDefault;
+ }
+
+ public void variantFilter(Action<VariantFilter> filter) {
+ setVariantFilter(filter);
+ }
+
+ public void setVariantFilter(Action<VariantFilter> filter) {
+ variantFilter = filter;
+ }
+
+ /**
+ * A variant filter to control which variants are excluded.
+ * <p>The filter is an {@link Action} which is passed a single object of type
+ * {@link com.android.build.gradle.internal.api.VariantFilter}. It should set the
+ * {@link VariantFilter#setIgnore(boolean)} flag to filter out the given variant.
+ */
+ @Override
+ public Action<VariantFilter> getVariantFilter() {
+ return variantFilter;
+ }
+
+ @Override
+ public AdbOptions getAdbOptions() {
+ return adbOptions;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getResourcePrefix() {
+ return resourcePrefix;
+ }
+
+ @Override
+ public List<String> getFlavorDimensionList() {
+ return flavorDimensionList;
+ }
+
+ @Override
+ public boolean getGeneratePureSplits() {
+ return generatePureSplits;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Beta
+ public PreprocessingOptions getPreprocessingOptions() {
+ return preprocessingOptions;
+ }
+
+ public void resourcePrefix(String prefix) {
+ resourcePrefix = prefix;
+ }
+
+ public abstract void addVariant(BaseVariant variant);
+
+ public void registerArtifactType(@NonNull String name,
+ boolean isTest,
+ int artifactType) {
+ extraModelInfo.registerArtifactType(name, isTest, artifactType);
+ }
+
+ public void registerBuildTypeSourceProvider(
+ @NonNull String name,
+ @NonNull BuildType buildType,
+ @NonNull SourceProvider sourceProvider) {
+ extraModelInfo.registerBuildTypeSourceProvider(name, buildType, sourceProvider);
+ }
+
+ public void registerProductFlavorSourceProvider(
+ @NonNull String name,
+ @NonNull CoreProductFlavor productFlavor,
+ @NonNull SourceProvider sourceProvider) {
+ extraModelInfo.registerProductFlavorSourceProvider(name, productFlavor, sourceProvider);
+ }
+
+ public void registerJavaArtifact(
+ @NonNull String name,
+ @NonNull BaseVariant variant,
+ @NonNull String assembleTaskName,
+ @NonNull String javaCompileTaskName,
+ @NonNull Collection<File> generatedSourceFolders,
+ @NonNull Iterable<String> ideSetupTaskNames,
+ @NonNull Configuration configuration,
+ @NonNull File classesFolder,
+ @NonNull File javaResourceFolder,
+ @Nullable SourceProvider sourceProvider) {
+ extraModelInfo.registerJavaArtifact(name, variant, assembleTaskName,
+ javaCompileTaskName, generatedSourceFolders, ideSetupTaskNames,
+ configuration, classesFolder, javaResourceFolder, sourceProvider);
+ }
+
+ public void registerMultiFlavorSourceProvider(
+ @NonNull String name,
+ @NonNull String flavorName,
+ @NonNull SourceProvider sourceProvider) {
+ extraModelInfo.registerMultiFlavorSourceProvider(name, flavorName, sourceProvider);
+ }
+
+ @NonNull
+ public SourceProvider wrapJavaSourceSet(@NonNull SourceSet sourceSet) {
+ return new SourceSetSourceProviderWrapper(sourceSet);
+ }
+
+ /**
+ * <strong>Required.</strong> Compile SDK version.
+ *
+ * <p>Your code will be compiled against the android.jar from this API level. You should
+ * generally use the most up-to-date SDK version here. Use the Lint tool to make sure you don't
+ * use APIs not available in earlier platform version without checking.
+ *
+ * <p>Setter can be called with a string like "android-21" or a number.
+ *
+ * <p>Value assigned to this property is parsed and stored in a normalized form, so reading it
+ * back may give a slightly different string.
+ */
+ @Override
+ public String getCompileSdkVersion() {
+ return target;
+ }
+
+ @Override
+ public FullRevision getBuildToolsRevision() {
+ return buildToolsRevision;
+ }
+
+ @Override
+ public Collection<LibraryRequest> getLibraryRequests() {
+ return libraryRequests;
+ }
+
+ public File getSdkDirectory() {
+ return sdkHandler.getSdkFolder();
+ }
+
+ public File getNdkDirectory() {
+ return sdkHandler.getNdkFolder();
+ }
+
+ public List<File> getBootClasspath() {
+ ensureTargetSetup();
+ return androidBuilder.getBootClasspath();
+ }
+
+ public File getAdbExe() {
+ return sdkHandler.getSdkInfo().getAdb();
+ }
+
+ public File getDefaultProguardFile(String name) {
+ File sdkDir = sdkHandler.getAndCheckSdkFolder();
+ return new File(sdkDir,
+ SdkConstants.FD_TOOLS + File.separatorChar
+ + SdkConstants.FD_PROGUARD + File.separatorChar
+ + name);
+ }
+
+ // ---------------
+ // TEMP for compatibility
+
+ // by default, we do not generate pure splits
+ boolean generatePureSplits = false;
+
+ public void generatePureSplits(boolean flag) {
+ if (flag) {
+ logger.warn("Pure splits are not supported by PlayStore yet.");
+ }
+ this.generatePureSplits = flag;
+ }
+
+ private boolean enforceUniquePackageName = true;
+
+ public void enforceUniquePackageName(boolean value) {
+ if (!value) {
+ LoggingUtil.displayDeprecationWarning(logger, project, "Support for libraries with same package name is deprecated and will be removed in a future release.");
+ }
+ enforceUniquePackageName = value;
+ }
+
+ public void setEnforceUniquePackageName(boolean value) {
+ enforceUniquePackageName(value);
+ }
+
+ @Override
+ public boolean getEnforceUniquePackageName() {
+ return enforceUniquePackageName;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public CoreProductFlavor getDefaultConfig() {
+ return defaultConfig;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public AaptOptions getAaptOptions() {
+ return aaptOptions;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public CompileOptions getCompileOptions() {
+ return compileOptions;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public DexOptions getDexOptions() {
+ return dexOptions;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public JacocoExtension getJacoco() {
+ return jacoco;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public LintOptions getLintOptions() {
+ return lintOptions;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PackagingOptions getPackagingOptions() {
+ return packagingOptions;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Splits getSplits() {
+ return splits;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public TestOptions getTestOptions() {
+ return testOptions;
+ }
+
+ private void ensureTargetSetup() {
+ // check if the target has been set.
+ TargetInfo targetInfo = androidBuilder.getTargetInfo();
+ if (targetInfo == null) {
+ sdkHandler.initTarget(
+ getCompileSdkVersion(),
+ buildToolsRevision,
+ libraryRequests,
+ androidBuilder);
+ }
+ }
+
+ // For compatibility with LibraryExtension.
+ @Override
+ public Boolean getPackageBuildConfig() {
+ throw new GradleException("packageBuildConfig is not supported.");
+ }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy b/build-system/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy
deleted file mode 100755
index b2a21b3..0000000
--- a/build-system/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy
+++ /dev/null
@@ -1,461 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.build.gradle
-import com.android.annotations.Nullable
-import com.android.annotations.VisibleForTesting
-import com.android.build.gradle.internal.BadPluginException
-import com.android.build.gradle.internal.DependencyManager
-import com.android.build.gradle.internal.ExtraModelInfo
-import com.android.build.gradle.internal.LibraryCache
-import com.android.build.gradle.internal.LoggerWrapper
-import com.android.build.gradle.internal.SdkHandler
-import com.android.build.gradle.internal.TaskContainerAdaptor
-import com.android.build.gradle.internal.TaskManager
-import com.android.build.gradle.internal.VariantManager
-import com.android.build.gradle.internal.coverage.JacocoPlugin
-import com.android.build.gradle.internal.dsl.BuildType
-import com.android.build.gradle.internal.dsl.BuildTypeFactory
-import com.android.build.gradle.internal.dsl.GroupableProductFlavor
-import com.android.build.gradle.internal.dsl.GroupableProductFlavorFactory
-import com.android.build.gradle.internal.dsl.SigningConfig
-import com.android.build.gradle.internal.dsl.SigningConfigFactory
-import com.android.build.gradle.internal.model.ModelBuilder
-import com.android.build.gradle.internal.process.GradleJavaProcessExecutor
-import com.android.build.gradle.internal.process.GradleProcessExecutor
-import com.android.build.gradle.internal.profile.RecordingBuildListener
-import com.android.build.gradle.internal.profile.SpanRecorders
-import com.android.build.gradle.internal.variant.VariantFactory
-import com.android.build.gradle.tasks.JillTask
-import com.android.build.gradle.tasks.PreDex
-import com.android.builder.Version
-import com.android.builder.core.AndroidBuilder
-import com.android.builder.core.DefaultBuildType
-import com.android.builder.internal.compiler.JackConversionCache
-import com.android.builder.internal.compiler.PreDexCache
-import com.android.builder.profile.ExecutionType
-import com.android.builder.profile.ProcessRecorderFactory
-import com.android.builder.profile.ThreadRecorder
-import com.android.builder.sdk.TargetInfo
-import com.android.ide.common.blame.output.BlameAwareLoggedProcessOutputHandler
-import com.android.ide.common.internal.ExecutorSingleton
-import com.android.utils.ILogger
-import groovy.transform.CompileStatic
-import org.gradle.api.Project
-import org.gradle.api.Task
-import org.gradle.api.artifacts.repositories.MavenArtifactRepository
-import org.gradle.api.execution.TaskExecutionGraph
-import org.gradle.api.internal.project.ProjectInternal
-import org.gradle.api.logging.LogLevel
-import org.gradle.api.plugins.JavaBasePlugin
-import org.gradle.api.plugins.JavaPlugin
-import org.gradle.internal.reflect.Instantiator
-import org.gradle.tooling.BuildException
-import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
-
-import java.security.MessageDigest
-import java.util.jar.Manifest
-import java.util.regex.Pattern
-
-import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-import static com.google.common.base.Preconditions.checkState
-import static java.io.File.separator
-/**
- * Base class for all Android plugins
- */
-@CompileStatic
-public abstract class BasePlugin {
-
- private static final String GRADLE_MIN_VERSION = "2.2"
- public static final Pattern GRADLE_ACCEPTABLE_VERSIONS = Pattern.compile("2\\.[2-9].*")
- private static final String GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY =
- "com.android.build.gradle.overrideVersionCheck"
-
- // default retirement age in days since its inception date for RC or beta versions.
- private static final int DEFAULT_RETIREMENT_AGE_FOR_NON_RELEASE = 40
-
- protected BaseExtension extension
-
- protected VariantManager variantManager
-
- protected TaskManager taskManager
-
- protected Project project
-
- protected SdkHandler sdkHandler
-
- protected AndroidBuilder androidBuilder
-
- protected Instantiator instantiator
-
- private ToolingModelBuilderRegistry registry
-
- private JacocoPlugin jacocoPlugin
-
- private LoggerWrapper loggerWrapper
-
- private ExtraModelInfo extraModelInfo
-
- private String creator
-
- private boolean hasCreatedTasks = false
-
- protected BasePlugin(Instantiator instantiator, ToolingModelBuilderRegistry registry) {
- this.instantiator = instantiator
- this.registry = registry
- creator = "Android Gradle " + Version.ANDROID_GRADLE_PLUGIN_VERSION
- verifyRetirementAge()
-
- ModelBuilder.clearCaches();
- }
-
- /**
- * Verify that this plugin execution is within its public time range.
- */
- private void verifyRetirementAge() {
-
- Manifest manifest;
- URLClassLoader cl = (URLClassLoader) getClass().getClassLoader();
- try {
- URL url = cl.findResource("META-INF/MANIFEST.MF");
- manifest = new Manifest(url.openStream());
- } catch (IOException ignore) {
- return;
- }
-
- String inceptionDateAttr = manifest.mainAttributes.getValue("Inception-Date")
- // when running in unit tests, etc... the manifest entries are absent.
- if (inceptionDateAttr == null) {
- return;
- }
- def items = inceptionDateAttr.split(':')
- GregorianCalendar inceptionDate = new GregorianCalendar(Integer.parseInt(items[0]),
- Integer.parseInt(items[1]), Integer.parseInt(items[2]));
-
- int retirementAge = getRetirementAge(manifest.mainAttributes.getValue("Plugin-Version"))
-
- if (retirementAge == -1) {
- return;
- }
- Calendar now = GregorianCalendar.getInstance()
- int days = now.minus(inceptionDate)
- if (days > retirementAge) {
- // this plugin is too old.
- String dailyOverride = System.getenv("ANDROID_DAILY_OVERRIDE")
- MessageDigest cript = MessageDigest.getInstance("SHA-1")
- cript.reset()
- // encode the day, not the current time.
- cript.update(
- "${now.get(Calendar.YEAR)}:${now.get(Calendar.MONTH)}:${now.get(Calendar.DATE)}"
- .getBytes("utf8"))
- String overrideValue = new BigInteger(1, cript.digest()).toString(16)
- if (dailyOverride == null) {
- String message = """
- Plugin is too old, please update to a more recent version,
- or set ANDROID_DAILY_OVERRIDE environment variable to
- \"${overrideValue}\""""
- System.err.println(message)
- throw new RuntimeException(message)
- } else {
- if (!dailyOverride.equals(overrideValue)) {
- String message = """
- Plugin is too old and ANDROID_DAILY_OVERRIDE value is
- also outdated, please use new value :
- \"${overrideValue}\""""
- System.err.println(message)
- throw new RuntimeException(message)
- }
- }
- }
- }
-
- private static int getRetirementAge(@Nullable String version) {
- if (version == null || version.contains("rc") || version.contains("beta")
- || version.contains("alpha")) {
- return DEFAULT_RETIREMENT_AGE_FOR_NON_RELEASE
- }
- return -1;
- }
-
- protected abstract Class<? extends BaseExtension> getExtensionClass()
- protected abstract VariantFactory getVariantFactory()
- protected abstract TaskManager createTaskManager(
- Project project,
- AndroidBuilder androidBuilder,
- BaseExtension extension,
- SdkHandler sdkHandler,
- DependencyManager dependencyManager,
- ToolingModelBuilderRegistry toolingRegistry)
-
- /**
- * Return whether this plugin creates Android library. Should be overridden if true.
- */
- protected boolean isLibrary() {
- return false;
- }
-
- @VisibleForTesting
- VariantManager getVariantManager() {
- return variantManager
- }
-
- protected ILogger getLogger() {
- if (loggerWrapper == null) {
- loggerWrapper = new LoggerWrapper(project.logger)
- }
-
- return loggerWrapper
- }
-
-
- protected void apply(Project project) {
- this.project = project
- ProcessRecorderFactory.initialize(logger, project.rootProject.
- file("profiler" + System.currentTimeMillis() + ".json"))
- project.gradle.addListener(new RecordingBuildListener(ThreadRecorder.get()));
-
- SpanRecorders.record(project, ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE) {
- configureProject()
- }
-
- SpanRecorders.record(project, ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSTION_CREATION) {
- createExtension()
- }
-
- SpanRecorders.record(project, ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION) {
- createTasks()
- }
- }
-
- protected void configureProject() {
- checkGradleVersion()
- extraModelInfo = new ExtraModelInfo(project)
- sdkHandler = new SdkHandler(project, logger)
- androidBuilder = new AndroidBuilder(
- project == project.rootProject ? project.name : project.path,
- creator,
- new GradleProcessExecutor(project),
- new GradleJavaProcessExecutor(project),
- new BlameAwareLoggedProcessOutputHandler(getLogger(),
- extraModelInfo.getErrorFormatMode()),
- logger,
- verbose)
-
- project.apply plugin: JavaBasePlugin
-
- project.apply plugin: JacocoPlugin
- jacocoPlugin = project.plugins.getPlugin(JacocoPlugin)
-
- project.tasks.getByName("assemble").description =
- "Assembles all variants of all applications and secondary packages."
-
- // call back on execution. This is called after the whole build is done (not
- // after the current project is done).
- // This is will be called for each (android) projects though, so this should support
- // being called 2+ times.
- project.gradle.buildFinished {
- ExecutorSingleton.shutdown()
- sdkHandler.unload()
- SpanRecorders.record(project, ExecutionType.BASE_PLUGIN_BUILD_FINISHED) {
- PreDexCache.getCache().clear(
- project.rootProject.file(
- "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/dex-cache/cache.xml"),
- logger)
- JackConversionCache.getCache().clear(
- project.rootProject.file(
- "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/jack-cache/cache.xml"),
- logger)
- LibraryCache.getCache().unload()
- }
- ProcessRecorderFactory.shutdown();
- }
-
- project.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
- for (Task task : taskGraph.allTasks) {
- if (task instanceof PreDex) {
- PreDexCache.getCache().load(
- project.rootProject.file(
- "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/dex-cache/cache.xml"))
- break;
- } else if (task instanceof JillTask) {
- JackConversionCache.getCache().load(
- project.rootProject.file(
- "${project.rootProject.buildDir}/${FD_INTERMEDIATES}/jack-cache/cache.xml"))
- break;
- }
- }
- }
- }
-
- private void createExtension() {
- def buildTypeContainer = project.container(BuildType,
- new BuildTypeFactory(instantiator, project, project.getLogger()))
- def productFlavorContainer = project.container(GroupableProductFlavor,
- new GroupableProductFlavorFactory(instantiator, project, project.getLogger()))
- def signingConfigContainer = project.container(SigningConfig,
- new SigningConfigFactory(instantiator))
-
- extension = project.extensions.create('android', getExtensionClass(),
- (ProjectInternal) project, instantiator, androidBuilder, sdkHandler,
- buildTypeContainer, productFlavorContainer, signingConfigContainer,
- extraModelInfo, isLibrary())
-
- // create the default mapping configuration.
- project.configurations.create("default-mapping").description = "Configuration for default mapping artifacts."
- project.configurations.create("default-metadata").description = "Metadata for the produced APKs."
-
- DependencyManager dependencyManager = new DependencyManager(project, extraModelInfo)
- taskManager = createTaskManager(
- project,
- androidBuilder,
- extension,
- sdkHandler,
- dependencyManager,
- registry)
-
- VariantFactory variantFactory = getVariantFactory()
- variantManager = new VariantManager(
- project,
- androidBuilder,
- extension,
- variantFactory,
- taskManager,
- instantiator)
-
- // Register a builder for the custom tooling model
- ModelBuilder modelBuilder = new ModelBuilder(
- androidBuilder, variantManager, taskManager, extension, extraModelInfo, isLibrary())
- registry.register(modelBuilder);
-
- // map the whenObjectAdded callbacks on the containers.
- signingConfigContainer.whenObjectAdded { SigningConfig signingConfig ->
- variantManager.addSigningConfig((SigningConfig) signingConfig)
- }
-
- buildTypeContainer.whenObjectAdded { DefaultBuildType buildType ->
- variantManager.addBuildType((BuildType) buildType)
- }
-
- productFlavorContainer.whenObjectAdded { GroupableProductFlavor productFlavor ->
- variantManager.addProductFlavor(productFlavor)
- }
-
- // map whenObjectRemoved on the containers to throw an exception.
- signingConfigContainer.whenObjectRemoved {
- throw new UnsupportedOperationException("Removing signingConfigs is not supported.")
- }
- buildTypeContainer.whenObjectRemoved {
- throw new UnsupportedOperationException("Removing build types is not supported.")
- }
- productFlavorContainer.whenObjectRemoved {
- throw new UnsupportedOperationException("Removing product flavors is not supported.")
- }
-
- // create default Objects, signingConfig first as its used by the BuildTypes.
- variantFactory.createDefaultComponents(buildTypeContainer, productFlavorContainer, signingConfigContainer)
- }
-
- private void createTasks() {
- SpanRecorders.record(project, ExecutionType.TASK_MANAGER_CREATE_TASKS) {
- taskManager.createTasksBeforeEvaluate(new TaskContainerAdaptor(project.getTasks()))
- }
-
- project.afterEvaluate {
- ensureTargetSetup()
- SpanRecorders.record(project, ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS) {
- createAndroidTasks(false)
- }
- }
- }
-
- private void checkGradleVersion() {
- if (!GRADLE_ACCEPTABLE_VERSIONS.matcher(project.getGradle().gradleVersion).matches()) {
- boolean allowNonMatching = Boolean.getBoolean(GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY)
- File file = new File("gradle" + separator + "wrapper" + separator +
- "gradle-wrapper.properties");
- String errorMessage = String.format(
- "Gradle version %s is required. Current version is %s. " +
- "If using the gradle wrapper, try editing the distributionUrl in %s " +
- "to gradle-%s-all.zip",
- GRADLE_MIN_VERSION, project.getGradle().gradleVersion, file.getAbsolutePath(),
- GRADLE_MIN_VERSION);
- if (allowNonMatching) {
- getLogger().warning(errorMessage)
- getLogger().warning("As %s is set, continuing anyways.",
- GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY)
- } else {
- throw new BuildException(errorMessage, null)
- }
- }
- }
-
- @VisibleForTesting
- final void createAndroidTasks(boolean force) {
- // Make sure unit tests set the required fields.
- checkState(extension.getBuildToolsRevision() != null, "buildToolsVersion is not specified.")
- checkState(extension.getCompileSdkVersion() != null, "compileSdkVersion is not specified.")
-
- // get current plugins and look for the default Java plugin.
- if (project.plugins.hasPlugin(JavaPlugin.class)) {
- throw new BadPluginException(
- "The 'java' plugin has been applied, but it is not compatible with the Android plugins.")
- }
-
- // don't do anything if the project was not initialized.
- // Unless TEST_SDK_DIR is set in which case this is unit tests and we don't return.
- // This is because project don't get evaluated in the unit test setup.
- // See AppPluginDslTest
- if (!force
- && (!project.state.executed || project.state.failure != null)
- && SdkHandler.sTestSdkFolder == null) {
- return
- }
-
- if (hasCreatedTasks) {
- return
- }
- hasCreatedTasks = true
-
- extension.disableWrite()
-
- // setup SDK repositories.
- for (File file : sdkHandler.sdkLoader.repositories) {
- project.repositories.maven { MavenArtifactRepository repo ->
- repo.url = file.toURI()
- }
- }
-
- taskManager.createMockableJarTask()
- SpanRecorders.record(project, ExecutionType.VARIANT_MANAGER_CREATE_ANDROID_TASKS) {
- variantManager.createAndroidTasks()
- }
- }
-
- private boolean isVerbose() {
- return project.logger.isEnabled(LogLevel.INFO)
- }
-
- private void ensureTargetSetup() {
- // check if the target has been set.
- TargetInfo targetInfo = androidBuilder.getTargetInfo()
- if (targetInfo == null) {
- sdkHandler.initTarget(
- extension.getCompileSdkVersion(),
- extension.buildToolsRevision,
- androidBuilder)
- }
- }
-}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.java
new file mode 100755
index 0000000..85bb8c6
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.java
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle;
+
+import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
+import static com.google.common.base.Preconditions.checkState;
+import static java.io.File.separator;
+
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
+import com.android.build.gradle.internal.ApiObjectFactory;
+import com.android.build.gradle.internal.BadPluginException;
+import com.android.build.gradle.internal.DependencyManager;
+import com.android.build.gradle.internal.ExecutionConfigurationUtil;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.LibraryCache;
+import com.android.build.gradle.internal.LoggerWrapper;
+import com.android.build.gradle.internal.NativeLibraryFactoryImpl;
+import com.android.build.gradle.internal.NdkHandler;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.TaskContainerAdaptor;
+import com.android.build.gradle.internal.TaskManager;
+import com.android.build.gradle.internal.VariantManager;
+import com.android.build.gradle.internal.coverage.JacocoPlugin;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.BuildTypeFactory;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.ProductFlavorFactory;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.build.gradle.internal.dsl.SigningConfigFactory;
+import com.android.build.gradle.internal.model.ModelBuilder;
+import com.android.build.gradle.internal.process.GradleJavaProcessExecutor;
+import com.android.build.gradle.internal.process.GradleProcessExecutor;
+import com.android.build.gradle.internal.profile.RecordingBuildListener;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.VariantFactory;
+import com.android.build.gradle.tasks.JillTask;
+import com.android.build.gradle.tasks.PreDex;
+import com.android.builder.Version;
+import com.android.builder.core.AndroidBuilder;
+import com.android.builder.core.BuilderConstants;
+import com.android.builder.internal.compiler.JackConversionCache;
+import com.android.builder.internal.compiler.PreDexCache;
+import com.android.builder.profile.ExecutionType;
+import com.android.builder.profile.ProcessRecorderFactory;
+import com.android.builder.profile.Recorder;
+import com.android.builder.profile.ThreadRecorder;
+import com.android.builder.sdk.TargetInfo;
+import com.android.ide.common.internal.ExecutorSingleton;
+import com.android.utils.ILogger;
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import org.gradle.BuildListener;
+import org.gradle.BuildResult;
+import org.gradle.api.Action;
+import org.gradle.api.GradleException;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
+import org.gradle.api.execution.TaskExecutionGraph;
+import org.gradle.api.execution.TaskExecutionGraphListener;
+import org.gradle.api.initialization.Settings;
+import org.gradle.api.invocation.Gradle;
+import org.gradle.api.logging.LogLevel;
+import org.gradle.api.plugins.JavaBasePlugin;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.tasks.StopExecutionException;
+import org.gradle.internal.reflect.Instantiator;
+import org.gradle.tooling.BuildException;
+import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.jar.Manifest;
+import java.util.regex.Pattern;
+
+/**
+ * Base class for all Android plugins
+ */
+public abstract class BasePlugin {
+
+ private static final String GRADLE_MIN_VERSION = "2.2";
+ public static final Pattern GRADLE_ACCEPTABLE_VERSIONS = Pattern.compile("2\\.[2-9].*");
+ private static final String GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY =
+ "com.android.build.gradle.overrideVersionCheck";
+ private static final String SKIP_PATH_CHECK_PROPERTY =
+ "com.android.build.gradle.overridePathCheck";
+ /** default retirement age in days since its inception date for RC or beta versions. */
+ private static final int DEFAULT_RETIREMENT_AGE_FOR_NON_RELEASE_IN_DAYS = 40;
+
+
+ protected BaseExtension extension;
+
+ protected VariantManager variantManager;
+
+ protected TaskManager taskManager;
+
+ protected Project project;
+
+ protected SdkHandler sdkHandler;
+
+ private NdkHandler ndkHandler;
+
+ protected AndroidBuilder androidBuilder;
+
+ protected Instantiator instantiator;
+
+ protected VariantFactory variantFactory;
+
+ private ToolingModelBuilderRegistry registry;
+
+ private JacocoPlugin jacocoPlugin;
+
+ private LoggerWrapper loggerWrapper;
+
+ private ExtraModelInfo extraModelInfo;
+
+ private String creator;
+
+ private boolean hasCreatedTasks = false;
+
+ protected BasePlugin(Instantiator instantiator, ToolingModelBuilderRegistry registry) {
+ this.instantiator = instantiator;
+ this.registry = registry;
+ creator = "Android Gradle " + Version.ANDROID_GRADLE_PLUGIN_VERSION;
+ verifyRetirementAge();
+
+ ModelBuilder.clearCaches();
+ }
+
+ /**
+ * Verify that this plugin execution is within its public time range.
+ */
+ private void verifyRetirementAge() {
+
+ Manifest manifest;
+ URLClassLoader cl = (URLClassLoader) getClass().getClassLoader();
+ try {
+ URL url = cl.findResource("META-INF/MANIFEST.MF");
+ manifest = new Manifest(url.openStream());
+ } catch (IOException ignore) {
+ return;
+ }
+
+ String inceptionDateAttr = manifest.getMainAttributes().getValue("Inception-Date");
+ // when running in unit tests, etc... the manifest entries are absent.
+ if (inceptionDateAttr == null) {
+ return;
+ }
+ List<String> items = ImmutableList.copyOf(Splitter.on(':').split(inceptionDateAttr));
+ GregorianCalendar inceptionDate = new GregorianCalendar(Integer.parseInt(items.get(0)),
+ Integer.parseInt(items.get(1)), Integer.parseInt(items.get(2)));
+
+ int retirementAgeInDays =
+ getRetirementAgeInDays(manifest.getMainAttributes().getValue("Plugin-Version"));
+
+ if (retirementAgeInDays == -1) {
+ return;
+ }
+ Calendar now = GregorianCalendar.getInstance();
+ long nowTimestamp = now.getTimeInMillis();
+ long inceptionTimestamp = inceptionDate.getTimeInMillis();
+ long days = TimeUnit.DAYS.convert(nowTimestamp - inceptionTimestamp, TimeUnit.MILLISECONDS);
+ if (days > retirementAgeInDays) {
+ // this plugin is too old.
+ String dailyOverride = System.getenv("ANDROID_DAILY_OVERRIDE");
+ final MessageDigest crypt;
+ try {
+ crypt = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ return;
+ }
+ crypt.reset();
+ // encode the day, not the current time.
+ try {
+ crypt.update(String.format("%1$s:%2$s:%3$s",
+ now.get(Calendar.YEAR),
+ now.get(Calendar.MONTH),
+ now.get(Calendar.DATE)).getBytes("utf8"));
+ } catch (UnsupportedEncodingException e) {
+ return;
+ }
+ String overrideValue = new BigInteger(1, crypt.digest()).toString(16);
+ if (dailyOverride == null) {
+ String message = "Plugin is too old, please update to a more recent version, or " +
+ "set ANDROID_DAILY_OVERRIDE environment variable to \"" + overrideValue + '"';
+ System.err.println(message);
+ throw new RuntimeException(message);
+ } else {
+ if (!dailyOverride.equals(overrideValue)) {
+ String message = "Plugin is too old and ANDROID_DAILY_OVERRIDE value is " +
+ "also outdated, please use new value :\"" + overrideValue + '"';
+ System.err.println(message);
+ throw new RuntimeException(message);
+ }
+ }
+ }
+ }
+
+ private static int getRetirementAgeInDays(@Nullable String version) {
+ if (version == null || version.contains("rc") || version.contains("beta")
+ || version.contains("alpha")) {
+ return DEFAULT_RETIREMENT_AGE_FOR_NON_RELEASE_IN_DAYS;
+ }
+ return -1;
+ }
+
+ protected abstract Class<? extends BaseExtension> getExtensionClass();
+ protected abstract VariantFactory createVariantFactory();
+ protected abstract TaskManager createTaskManager(
+ Project project,
+ AndroidBuilder androidBuilder,
+ AndroidConfig extension,
+ SdkHandler sdkHandler,
+ DependencyManager dependencyManager,
+ ToolingModelBuilderRegistry toolingRegistry);
+
+ /**
+ * Return whether this plugin creates Android library. Should be overridden if true.
+ */
+ protected boolean isLibrary() {
+ return false;
+ }
+
+ @VisibleForTesting
+ VariantManager getVariantManager() {
+ return variantManager;
+ }
+
+ protected ILogger getLogger() {
+ if (loggerWrapper == null) {
+ loggerWrapper = new LoggerWrapper(project.getLogger());
+ }
+
+ return loggerWrapper;
+ }
+
+
+ protected void apply(Project project) throws IOException {
+ this.project = project;
+
+ ExecutionConfigurationUtil.setThreadPoolSize(project);
+ checkPathForErrors();
+ checkModulesForErrors();
+
+ List<Recorder.Property> propertyList = Lists.newArrayList(
+ new Recorder.Property("plugin_version", Version.ANDROID_GRADLE_PLUGIN_VERSION),
+ new Recorder.Property("next_gen_plugin", "false"),
+ new Recorder.Property("gradle_version", project.getGradle().getGradleVersion())
+ );
+ String benchmarkName = AndroidGradleOptions.getBenchmarkName(project);
+ if (benchmarkName != null) {
+ propertyList.add(new Recorder.Property("benchmark_name", benchmarkName));
+ }
+ String benchmarkMode = AndroidGradleOptions.getBenchmarkMode(project);
+ if (benchmarkMode != null) {
+ propertyList.add(new Recorder.Property("benchmark_mode", benchmarkMode));
+ }
+
+ ProcessRecorderFactory.initialize(
+ getLogger(),
+ project.getRootProject().file("profiler" + System.currentTimeMillis() + ".json"),
+ propertyList);
+ project.getGradle().addListener(new RecordingBuildListener(ThreadRecorder.get()));
+
+
+ ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ configureProject();
+ return null;
+ }
+ }, new Recorder.Property("project", project.getName()));
+
+ ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSTION_CREATION,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ createExtension();
+ return null;
+ }
+ }, new Recorder.Property("project", project.getName()));
+
+ ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ createTasks();
+ return null;
+ }
+ }, new Recorder.Property("project", project.getName()));
+ }
+
+ protected void configureProject() {
+ checkGradleVersion();
+ extraModelInfo = new ExtraModelInfo(project, isLibrary());
+ sdkHandler = new SdkHandler(project, getLogger());
+ androidBuilder = new AndroidBuilder(
+ project == project.getRootProject() ? project.getName() : project.getPath(),
+ creator,
+ new GradleProcessExecutor(project),
+ new GradleJavaProcessExecutor(project),
+ extraModelInfo,
+ getLogger(),
+ isVerbose());
+
+ project.getPlugins().apply(JavaBasePlugin.class);
+
+ jacocoPlugin = project.getPlugins().apply(JacocoPlugin.class);
+
+ project.getTasks().getByName("assemble").setDescription(
+ "Assembles all variants of all applications and secondary packages.");
+
+ // call back on execution. This is called after the whole build is done (not
+ // after the current project is done).
+ // This is will be called for each (android) projects though, so this should support
+ // being called 2+ times.
+ project.getGradle().addBuildListener(new BuildListener() {
+ @Override
+ public void buildStarted(Gradle gradle) { }
+
+ @Override
+ public void settingsEvaluated(Settings settings) { }
+
+ @Override
+ public void projectsLoaded(Gradle gradle) { }
+
+ @Override
+ public void projectsEvaluated(Gradle gradle) { }
+
+ @Override
+ public void buildFinished(BuildResult buildResult) {
+ ExecutorSingleton.shutdown();
+ sdkHandler.unload();
+ ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_BUILD_FINISHED,
+ new Recorder.Block() {
+ @Override
+ public Void call() throws Exception {
+ PreDexCache.getCache().clear(
+ new File(project.getRootProject().getBuildDir(),
+ FD_INTERMEDIATES + "/dex-cache/cache.xml"),
+ getLogger());
+ JackConversionCache.getCache().clear(
+ new File(project.getRootProject().getBuildDir(),
+ FD_INTERMEDIATES + "/jack-cache/cache.xml"),
+ getLogger());
+ LibraryCache.getCache().unload();
+ return null;
+ }
+ }, new Recorder.Property("project", project.getName()));
+
+ try {
+ ProcessRecorderFactory.shutdown();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ project.getGradle().getTaskGraph().addTaskExecutionGraphListener(
+ new TaskExecutionGraphListener() {
+ @Override
+ public void graphPopulated(TaskExecutionGraph taskGraph) {
+ for (Task task : taskGraph.getAllTasks()) {
+ if (task instanceof PreDex) {
+ PreDexCache.getCache().load(
+ new File(project.getRootProject().getBuildDir(),
+ FD_INTERMEDIATES + "/dex-cache/cache.xml"));
+ break;
+ } else if (task instanceof JillTask) {
+ JackConversionCache.getCache().load(
+ new File(project.getRootProject().getBuildDir(),
+ FD_INTERMEDIATES + "/jack-cache/cache.xml"));
+ break;
+ }
+ }
+ }
+ });
+ }
+
+
+ private void createExtension() {
+ final NamedDomainObjectContainer<BuildType> buildTypeContainer = project.container(
+ BuildType.class,
+ new BuildTypeFactory(instantiator, project, project.getLogger()));
+ final NamedDomainObjectContainer<ProductFlavor> productFlavorContainer = project.container(
+ ProductFlavor.class,
+ new ProductFlavorFactory(instantiator, project, project.getLogger()));
+ final NamedDomainObjectContainer<SigningConfig> signingConfigContainer = project.container(
+ SigningConfig.class,
+ new SigningConfigFactory(instantiator));
+
+ extension = project.getExtensions().create("android", getExtensionClass(),
+ project, instantiator, androidBuilder, sdkHandler,
+ buildTypeContainer, productFlavorContainer, signingConfigContainer,
+ extraModelInfo, isLibrary());
+
+ // create the default mapping configuration.
+ project.getConfigurations().create("default-mapping")
+ .setDescription("Configuration for default mapping artifacts.");
+ project.getConfigurations().create("default-metadata")
+ .setDescription("Metadata for the produced APKs.");
+
+ DependencyManager dependencyManager = new DependencyManager(project, extraModelInfo);
+ taskManager = createTaskManager(
+ project,
+ androidBuilder,
+ extension,
+ sdkHandler,
+ dependencyManager,
+ registry);
+
+ variantFactory = createVariantFactory();
+ variantManager = new VariantManager(
+ project,
+ androidBuilder,
+ extension,
+ variantFactory,
+ taskManager,
+ instantiator);
+
+ ndkHandler = new NdkHandler(
+ project.getRootDir(),
+ null, /* compileSkdVersion, this will be set in afterEvaluate */
+ "gcc",
+ "" /*toolchainVersion*/);
+
+ // Register a builder for the custom tooling model
+ ModelBuilder modelBuilder = new ModelBuilder(
+ androidBuilder,
+ variantManager,
+ taskManager,
+ extension,
+ extraModelInfo,
+ ndkHandler,
+ new NativeLibraryFactoryImpl(ndkHandler),
+ isLibrary());
+ registry.register(modelBuilder);
+
+ // map the whenObjectAdded callbacks on the containers.
+ signingConfigContainer.whenObjectAdded(new Action<SigningConfig>() {
+ @Override
+ public void execute(SigningConfig signingConfig) {
+ variantManager.addSigningConfig(signingConfig);
+ }
+ });
+
+
+ buildTypeContainer.whenObjectAdded(new Action<BuildType>() {
+ @Override
+ public void execute(BuildType buildType) {
+ SigningConfig signingConfig = signingConfigContainer.findByName(BuilderConstants.DEBUG);
+ buildType.init(signingConfig);
+ variantManager.addBuildType(buildType);
+ }
+ });
+
+ productFlavorContainer.whenObjectAdded(new Action<ProductFlavor>() {
+ @Override
+ public void execute(ProductFlavor productFlavor) {
+ variantManager.addProductFlavor(productFlavor);
+ }
+ });
+
+ // map whenObjectRemoved on the containers to throw an exception.
+ signingConfigContainer.whenObjectRemoved(
+ new UnsupportedAction("Removing signingConfigs is not supported."));
+ buildTypeContainer.whenObjectRemoved(
+ new UnsupportedAction("Removing build types is not supported."));
+ productFlavorContainer.whenObjectRemoved(
+ new UnsupportedAction("Removing product flavors is not supported."));
+
+ // create default Objects, signingConfig first as its used by the BuildTypes.
+ variantFactory.createDefaultComponents(
+ buildTypeContainer, productFlavorContainer, signingConfigContainer);
+ }
+
+ private static class UnsupportedAction implements Action<Object> {
+
+ private final String message;
+
+ UnsupportedAction(String message) {
+ this.message = message;
+ }
+
+ @Override
+ public void execute(Object o) {
+ throw new UnsupportedOperationException(message);
+ }
+ }
+
+ private void createTasks() {
+ ThreadRecorder.get().record(ExecutionType.TASK_MANAGER_CREATE_TASKS,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ taskManager.createTasksBeforeEvaluate(
+ new TaskContainerAdaptor(project.getTasks()));
+ return null;
+ }
+ },
+ new Recorder.Property("project", project.getName()));
+
+ project.afterEvaluate(new Action<Project>() {
+ @Override
+ public void execute(Project project) {
+ ThreadRecorder.get().record(ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ createAndroidTasks(false);
+ return null;
+ }
+ },
+ new Recorder.Property("project", project.getName()));
+ }
+ });
+ }
+
+ private void checkGradleVersion() {
+ if (!GRADLE_ACCEPTABLE_VERSIONS.matcher(project.getGradle().getGradleVersion()).matches()) {
+ boolean allowNonMatching = Boolean.getBoolean(GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY);
+ File file = new File("gradle" + separator + "wrapper" + separator +
+ "gradle-wrapper.properties");
+ String errorMessage = String.format(
+ "Gradle version %s is required. Current version is %s. " +
+ "If using the gradle wrapper, try editing the distributionUrl in %s " +
+ "to gradle-%s-all.zip",
+ GRADLE_MIN_VERSION, project.getGradle().getGradleVersion(), file.getAbsolutePath(),
+ GRADLE_MIN_VERSION);
+ if (allowNonMatching) {
+ getLogger().warning(errorMessage);
+ getLogger().warning("As %s is set, continuing anyways.",
+ GRADLE_VERSION_CHECK_OVERRIDE_PROPERTY);
+ } else {
+ throw new BuildException(errorMessage, null);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ final void createAndroidTasks(boolean force) {
+ // Make sure unit tests set the required fields.
+ checkState(extension.getBuildToolsRevision() != null, "buildToolsVersion is not specified.");
+ checkState(extension.getCompileSdkVersion() != null, "compileSdkVersion is not specified.");
+
+ ndkHandler.setCompileSdkVersion(extension.getCompileSdkVersion());
+
+ // get current plugins and look for the default Java plugin.
+ if (project.getPlugins().hasPlugin(JavaPlugin.class)) {
+ throw new BadPluginException(
+ "The 'java' plugin has been applied, but it is not compatible with the Android plugins.");
+ }
+
+ ensureTargetSetup();
+
+ // don't do anything if the project was not initialized.
+ // Unless TEST_SDK_DIR is set in which case this is unit tests and we don't return.
+ // This is because project don't get evaluated in the unit test setup.
+ // See AppPluginDslTest
+ if (!force
+ && (!project.getState().getExecuted() || project.getState().getFailure()!= null)
+ && SdkHandler.sTestSdkFolder == null) {
+ return;
+ }
+
+ if (hasCreatedTasks) {
+ return;
+ }
+ hasCreatedTasks = true;
+
+ extension.disableWrite();
+
+ ThreadRecorder.get().record(
+ ExecutionType.GENERAL_CONFIG,
+ Recorder.EmptyBlock,
+ new Recorder.Property("build_tools_version",
+ extension.getBuildToolsRevision().toString()));
+
+ // setup SDK repositories.
+ for (final File file : sdkHandler.getSdkLoader().getRepositories()) {
+ project.getRepositories().maven(new Action<MavenArtifactRepository>() {
+ @Override
+ public void execute(MavenArtifactRepository mavenArtifactRepository) {
+ mavenArtifactRepository.setUrl(file.toURI());
+ }
+ });
+ }
+
+ taskManager.createMockableJarTask();
+ ThreadRecorder.get().record(ExecutionType.VARIANT_MANAGER_CREATE_ANDROID_TASKS,
+ new Recorder.Block<Void>() {
+ @Override
+ public Void call() throws Exception {
+ variantManager.createAndroidTasks();
+ ApiObjectFactory apiObjectFactory = new ApiObjectFactory(
+ androidBuilder, extension, variantFactory, instantiator);
+ for (BaseVariantData variantData : variantManager.getVariantDataList()) {
+ apiObjectFactory.create(variantData);
+ }
+ return null;
+ }
+ }, new Recorder.Property("project", project.getName()));
+ }
+
+ private boolean isVerbose() {
+ return project.getLogger().isEnabled(LogLevel.INFO);
+ }
+
+ private void ensureTargetSetup() {
+ // check if the target has been set.
+ TargetInfo targetInfo = androidBuilder.getTargetInfo();
+ if (targetInfo == null) {
+ if (extension.getCompileOptions() == null) {
+ throw new GradleException("Calling getBootClasspath before compileSdkVersion");
+ }
+
+ sdkHandler.initTarget(
+ extension.getCompileSdkVersion(),
+ extension.getBuildToolsRevision(),
+ extension.getLibraryRequests(),
+ androidBuilder);
+ }
+ }
+
+ /**
+ * Check the sub-projects structure :
+ * So far, checks that 2 modules do not have the same identification (group+name).
+ */
+ private void checkModulesForErrors() {
+ Project rootProject = project.getRootProject();
+ Map<String, Project> subProjectsById = new HashMap<String, Project>();
+ for (Project subProject : rootProject.getAllprojects()) {
+ String id = subProject.getGroup().toString() + ":" + subProject.getName();
+ if (subProjectsById.containsKey(id)) {
+ String message = String.format(
+ "Your project contains 2 or more modules with the same " +
+ "identification %1$s\n" +
+ "at \"%2$s\" and \"%3$s\".\n" +
+ "You must use different identification (either name or group) for " +
+ "each modules.",
+ id,
+ subProjectsById.get(id).getPath(),
+ subProject.getPath() );
+ throw new StopExecutionException(message);
+ } else {
+ subProjectsById.put(id, subProject);
+ }
+ }
+ }
+
+ private void checkPathForErrors() {
+ // See if the user disabled the check:
+ if (Boolean.getBoolean(SKIP_PATH_CHECK_PROPERTY)) {
+ return;
+ }
+
+ if (project.hasProperty(SKIP_PATH_CHECK_PROPERTY)
+ && project.property(SKIP_PATH_CHECK_PROPERTY) instanceof String
+ && Boolean.valueOf((String) project.property(SKIP_PATH_CHECK_PROPERTY))) {
+ return;
+ }
+
+ // See if we're on Windows:
+ if (!System.getProperty("os.name").toLowerCase().contains("windows")) {
+ return;
+ }
+
+ // See if the path contains non-ASCII characters.
+ if (CharMatcher.ASCII.matchesAllOf(project.getRootDir().getAbsolutePath())) {
+ return;
+ }
+
+ String message = "Your project path contains non-ASCII characters. This will most likely " +
+ "cause the build to fail on Windows. Please move your project to a different " +
+ "directory. See http://b.android.com/95744 for details. " +
+ "This warning can be disabled by using the command line flag -D" +
+ SKIP_PATH_CHECK_PROPERTY + "=true, or adding the line " +
+ SKIP_PATH_CHECK_PROPERTY + "=true' to gradle.properties file " +
+ "in the project directory.";
+
+ throw new StopExecutionException(message);
+ }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/LibraryExtension.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/LibraryExtension.java
new file mode 100644
index 0000000..bb48a2d
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/LibraryExtension.java
@@ -0,0 +1,73 @@
+package com.android.build.gradle;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.api.BaseVariant;
+import com.android.build.gradle.api.LibraryVariant;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.LoggingUtil;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.builder.core.AndroidBuilder;
+
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.internal.reflect.Instantiator;
+
+/**
+ * 'android' extension for 'com.android.library' project.
+ */
+public class LibraryExtension extends TestedExtension {
+
+ private final DefaultDomainObjectSet<LibraryVariant> libraryVariantList
+ = new DefaultDomainObjectSet<LibraryVariant>(LibraryVariant.class);
+
+ private boolean packageBuildConfig = true;
+
+ public LibraryExtension(@NonNull ProjectInternal project, @NonNull Instantiator instantiator,
+ @NonNull AndroidBuilder androidBuilder, @NonNull SdkHandler sdkHandler,
+ @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
+ @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
+ @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
+ @NonNull ExtraModelInfo extraModelInfo, boolean isLibrary) {
+ super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
+ signingConfigs, extraModelInfo, isLibrary);
+ }
+
+ /**
+ * Returns the list of library variants. Since the collections is built after evaluation, it
+ * should be used with Gradle's <code>all</code> iterator to process future items.
+ */
+ public DefaultDomainObjectSet<LibraryVariant> getLibraryVariants() {
+ return libraryVariantList;
+ }
+
+ @Override
+ public void addVariant(BaseVariant variant) {
+ libraryVariantList.add((LibraryVariant) variant);
+ }
+
+ // ---------------
+ // TEMP for compatibility
+ // STOPSHIP Remove in 1.0
+
+ public void packageBuildConfig(boolean value) {
+ if (!value) {
+ LoggingUtil.displayDeprecationWarning(logger, project,
+ "Support for not packaging BuildConfig is deprecated and will be removed in 1.0");
+ }
+
+ packageBuildConfig = value;
+ }
+
+ public void setPackageBuildConfig(boolean value) {
+ packageBuildConfig(value);
+ }
+
+ @Override
+ public Boolean getPackageBuildConfig() {
+ return packageBuildConfig;
+ }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy b/build-system/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy
index c01ebc9..df69746 100644
--- a/build-system/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/LibraryPlugin.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle
-import com.android.build.gradle.internal.ApplicationTaskManager
import com.android.build.gradle.internal.DependencyManager
import com.android.build.gradle.internal.LibraryTaskManager
import com.android.build.gradle.internal.SdkHandler
@@ -26,7 +25,6 @@
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
-import org.gradle.api.tasks.TaskContainer
import org.gradle.internal.reflect.Instantiator
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
@@ -54,7 +52,7 @@
}
@Override
- protected VariantFactory getVariantFactory() {
+ protected VariantFactory createVariantFactory() {
return new LibraryVariantFactory(
instantiator,
androidBuilder,
@@ -70,7 +68,7 @@
protected TaskManager createTaskManager(
Project project,
AndroidBuilder androidBuilder,
- BaseExtension extension,
+ AndroidConfig extension,
SdkHandler sdkHandler,
DependencyManager dependencyManager,
ToolingModelBuilderRegistry toolingRegistry) {
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/TestExtension.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/TestExtension.java
new file mode 100644
index 0000000..d17a3ca
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/TestExtension.java
@@ -0,0 +1,88 @@
+package com.android.build.gradle;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.api.ApplicationVariant;
+import com.android.build.gradle.api.BaseVariant;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.builder.core.AndroidBuilder;
+
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.internal.reflect.Instantiator;
+
+/**
+ * 'android' extension for 'com.android.test' project.
+ */
+public class TestExtension extends BaseExtension implements TestAndroidConfig {
+
+ private final DefaultDomainObjectSet<ApplicationVariant> applicationVariantList
+ = new DefaultDomainObjectSet<ApplicationVariant>(ApplicationVariant.class);
+
+ private String targetProjectPath = null;
+
+ private String targetVariant = "debug";
+
+ public TestExtension(@NonNull ProjectInternal project, @NonNull Instantiator instantiator,
+ @NonNull AndroidBuilder androidBuilder, @NonNull SdkHandler sdkHandler,
+ @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
+ @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
+ @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
+ @NonNull ExtraModelInfo extraModelInfo, boolean isLibrary) {
+ super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
+ signingConfigs, extraModelInfo, isLibrary);
+ }
+
+ /**
+ * Returns the list of Application variants. Since the collections is built after evaluation, it
+ * should be used with Gradle's <code>all</code> iterator to process future items.
+ */
+ public DefaultDomainObjectSet<ApplicationVariant> getApplicationVariants() {
+ return applicationVariantList;
+ }
+
+ @Override
+ public void addVariant(BaseVariant variant) {
+ applicationVariantList.add((ApplicationVariant) variant);
+ }
+
+ /**
+ * Returns the Gradle path of the project that this test project tests.
+ */
+ @Override
+ public String getTargetProjectPath() {
+ return targetProjectPath;
+ }
+
+ public void setTargetProjectPath(String targetProjectPath) {
+ checkWritability();
+ this.targetProjectPath = targetProjectPath;
+ }
+
+ public void targetProjectPath(String targetProjectPath) {
+ setTargetProjectPath(targetProjectPath);
+ }
+
+ /**
+ * Returns the variant of the tested project.
+ *
+ * Default is 'debug'
+ */
+ @Override
+ public String getTargetVariant() {
+ return targetVariant;
+ }
+
+ public void setTargetVariant(String targetVariant) {
+ checkWritability();
+ this.targetVariant = targetVariant;
+ }
+
+ public void targetVariant(String targetVariant) {
+ setTargetVariant(targetVariant);
+ }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/TestPlugin.groovy b/build-system/gradle/src/main/groovy/com/android/build/gradle/TestPlugin.groovy
index 35cc6e6..01bbd24 100644
--- a/build-system/gradle/src/main/groovy/com/android/build/gradle/TestPlugin.groovy
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/TestPlugin.groovy
@@ -48,7 +48,7 @@
protected TaskManager createTaskManager(
Project project,
AndroidBuilder androidBuilder,
- BaseExtension extension,
+ AndroidConfig extension,
SdkHandler sdkHandler,
DependencyManager dependencyManager,
ToolingModelBuilderRegistry toolingRegistry) {
@@ -67,7 +67,7 @@
}
@Override
- protected VariantFactory getVariantFactory() {
+ protected VariantFactory createVariantFactory() {
return new TestVariantFactory(instantiator, androidBuilder, extension)
}
}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/TestedExtension.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/TestedExtension.java
new file mode 100644
index 0000000..7cdb20b
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/TestedExtension.java
@@ -0,0 +1,90 @@
+package com.android.build.gradle;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.api.TestVariant;
+import com.android.build.gradle.api.UnitTestVariant;
+import com.android.build.gradle.internal.ExtraModelInfo;
+import com.android.build.gradle.internal.SdkHandler;
+import com.android.build.gradle.internal.dsl.BuildType;
+import com.android.build.gradle.internal.dsl.ProductFlavor;
+import com.android.build.gradle.internal.dsl.SigningConfig;
+import com.android.builder.core.AndroidBuilder;
+
+import org.gradle.api.DomainObjectSet;
+import org.gradle.api.NamedDomainObjectContainer;
+import org.gradle.api.internal.DefaultDomainObjectSet;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.internal.reflect.Instantiator;
+
+import static com.android.builder.core.VariantType.ANDROID_TEST;
+import static com.android.builder.core.VariantType.UNIT_TEST;
+
+/**
+ * base 'android' extension for plugins that have a test component.
+ */
+public abstract class TestedExtension extends BaseExtension implements TestedAndroidConfig {
+
+ private final DomainObjectSet<TestVariant> testVariantList =
+ new DefaultDomainObjectSet<TestVariant>(TestVariant.class);
+
+ private final DomainObjectSet<UnitTestVariant> unitTestVariantList =
+ new DefaultDomainObjectSet<UnitTestVariant>(UnitTestVariant.class);
+
+ private String testBuildType = "debug";
+
+ public TestedExtension(@NonNull ProjectInternal project, @NonNull Instantiator instantiator,
+ @NonNull AndroidBuilder androidBuilder, @NonNull SdkHandler sdkHandler,
+ @NonNull NamedDomainObjectContainer<BuildType> buildTypes,
+ @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors,
+ @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs,
+ @NonNull ExtraModelInfo extraModelInfo, boolean isLibrary) {
+ super(project, instantiator, androidBuilder, sdkHandler, buildTypes, productFlavors,
+ signingConfigs, extraModelInfo, isLibrary);
+
+ getSourceSets().create(ANDROID_TEST.getPrefix());
+ getSourceSets().create(UNIT_TEST.getPrefix());
+ }
+
+ /**
+ * Returns the list of (Android) test variants. Since the collections is built after evaluation,
+ * it should be used with Gradle's <code>all</code> iterator to process future items.
+ */
+ @Override
+ @NonNull
+ public DomainObjectSet<TestVariant> getTestVariants() {
+ return testVariantList;
+ }
+
+ public void addTestVariant(TestVariant testVariant) {
+ testVariantList.add(testVariant);
+ }
+
+ /**
+ * Returns the list of (Android) test variants. Since the collections is built after evaluation,
+ * it should be used with Gradle's <code>all</code> iterator to process future items.
+ */
+ @Override
+ @NonNull
+ public DomainObjectSet<UnitTestVariant> getUnitTestVariants() {
+ return unitTestVariantList;
+ }
+
+ public void addUnitTestVariant(UnitTestVariant testVariant) {
+ unitTestVariantList.add(testVariant);
+ }
+
+ /**
+ * Name of the build type that will be used when running Android (on-device) tests.
+ *
+ * <p>Defaults to "debug".
+ */
+ @Override
+ @NonNull
+ public String getTestBuildType() {
+ return testBuildType;
+ }
+
+ public void setTestBuildType(String testBuildType) {
+ this.testBuildType = testBuildType;
+ }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/ApiObjectFactory.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/ApiObjectFactory.java
new file mode 100644
index 0000000..53e383c
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/ApiObjectFactory.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.TestedAndroidConfig;
+import com.android.build.gradle.api.BaseVariant;
+import com.android.build.gradle.internal.api.ReadOnlyObjectProvider;
+import com.android.build.gradle.internal.api.TestVariantImpl;
+import com.android.build.gradle.internal.api.TestedVariant;
+import com.android.build.gradle.internal.api.UnitTestVariantImpl;
+import com.android.build.gradle.internal.variant.ApplicationVariantFactory;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.TestVariantData;
+import com.android.build.gradle.internal.variant.TestedVariantData;
+import com.android.build.gradle.internal.variant.VariantFactory;
+import com.android.builder.core.AndroidBuilder;
+
+import org.gradle.internal.reflect.Instantiator;
+
+import static com.android.builder.core.VariantType.ANDROID_TEST;
+import static com.android.builder.core.VariantType.UNIT_TEST;
+
+/**
+ * Factory to create ApiObject from VariantData.
+ */
+public class ApiObjectFactory {
+ @NonNull
+ private final AndroidBuilder androidBuilder;
+ @NonNull
+ private final BaseExtension extension;
+ @NonNull
+ private final VariantFactory variantFactory;
+ @NonNull
+ private final Instantiator instantiator;
+ @NonNull
+ private final ReadOnlyObjectProvider readOnlyObjectProvider = new ReadOnlyObjectProvider();
+
+ public ApiObjectFactory(
+ @NonNull AndroidBuilder androidBuilder,
+ @NonNull BaseExtension extension,
+ @NonNull VariantFactory variantFactory,
+ @NonNull Instantiator instantiator) {
+ this.androidBuilder = androidBuilder;
+ this.extension = extension;
+ this.variantFactory = variantFactory;
+ this.instantiator = instantiator;
+ }
+
+ public void create(BaseVariantData<?> variantData) {
+ if (variantData.getType().isForTesting()) {
+ // Testing variants are handled together with their "owners".
+ return;
+ }
+
+ BaseVariant variantApi =
+ variantFactory.createVariantApi(variantData, readOnlyObjectProvider);
+
+ if (variantFactory.hasTestScope()) {
+ TestVariantData androidTestVariantData =
+ ((TestedVariantData) variantData).getTestVariantData(ANDROID_TEST);
+
+ if (androidTestVariantData != null) {
+ TestVariantImpl androidTestVariant = instantiator.newInstance(
+ TestVariantImpl.class,
+ androidTestVariantData,
+ variantApi,
+ androidBuilder,
+ readOnlyObjectProvider);
+
+ // add the test output.
+ ApplicationVariantFactory.createApkOutputApiObjects(
+ instantiator,
+ androidTestVariantData,
+ androidTestVariant);
+
+ ((TestedAndroidConfig) extension).getTestVariants().add(androidTestVariant);
+ ((TestedVariant) variantApi).setTestVariant(androidTestVariant);
+ }
+
+ TestVariantData unitTestVariantData =
+ ((TestedVariantData) variantData).getTestVariantData(UNIT_TEST);
+ if (unitTestVariantData != null) {
+ UnitTestVariantImpl unitTestVariant = instantiator.newInstance(
+ UnitTestVariantImpl.class,
+ unitTestVariantData,
+ variantApi,
+ androidBuilder,
+ readOnlyObjectProvider);
+
+ ((TestedAndroidConfig) extension).getUnitTestVariants().add(unitTestVariant);
+ ((TestedVariant) variantApi).setUnitTestVariant(unitTestVariant);
+ }
+ }
+
+ // Only add the variant API object to the domain object set once it's been fully
+ // initialized.
+ extension.addVariant(variantApi);
+ }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/NativeLibraryFactoryImpl.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/NativeLibraryFactoryImpl.java
new file mode 100644
index 0000000..ccbaeba
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/NativeLibraryFactoryImpl.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.internal;
+
+import com.android.annotations.NonNull;
+import com.android.build.gradle.internal.core.Abi;
+import com.android.build.gradle.internal.dsl.CoreNdkOptions;
+import com.android.build.gradle.internal.model.NativeLibraryFactory;
+import com.android.build.gradle.internal.model.NativeLibraryImpl;
+import com.android.build.gradle.internal.scope.VariantScope;
+import com.android.build.gradle.internal.variant.BaseVariantData;
+import com.android.build.gradle.internal.variant.BaseVariantOutputData;
+import com.android.build.gradle.tasks.NdkCompile;
+import com.android.builder.model.NativeLibrary;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Implementation of NativeLibraryFactory for gradle plugin.
+ */
+public class NativeLibraryFactoryImpl implements NativeLibraryFactory {
+
+ @NonNull
+ final NdkHandler ndkHandler;
+
+ public NativeLibraryFactoryImpl(
+ @NonNull NdkHandler ndkHandler) {
+ this.ndkHandler = ndkHandler;
+ }
+
+ @NonNull
+ @Override
+ public Optional<NativeLibrary> create(
+ @NonNull VariantScope scope,
+ @NonNull String toolchainName, @NonNull Abi abi) {
+ BaseVariantData<? extends BaseVariantOutputData> variantData = scope.getVariantData();
+ if (!scope.getGlobalScope().getProject().hasProperty(NdkCompile.USE_DEPRECATED_NDK)) {
+ return Optional.absent();
+ }
+
+ CoreNdkOptions ndkConfig = variantData.getVariantConfiguration().getNdkConfig();
+
+ String sysrootFlag = "--sysroot=" + ndkHandler.getSysroot(abi);
+ List<String> cFlags = ndkConfig.getcFlags() == null
+ ? ImmutableList.of(sysrootFlag)
+ : ImmutableList.of(sysrootFlag, ndkConfig.getcFlags());
+
+ // The DSL currently do not support all options available in the model such as the
+ // include dirs and the defines. Therefore, just pass an empty collection for now.
+ return Optional.<NativeLibrary>of(new NativeLibraryImpl(
+ ndkConfig.getModuleName(),
+ toolchainName,
+ abi.getName(),
+ Collections.<File>emptyList(), /*cIncludeDirs*/
+ Collections.<File>emptyList(), /*cppIncludeDirs*/
+ Collections.<File>emptyList(), /*cSystemIncludeDirs*/
+ ndkHandler.getStlIncludes(ndkConfig.getStl(), abi),
+ Collections.<String>emptyList(), /*cDefines*/
+ Collections.<String>emptyList(), /*cppDefines*/
+ cFlags,
+ cFlags, // TODO: NdkConfig should allow cppFlags to be set separately.
+ ImmutableList.of(scope.getNdkDebuggableLibraryFolders(abi))));
+ }
+}
diff --git a/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavorFactory.java b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavorFactory.java
new file mode 100644
index 0000000..bb5ed02
--- /dev/null
+++ b/build-system/gradle/src/main/groovy/com/android/build/gradle/internal/dsl/ProductFlavorFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.build.gradle.internal.dsl;
+
+import com.android.annotations.NonNull;
+
+import org.gradle.api.NamedDomainObjectFactory;
+import org.gradle.api.Project;
+import org.gradle.api.logging.Logger;
+import org.gradle.internal.reflect.Instantiator;
+
+/**
+ * Factory to create ProductFlavor object using an {@link Instantiator} to add
+ * the DSL methods.
+ */
+public class ProductFlavorFactory implements NamedDomainObjectFactory<ProductFlavor> {
+
+ @NonNull
+ private final Instantiator instantiator;
+ @NonNull
+ private final Project project;
+ @NonNull
+ private final Logger logger;
+
+ public ProductFlavorFactory(@NonNull Instantiator instantiator,
+ @NonNull Project project,
+ @NonNull Logger logger) {
+ this.instantiator = instantiator;
+ this.project = project;
+ this.logger = logger;
+ }
+
+ @Override
+ public ProductFlavor create(String name) {
+ return instantiator.newInstance(ProductFlavor.class,
+ name, project, instantiator, logger);
+ }
+}
diff --git a/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginDslTest.groovy b/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginDslTest.groovy
index 524cdee..66a133c 100644
--- a/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginDslTest.groovy
+++ b/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginDslTest.groovy
@@ -37,7 +37,7 @@
@Override
protected void setUp() throws Exception {
- SdkHandler.testSdkFolder = new File("foo")
+ SdkHandler.testSdkFolder = new File(System.getenv("ANDROID_HOME"))
}
public void testBasic() {
@@ -47,7 +47,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
+ compileSdkVersion COMPILE_SDK_VERSION
buildToolsVersion '20.0.0'
}
@@ -77,7 +77,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion = 15
+ compileSdkVersion COMPILE_SDK_VERSION
buildToolsVersion '20.0.0'
}
@@ -104,7 +104,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion "android-15"
+ compileSdkVersion "android-" + COMPILE_SDK_VERSION
buildToolsVersion '20.0.0'
}
@@ -131,7 +131,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
+ compileSdkVersion COMPILE_SDK_VERSION
buildToolsVersion '20.0.0'
sourceSets {
@@ -153,7 +153,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
+ compileSdkVersion COMPILE_SDK_VERSION
buildToolsVersion '20.0.0'
testBuildType "staging"
@@ -193,7 +193,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
+ compileSdkVersion COMPILE_SDK_VERSION
buildToolsVersion '20.0.0'
productFlavors {
@@ -236,7 +236,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
+ compileSdkVersion COMPILE_SDK_VERSION
buildToolsVersion '20.0.0'
flavorDimensions "dimension1", "dimension2"
@@ -299,7 +299,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
+ compileSdkVersion COMPILE_SDK_VERSION
buildToolsVersion '20.0.0'
}
@@ -318,7 +318,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
+ compileSdkVersion COMPILE_SDK_VERSION
buildToolsVersion '20.0.0'
buildTypes {
@@ -356,7 +356,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
+ compileSdkVersion COMPILE_SDK_VERSION
buildToolsVersion '20.0.0'
buildTypes {
@@ -419,7 +419,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
+ compileSdkVersion COMPILE_SDK_VERSION
buildToolsVersion '20.0.0'
buildTypes {
@@ -472,11 +472,11 @@
assertEquals(
"target compatibility for ${version}",
expectedLanguageLevel.toString(),
- project.compileReleaseJava.targetCompatibility)
+ project.compileReleaseJavaWithJavac.targetCompatibility)
assertEquals(
"source compatibility for ${version}",
expectedLanguageLevel.toString(),
- project.compileReleaseJava.sourceCompatibility)
+ project.compileReleaseJavaWithJavac.sourceCompatibility)
}
for (useJack in [true, false]) {
@@ -484,16 +484,16 @@
String originalVersion = System.getProperty(propName)
try{
System.setProperty(propName, '1.7')
- testLanguageLevel(15, JavaVersion.VERSION_1_6, useJack)
- testLanguageLevel(21, JavaVersion.VERSION_1_7, useJack)
+ testLanguageLevel('android-15', JavaVersion.VERSION_1_6, useJack)
testLanguageLevel('android-21', JavaVersion.VERSION_1_7, useJack)
- testLanguageLevel('Google:GoogleInc:22', JavaVersion.VERSION_1_7, useJack)
+ testLanguageLevel('android-21', JavaVersion.VERSION_1_7, useJack)
+ testLanguageLevel('Google Inc.:Google APIs:22', JavaVersion.VERSION_1_7, useJack)
System.setProperty(propName, '1.6')
testLanguageLevel(15, JavaVersion.VERSION_1_6, useJack)
testLanguageLevel(21, JavaVersion.VERSION_1_6, useJack)
testLanguageLevel('android-21', JavaVersion.VERSION_1_6, useJack)
- testLanguageLevel('Google:GoogleInc:22', JavaVersion.VERSION_1_6, useJack)
+ testLanguageLevel('Google Inc.:Google APIs:22', JavaVersion.VERSION_1_6, useJack)
} finally {
System.setProperty(propName, originalVersion)
}
@@ -506,7 +506,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 21
+ compileSdkVersion COMPILE_SDK_VERSION
buildToolsVersion '20.0.0'
compileOptions {
@@ -519,30 +519,10 @@
assertEquals(
JavaVersion.VERSION_1_6.toString(),
- project.compileReleaseJava.targetCompatibility)
+ project.compileReleaseJavaWithJavac.targetCompatibility)
assertEquals(
JavaVersion.VERSION_1_6.toString(),
- project.compileReleaseJava.sourceCompatibility)
- }
-
- public void testSettingLanguageLevelFromCompileSdk_unknownVersion() {
- Project project = ProjectBuilder.builder().withProjectDir(
- new File(testDir, "${FOLDER_TEST_PROJECTS}/basic")).build()
-
- project.apply plugin: 'com.android.application'
- project.android {
- compileSdkVersion 'foo'
- buildToolsVersion '20.0.0'
- }
- AppPlugin plugin = project.plugins.getPlugin(AppPlugin)
- plugin.createAndroidTasks(false)
-
- assertEquals(
- JavaVersion.VERSION_1_6.toString(),
- project.compileReleaseJava.targetCompatibility)
- assertEquals(
- JavaVersion.VERSION_1_6.toString(),
- project.compileReleaseJava.sourceCompatibility)
+ project.compileReleaseJavaWithJavac.sourceCompatibility)
}
public void testMockableJarName() {
@@ -552,7 +532,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion "Google Inc.:Google APIs:21"
+ compileSdkVersion "Google Inc.:Google APIs:" + COMPILE_SDK_VERSION
buildToolsVersion '20.0.0'
}
@@ -570,7 +550,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 21
+ compileSdkVersion COMPILE_SDK_VERSION
buildToolsVersion '20.0.0'
compileOptions {
@@ -583,7 +563,59 @@
assertEquals(
"foo",
- project.compileReleaseJava.options.encoding)
+ project.compileReleaseJavaWithJavac.options.encoding)
+ }
+
+ public void testInstrumentationRunnerArguments_merging() throws Exception {
+ Project project = ProjectBuilder.builder().withProjectDir(
+ new File(testDir, "${FOLDER_TEST_PROJECTS}/basic")).build()
+
+ project.apply plugin: 'com.android.application'
+
+ project.android {
+ compileSdkVersion COMPILE_SDK_VERSION
+ buildToolsVersion '20.0.0'
+
+ defaultConfig {
+ testInstrumentationRunnerArguments(value: "default", size: "small")
+ }
+
+ productFlavors {
+ f1 {
+ }
+
+ f2 {
+ testInstrumentationRunnerArgument "value", "f2"
+ }
+
+ f3 {
+ testInstrumentationRunnerArguments["otherValue"] = "f3"
+ }
+
+ f4 {
+ testInstrumentationRunnerArguments(otherValue: "f4.1")
+ testInstrumentationRunnerArguments = [otherValue: "f4.2"]
+ }
+ }
+ }
+
+ AppPlugin plugin = project.plugins.getPlugin(AppPlugin)
+ plugin.createAndroidTasks(false)
+
+ def variantsData = plugin.variantManager.variantDataList
+ Map<String, GradleVariantConfiguration> variantMap =
+ variantsData.collectEntries {[it.name, it.variantConfiguration]}
+
+ def expectedArgs = [
+ f1Debug: [value: "default", size: "small"],
+ f2Debug: [value: "f2", size: "small"],
+ f3Debug: [value: "default", size: "small", otherValue: "f3"],
+ f4Debug: [value: "default", size: "small", otherValue: "f4.2"],
+ ]
+
+ expectedArgs.each { name, expected ->
+ assert expected == variantMap[name].instrumentationRunnerArguments
+ }
}
private static void checkTestedVariant(@NonNull String variantName,
diff --git a/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginInternalTest.groovy b/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginInternalTest.groovy
index fa0232e..d189740 100644
--- a/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginInternalTest.groovy
+++ b/build-system/gradle/src/test/groovy/com/android/build/gradle/AppPluginInternalTest.groovy
@@ -41,7 +41,7 @@
@Override
protected void setUp() throws Exception {
- SdkHandler.testSdkFolder = new File("foo")
+ SdkHandler.testSdkFolder = new File(System.getenv("ANDROID_HOME"))
}
public void testBasic() {
@@ -49,10 +49,10 @@
new File(testDir, "${FOLDER_TEST_PROJECTS}/basic")).build()
project.apply plugin: 'com.android.application'
-
+1
project.android {
- compileSdkVersion 15
- buildToolsVersion "19"
+ compileSdkVersion COMPILE_SDK_VERSION
+ buildToolsVersion BUILD_TOOL_VERSION
}
AppPlugin plugin = project.plugins.getPlugin(AppPlugin)
@@ -79,8 +79,8 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
- buildToolsVersion "19"
+ compileSdkVersion COMPILE_SDK_VERSION
+ buildToolsVersion BUILD_TOOL_VERSION
signingConfigs {
fakeConfig {
@@ -125,8 +125,8 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
- buildToolsVersion "19"
+ compileSdkVersion COMPILE_SDK_VERSION
+ buildToolsVersion BUILD_TOOL_VERSION
testBuildType "staging"
buildTypes {
@@ -162,8 +162,8 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
- buildToolsVersion "19"
+ compileSdkVersion COMPILE_SDK_VERSION
+ buildToolsVersion BUILD_TOOL_VERSION
productFlavors {
flavor1 {
@@ -199,8 +199,8 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
- buildToolsVersion "19"
+ compileSdkVersion COMPILE_SDK_VERSION
+ buildToolsVersion BUILD_TOOL_VERSION
flavorDimensions "dimension1", "dimension2"
@@ -264,8 +264,8 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
- buildToolsVersion "19"
+ compileSdkVersion COMPILE_SDK_VERSION
+ buildToolsVersion BUILD_TOOL_VERSION
signingConfigs {
one {
@@ -365,8 +365,8 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
- buildToolsVersion "19"
+ compileSdkVersion COMPILE_SDK_VERSION
+ buildToolsVersion BUILD_TOOL_VERSION
signingConfigs {
debug {
@@ -391,7 +391,7 @@
project.apply plugin: 'com.android.application'
project.android {
- compileSdkVersion 15
+ compileSdkVersion COMPILE_SDK_VERSION
signingConfigs {
foo.initWith(owner.signingConfigs.debug)
@@ -419,8 +419,8 @@
project.apply plugin: 'java'
project.android {
- compileSdkVersion 15
- buildToolsVersion "19"
+ compileSdkVersion COMPILE_SDK_VERSION
+ buildToolsVersion BUILD_TOOL_VERSION
}
AppPlugin plugin = project.plugins.getPlugin(AppPlugin)
diff --git a/build-system/gradle/src/test/groovy/com/android/build/gradle/LibraryPluginDslTest.groovy b/build-system/gradle/src/test/groovy/com/android/build/gradle/LibraryPluginDslTest.groovy
index 2e49da3..1c7690c 100644
--- a/build-system/gradle/src/test/groovy/com/android/build/gradle/LibraryPluginDslTest.groovy
+++ b/build-system/gradle/src/test/groovy/com/android/build/gradle/LibraryPluginDslTest.groovy
@@ -31,7 +31,7 @@
@Override
protected void setUp() throws Exception {
- SdkHandler.testSdkFolder = new File("foo")
+ SdkHandler.testSdkFolder = new File(System.getenv("ANDROID_HOME"))
}
public void testBasic() {
@@ -41,7 +41,7 @@
project.apply plugin: 'com.android.library'
project.android {
- compileSdkVersion 15
+ compileSdkVersion 21
}
project.afterEvaluate {
diff --git a/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/dsl/BuildTypeTest.groovy b/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/dsl/BuildTypeTest.groovy
index 9885fd5..e69ec0e 100644
--- a/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/dsl/BuildTypeTest.groovy
+++ b/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/dsl/BuildTypeTest.groovy
@@ -19,7 +19,7 @@
import com.android.build.gradle.AppPlugin
import com.android.build.gradle.internal.test.BaseTest
import com.android.builder.core.BuilderConstants
-import com.android.builder.core.DefaultBuildType
+import com.android.builder.model.BuildType
import org.gradle.api.Project
import org.gradle.testfixtures.ProjectBuilder
@@ -40,7 +40,7 @@
AppPlugin plugin = project.plugins.getPlugin(AppPlugin)
- DefaultBuildType type = plugin.variantManager.buildTypes.get(BuilderConstants.DEBUG).buildType
+ BuildType type = plugin.variantManager.buildTypes.get(BuilderConstants.DEBUG).buildType
assertTrue(type.isDebuggable())
assertFalse(type.isJniDebuggable())
@@ -62,7 +62,7 @@
AppPlugin plugin = project.plugins.getPlugin(AppPlugin)
- DefaultBuildType type = plugin.variantManager.buildTypes.get(BuilderConstants.RELEASE).buildType
+ BuildType type = plugin.variantManager.buildTypes.get(BuilderConstants.RELEASE).buildType
assertFalse(type.isDebuggable())
assertFalse(type.isJniDebuggable())
@@ -74,7 +74,8 @@
Project project = ProjectBuilder.builder().withProjectDir(
new File(testDir, "basic")).build()
- BuildType object1 = new BuildType("foo", project, project.getLogger())
+ com.android.build.gradle.internal.dsl.BuildType object1 =
+ new com.android.build.gradle.internal.dsl.BuildType("foo", project, project.getLogger())
// change every value from their default.
object1.setDebuggable(true)
@@ -89,7 +90,8 @@
object1.setShrinkResources(true)
object1.setUseJack(Boolean.FALSE)
- BuildType object2 = new BuildType(object1.name, project, project.getLogger())
+ com.android.build.gradle.internal.dsl.BuildType object2 =
+ new com.android.build.gradle.internal.dsl.BuildType(object1.name, project, project.getLogger())
object2.initWith(object1)
assertEquals(object1, object2)
diff --git a/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy b/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy
index fb3ecc2..aff91e4 100755
--- a/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy
+++ b/build-system/gradle/src/test/groovy/com/android/build/gradle/internal/test/BaseTest.groovy
@@ -26,6 +26,9 @@
*/
public abstract class BaseTest extends TestCase {
+ protected final static int COMPILE_SDK_VERSION = 21;
+ protected static final String BUILD_TOOL_VERSION = "22.0.1";
+
public static final String FOLDER_TEST_PROJECTS = "test-projects";
protected File sdkDir;
diff --git a/build-system/integration-test/build.gradle b/build-system/integration-test/build.gradle
index 48b35f7..36a3527 100644
--- a/build-system/integration-test/build.gradle
+++ b/build-system/integration-test/build.gradle
@@ -8,21 +8,36 @@
dependencies {
compile gradleApi()
compile localGroovy()
- compile project(':base:builder-model')
- compile project(':base:builder')
- compile project(':base:sdk-common')
- compile "com.google.truth:truth:0.24"
- testCompile "org.jacoco:org.jacoco.agent:0.7.4.201502262128"
+ testCompile project(':base:builder-model')
+ testCompile project(':base:builder')
+ testCompile project(':base:sdk-common')
+ testCompile 'com.google.truth:truth:0.26'
+ testCompile 'org.jacoco:org.jacoco.agent:0.7.4.201502262128'
+
+ // Add dependency on plugin code. Exclude transitive dependencies to avoid conflict due to
+ // Groovy versions.
+ testCompile(project(':base:gradle-core')) {
+ transitive = false
+ }
+ testCompile(project(':base:gradle')) {
+ transitive = false
+ }
+ testCompile(project(':base:gradle-experimental')) {
+ transitive = false
+ }
}
def testEnvironment = [
PROJECT_BUILD_DIR: project.buildDir,
CUSTOM_REPO: rootProject.file("../out/repo"),
CUSTOM_GRADLE: System.env.CUSTOM_GRADLE ?: rootProject.ext.buildVersion,
+ CUSTOM_EXPERIMENTAL_GRADLE: System.env.CUSTOM_EXPERIMENTAL_GRADLE ?:rootProject.ext.experimentalVersion,
ANDROID_HOME: System.env.ANDROID_HOME,
ANDROID_NDK_HOME: System.env.ANDROID_NDK_HOME,
CUSTOM_BUILDTOOLS: System.env.CUSTOM_BUILDTOOLS,
CUSTOM_JACK: System.env.CUSTOM_JACK,
+ DEBUG_INNER_TEST: System.env.DEBUG_INNER_TEST,
+ RECORD_SPANS: System.env.RECORD_SPANS,
].findAll { it.value != null }
// These tasks will not depend on publishLocal, so they will run integration
@@ -39,12 +54,22 @@
systemProperties['jar.path'] = jar.archivePath
environment = testEnvironment
+ // Always run the task, when requested.
+ outputs.upToDateWhen { false }
+
forkEvery = 1
maxParallelForks = Runtime.runtime.availableProcessors() / 2
useJUnit {
+ if (System.properties['test.includeCategories'] != null) {
+ def categories = System.properties['test.includeCategories'].split(',')
+ String defaultPackage = "com.android.build.gradle.integration.common.category."
+ categories = categories.collect { it.charAt(0).isUpperCase() ? defaultPackage + it : it }
+ includeCategories categories as String[]
+ }
excludeCategories "com.android.build.gradle.integration.common.category.DeviceTests"
}
+ exclude "com/android/build/gradle/integration/performance/**"
}
task connectedIntegrationTest(type: Test)
@@ -59,10 +84,21 @@
"and a device."
group = "verification"
systemProperties['jar.path'] = jar.archivePath
- environment = testEnvironment
+ // Add to, rather than replace the environment, so that TEST_CLASSPATH_DEPENDENCY,
+ // REMOTE_TEST_PROVIDER, ADDITIONAL_TEST_CUSTOM_REPO and any dependencies of the remote test
+ // provider are present in the test environment only for these tests.
+ environment testEnvironment
+
+ // Always run the task, when requested.
+ outputs.upToDateWhen { false }
+
+ forkEvery= 1
+ maxParallelForks = System.env.containsKey("REMOTE_TEST_PROVIDER") ? Runtime.runtime.availableProcessors() : 1
+
useJUnit {
includeCategories "com.android.build.gradle.integration.common.category.DeviceTests"
}
+ exclude "com/android/build/gradle/integration/performance/**"
}
task performanceTest(type: Test) {
@@ -86,12 +122,14 @@
test.dependsOn ':publishLocal'
connectedIntegrationTest.dependsOn ':publishLocal'
performanceTest.dependsOn ':publishLocal'
-check.dependsOn performanceTest
jacocoTestReport {
+ sourceSets project(':base:gradle-experimental').sourceSets.main
sourceSets project(':base:gradle').sourceSets.main
+ sourceSets project(':base:gradle-core').sourceSets.main
sourceSets project(':base:builder').sourceSets.main
sourceSets project(':base:builder-model').sourceSets.main
+ sourceSets project(':base:builder-test-api').sourceSets.main
}
// Due to memory constraints, apply jacoco only when jacocoTestReport is invoked. Make sure to
diff --git a/build-system/integration-test/integration-test.iml b/build-system/integration-test/integration-test.iml
index bf9181c..7438448 100644
--- a/build-system/integration-test/integration-test.iml
+++ b/build-system/integration-test/integration-test.iml
@@ -4,6 +4,7 @@
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/test/groovy" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AaptOptionsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AaptOptionsTest.groovy
index 609e273..49bacce 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AaptOptionsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AaptOptionsTest.groovy
@@ -20,14 +20,11 @@
import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
-import com.android.builder.model.SyncIssue
import groovy.transform.CompileStatic
import org.junit.Before
import org.junit.Rule
import org.junit.Test
-import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
-import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
/**
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AbiPureSplits.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AbiPureSplits.groovy
index db3be84..c88394e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AbiPureSplits.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AbiPureSplits.groovy
@@ -42,10 +42,12 @@
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
.fromTestProject("abiPureSplits")
+ .addGradleProperties("android.useDeprecatedNdk=true")
.create()
@BeforeClass
static void setup() {
+ GradleTestProject.assumeBuildToolsAtLeast(21)
model = project.executeAndReturnModel("clean", "assembleDebug")
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AndroidTestResourcesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AndroidTestResourcesTest.groovy
index 2d1a744..2f8918e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AndroidTestResourcesTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AndroidTestResourcesTest.groovy
@@ -23,6 +23,7 @@
import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
import com.android.builder.model.AndroidProject
import com.google.common.base.Joiner
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -34,6 +35,7 @@
/**
* Check resources in androidTest are available in the generated R.java.
*/
+@CompileStatic
class AndroidTestResourcesTest {
private static AndroidTestApp testApp = new HelloWorldApp()
static {
@@ -140,7 +142,7 @@
@Test
@Category(DeviceTests.class)
public void "check test layout can be used in device tests"() {
- appProject.execute("connectedAndroidTest")
+ appProject.executeConnectedCheck()
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ApplicationIdInLibsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ApplicationIdInLibsTest.groovy
new file mode 100644
index 0000000..9b5dcaa
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ApplicationIdInLibsTest.groovy
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.application
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.utils.ApkHelper
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidArtifactOutput
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.Assert
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+
+import static org.junit.Assert.assertEquals
+import static org.junit.Assert.assertNotNull
+
+/**
+ * Tests for @{applicationId} placeholder presence in library manifest files.
+ * Such placeholders should be left intact until the library is merged into a consuming application
+ * with a known application Id.
+ */
+@CompileStatic
+class ApplicationIdInLibsTest {
+ static Map<String, AndroidProject> models
+
+ @ClassRule
+ static public GradleTestProject project = GradleTestProject.builder()
+ .fromTestProject("applicationIdInLibsTest")
+ .create()
+
+ @BeforeClass
+ static void setup() {
+ models = project.executeAndReturnMultiModel("clean",
+ ":examplelibrary:generateDebugAndroidTestSources", "app:assembleDebug")
+ }
+
+ @AfterClass
+ static void cleanUp() {
+ project = null
+ models = null
+ }
+
+ @Test
+ public void "test library placeholder substitution in final apk"() throws Exception {
+
+ // Load the custom model for the project
+ Collection<Variant> variants = models.get(":app").getVariants()
+ assertEquals("Variant Count", 2 , variants.size())
+
+ // get the main artifact of the debug artifact
+ Variant debugVariant = ModelHelper.getVariant(variants, "flavorDebug")
+ assertNotNull("debug Variant null-check", debugVariant)
+ AndroidArtifact debugMainArtifact = debugVariant.getMainArtifact()
+ assertNotNull("Debug main info null-check", debugMainArtifact)
+
+ // get the outputs.
+ Collection<AndroidArtifactOutput> debugOutputs = debugMainArtifact.getOutputs()
+ assertNotNull(debugOutputs)
+
+ assertEquals(1, debugOutputs.size())
+ AndroidArtifactOutput output = debugOutputs.iterator().next()
+ assertEquals(1, output.getOutputs().size())
+
+ List<String> apkBadging =
+ ApkHelper.getApkBadging(output.getOutputs().iterator().next().getOutputFile());
+
+ for (String line : apkBadging) {
+ if (line.contains("uses-permission: name=" +
+ "'com.example.manifest_merger_example.flavor.permission.C2D_MESSAGE'")) {
+ return;
+ }
+ }
+ Assert.fail("failed to find the permission with the right placeholder substitution.")
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArchivesBaseNameTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArchivesBaseNameTest.groovy
index 1493f9b..707d562 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArchivesBaseNameTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArchivesBaseNameTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.builder.model.AndroidProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -29,6 +30,7 @@
* Ensures that archivesBaseName setting on android project is used when choosing the apk file
* names
*/
+@CompileStatic
class ArchivesBaseNameTest {
@ClassRule
@@ -56,8 +58,11 @@
@Test
void "check model failed to load"() {
- File outputFile = models.get(":").getVariants().get(0).getMainArtifact().getOutputs()
- .get(0).getMainOutputFile().getOutputFile()
+ File outputFile = models.get(":").getVariants().iterator().next()
+ .getMainArtifact()
+ .getOutputs().iterator().next()
+ .getMainOutputFile()
+ .getOutputFile()
assertThat(outputFile.getName().startsWith("random_apk_name"))
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArtifactApiTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArtifactApiTest.groovy
index c06402e..64019fe 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArtifactApiTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ArtifactApiTest.groovy
@@ -28,6 +28,8 @@
import com.android.builder.model.SourceProvider
import com.android.builder.model.SourceProviderContainer
import com.android.builder.model.Variant
+import com.android.utils.FileUtils
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -41,9 +43,10 @@
/**
* Assemble tests for artifactApi.
*/
+@CompileStatic
class ArtifactApiTest {
// Unit test variants produce an extra Java artifact.
- private static final DEFAULT_EXTRA_JAVA_ARTIFACTS = 1
+ private static final int DEFAULT_EXTRA_JAVA_ARTIFACTS = 1
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -174,4 +177,12 @@
assertFalse(deps.getJavaLibraries().isEmpty())
}
}
+
+ @Test
+ public void backwardsCompatible() throws Exception {
+ // ATTENTION Author and Reviewers - please make sure required changes to the build file
+ // are backwards compatible before updating this test.
+ assertThat(FileUtils.sha1(project.file("build.gradle")))
+ .isEqualTo("cf6fa23a32f342718b1f342fc97846f56665a155")
+ }
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoDensitySplitTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoDensitySplitTest.groovy
new file mode 100644
index 0000000..0b5932e
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoDensitySplitTest.groovy
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.application
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidArtifactOutput
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
+import static org.junit.Assert.assertEquals
+
+/**
+ * MultiAPK test where densities are obtained automatically.
+ */
+@CompileStatic
+class AutoDensitySplitTest {
+ static AndroidProject model
+
+ @ClassRule
+ static public GradleTestProject project = GradleTestProject.builder()
+ .fromTestProject("densitySplit")
+ .create()
+
+ @BeforeClass
+ static void setUp() {
+ project.getBuildFile() << """android {
+ splits {
+ density {
+ enable true
+ auto true
+ compatibleScreens 'small', 'normal', 'large', 'xlarge'
+ }
+ }
+ }"""
+ model = project.executeAndReturnModel("clean", "assembleDebug")
+ }
+
+ @AfterClass
+ static void cleanUp() {
+ project = null
+ model = null
+ }
+
+ @Test
+ void lint() {
+ project.execute("lint")
+ }
+
+ @Test
+ void testPackaging() {
+ for (Variant variant : model.getVariants()) {
+ AndroidArtifact mainArtifact = variant.getMainArtifact()
+ if (!variant.getBuildType().equalsIgnoreCase("Debug")) {
+ continue
+ }
+ assertEquals(5, mainArtifact.getOutputs().size())
+
+ File mdpiApk = project.getApk("mdpi", "debug")
+ assertThatZip(mdpiApk).contains("res/drawable-mdpi-v4/other.png")
+ }
+ }
+
+ @Test
+ void "check version code in apk"() {
+ File universalApk = project.getApk("universal", "debug")
+ assertThatApk(universalApk).hasVersionCode(112)
+ assertThatApk(universalApk).hasVersionName("version 112")
+
+ File mdpiApk = project.getApk("mdpi", "debug")
+ assertThatApk(mdpiApk).hasVersionCode(212)
+ assertThatApk(mdpiApk).hasVersionName("version 212")
+
+ File hdpiApk = project.getApk("hdpi", "debug")
+ assertThatApk(hdpiApk).hasVersionCode(312)
+ assertThatApk(hdpiApk).hasVersionName("version 312")
+
+ File xhdpiApk = project.getApk("xhdpi", "debug")
+ assertThatApk(xhdpiApk).hasVersionCode(412)
+ assertThatApk(xhdpiApk).hasVersionName("version 412")
+
+ File xxhdiApk = project.getApk("xxhdpi", "debug")
+ assertThatApk(xxhdiApk).hasVersionCode(512)
+ assertThatApk(xxhdiApk).hasVersionName("version 512")
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoPureSplits.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoPureSplits.groovy
index bda2d28..db856c2 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoPureSplits.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoPureSplits.groovy
@@ -15,17 +15,15 @@
*/
package com.android.build.gradle.integration.application
-
import com.android.build.OutputFile
import com.android.build.gradle.integration.common.fixture.GradleTestProject
-import com.android.build.gradle.integration.common.utils.ApkHelper
import com.android.build.gradle.integration.common.utils.ModelHelper
import com.android.builder.model.AndroidArtifact
import com.android.builder.model.AndroidArtifactOutput
import com.android.builder.model.AndroidProject
import com.android.builder.model.Variant
import com.google.common.collect.Sets
-import com.google.common.truth.Truth
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -35,7 +33,6 @@
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull
import static org.junit.Assert.assertTrue
-
/**
* Test to ensure that "auto" resConfig setting only package application's languages.
*/
@@ -50,6 +47,7 @@
@BeforeClass
static void setup() {
+ GradleTestProject.assumeBuildToolsAtLeast(21)
project.getBuildFile() << """android {
defaultConfig {
versionCode 12
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoResConfig.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoResConfig.groovy
index 1880153..ac985d5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoResConfig.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/AutoResConfig.groovy
@@ -24,6 +24,7 @@
import com.android.builder.model.AndroidProject
import com.android.builder.model.Variant
import com.google.common.truth.Truth
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -36,6 +37,7 @@
/**
* Test to ensure that "auto" resConfig setting only package application's languages.
*/
+@CompileStatic
class AutoResConfig {
static AndroidProject model
@@ -47,6 +49,7 @@
@BeforeClass
static void setup() {
+ GradleTestProject.assumeBuildToolsAtLeast(21)
project.getBuildFile() << """android {
defaultConfig {
versionCode 12
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicMultiFlavorTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicMultiFlavorTest.groovy
index 0085d90..3d6dfee 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicMultiFlavorTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicMultiFlavorTest.groovy
@@ -24,6 +24,7 @@
import com.android.builder.model.ProductFlavorContainer
import com.android.builder.model.SourceProviderContainer
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -36,6 +37,7 @@
/**
* Assemble tests for basicMultiFlavors
*/
+@CompileStatic
class BasicMultiFlavorTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest.groovy
index b696944..7b41d85 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.category.SmokeTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.utils.ModelHelper
import com.android.build.gradle.integration.common.utils.SigningConfigHelper
@@ -42,11 +43,13 @@
/**
* Assemble tests for basic.
*/
+@Category(SmokeTests.class)
@CompileStatic
class BasicTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
.fromTestProject("basic")
+ .withoutNdk()
.create()
static public AndroidProject model
@@ -85,8 +88,12 @@
model.getAaptOptions().getFailOnMissingConfigEntry())
JavaCompileOptions javaCompileOptions = model.getJavaCompileOptions()
- assertEquals("1.6", javaCompileOptions.getSourceCompatibility())
- assertEquals("1.6", javaCompileOptions.getTargetCompatibility())
+ // since source and target compatibility are not explicitly set in the build.gradle,
+ // the default value should be the JDK version used to build against.
+ assertEquals(System.getProperty("java.specification.version"),
+ javaCompileOptions.getSourceCompatibility())
+ assertEquals(System.getProperty("java.specification.version"),
+ javaCompileOptions.getTargetCompatibility())
assertEquals("UTF-8", javaCompileOptions.getEncoding())
}
@@ -150,12 +157,13 @@
@Test
@Category(DeviceTests.class)
void install() {
+ GradleTestProject.assumeLocalDevice();
project.execute("installDebug", "uninstallAll")
}
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest2.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest2.groovy
index 3683068..df29fac 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest2.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BasicTest2.groovy
@@ -35,6 +35,7 @@
import com.android.builder.model.Variant
import com.google.common.collect.Maps
import com.google.common.collect.Sets
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -52,6 +53,7 @@
/**
* Assemble tests for basic that loads the model but doesn't build.
*/
+@CompileStatic
class BasicTest2 {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -282,12 +284,13 @@
@Test
@Category(DeviceTests.class)
void install() {
+ GradleTestProject.assumeLocalDevice();
project.execute("installDebug", "uninstallAll")
}
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BootClasspathTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BootClasspathTest.groovy
new file mode 100644
index 0000000..54e6e2d
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BootClasspathTest.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.application
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import groovy.transform.CompileStatic
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+/**
+ * Test BaseExtension.getBootClasspath can be use before afterEvaluate.
+ */
+@CompileStatic
+class BootClasspathTest {
+ @Rule
+ public GradleTestProject project = GradleTestProject.builder()
+ .fromTestApp(new HelloWorldApp())
+ .create()
+
+ @Before
+ public void setUp() {
+ project.getBuildFile() << """
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+}
+
+task checkBootClasspath {
+ assert android.getBootClasspath() != null
+}
+"""
+ }
+
+ @Test
+ public void "check getBootClasspath can be called"() {
+ project.execute("checkBootClasspath")
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildConfigTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildConfigTest.groovy
index 8f65b20..30eec74 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildConfigTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildConfigTest.groovy
@@ -28,7 +28,8 @@
import com.google.common.base.Charsets
import com.google.common.collect.Maps
import com.google.common.io.Files
-import junit.framework.Assert
+import groovy.transform.CompileStatic
+import org.junit.Assert
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -41,6 +42,7 @@
* Test for BuildConfig field declared in build type, flavors, and variant and how they
* override each other
*/
+@CompileStatic
class BuildConfigTest {
@ClassRule
public static GradleTestProject project = GradleTestProject.builder()
@@ -121,7 +123,7 @@
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "flavor1";
public static final int VERSION_CODE = 1;
- public static final String VERSION_NAME = "";
+ public static final String VERSION_NAME = "1.0";
// Fields from the variant
public static final int VALUE_VARIANT = 1000;
// Fields from build type: debug
@@ -159,7 +161,7 @@
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "flavor2";
public static final int VERSION_CODE = 1;
- public static final String VERSION_NAME = "";
+ public static final String VERSION_NAME = "1.0";
// Fields from the variant
public static final int VALUE_VARIANT = 1000;
// Fields from build type: debug
@@ -197,7 +199,7 @@
public static final String BUILD_TYPE = "release";
public static final String FLAVOR = "flavor1";
public static final int VERSION_CODE = 1;
- public static final String VERSION_NAME = "";
+ public static final String VERSION_NAME = "1.0";
// Fields from product flavor: flavor1
public static final int VALUE_DEBUG = 10;
public static final int VALUE_FLAVOR = 10;
@@ -233,7 +235,7 @@
public static final String BUILD_TYPE = "release";
public static final String FLAVOR = "flavor2";
public static final int VERSION_CODE = 1;
- public static final String VERSION_NAME = "";
+ public static final String VERSION_NAME = "1.0";
// Fields from product flavor: flavor2
public static final int VALUE_DEBUG = 20;
public static final int VALUE_FLAVOR = 20;
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildToolsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildToolsTest.groovy
index 05ea823..ac203e4 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildToolsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/BuildToolsTest.groovy
@@ -18,7 +18,9 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.google.common.collect.ImmutableList
import com.google.common.collect.Sets
+import groovy.transform.CompileStatic
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -26,8 +28,13 @@
import java.util.regex.Matcher
import java.util.regex.Pattern
-import static org.junit.Assert.assertTrue
+import static com.google.common.truth.Truth.assert_
+@CompileStatic
+/**
+ * Tests to ensure that changing the build tools version in the build.gradle will trigger
+ * re-execution of some tasks even if no source file change was detected.
+ */
class BuildToolsTest {
private static final Pattern UP_TO_DATE_PATTERN = ~/:(\S+)\s+UP-TO-DATE/
@@ -35,13 +42,19 @@
private static final Pattern INPUT_CHANGED_PATTERN =
~/Value of input property 'buildToolsVersion' has changed for task ':(\S+)'/
- private static final String[] tasks = [
- "preDexDebug", "dexDebug", "compileDebugAidl", "compileDebugRenderscript",
+ private static final String[] COMMON_TASKS = [
+ "compileDebugAidl", "compileDebugRenderscript",
"mergeDebugResources", "processDebugResources",
- "preDexRelease", "dexRelease", "compileReleaseAidl", "compileReleaseRenderscript",
+ "compileReleaseAidl", "compileReleaseRenderscript",
"mergeReleaseResources", "processReleaseResources"
]
+ private static final List<String> JAVAC_TASKS = ImmutableList.builder().add(COMMON_TASKS)
+ .add("preDexDebug").add("dexDebug").add("preDexRelease").add("dexRelease").build()
+ private static final List<String> JACK_TASKS = ImmutableList.builder().add(COMMON_TASKS)
+ .add("jillDebugRuntimeLibraries").add("jillDebugPackagedLibraries")
+ .add("jillReleaseRuntimeLibraries").add("jillReleasePackagedLibraries").build()
+
@Rule
public GradleTestProject project = GradleTestProject.builder()
.fromTestApp(new HelloWorldApp())
@@ -67,32 +80,35 @@
project.execute("assemble")
Set<String> skippedTasks = getTasksMatching(UP_TO_DATE_PATTERN, project.stdout)
- for (String task : tasks) {
- assertTrue(String.format("Expecting task %s to be UP-TO-DATE" , task),
- skippedTasks.contains(task))
- }
+ assert_().withFailureMessage("Expecting tasks to be UP-TO-DATE").that(skippedTasks)
+ .containsAllIn(GradleTestProject.USE_JACK ? JACK_TASKS : JAVAC_TASKS)
}
@Test
public void invalidateBuildTools() {
project.execute("assemble")
+ // Change our build tools version to 22.0.1 unless it is already the current version,
+ // in that case, downgrade to 21.1.2.
+ // The point is, change the build tools version from what it was when the "assemble" task
+ // was executed right before this comment.
+ String newBuildToolVersion =
+ GradleTestProject.DEFAULT_BUILD_TOOL_VERSION.contentEquals("22.0.1") ?
+ "21.1.2" : "22.0.1"
project.getBuildFile() << """
apply plugin: 'com.android.application'
android {
compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
- buildToolsVersion "19.1.0"
+ buildToolsVersion '$newBuildToolVersion'
}
"""
project.stdout.reset()
project.execute("assemble")
Set<String> affectedTasks = getTasksMatching(INPUT_CHANGED_PATTERN, project.stdout)
- for (String task : tasks) {
- assertTrue(String.format("Expecting task %s to be invalidated", task),
- affectedTasks.contains(task))
- }
+ assert_().withFailureMessage("Expecting tasks to be invalidated").that(affectedTasks)
+ .containsAllIn(GradleTestProject.USE_JACK ? JACK_TASKS : JAVAC_TASKS)
}
private static Set<String> getTasksMatching(Pattern pattern, ByteArrayOutputStream output) {
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedAbiDensityPureSplits.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedAbiDensityPureSplits.groovy
index cd5fbbf..5559724 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedAbiDensityPureSplits.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedAbiDensityPureSplits.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle.integration.application
-
import com.android.build.OutputFile
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.utils.ModelHelper
@@ -34,10 +33,6 @@
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull
import static org.junit.Assert.assertTrue
-
-import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
-
-
/**
* Test drive for the CombinedAbiDensityPureSplits samples test.
*/
@@ -47,10 +42,12 @@
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
.fromTestProject("combinedAbiDensityPureSplits")
+ .addGradleProperties("android.useDeprecatedNdk=true")
.create()
@BeforeClass
static void setup() {
+ GradleTestProject.assumeBuildToolsAtLeast(21)
model = project.executeAndReturnModel("clean", "assembleDebug")
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityAndLanguageTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityAndLanguageTest.groovy
index d726701..80606da 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityAndLanguageTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityAndLanguageTest.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle.integration.application
-
import com.android.build.OutputFile
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.utils.ModelHelper
@@ -33,7 +32,6 @@
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull
import static org.junit.Assert.assertTrue
-
/**
* test driver for combined density and language pure splits test.
*/
@@ -48,6 +46,7 @@
@BeforeClass
static void setup() {
+ GradleTestProject.assumeBuildToolsAtLeast(21)
model = project.executeAndReturnModel("clean", "assembleDebug")
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityWithDisabledLanguageTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityWithDisabledLanguageTest.groovy
index ff311c3..0078173 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityWithDisabledLanguageTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedDensityWithDisabledLanguageTest.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle.integration.application
-
import com.android.build.OutputFile
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.utils.ApkHelper
@@ -35,7 +34,6 @@
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull
import static org.junit.Assert.assertTrue
-
/**
* Tests that produce density pure splits but leave languages in the main APK.
*/
@@ -50,6 +48,7 @@
@BeforeClass
static void setup() {
+ GradleTestProject.assumeBuildToolsAtLeast(21)
project.getBuildFile() << """android {
splits {
language {
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedLanguageWithDisabledDensityTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedLanguageWithDisabledDensityTest.groovy
index f0faa35..684b121 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedLanguageWithDisabledDensityTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CombinedLanguageWithDisabledDensityTest.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle.integration.application
-
import com.android.build.OutputFile
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.utils.ApkHelper
@@ -35,8 +34,6 @@
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull
import static org.junit.Assert.assertTrue
-import static org.junit.Assert.fail;
-
/**
* Tests that create pure splits for language but keep drawable resources in the main APK.
*/
@@ -51,6 +48,7 @@
@BeforeClass
static void setup() {
+ GradleTestProject.assumeBuildToolsAtLeast(21)
project.getBuildFile() << """android {
splits {
density {
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CustomArtifactDepTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CustomArtifactDepTest.groovy
index db4f889..9479622 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CustomArtifactDepTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/CustomArtifactDepTest.groovy
@@ -22,6 +22,7 @@
import com.android.builder.model.Dependencies
import com.android.builder.model.JavaLibrary
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -33,6 +34,7 @@
/**
* Assemble tests for customArtifactDep.
*/
+@CompileStatic
class CustomArtifactDepTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitInLTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitInLTest.groovy
index fa59f50..38e7a28 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitInLTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitInLTest.groovy
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-
-
package com.android.build.gradle.integration.application
import com.android.build.OutputFile
import com.android.build.gradle.integration.common.fixture.GradleTestProject
@@ -25,6 +23,7 @@
import com.android.builder.model.AndroidProject
import com.android.builder.model.Variant
import com.google.common.collect.Sets
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -34,7 +33,6 @@
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull
import static org.junit.Assert.assertTrue
-
/**
* Assemble tests for class densitySplitInL
.
@@ -50,6 +48,7 @@
@BeforeClass
static void setUp() {
+ GradleTestProject.assumeBuildToolsAtLeast(21)
model = project.executeAndReturnModel("clean", "assembleDebug")
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitTest.groovy
index 55fb264..b930382 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitTest.groovy
@@ -75,10 +75,8 @@
}
assertEquals(5, mainArtifact.getOutputs().size())
- for (AndroidArtifactOutput output : mainArtifact.getOutputs()) {
- assertThatZip(output.getMainOutputFile().getOutputFile())
- .contains("res/drawable-mdpi-v4/other.png")
- }
+ File mdpiApk = project.getApk("mdpi", "debug")
+ assertThatZip(mdpiApk).contains("res/drawable-mdpi-v4/other.png")
}
}
@@ -154,6 +152,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitWithPublishNonDefaultTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitWithPublishNonDefaultTest.groovy
index 4b8542e..8377beb 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitWithPublishNonDefaultTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DensitySplitWithPublishNonDefaultTest.groovy
@@ -14,17 +14,15 @@
* limitations under the License.
*/
-
-
-
-
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import groovy.transform.CompileStatic
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+@CompileStatic
class DensitySplitWithPublishNonDefaultTest {
@Rule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesTest.groovy
index fba9160..d5ca959 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for dependencies.
*/
+@CompileStatic
class DependenciesTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesWithVariantsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesWithVariantsTest.groovy
index 432a1b9..e81aab5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesWithVariantsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependenciesWithVariantsTest.groovy
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-
-
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +26,7 @@
/**
* Assemble tests for dependenciesWithVariants.
*/
+@CompileStatic
class DependenciesWithVariantsTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependencyCheckerTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependencyCheckerTest.groovy
index 7f86afb..ad17ddf 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependencyCheckerTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependencyCheckerTest.groovy
@@ -16,6 +16,7 @@
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.gradle.tooling.BuildException
import org.junit.AfterClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for dependencyChecker.
*/
+@CompileStatic
class DependencyCheckerTest {
@ClassRule
static public GradleTestProject httpClientProject = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependencyCyclesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependencyCyclesTest.groovy
new file mode 100644
index 0000000..97c6ed0
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/DependencyCyclesTest.groovy
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.application
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
+import org.gradle.tooling.BuildException
+import org.junit.Rule
+import org.junit.Test
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+import static org.junit.Assert.fail
+
+/**
+ * Check that we recognize dependency cycles.
+ */
+@CompileStatic
+class DependencyCyclesTest {
+
+ @Rule
+ public GradleTestProject project = GradleTestProject.builder()
+ .fromTestProject("applibtest")
+ .captureStdErr(true)
+ .create()
+
+ @Test
+ public void cycle() {
+ project.file("lib/build.gradle") << """
+dependencies {
+ compile project(':app')
+}
+"""
+
+ try {
+ project.execute("clean", ":app:assemble")
+ fail("should throw")
+ } catch (BuildException e) {
+ // expected.
+ }
+
+ String output = project.stderr.toString()
+ assertThat(output).contains(":app -> :lib -> :app")
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/EmptySplitTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/EmptySplitTest.groovy
index 54658d1..218957b 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/EmptySplitTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/EmptySplitTest.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -25,6 +26,7 @@
/**
* Assemble tests for emptySplit.
*/
+@CompileStatic
class EmptySplitTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExternalTestProjectTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExternalTestProjectTest.groovy
index 8c63819..356024b 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExternalTestProjectTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExternalTestProjectTest.groovy
@@ -154,7 +154,7 @@
AndroidProject model = modelMap.get(':app2')
assertNotNull(model)
- SyncIssue issue = assertThat(model).issues().hasSingleIssue(
+ SyncIssue issue = assertThat(model).hasSingleIssue(
SyncIssue.SEVERITY_ERROR,
SyncIssue.TYPE_DEPENDENCY_IS_APK,
'project:app1:unspecified')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExtractAnnotationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExtractAnnotationTest.groovy
index bca6937..d40c63b 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExtractAnnotationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ExtractAnnotationTest.groovy
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-
-
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.builder.model.AndroidProject
+import com.google.common.base.Charsets
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -29,7 +29,14 @@
/**
* Integration test for extracting annotations.
+ * <p>
+ * Tip: To execute just this test after modifying the annotations extraction code:
+ * <pre>
+ * $ cd tools
+ * $ ./gradlew :base:i:test -Dtest.single=ExtractAnnotationTest
+ * </pre>
*/
+@CompileStatic
class ExtractAnnotationTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -49,44 +56,18 @@
@Test
void "check extract annotation"() {
File debugFileOutput = project.file("build/$AndroidProject.FD_INTERMEDIATES/annotations/debug")
+ File classesJar = project.file("build/$AndroidProject.FD_INTERMEDIATES/bundles/debug/classes.jar")
File file = new File(debugFileOutput, "annotations.zip")
//noinspection SpellCheckingInspection
String expectedContent = (""
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<root>\n"
- + " <item name=\"com.android.tests.extractannotations.ExtractTest ExtractTest(int, java.lang.String) 0\">\n"
- + " <annotation name=\"android.support.annotation.IdRes\" />\n"
- + " </item>\n"
- + " <item name=\"com.android.tests.extractannotations.ExtractTest ExtractTest(int, java.lang.String) 1\">\n"
- + " <annotation name=\"android.support.annotation.StringRes\" />\n"
- + " </item>\n"
- + " <item name=\"com.android.tests.extractannotations.ExtractTest int getHiddenMethod()\">\n"
- + " <annotation name=\"android.support.annotation.IdRes\" />\n"
- + " </item>\n"
- + " <item name=\"com.android.tests.extractannotations.ExtractTest int getPrivate()\">\n"
- + " <annotation name=\"android.support.annotation.IdRes\" />\n"
- + " </item>\n"
+ " <item name=\"com.android.tests.extractannotations.ExtractTest int getVisibility()\">\n"
+ " <annotation name=\"android.support.annotation.IntDef\">\n"
+ " <val name=\"value\" val=\"{com.android.tests.extractannotations.ExtractTest.VISIBLE, com.android.tests.extractannotations.ExtractTest.INVISIBLE, com.android.tests.extractannotations.ExtractTest.GONE, 5, 17, com.android.tests.extractannotations.Constants.CONSTANT_1}\" />\n"
+ " </annotation>\n"
+ " </item>\n"
- + " <item name=\"com.android.tests.extractannotations.ExtractTest int resourceTypeMethod(int, int)\">\n"
- + " <annotation name=\"android.support.annotation.StringRes\" />\n"
- + " <annotation name=\"android.support.annotation.IdRes\" />\n"
- + " </item>\n"
- + " <item name=\"com.android.tests.extractannotations.ExtractTest int resourceTypeMethod(int, int) 0\">\n"
- + " <annotation name=\"android.support.annotation.DrawableRes\" />\n"
- + " </item>\n"
- + " <item name=\"com.android.tests.extractannotations.ExtractTest int resourceTypeMethod(int, int) 1\">\n"
- + " <annotation name=\"android.support.annotation.IdRes\" />\n"
- + " <annotation name=\"android.support.annotation.ColorRes\" />\n"
- + " </item>\n"
- // This item should be removed when I start supporting @hide
- + " <item name=\"com.android.tests.extractannotations.ExtractTest java.lang.Object getPackagePrivate()\">\n"
- + " <annotation name=\"android.support.annotation.IdRes\" />\n"
- + " </item>\n"
+ " <item name=\"com.android.tests.extractannotations.ExtractTest java.lang.String getStringMode(int)\">\n"
+ " <annotation name=\"android.support.annotation.StringDef\">\n"
+ " <val name=\"value\" val=\"{com.android.tests.extractannotations.ExtractTest.STRING_1, com.android.tests.extractannotations.ExtractTest.STRING_2, "literalValue", "concatenated"}\" />\n"
@@ -103,15 +84,6 @@
+ " <val name=\"value\" val=\"{com.android.tests.extractannotations.Constants.CONSTANT_1, com.android.tests.extractannotations.Constants.CONSTANT_2}\" />\n"
+ " </annotation>\n"
+ " </item>\n"
- + " <item name=\"com.android.tests.extractannotations.ExtractTest void resourceTypeMethodWithTypeArgs(java.util.Map<java.lang.String,? extends java.lang.Number>, T, int) 0\">\n"
- + " <annotation name=\"android.support.annotation.StringRes\" />\n"
- + " </item>\n"
- + " <item name=\"com.android.tests.extractannotations.ExtractTest void resourceTypeMethodWithTypeArgs(java.util.Map<java.lang.String,? extends java.lang.Number>, T, int) 1\">\n"
- + " <annotation name=\"android.support.annotation.DrawableRes\" />\n"
- + " </item>\n"
- + " <item name=\"com.android.tests.extractannotations.ExtractTest void resourceTypeMethodWithTypeArgs(java.util.Map<java.lang.String,? extends java.lang.Number>, T, int) 2\">\n"
- + " <annotation name=\"android.support.annotation.IdRes\" />\n"
- + " </item>\n"
+ " <item name=\"com.android.tests.extractannotations.ExtractTest void testMask(int) 0\">\n"
+ " <annotation name=\"android.support.annotation.IntDef\">\n"
+ " <val name=\"flag\" val=\"true\" />\n"
@@ -124,9 +96,6 @@
+ " <val name=\"value\" val=\"{0, com.android.tests.extractannotations.Constants.CONSTANT_1, com.android.tests.extractannotations.Constants.CONSTANT_3}\" />\n"
+ " </annotation>\n"
+ " </item>\n"
- + " <item name=\"com.android.tests.extractannotations.ExtractTest.HiddenClass int getHiddenMember()\">\n"
- + " <annotation name=\"android.support.annotation.IdRes\" />\n"
- + " </item>\n"
+ " <item name=\"com.android.tests.extractannotations.TopLevelTypeDef\">\n"
+ " <annotation name=\"android.support.annotation.IntDef\">\n"
+ " <val name=\"flag\" val=\"true\" />\n"
@@ -135,11 +104,28 @@
+ " </item>\n"
+ "</root>\n")
-
assertThatZip(file).containsFileWithContent(
"com/android/tests/extractannotations/annotations.xml", expectedContent)
// check the resulting .aar file to ensure annotations.zip inclusion.
assertThatZip(project.getAar("debug")).contains("annotations.zip")
+
+ // Check typedefs removals:
+
+ // public typedef: should be present
+ assertThatZip(classesJar).contains(
+ "com/android/tests/extractannotations/ExtractTest\$Visibility.class")
+
+ // private/protected typedefs: should have been removed
+ assertThatZip(classesJar).doesNotContain(
+ "com/android/tests/extractannotations/ExtractTest\$Mask.class")
+ assertThatZip(classesJar).doesNotContain(
+ "com/android/tests/extractannotations/ExtractTest\$NonMaskType.class")
+
+ // Make sure the NonMask symbol (from a private typedef) is completely gone from the
+ // outer class
+ assertThatZip(classesJar).containsFileWithoutContent(
+ "com/android/tests/extractannotations/ExtractTest.class",
+ "NonMaskType".getBytes(Charsets.UTF_8));
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutBuildTypeTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutBuildTypeTest.groovy
index 0defc8a..157bc12 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutBuildTypeTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutBuildTypeTest.groovy
@@ -19,6 +19,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.builder.model.AndroidProject
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -29,6 +30,7 @@
/**
* Assemble tests for filteredOutBuildType.
*/
+@CompileStatic
class FilteredOutBuildTypeTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutVariantsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutVariantsTest.groovy
index b18b33e..a322801 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutVariantsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FilteredOutVariantsTest.groovy
@@ -19,6 +19,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.builder.model.AndroidProject
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -30,6 +31,7 @@
/**
* Assemble tests for filteredOutVariants.
*/
+@CompileStatic
class FilteredOutVariantsTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavoredTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavoredTest.groovy
index d95302d..d700363 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavoredTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavoredTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for flavored.
*/
+@CompileStatic
class FlavoredTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavorsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavorsTest.groovy
index 65bae99..ef71787 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavorsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/FlavorsTest.groovy
@@ -27,6 +27,7 @@
import com.android.builder.model.ProductFlavorContainer
import com.android.builder.model.SourceProviderContainer
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -43,6 +44,7 @@
/**
* Assemble tests for flavors.
*/
+@CompileStatic
class FlavorsTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -124,6 +126,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApi2Test.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApi2Test.groovy
index 27f5001..7f2f7f1 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApi2Test.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApi2Test.groovy
@@ -20,6 +20,8 @@
import com.android.builder.model.AndroidProject
import com.android.builder.model.JavaArtifact
import com.android.builder.model.Variant
+import com.android.utils.FileUtils
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -31,6 +33,7 @@
/**
* Assemble tests for genFolderApi2.
*/
+@CompileStatic
class GenFolderApi2Test {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -72,7 +75,7 @@
})
// Unit testing artifact:
- assertThat(variant.extraJavaArtifacts).hasSize(1)
+ assertThat(variant.getExtraJavaArtifacts()).hasSize(1)
JavaArtifact unitTestArtifact = variant.extraJavaArtifacts.first()
def sortedFolders = unitTestArtifact.generatedSourceFolders.sort()
assertThat(sortedFolders).hasSize(2)
@@ -82,4 +85,12 @@
assertThat(sortedFolders[1].absolutePath).endsWith("-2")
}
}
+
+ @Test
+ public void backwardsCompatible() throws Exception {
+ // ATTENTION Author and Reviewers - please make sure required changes to the build file
+ // are backwards compatible before updating this test.
+ assertThat(FileUtils.sha1(project.file("build.gradle")))
+ .isEqualTo("93b7507ce31a087a4efa4cae66474ad320e25b6c")
+ }
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApiTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApiTest.groovy
index 2a321b8..4a0240d 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApiTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/GenFolderApiTest.groovy
@@ -19,11 +19,14 @@
import com.android.builder.model.AndroidArtifact
import com.android.builder.model.AndroidProject
import com.android.builder.model.Variant
+import com.android.utils.FileUtils
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
import static org.junit.Assert.assertNotNull
@@ -31,6 +34,7 @@
/**
* Assemble tests for genFolderApi.
*/
+@CompileStatic
class GenFolderApiTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -121,4 +125,12 @@
assertTrue("custom generated res folder check", found)
}
}
+
+ @Test
+ public void backwardsCompatible() throws Exception {
+ // ATTENTION Author and Reviewers - please make sure required changes to the build file
+ // are backwards compatible before updating this test.
+ assertThat(FileUtils.sha1(project.file("build.gradle")))
+ .isEqualTo("0d39ec75ccdc0f4adbc95b69a639117246bc0574")
+ }
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/JackTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/JackTest.groovy
index 48f8327..fe15388 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/JackTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/JackTest.groovy
@@ -17,20 +17,19 @@
package com.android.build.gradle.integration.application
-
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.google.common.collect.ImmutableList
-import org.gradle.tooling.BuildException
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
import org.junit.experimental.categories.Category
-
/**
* Test Jack integration.
*/
+@CompileStatic
class JackTest {
private final static List<String> JACK_OPTIONS = ImmutableList.of(
"-PCUSTOM_JACK=1",
@@ -56,6 +55,7 @@
@BeforeClass
static void setUp() {
+ GradleTestProject.assumeBuildToolsAtLeast(21, 1, 0)
basic.execute(JACK_OPTIONS, "clean", "assembleDebug")
minify.execute(JACK_OPTIONS, "clean", "assembleDebug")
multiDex.execute(JACK_OPTIONS, "clean", "assembleDebug")
@@ -76,13 +76,13 @@
@Test
@Category(DeviceTests.class)
void "basic connectedCheck"() {
- basic.execute(JACK_OPTIONS, "connectedCheck")
+ basic.executeConnectedCheck(JACK_OPTIONS)
}
@Test
@Category(DeviceTests.class)
void "multiDex connectedCheck"() {
- multiDex.execute(JACK_OPTIONS, "connectedCheck")
+ multiDex.executeConnectedCheck(JACK_OPTIONS)
}
@Test
@@ -90,8 +90,16 @@
minify.execute("testMinified")
}
- @Test(expected = BuildException.class)
+ @Test
void "minify unitTests with Jack"() {
- minify.execute(JACK_OPTIONS, "testMinified")
+ minify.execute(JACK_OPTIONS, "clean", "testMinified")
+
+ // Make sure javac was run.
+ File classesDir = new File(minify.testDir, "/build/intermediates/classes/minified")
+ assert classesDir.exists()
+
+ // Make sure jack was not run.
+ File jillDir = new File(minify.testDir, "/build/intermediates/jill")
+ assert !jillDir.exists()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/JarJarTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/JarJarTest.groovy
new file mode 100644
index 0000000..ad7f519
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/JarJarTest.groovy
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.application
+
+import com.android.build.gradle.integration.common.truth.ApkSubject
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.google.common.collect.Iterators;
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk;
+import static com.android.builder.core.BuilderConstants.DEBUG;
+import static org.junit.Assert.assertEquals;
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject;
+import com.android.builder.model.AndroidArtifact;
+import com.android.builder.model.AndroidArtifactOutput;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.Variant;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Test for the jarjar integration.
+ */
+public class JarJarTest {
+
+ static AndroidProject model
+
+ @ClassRule
+ static public GradleTestProject project = GradleTestProject.builder()
+ .fromTestProject(GradleTestProject.USE_JACK ? "jarjarWithJack" : "jarjarIntegration")
+ .create()
+
+ @BeforeClass
+ static void setUp() {
+ model = project.executeAndReturnModel("clean", "assembleDebug")
+ }
+
+ @AfterClass
+ static void cleanUp() {
+ project = null
+ model = null
+ }
+
+ @Test
+ void "check repackaged gson library"() {
+ Collection<Variant> variants = model.getVariants()
+ assertEquals("Variant Count", 2, variants.size())
+
+ // get the main artifact of the debug artifact
+ Variant debugVariant = ModelHelper.getVariant(variants, DEBUG)
+ assertNotNull("debug Variant null-check", debugVariant)
+ AndroidArtifact debugMainArtifact = debugVariant.getMainArtifact()
+ assertNotNull("Debug main info null-check", debugMainArtifact)
+
+ // get the outputs.
+ Collection<AndroidArtifactOutput> debugOutputs = debugMainArtifact.getOutputs()
+ assertNotNull(debugOutputs)
+ assertEquals(1, debugOutputs.size())
+
+ // make sure the Gson library has been renamed and the original one is not present.
+ File outputFile = Iterators.getOnlyElement(debugOutputs.iterator()).mainOutputFile.
+ getOutputFile()
+ assertThatApk(outputFile).containsClass("Lcom/google/repacked/gson/Gson;");
+ assertThatApk(outputFile).doesNotContainClass("Lcom/google/gson/Gson;")
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ManifestMergingTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ManifestMergingTest.groovy
index b32c1b4..b751224 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ManifestMergingTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ManifestMergingTest.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.ClassRule
import org.junit.Test
@@ -28,6 +29,7 @@
/**
* Integration tests for manifest merging.
*/
+@CompileStatic
class ManifestMergingTest {
@ClassRule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MessageRewriteTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MessageRewriteTest.groovy
new file mode 100644
index 0000000..6c90eb6
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MessageRewriteTest.groovy
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.application
+
+import com.android.build.gradle.integration.common.fixture.TemporaryProjectModification
+import com.android.builder.model.AndroidProject
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.google.common.base.Charsets
+import com.google.common.io.Files
+import groovy.transform.CompileStatic
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+
+/**
+ * Tests the error message rewriting logic.
+ */
+@CompileStatic
+class MessageRewriteTest {
+
+ private static List<String> INVOKED_FROM_IDE_ARGS =
+ Collections.singletonList("-P" + AndroidProject.PROPERTY_INVOKED_FROM_IDE + "=true")
+
+ @ClassRule
+ static public GradleTestProject project = GradleTestProject.builder()
+ .fromTestProject("flavored")
+ .withoutNdk()
+ .captureStdOut(true)
+ .captureStdErr(true)
+ .create()
+
+ @BeforeClass
+ public static void assemble() {
+ project.execute('assembleDebug')
+ }
+
+ @Test
+ public void "invalid layout file"() {
+ TemporaryProjectModification.doTest(project) {
+ it.replaceInFile("src/main/res/layout/main.xml", "</LinearLayout>", "");
+ project.getStderr().reset()
+ project.executeExpectingFailure(INVOKED_FROM_IDE_ARGS, 'assembleF1Debug')
+ String err = project.getStderr().toString()
+ assertThat(err).contains("src/main/res/layout/main.xml")
+ }
+
+ project.execute('assembleDebug')
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MigratedTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MigratedTest.groovy
index 47de17e..03443a2 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MigratedTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MigratedTest.groovy
@@ -23,6 +23,7 @@
import com.android.builder.model.AndroidProject
import com.android.builder.model.ProductFlavorContainer
import com.android.builder.model.SourceProviderContainer
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -37,6 +38,7 @@
/**
* Assemble tests for migrated.
*/
+@CompileStatic
class MigratedTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -100,6 +102,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MinifyLibAndAppWithJavaResTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MinifyLibAndAppWithJavaResTest.groovy
new file mode 100644
index 0000000..669f194
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MinifyLibAndAppWithJavaResTest.groovy
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.application;
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.truth.ApkSubject;
+import com.android.builder.model.AndroidProject;
+
+import org.junit.AfterClass
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import groovy.transform.CompileStatic
+
+import static org.junit.Assert.assertNotNull;
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
+
+/**
+ * Tests that ensure that java resources files accessed with a relative or absolute path are
+ * packaged correctly.
+ */
+@CompileStatic
+public class MinifyLibAndAppWithJavaResTest {
+
+ static Map<String, AndroidProject> models
+
+ @ClassRule
+ static public GradleTestProject project = GradleTestProject.builder()
+ .fromTestProject("minifyLibWithJavaRes")
+ .create()
+
+ @BeforeClass
+ static void setUp() {
+ models = project.executeAndReturnMultiModel("clean", "assemble")
+ }
+
+ @AfterClass
+ static void cleanUp() {
+ project = null
+ models = null
+ }
+
+ @Test
+ void testDebugPackaging() {
+ File debugApk = project.getSubproject("app").getApk("debug")
+ assertNotNull(debugApk)
+ ApkSubject apkSubject = assertThatApk(debugApk);
+ // check that resources with relative path lookup code have a matching obfuscated package
+ // name.
+ apkSubject.contains("com/android/tests/util/resources.properties")
+ apkSubject.contains("com/android/tests/other/resources.properties")
+ // check that resources with absolute path lookup remain in the original package name.
+ apkSubject.contains("com/android/tests/util/another.properties")
+ apkSubject.contains("com/android/tests/other/some.xml")
+ apkSubject.contains("com/android/tests/other/another.properties")
+ }
+
+ @Test
+ void testReleasePackaging() {
+ Assume.assumeFalse("Ignore until Jack fixed proguard confusion", GradleTestProject.USE_JACK)
+ File releaseApk = project.getSubproject("app").getApk("release")
+ assertNotNull(releaseApk)
+ ApkSubject apkSubject = assertThatApk(releaseApk);
+ // check that resources with relative path lookup code have a matching obfuscated package
+ // name.
+ apkSubject.contains("com/android/tests/b/resources.properties")
+ apkSubject.contains("com/android/tests/a/resources.properties")
+ // check that resources with absolute path lookup remain in the original package name.
+ apkSubject.contains("com/android/tests/util/another.properties")
+ apkSubject.contains("com/android/tests/other/some.xml")
+ apkSubject.contains("com/android/tests/other/another.properties")
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MinifyTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MinifyTest.groovy
index 5fc3eb0..3c0a87a 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MinifyTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MinifyTest.groovy
@@ -65,7 +65,7 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
@Test
@@ -97,7 +97,6 @@
.findAll { !it.startsWith("org/hamcrest") }
assertThat(testClassFiles).containsExactly(
- "LICENSE.txt", // Comes from hamcrest, is not packaged.
"com/android/tests/basic/MainTest.class",
"com/android/tests/basic/UnusedTestClass.class",
"com/android/tests/basic/UsedTestClass.class",
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ModelTest.groovy
index 0b97a2e..cc983d5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ModelTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ModelTest.groovy
@@ -15,9 +15,9 @@
*/
package com.android.build.gradle.integration.application
-
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.android.builder.model.AndroidProject
import com.android.builder.model.SyncIssue
import groovy.transform.CompileStatic
import org.junit.Before
@@ -55,7 +55,8 @@
compile 'foo:bar:1.2.3'
}
"""
- assertThat(project.getSingleModelIgnoringSyncIssues()).issues().hasSingleIssue(
+ AndroidProject model = project.getSingleModelIgnoringSyncIssues()
+ assertThat(model).hasSingleIssue(
SyncIssue.SEVERITY_ERROR,
SyncIssue.TYPE_UNRESOLVED_DEPENDENCY,
'foo:bar:1.2.3')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiDexTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiDexTest.groovy
index 8205a57..6528a3f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiDexTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiDexTest.groovy
@@ -19,6 +19,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.google.common.io.Files
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -31,6 +32,7 @@
/**
* Assemble tests for multiDex.
*/
+@CompileStatic
class MultiDexTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -39,6 +41,7 @@
@BeforeClass
static void setUp() {
+ GradleTestProject.assumeBuildToolsAtLeast(21)
project.execute("clean", "assembleDebug")
}
@@ -71,6 +74,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiProjectTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiProjectTest.groovy
index 5f1ed2a..d799126 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiProjectTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiProjectTest.groovy
@@ -22,6 +22,7 @@
import com.android.builder.model.Dependencies
import com.android.builder.model.JavaLibrary
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -32,6 +33,7 @@
/**
* Assemble tests for multiproject.
*/
+@CompileStatic
class MultiProjectTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiresTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiresTest.groovy
index de9d292..392e6c7 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiresTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/MultiresTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for multires.
*/
+@CompileStatic
class MultiresTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NdkJniPureSplitLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NdkJniPureSplitLibTest.groovy
index b47a2fa..6c696eb 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NdkJniPureSplitLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NdkJniPureSplitLibTest.groovy
@@ -32,10 +32,12 @@
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
.fromTestProject("ndkJniPureSplitLib")
+ .addGradleProperties("android.useDeprecatedNdk=true")
.create()
@BeforeClass
static void setUp() {
+ GradleTestProject.assumeBuildToolsAtLeast(21)
project.execute("clean", ":app:assembleDebug")
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoCruncherTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoCruncherTest.groovy
index 1d4f49b..358b2c4 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoCruncherTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoCruncherTest.groovy
@@ -17,17 +17,19 @@
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.ClassRule
import org.junit.Test
import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
-import static junit.framework.Assert.assertEquals
-import static junit.framework.Assert.assertNotSame
-import static junit.framework.Assert.assertTrue
+import static org.junit.Assert.assertEquals
+import static org.junit.Assert.assertNotSame
+import static org.junit.Assert.assertTrue
/**
* Integration test for the cruncherEnabled settings.
*/
+@CompileStatic
class NoCruncherTest {
@ClassRule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoPreDexTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoPreDexTest.groovy
index 3cd4fa2..e19be55 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoPreDexTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/NoPreDexTest.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -25,6 +26,7 @@
/**
* Assemble tests for noPreDex.
*/
+@CompileStatic
class NoPreDexTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/OptionalLibraryTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/OptionalLibraryTest.groovy
new file mode 100644
index 0000000..6fb08be
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/OptionalLibraryTest.groovy
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.application
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.SyncIssue
+import com.android.sdklib.IAndroidTarget
+import com.android.sdklib.SdkManager
+import com.android.utils.NullLogger
+import groovy.transform.CompileStatic
+import org.junit.After
+import org.junit.Assume
+import org.junit.Rule
+import org.junit.Test
+
+import static com.android.SdkConstants.FN_FRAMEWORK_LIBRARY
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+/**
+ * Test for BuildConfig field declared in build type, flavors, and variant and how they
+ * override each other
+ */
+@CompileStatic
+class OptionalLibraryTest {
+ @Rule
+ public GradleTestProject project = GradleTestProject.builder()
+ .fromTestApp(new HelloWorldApp())
+ .create()
+
+ @After
+ void cleanUp() {
+ project = null
+ }
+
+ @Test
+ void "test unknown useLibrary trigger sync issue"() {
+ Assume.assumeNotNull("Next platform missing", System.getenv("ANDROID_NEXT_PLATFORM"));
+
+ project.getBuildFile() << """
+ apply plugin: 'com.android.application'
+
+ android {
+ compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+ useLibrary 'foo'
+
+ }
+ """.stripIndent()
+
+ AndroidProject project = project.getSingleModelIgnoringSyncIssues()
+
+ assertThat(project).hasSingleIssue(
+ SyncIssue.SEVERITY_ERROR,
+ SyncIssue.TYPE_OPTIONAL_LIB_NOT_FOUND,
+ 'foo');
+ }
+
+ @Test
+ void "test using optional library"() {
+ Assume.assumeNotNull("Next platform missing", System.getenv("ANDROID_NEXT_PLATFORM"));
+
+ project.getBuildFile() << """
+ apply plugin: 'com.android.application'
+
+ android {
+ compileSdkVersion 23
+ buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+ useLibrary 'org.apache.http.legacy'
+
+ }
+ """.stripIndent()
+
+ AndroidProject project = project.getSingleModel()
+
+ // get the SDK folder
+ File sdkLocation = new File(System.getenv("ANDROID_HOME"))
+ SdkManager sdkManager = SdkManager.createManager(
+ sdkLocation.getAbsolutePath(),
+ new NullLogger())
+ IAndroidTarget target = sdkManager.getTargetFromHashString('android-23')
+
+ File targetLocation = new File(target.getLocation())
+
+ assertThat(project.getBootClasspath()).containsExactly(
+ new File(targetLocation, FN_FRAMEWORK_LIBRARY).getAbsolutePath(),
+ new File(targetLocation, "optional/org.apache.http.legacy.jar").getAbsolutePath())
+ }
+
+ @Test
+ void "test not using optional library"() {
+ Assume.assumeNotNull("Next platform missing", System.getenv("ANDROID_NEXT_PLATFORM"));
+
+ project.getBuildFile() << """
+ apply plugin: 'com.android.application'
+
+ android {
+ compileSdkVersion 23
+ buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ }
+ """.stripIndent()
+
+ AndroidProject project = project.getSingleModel()
+
+ // get the SDK folder
+ File sdkLocation = new File(System.getenv("ANDROID_HOME"))
+ SdkManager sdkManager = SdkManager.createManager(
+ sdkLocation.getAbsolutePath(),
+ new NullLogger())
+ IAndroidTarget target = sdkManager.getTargetFromHashString('android-23')
+
+ File targetLocation = new File(target.getLocation())
+
+ assertThat(project.getBootClasspath()).containsExactly(
+ new File(targetLocation, FN_FRAMEWORK_LIBRARY).getAbsolutePath())
+ }
+
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay1Test.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay1Test.groovy
index f462302..cfa0e28 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay1Test.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay1Test.groovy
@@ -20,6 +20,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.utils.ImageHelper
import com.android.builder.model.AndroidProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -29,6 +30,7 @@
/**
* Assemble tests for overlay1.
*/
+@CompileStatic
class Overlay1Test {
@ClassRule
@@ -63,7 +65,7 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay2Test.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay2Test.groovy
index 33fcdb9..5ddb6d9 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay2Test.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay2Test.groovy
@@ -20,6 +20,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.utils.ImageHelper
import com.android.builder.model.AndroidProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -29,6 +30,7 @@
/**
* Assemble tests for overlay2.
*/
+@CompileStatic
class Overlay2Test {
@ClassRule
@@ -67,6 +69,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay3Test.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay3Test.groovy
index 88d06c3..3206c65 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay3Test.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/Overlay3Test.groovy
@@ -20,6 +20,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.utils.ImageHelper
import com.android.builder.model.AndroidProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -29,6 +30,7 @@
/**
* Assemble tests for overlay3.
*/
+@CompileStatic
class Overlay3Test {
@ClassRule
@@ -52,7 +54,7 @@
int RED = ImageHelper.RED
File drawableOutput = project.file(
- "build/" + AndroidProject.FD_INTERMEDIATES + "/res/merged/freebeta/debug/drawable")
+ "build/" + AndroidProject.FD_INTERMEDIATES + "/res/merged/freeBeta/debug/drawable")
ImageHelper.checkImageColor(drawableOutput, "no_overlay.png", GREEN)
ImageHelper.checkImageColor(drawableOutput, "debug_overlay.png", GREEN)
@@ -63,7 +65,7 @@
ImageHelper.checkImageColor(drawableOutput, "free_normal_overlay.png", RED)
drawableOutput = project.file(
- "build/" + AndroidProject.FD_INTERMEDIATES + "/res/merged/freenormal/debug/drawable")
+ "build/" + AndroidProject.FD_INTERMEDIATES + "/res/merged/freeNormal/debug/drawable")
ImageHelper.checkImageColor(drawableOutput, "no_overlay.png", GREEN)
ImageHelper.checkImageColor(drawableOutput, "debug_overlay.png", GREEN)
@@ -74,7 +76,7 @@
ImageHelper.checkImageColor(drawableOutput, "free_normal_overlay.png", GREEN)
drawableOutput = project.file(
- "build/" + AndroidProject.FD_INTERMEDIATES + "/res/merged/paidbeta/debug/drawable")
+ "build/" + AndroidProject.FD_INTERMEDIATES + "/res/merged/paidBeta/debug/drawable")
ImageHelper.checkImageColor(drawableOutput, "no_overlay.png", GREEN)
ImageHelper.checkImageColor(drawableOutput, "debug_overlay.png", GREEN)
@@ -93,6 +95,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PackagingOptionsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PackagingOptionsTest.groovy
index 07694a0..d07f43c 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PackagingOptionsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PackagingOptionsTest.groovy
@@ -15,47 +15,198 @@
*/
package com.android.build.gradle.integration.application
-import com.android.build.gradle.integration.common.category.DeviceTests
+
import com.android.build.gradle.integration.common.fixture.GradleTestProject
-import org.junit.AfterClass
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.EmptyAndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import com.google.common.io.Files
+import groovy.transform.CompileStatic
+import org.junit.Before
import org.junit.BeforeClass
import org.junit.ClassRule
+import org.junit.Rule
import org.junit.Test
-import org.junit.experimental.categories.Category
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
/**
* Assemble tests for packagingOptions.
+ *
+ * Creates two jar files and test various packaging options.
*/
+@CompileStatic
class PackagingOptionsTest {
+
+ // Projects to create jar files.
+ private static AndroidTestApp jarProject1 = new EmptyAndroidTestApp()
+ static {
+ jarProject1.addFile(new TestSourceFile("", "build.gradle", "apply plugin: 'java'"))
+ jarProject1.addFile(new TestSourceFile("src/main/resources", "conflict.txt", "foo"))
+ }
+ private static AndroidTestApp jarProject2 = new EmptyAndroidTestApp()
+ static {
+ jarProject2.addFile(new TestSourceFile("", "build.gradle", "apply plugin: 'java'"))
+ jarProject2.addFile(new TestSourceFile("src/main/resources", "conflict.txt", "foo"))
+ // add an extra file so that jar1 is different from jar2.
+ jarProject2.addFile(new TestSourceFile("src/main/resources", "dummy2.txt", "bar"))
+ }
+
@ClassRule
- static public GradleTestProject project = GradleTestProject.builder()
- .fromTestProject("packagingOptions")
+ public static GradleTestProject jar1 = GradleTestProject.builder()
+ .fromTestApp(jarProject1)
+ .withName("jar1")
+ .create()
+ @ClassRule
+ public static GradleTestProject jar2 = GradleTestProject.builder()
+ .fromTestApp(jarProject2)
+ .withName("jar2")
.create()
@BeforeClass
- static void setUp() {
+ static void createJars() {
+ jar1.execute("assemble")
+ jar2.execute("assemble")
+ }
+
+
+ // Main test project.
+ @Rule
+ public GradleTestProject project = GradleTestProject.builder()
+ .fromTestApp(new HelloWorldApp())
+ .create()
+
+ @Before
+ void setUp() {
+ Files.copy(jar1.file("build/libs/jar1.jar"), project.file("jar1.jar"))
+ Files.copy(jar2.file("build/libs/jar2.jar"), project.file("jar2.jar"))
+
+ project.getBuildFile() << """
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+}
+"""
+
+ }
+
+ @Test
+ void "check pickFirst"() {
+ project.getBuildFile() << """
+android {
+ packagingOptions {
+ pickFirst 'conflict.txt'
+ }
+}
+
+dependencies {
+ compile files('jar1.jar')
+ compile files('jar2.jar')
+}
+"""
project.execute("clean", "assembleDebug")
- }
-
- @AfterClass
- static void cleanUp() {
- project = null
+ assertThatZip(project.getApk("debug")).contains("conflict.txt")
}
@Test
- void lint() {
- project.execute("lint")
+ void "check exclude"() {
+ project.getBuildFile() << """
+android {
+ packagingOptions {
+ exclude 'conflict.txt'
+ }
+}
+
+dependencies {
+ compile files('jar1.jar')
+ compile files('jar2.jar')
+}
+"""
+ project.execute("clean", "assembleDebug")
+ assertThatZip(project.getApk("debug")).doesNotContain("conflict.txt")
}
@Test
- void "check packinging"() {
- assertThatZip(project.getApk("debug")).contains("first_pick.txt")
+ void "check exclude on direct files"() {
+ project.getBuildFile() << """
+android {
+ packagingOptions {
+ exclude 'lib/x86/libconflict.so'
+ exclude 'conflict.txt'
+ }
+}
+"""
+ createFile('src/main/jniLibs/x86/libconflict.so')
+ createFile('src/main/resources/conflict.txt')
+ project.execute("clean", "assembleDebug")
+ assertThatZip(project.getApk("debug")).doesNotContain('lib/x86/libconflict.so')
+ assertThatZip(project.getApk("debug")).doesNotContain('conflict.txt')
}
@Test
- @Category(DeviceTests.class)
- void connectedCheck() {
- project.execute("connectedCheck")
+ void "check merge on jar entries"() {
+ project.getBuildFile() << """
+android {
+ packagingOptions {
+ merge 'conflict.txt'
+ }
+}
+
+dependencies {
+ compile files('jar1.jar')
+ compile files('jar2.jar')
+}
+"""
+ project.execute("clean", "assembleDebug")
+
+ assertThatZip(project.getApk("debug")).containsFileWithContent("conflict.txt", "foofoo")
+ }
+
+ @Test
+ void "check merge on direct files"() {
+ project.getBuildFile() << """
+android {
+ packagingOptions {
+ // Doesn't make sense to merge native library, but it should work.
+ merge 'lib/x86/libconflict.so'
+ }
+}
+"""
+ createFile('src/main/jniLibs/x86/libconflict.so') << "foo"
+ createFile('src/debug/jniLibs/x86/libconflict.so') << "foo"
+ project.execute("clean", "assembleDebug")
+ assertThatZip(project.getApk("debug")).containsFileWithContent("lib/x86/libconflict.so", "foofoo")
+ }
+
+ @Test
+ void "check merge on a direct file and a jar entry"() {
+ project.getBuildFile() << """
+android {
+ packagingOptions {
+ merge 'conflict.txt'
+ }
+}
+
+dependencies {
+ compile files('jar1.jar')
+}
+"""
+ createFile('src/main/resources/conflict.txt') << "foo"
+ project.execute("clean", "assembleDebug")
+ assertThatZip(project.getApk("debug")).containsFileWithContent("conflict.txt", "foofoo")
+ }
+
+ /**
+ * Create a new empty file including its directories.
+ */
+ private File createFile(String filename) {
+ File newFile = project.file(filename)
+ newFile.getParentFile().mkdirs()
+ newFile.createNewFile()
+ assertThat(newFile).exists()
+ return newFile
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ParentLibsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ParentLibsTest.groovy
index ffeac1f..caceedf 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ParentLibsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ParentLibsTest.groovy
@@ -20,6 +20,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.builder.model.AndroidProject
import com.google.common.collect.ImmutableList
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -29,9 +30,8 @@
/**
* Assemble tests for parentLibTest
*/
+@CompileStatic
public class ParentLibsTest {
- static AndroidProject model
-
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
.fromTestProject("parentLibsTest")
@@ -39,14 +39,13 @@
@BeforeClass
static void setUp() {
- model = project.execute(ImmutableList.of("-p", "app"),
+ project.execute(ImmutableList.of("-p", "app"),
"clean", "assembleDebug")
}
@AfterClass
static void cleanUp() {
project = null
- model = null
}
@Test
@@ -57,6 +56,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute(ImmutableList.of("-p", "app"), "connectedCheck")
+ project.executeConnectedCheck(ImmutableList.of("-p", "app"))
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PkgOverrideTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PkgOverrideTest.groovy
index a1ec339..4c0263f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PkgOverrideTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PkgOverrideTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for pkgOverride.
*/
+@CompileStatic
class PkgOverrideTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PlaceholderInLibsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PlaceholderInLibsTest.groovy
index c97a466..4e266ac 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PlaceholderInLibsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PlaceholderInLibsTest.groovy
@@ -23,6 +23,7 @@
import com.android.builder.model.AndroidArtifactOutput
import com.android.builder.model.AndroidProject
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.Assert
import org.junit.BeforeClass
@@ -35,6 +36,7 @@
/**
* Test for unresolved placeholders in libraries.
*/
+@CompileStatic
class PlaceholderInLibsTest {
static Map<String, AndroidProject> models
@@ -77,7 +79,7 @@
assertEquals(1, output.getOutputs().size())
List<String> apkBadging =
- ApkHelper.getApkBadging(output.getOutputs().getAt(0).getOutputFile());
+ ApkHelper.getApkBadging(output.getOutputs().iterator().next().getOutputFile());
for (String line : apkBadging) {
if (line.contains("uses-permission: name=" +
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PrivateResourceTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PrivateResourceTest.groovy
index 8f1f5ba..1bdf40d 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PrivateResourceTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PrivateResourceTest.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for privateResources.
*/
+@CompileStatic
class PrivateResourceTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -48,6 +50,8 @@
String expected = """\
string mylib_app_name
string mylib_public_string
+string shared_name
+id shared_name
"""
assertThatZip(project.getSubproject('mylibrary').getAar("release")).containsFileWithContent('public.txt', expected);
assertThatZip(project.getSubproject('mylibrary').getAar("debug")).containsFileWithContent('public.txt', expected);
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PseudoLocalizationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PseudoLocalizationTest.groovy
index 7c04f7b..1916c2f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PseudoLocalizationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/PseudoLocalizationTest.groovy
@@ -35,6 +35,7 @@
@BeforeClass
static void setUp() {
+ GradleTestProject.assumeBuildToolsAtLeast(21)
project.execute("clean", "assembleDebug")
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenamedApkTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenamedApkTest.groovy
index f03df6f..2340faf 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenamedApkTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenamedApkTest.groovy
@@ -21,6 +21,7 @@
import com.android.builder.model.AndroidArtifactOutput
import com.android.builder.model.AndroidProject
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -33,6 +34,7 @@
/**
* Assemble tests for renamedApk.
*/
+@CompileStatic
class RenamedApkTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptMultiSrcTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptMultiSrcTest.groovy
index ae7f452..5145c9a 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptMultiSrcTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptMultiSrcTest.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -25,6 +26,7 @@
/**
* Assemble tests for renderscriptMultiSrc.
*/
+@CompileStatic
class RenderscriptMultiSrcTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptTest.groovy
index eadf409..4ae1e5f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RenderscriptTest.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -25,6 +26,7 @@
/**
* Assemble tests for renderscript.
*/
+@CompileStatic
class RenderscriptTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RepoTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RepoTest.groovy
index c292651..06272b9 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RepoTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RepoTest.groovy
@@ -16,12 +16,14 @@
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.ClassRule
import org.junit.Test
/**
* Integration test for uploadAchives with multiple projects.
*/
+@CompileStatic
class RepoTest {
@ClassRule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTest.groovy
index 0cf6f42..155f81e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTest.groovy
@@ -28,13 +28,14 @@
import com.google.common.base.Charsets
import com.google.common.collect.Maps
import com.google.common.io.Files
-import junit.framework.Assert
+import groovy.transform.CompileStatic
+import org.junit.Assert
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
-import static junit.framework.Assert.assertEquals
+import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull
@@ -42,6 +43,7 @@
* Test for Res Values declared in build type, flavors, and variant and how they
* override each other
*/
+@CompileStatic
class ResValueTest {
@ClassRule
public static GradleTestProject project = GradleTestProject.builder()
@@ -117,13 +119,13 @@
<!-- Automatically generated file. DO NOT MODIFY -->
<!-- Values from the variant -->
- <string name="VALUE_VARIANT">1000</string>
+ <string name="VALUE_VARIANT" translatable="false">1000</string>
<!-- Values from build type: debug -->
- <string name="VALUE_DEBUG">100</string>
+ <string name="VALUE_DEBUG" translatable="false">100</string>
<!-- Values from product flavor: flavor1 -->
- <string name="VALUE_FLAVOR">10</string>
+ <string name="VALUE_FLAVOR" translatable="false">10</string>
<!-- Values from default config. -->
- <string name="VALUE_DEFAULT">1</string>
+ <string name="VALUE_DEFAULT" translatable="false">1</string>
</resources>
"""
@@ -149,13 +151,13 @@
<!-- Automatically generated file. DO NOT MODIFY -->
<!-- Values from the variant -->
- <string name="VALUE_VARIANT">1000</string>
+ <string name="VALUE_VARIANT" translatable="false">1000</string>
<!-- Values from build type: debug -->
- <string name="VALUE_DEBUG">100</string>
+ <string name="VALUE_DEBUG" translatable="false">100</string>
<!-- Values from product flavor: flavor2 -->
- <string name="VALUE_FLAVOR">20</string>
+ <string name="VALUE_FLAVOR" translatable="false">20</string>
<!-- Values from default config. -->
- <string name="VALUE_DEFAULT">1</string>
+ <string name="VALUE_DEFAULT" translatable="false">1</string>
</resources>
"""
@@ -181,11 +183,11 @@
<!-- Automatically generated file. DO NOT MODIFY -->
<!-- Values from product flavor: flavor1 -->
- <string name="VALUE_DEBUG">10</string>
- <string name="VALUE_FLAVOR">10</string>
- <string name="VALUE_VARIANT">10</string>
+ <string name="VALUE_DEBUG" translatable="false">10</string>
+ <string name="VALUE_FLAVOR" translatable="false">10</string>
+ <string name="VALUE_VARIANT" translatable="false">10</string>
<!-- Values from default config. -->
- <string name="VALUE_DEFAULT">1</string>
+ <string name="VALUE_DEFAULT" translatable="false">1</string>
</resources>
"""
@@ -211,11 +213,11 @@
<!-- Automatically generated file. DO NOT MODIFY -->
<!-- Values from product flavor: flavor2 -->
- <string name="VALUE_DEBUG">20</string>
- <string name="VALUE_FLAVOR">20</string>
- <string name="VALUE_VARIANT">20</string>
+ <string name="VALUE_DEBUG" translatable="false">20</string>
+ <string name="VALUE_FLAVOR" translatable="false">20</string>
+ <string name="VALUE_VARIANT" translatable="false">20</string>
<!-- Values from default config. -->
- <string name="VALUE_DEFAULT">1</string>
+ <string name="VALUE_DEFAULT" translatable="false">1</string>
</resources>
"""
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTypeTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTypeTest.groovy
index e672f3d..917087a 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTypeTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ResValueTypeTest.groovy
@@ -21,6 +21,7 @@
import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -33,6 +34,7 @@
/**
* Test resValue for string type is treated as String.
*/
+@CompileStatic
class ResValueTypeTest {
static AndroidTestApp app = new HelloWorldApp()
static {
@@ -121,7 +123,7 @@
<plurals name="resPlurals">s</plurals>
- <string name="resString">00</string>
+ <string name="resString" translatable="false">00</string>
<style name="resStyle">foo</style>
@@ -133,6 +135,7 @@
@Test
@Category(DeviceTests.class)
void "check resValue is treated as string"() {
- project.execute("clean", "connectedAndroidTest")
+ project.execute("clean")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsEnabledAnnotationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsEnabledAnnotationTest.groovy
index cf514a5..7b0f1d7 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsEnabledAnnotationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsEnabledAnnotationTest.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Integration test for extracting RS enabled annotations.
*/
+@CompileStatic
class RsEnabledAnnotationTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsSupportModeTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsSupportModeTest.groovy
index 82f1881..01b3d5f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsSupportModeTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/RsSupportModeTest.groovy
@@ -24,6 +24,7 @@
import com.android.builder.model.Dependencies
import com.android.builder.model.JavaLibrary
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -36,10 +37,12 @@
/**
* Assemble tests for rsSupportMode.
*/
+@CompileStatic
class RsSupportModeTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
.fromTestProject("rsSupportMode")
+ .addGradleProperties("android.useDeprecatedNdk=true")
.create()
static AndroidProject model
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ShrinkTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ShrinkTest.groovy
index 818515a..93f22bd 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ShrinkTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ShrinkTest.groovy
@@ -21,6 +21,7 @@
import com.google.common.base.Joiner
import com.google.common.collect.Lists
import com.google.common.io.ByteStreams
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -40,6 +41,7 @@
/**
* Assemble tests for shrink.
*/
+@CompileStatic
class ShrinkTest {
@ClassRule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/SigningConfigTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/SigningConfigTest.groovy
index 1c7dc4f..16b7af7 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/SigningConfigTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/SigningConfigTest.groovy
@@ -20,6 +20,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.google.common.collect.ImmutableList
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.ClassRule
import org.junit.Test
@@ -33,6 +34,7 @@
/**
* Integration test with signing overrider.
*/
+@CompileStatic
class SigningConfigTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TestWithDepTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TestWithDepTest.groovy
index e5571d5..9b882bf 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TestWithDepTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TestWithDepTest.groovy
@@ -22,6 +22,7 @@
import com.android.builder.model.AndroidProject
import com.android.builder.model.Dependencies
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -35,6 +36,7 @@
/**
* Assemble tests for testWithDep that loads the model but doesn't build.
*/
+@CompileStatic
class TestWithDepTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ThirdPartyTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ThirdPartyTest.groovy
index 83cd61f..39463ce 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ThirdPartyTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/ThirdPartyTest.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.ClassRule
import org.junit.Test
@@ -24,6 +25,7 @@
/**
* Assemble tests for 3rdPartyTests.
*/
+@CompileStatic
class ThirdPartyTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TictactoeTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TictactoeTest.groovy
index 963c013..ba713a2 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TictactoeTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/TictactoeTest.groovy
@@ -21,6 +21,7 @@
import com.android.builder.model.AndroidProject
import com.android.builder.model.Dependencies
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -33,6 +34,7 @@
/**
* Assemble tests for tictactoe.
*/
+@CompileStatic
class TictactoeTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/UnitTestingModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/UnitTestingModelTest.groovy
index 4d24560..a15808a 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/UnitTestingModelTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/UnitTestingModelTest.groovy
@@ -16,11 +16,10 @@
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
-import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
import com.android.builder.model.AndroidProject
import com.android.builder.model.ArtifactMetaData
import com.android.builder.model.JavaArtifact
-import org.junit.Before
+import groovy.transform.CompileStatic
import org.junit.Rule
import org.junit.Test
@@ -29,28 +28,17 @@
/**
* Tests for the unit-tests related parts of the builder model.
*/
+@CompileStatic
class UnitTestingModelTest {
@Rule
public GradleTestProject project = GradleTestProject.builder()
- .fromTestApp(new HelloWorldApp())
+ .fromTestProject("unitTestingComplexProject")
.create();
- @Before
- public void setUp() {
- project.buildFile << """
-apply plugin: 'com.android.application'
-
-android {
- compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
- buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
-}
-"""
- }
-
@Test
public void "Unit testing artifacts are included in the model"() {
- AndroidProject model = project.singleModel
+ AndroidProject model = project.allModels[":app"]
assertThat(model.extraArtifacts*.name).containsExactly(
AndroidProject.ARTIFACT_ANDROID_TEST,
@@ -95,13 +83,12 @@
@Test
public void flavors() throws Exception {
- project.buildFile << """
+ project.getSubproject("app").buildFile << """
android {
productFlavors { paid; free }
}
"""
-
- AndroidProject model = project.singleModel
+ AndroidProject model = project.allModels[":app"]
assertThat(model.productFlavors).hasSize(2)
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/VectorDrawableTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/VectorDrawableTest.groovy
index 6ff0de7..9b39669 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/VectorDrawableTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/VectorDrawableTest.groovy
@@ -16,14 +16,17 @@
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
-import com.google.common.base.Charsets
+import com.android.utils.FileUtils
import com.google.common.io.Files
import groovy.transform.CompileStatic
+import org.junit.BeforeClass
import org.junit.Rule
import org.junit.Test
import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertWithMessage
+import static com.google.common.base.Charsets.UTF_8
/**
* Tests for the PNG generation feature.
*/
@@ -34,23 +37,79 @@
.fromTestProject("vectorDrawables")
.create()
+ @BeforeClass
+ public static void checkBuildTools() {
+ GradleTestProject.assumeBuildToolsAtLeast(21)
+ }
+
@Test
public void "vector file is moved and PNGs are generated"() throws Exception {
project.execute("clean", "assembleDebug")
File apk = project.getApk("debug")
assertThatApk(apk).containsResource("drawable/icon.png")
assertThatApk(apk).doesNotContainResource("drawable/heart.xml")
- assertThatApk(apk).containsResource("drawable-v21/heart.xml")
+ assertThatApk(apk).doesNotContainResource("drawable-v21/heart.xml")
+ assertThatApk(apk).containsResource("drawable-hdpi-v21/heart.xml")
assertThatApk(apk).containsResource("drawable-hdpi-v4/heart.png")
assertThatApk(apk).containsResource("drawable-xhdpi-v4/heart.png")
+ assertThatApk(apk).containsResource("drawable-xhdpi-v21/heart.xml")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi-v22/no_need.png")
+ assertThatApk(apk).containsResource("drawable-v22/no_need.xml")
+
+ // Check HDPI. Test project contains the hdpi png, it should be used instead of the
+ // generated one.
+ File originalPng = new File(
+ project.testDir,
+ "src/main/res/drawable-hdpi/special_heart.png")
+ File generatedPng = new File(
+ project.testDir,
+ "build/generated/res/pngs/debug/drawable-hdpi/special_heart.png")
+ File pngToUse = new File(
+ project.testDir,
+ "build/intermediates/res/merged/debug/drawable-hdpi/special_heart.png")
+
+ assertThat(generatedPng).doesNotExist()
+ assertWithMessage("Wrong file used.")
+ .that(FileUtils.sha1(pngToUse))
+ .isEqualTo(FileUtils.sha1(originalPng))
+
+ // Check XHDPI.
+ generatedPng = new File(
+ project.testDir,
+ "build/generated/res/pngs/debug/drawable-xhdpi/special_heart.png")
+ pngToUse = new File(
+ project.testDir,
+ "build/intermediates/res/merged/debug/drawable-xhdpi/special_heart.png")
+
+ assertWithMessage("Wrong file used.")
+ .that(FileUtils.sha1(pngToUse))
+ .isEqualTo(FileUtils.sha1(generatedPng))
+
+ // Check interactions with other qualifiers.
+ assertThatApk(apk).containsResource("drawable-fr-hdpi-v21/french_heart.xml")
+ assertThatApk(apk).containsResource("drawable-fr-xhdpi-v21/french_heart.xml")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi-v21/french_heart.xml")
+ assertThatApk(apk).doesNotContainResource("drawable-fr/french_heart.xml")
+ assertThatApk(apk).containsResource("drawable-fr-hdpi-v4/french_heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi/french_heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi-v4/french_heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-fr/french_heart.png")
+
+ assertThatApk(apk).containsResource("drawable-hdpi-v21/modern_heart.xml")
+ assertThatApk(apk).doesNotContainResource("drawable-v16/modern_heart.xml")
+ assertThatApk(apk).containsResource("drawable-hdpi-v16/modern_heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-v16/modern_heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi-v21/modern_heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi/modern_heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi-v4/modern_heart.png")
}
@Test
- public void "incremental build: add file"() throws Exception {
+ public void "incremental build: add xml"() throws Exception {
project.execute("assembleDebug")
File heartXml = new File(project.testDir, "src/main/res/drawable/heart.xml")
- File heartXmlCopy = new File(project.testDir, "src/main/res/drawable/heart2.xml")
+ File heartXmlCopy = new File(project.testDir, "src/main/res/drawable/heart_copy.xml")
Files.copy(heartXml, heartXmlCopy)
project.execute("assembleDebug")
@@ -58,14 +117,15 @@
File apk = project.getApk("debug")
assertThatApk(apk).containsResource("drawable/icon.png")
- assertThatApk(apk).doesNotContainResource("drawable/heart2.xml")
- assertThatApk(apk).containsResource("drawable-v21/heart2.xml")
- assertThatApk(apk).containsResource("drawable-hdpi-v4/heart2.png")
- assertThatApk(apk).containsResource("drawable-xhdpi-v4/heart2.png")
+ assertThatApk(apk).doesNotContainResource("drawable/heart_copy.xml")
+ assertThatApk(apk).containsResource("drawable-hdpi-v21/heart_copy.xml")
+ assertThatApk(apk).containsResource("drawable-xhdpi-v21/heart_copy.xml")
+ assertThatApk(apk).containsResource("drawable-hdpi-v4/heart_copy.png")
+ assertThatApk(apk).containsResource("drawable-xhdpi-v4/heart_copy.png")
}
@Test
- public void "incremental build: delete file"() throws Exception {
+ public void "incremental build: delete xml"() throws Exception {
project.execute("assembleDebug")
File heartXml = new File(project.testDir, "src/main/res/drawable/heart.xml")
@@ -76,39 +136,181 @@
File apk = project.getApk("debug")
assertThatApk(apk).containsResource("drawable/icon.png")
- assertThatApk(apk).doesNotContainResource("drawable/heart2.xml")
- assertThatApk(apk).doesNotContainResource("drawable-v21/heart2.xml")
- assertThatApk(apk).doesNotContainResource("drawable-hdpi-v4/heart2.png")
- assertThatApk(apk).doesNotContainResource("drawable-xhdpi-v4/heart2.png")
+ assertThatApk(apk).doesNotContainResource("drawable/heart.xml")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi-v21/heart.xml")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi/heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-xhdpi/heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi-v4/heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-xhdpi-v4/heart.png")
}
@Test
- public void "incremental build: modify file"() throws Exception {
+ public void "incremental build: delete png"() throws Exception {
project.execute("assembleDebug")
- File preprocessedHeartXml = new File(project.testDir, "build/intermediates/res/preprocessed/debug/drawable-hdpi/heart.png")
- File preprocessedIconPng = new File(project.testDir, "build/intermediates/res/preprocessed/debug/drawable/icon.png")
- long heartXmlModified = preprocessedHeartXml.lastModified()
- long iconModified = preprocessedIconPng.lastModified()
+ File generatedPng = new File(
+ project.testDir,
+ "build/generated/res/pngs/debug/drawable-hdpi/special_heart.png")
+ File originalPng = new File(
+ project.testDir,
+ "src/main/res/drawable-hdpi/special_heart.png")
+ File pngToUse = new File(
+ project.testDir,
+ "build/intermediates/res/merged/debug/drawable-hdpi/special_heart.png")
- File heartXml = new File(project.testDir, "src/main/res/drawable/heart.xml")
- String content = Files.toString(heartXml, Charsets.UTF_8)
- // Change the heart to blue.
- Files.write(content.replace("ff0000", "0000ff"), heartXml, Charsets.UTF_8)
+ assertThat(generatedPng).doesNotExist()
+ assertWithMessage("Wrong file used.")
+ .that(FileUtils.sha1(pngToUse))
+ .isEqualTo(FileUtils.sha1(originalPng))
+
+ originalPng.delete()
project.execute("assembleDebug")
checkIncrementalBuild()
- assertThat(preprocessedIconPng.lastModified()).isEqualTo(iconModified)
- assertThat(preprocessedHeartXml.lastModified()).isNotEqualTo(heartXmlModified)
+ assertWithMessage("Wrong file used.")
+ .that(FileUtils.sha1(pngToUse))
+ .isEqualTo(FileUtils.sha1(generatedPng))
+ }
+
+ @Test
+ public void "incremental build: add png"() throws Exception {
+ project.execute("assembleDebug")
+
+ File generatedPng = new File(
+ project.testDir,
+ "build/generated/res/pngs/debug/drawable-xhdpi/special_heart.png")
+ File pngToUse = new File(
+ project.testDir,
+ "build/intermediates/res/merged/debug/drawable-xhdpi/special_heart.png")
+
+ assertWithMessage("Wrong file used.")
+ .that(FileUtils.sha1(pngToUse))
+ .isEqualTo(FileUtils.sha1(generatedPng))
+
+ // Create a PNG file for XHDPI. It should be used instead of the generated one.
+ File hdpiPng = new File(project.testDir, "src/main/res/drawable-hdpi/special_heart.png")
+ File xhdpiPng = new File(project.testDir, "src/main/res/drawable-xhdpi/special_heart.png")
+ Files.createParentDirs(xhdpiPng)
+ Files.copy(hdpiPng, xhdpiPng)
+
+ project.execute("assembleDebug")
+ checkIncrementalBuild()
+
+ assertWithMessage("Wrong file used.")
+ .that(FileUtils.sha1(pngToUse))
+ .isNotEqualTo(FileUtils.sha1(generatedPng))
+
+ assertWithMessage("Wrong file used.")
+ .that(FileUtils.sha1(pngToUse))
+ .isEqualTo(FileUtils.sha1(xhdpiPng))
+ }
+
+ @Test
+ public void "incremental build: modify xml"() throws Exception {
+ project.execute("assembleDebug")
+
+ File heartPngToUse = new File(
+ project.testDir,
+ "build/intermediates/res/merged/debug/drawable-hdpi/heart.png")
+ File iconPngToUse = new File(
+ project.testDir,
+ "build/intermediates/res/merged/debug/drawable/icon.png")
+
+ String oldHashCode = FileUtils.sha1(heartPngToUse)
+ long heartPngModified = heartPngToUse.lastModified()
+ long iconPngModified = iconPngToUse.lastModified()
+
+ File heartXml = new File(project.testDir, "src/main/res/drawable/heart.xml")
+ String content = Files.toString(heartXml, UTF_8)
+ // Change the heart to blue.
+ Files.write(content.replace("ff0000", "0000ff"), heartXml, UTF_8)
+
+ project.execute("assembleDebug")
+ checkIncrementalBuild()
+
+ assertThat(iconPngToUse.lastModified()).isEqualTo(iconPngModified)
+ assertThat(heartPngToUse.lastModified()).isNotEqualTo(heartPngModified)
+ assertWithMessage("XML file change not reflected in PNG.")
+ .that(FileUtils.sha1(heartPngToUse))
+ .isNotEqualTo(oldHashCode)
+ }
+
+ @Test
+ public void "incremental build: replace vector drawable with bitmap alias"() throws Exception {
+ project.execute("assembleDebug")
+
+ File heartXml = new File(project.testDir, "src/main/res/drawable/heart.xml")
+ Files.write(
+ "<bitmap xmlns:android=\"http://schemas.android.com/apk/res/android\" " +
+ "android:src=\"@drawable/icon\" />",
+ heartXml,
+ UTF_8)
+
+ project.execute("assembleDebug")
+ checkIncrementalBuild()
+
+ File apk = project.getApk("debug")
+ assertThatApk(apk).containsResource("drawable/icon.png")
+ assertThatApk(apk).containsResource("drawable/heart.xml")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi-v21/heart.xml")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi/heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-xhdpi/heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi-v4/heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-xhdpi-v4/heart.png")
+
+ File heartXmlToUse = new File(
+ project.testDir,
+ "build/intermediates/res/merged/debug/drawable/heart.xml")
+
+ // They won't be equal, because of the source marker added in the XML.
+ assertThat(Files.toString(heartXmlToUse, UTF_8)).contains(Files.toString(heartXml, UTF_8))
+ }
+
+ @Test
+ public void "incremental build: replace bitmap alias with vector drawable"() throws Exception {
+ File heartXml = new File(project.testDir, "src/main/res/drawable/heart.xml")
+
+ String vectorDrawable = Files.toString(heartXml, UTF_8)
+
+ Files.write(
+ "<bitmap xmlns:android=\"http://schemas.android.com/apk/res/android\" " +
+ "android:src=\"@drawable/icon\" />",
+ heartXml,
+ UTF_8)
+
+ project.execute("clean", "assembleDebug")
+
+ File apk = project.getApk("debug")
+ assertThatApk(apk).containsResource("drawable/icon.png")
+ assertThatApk(apk).containsResource("drawable/heart.xml")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi-v21/heart.xml")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi/heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-xhdpi/heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-hdpi-v4/heart.png")
+ assertThatApk(apk).doesNotContainResource("drawable-xhdpi-v4/heart.png")
+
+ File heartXmlToUse = new File(
+ project.testDir,
+ "build/intermediates/res/merged/debug/drawable/heart.xml")
+
+ // They won't be equal, because of the source marker added in the XML.
+ assertThat(Files.toString(heartXmlToUse, UTF_8)).contains(Files.toString(heartXml, UTF_8))
+
+ Files.write(vectorDrawable, heartXml, UTF_8)
+ project.execute("assembleDebug")
+ checkIncrementalBuild()
+
+ assertThatApk(apk).doesNotContainResource("drawable/heart.xml")
+ assertThatApk(apk).doesNotContainResource("drawable-v21/heart.xml")
+ assertThatApk(apk).containsResource("drawable-hdpi-v21/heart.xml")
+ assertThatApk(apk).containsResource("drawable-hdpi-v4/heart.png")
+ assertThatApk(apk).containsResource("drawable-xhdpi-v4/heart.png")
+ assertThatApk(apk).containsResource("drawable-xhdpi-v21/heart.xml")
}
private void checkIncrementalBuild() {
- File incrementalFolder = new File(
- project.testDir,
- "build/intermediates/incremental/preprocessResourcesTask/debug")
- // state.json is always left behind, this is to make sure incrementalFolder is correct.
- assertThat(new File(incrementalFolder, "state.json").exists()).isTrue()
- assertThat(new File(incrementalFolder, "build_was_incremental").exists()).isTrue()
+ // Do nothing for now, the incremental marker was removed.
+ // TODO: remove the method or re-enable incremental markers.
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/WearWithCustomApplicationIdTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/WearWithCustomApplicationIdTest.groovy
index dbdb73f..02d1d7d 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/WearWithCustomApplicationIdTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/application/WearWithCustomApplicationIdTest.groovy
@@ -18,18 +18,20 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.google.common.base.Throwables
+import groovy.transform.CompileStatic
import org.gradle.tooling.BuildException
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
-import static junit.framework.Assert.fail
+import static org.junit.Assert.fail
/**
* Debug builds with a wearApp with applicationId that does not match that of the main application
* should fail.
*/
+@CompileStatic
class WearWithCustomApplicationIdTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/category/SmokeTests.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/category/SmokeTests.java
new file mode 100644
index 0000000..ce2e120
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/category/SmokeTests.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.common.category;
+
+/**
+ * JUnit category used to label good candidates for smoke testing.
+ */
+public interface SmokeTests {
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GetEmptyModelAction.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GetEmptyModelAction.java
index f7e4f8c..00b4842 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GetEmptyModelAction.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GetEmptyModelAction.java
@@ -17,13 +17,9 @@
package com.android.build.gradle.integration.common.fixture;
import com.android.builder.model.AndroidProject;
-import com.google.common.collect.Maps;
import org.gradle.tooling.BuildAction;
import org.gradle.tooling.BuildController;
-import org.gradle.tooling.model.DomainObjectSet;
-import org.gradle.tooling.model.gradle.BasicGradleProject;
-import org.gradle.tooling.model.gradle.GradleBuild;
import java.util.Collections;
import java.util.Map;
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GradleTestProject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GradleTestProject.java
index 912bdbd..d957ce3 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GradleTestProject.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/GradleTestProject.java
@@ -29,15 +29,18 @@
import com.android.build.gradle.integration.common.utils.FileHelper;
import com.android.build.gradle.integration.common.utils.JacocoAgent;
import com.android.build.gradle.integration.common.utils.SdkHelper;
+import com.android.builder.core.BuilderConstants;
import com.android.builder.model.AndroidProject;
import com.android.builder.model.SyncIssue;
-import com.android.builder.Version;
+import com.android.builder.model.Version;
import com.android.io.StreamException;
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy;
+import com.android.sdklib.repository.FullRevision;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
@@ -45,11 +48,15 @@
import org.gradle.tooling.BuildAction;
import org.gradle.tooling.BuildActionExecuter;
import org.gradle.tooling.BuildLauncher;
+import org.gradle.tooling.GradleConnectionException;
import org.gradle.tooling.GradleConnector;
+import org.gradle.tooling.LongRunningOperation;
import org.gradle.tooling.ProjectConnection;
+import org.gradle.tooling.ResultHandler;
import org.gradle.tooling.internal.consumer.DefaultGradleConnector;
import org.gradle.tooling.model.GradleProject;
import org.gradle.tooling.model.GradleTask;
+import org.junit.Assume;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
@@ -59,11 +66,14 @@
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
+import java.nio.charset.Charset;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -80,15 +90,29 @@
*/
public class GradleTestProject implements TestRule {
+ public static final File TEST_RES_DIR = new File("src/test/resources");
+
public static final int DEFAULT_COMPILE_SDK_VERSION = 21;
public static final String DEFAULT_BUILD_TOOL_VERSION;
+ public static final String REMOTE_TEST_PROVIDER = System.getenv().get("REMOTE_TEST_PROVIDER");
+
+ public static final String DEVICE_PROVIDER_NAME = REMOTE_TEST_PROVIDER != null ?
+ REMOTE_TEST_PROVIDER : BuilderConstants.CONNECTED;
public static final String GRADLE_TEST_VERSION = "2.2.1";
- public static final String GRADLE_EXP_TEST_VERSION = "2.4-20150322230018+0000";
+ public static final String GRADLE_EXP_TEST_VERSION = "2.5";
public static final String ANDROID_GRADLE_PLUGIN_VERSION;
public static final String CUSTOM_JACK;
+ public static final boolean USE_JACK;
+
+ private static final String RECORD_BENCHMARK_NAME = "com.android.benchmark.name";
+ private static final String RECORD_BENCHMARK_MODE = "com.android.benchmark.mode";
+
+ public enum BenchmarkMode {
+ EVALUATION, SYNC, BUILD_FULL, BUILD_INC_JAVA, BUILD_INC_RES_EDIT, BUILD_INC_RES_ADD
+ }
static {
String envBuildToolVersion = System.getenv("CUSTOM_BUILDTOOLS");
@@ -99,6 +123,7 @@
: Version.ANDROID_GRADLE_PLUGIN_VERSION;
String envJack = System.getenv().get("CUSTOM_JACK");
CUSTOM_JACK = !Strings.isNullOrEmpty(envJack) ? envJack : "false";
+ USE_JACK = Boolean.parseBoolean(CUSTOM_JACK);
}
private static final String COMMON_HEADER = "commonHeader.gradle";
@@ -118,24 +143,43 @@
@Nullable
private TestProject testProject = null;
+ @Nullable
+ File sdkDir = SdkHelper.findSdkDir();
+ @Nullable
+ File ndkDir = findNdkDir();
boolean captureStdOut = false;
boolean captureStdErr = false;
boolean experimentalMode = false;
+ @Nullable
+ private String targetGradleVersion;
+ boolean useJack = false;
+ boolean useMinify = false;
+ @NonNull
+ private List<String> gradleProperties = Lists.newArrayList();
@Nullable
private String heapSize;
/**
* Create a GradleTestProject.
*/
- public GradleTestProject create() {
+ public GradleTestProject create() {
+ if (targetGradleVersion == null) {
+ targetGradleVersion =
+ experimentalMode ? GRADLE_EXP_TEST_VERSION : GRADLE_TEST_VERSION;
+ }
return new GradleTestProject(
name,
testProject,
experimentalMode,
- experimentalMode ? GRADLE_EXP_TEST_VERSION : GRADLE_TEST_VERSION,
+ useMinify,
+ useJack,
+ targetGradleVersion,
captureStdOut,
captureStdErr,
+ sdkDir,
+ ndkDir,
+ gradleProperties,
heapSize);
}
@@ -159,12 +203,42 @@
return this;
}
+ /**
+ * Use experimental plugin for the test project.
+ */
public Builder forExpermimentalPlugin(boolean mode) {
this.experimentalMode = mode;
return this;
}
/**
+ * Use the gradle version for experimental plugin, but the test project do not necessarily
+ * have to use experimental plugin.
+ */
+ public Builder useExperimentalGradleVersion(boolean mode) {
+ if (mode) {
+ targetGradleVersion = GRADLE_EXP_TEST_VERSION;
+ }
+ return this;
+ }
+
+ /**
+ * Use the gradle version specified, e.g. "2.4".
+ */
+ public Builder useGradleVersion(String targetGradleVersion) {
+ this.targetGradleVersion = targetGradleVersion;
+ return this;
+ }
+
+ /**
+ * Create a project without setting ndk.dir in local.properties.
+ */
+ public Builder withoutNdk() {
+ this.ndkDir = null;
+ return this;
+ }
+
+ /**
* Create GradleTestProject from a TestProject.
*/
public Builder fromTestApp(@NonNull TestProject testProject) {
@@ -184,6 +258,29 @@
}
/**
+ * Create GradleTestProject from an existing test project.
+ */
+ public Builder fromExternalProject(@NonNull String project) throws IOException {
+ AndroidTestApp app = new EmptyTestApp();
+ name = project;
+ // compute the root folder of the checkout, based on test-projects.
+ File parentDir = TEST_PROJECT_DIR.getCanonicalFile().getParentFile().getParentFile()
+ .getParentFile().getParentFile().getParentFile();
+ parentDir = new File(parentDir, "external");
+ File projectDir = new File(parentDir, project);
+ addAllFiles(app, projectDir);
+ return fromTestApp(app);
+ }
+
+ /**
+ * Add gradle properties.
+ */
+ public Builder addGradleProperties(@NonNull String property) {
+ gradleProperties.add(property);
+ return this;
+ }
+
+ /**
* Sets the test heap size requirement. Example values : 1024m, 2048m...
*
* @param heapSize the heap size in a format understood by the -Xmx JVM parameter
@@ -194,6 +291,16 @@
return this;
}
+ public Builder withJack(boolean useJack) {
+ this.useJack = useJack;
+ return this;
+ }
+
+ public Builder withMinify(boolean useMinify) {
+ this.useMinify = useMinify;
+ return this;
+ }
+
private static class EmptyTestApp extends AbstractAndroidTestApp {
@Override
public boolean containsFullBuildScript() {
@@ -202,28 +309,27 @@
}
}
- private String name;
-
- private File outDir;
-
+ private final String name;
+ private final File outDir;
private File testDir;
-
private File sourceDir;
-
private File buildFile;
-
- private File ndkDir;
-
- private File sdkDir;
+ private final File ndkDir;
+ private final File sdkDir;
private final ByteArrayOutputStream stdout;
private final ByteArrayOutputStream stderr;
- @Nullable
- private TestProject testProject;
+ private final Collection<String> gradleProperties;
- private boolean experimentalMode;
- private String targetGradleVersion;
+ @Nullable
+ private final TestProject testProject;
+
+ private final boolean experimentalMode;
+ private final String targetGradleVersion;
+
+ private final boolean useJack;
+ private final boolean minifyEnabled;
@Nullable
private String heapSize;
@@ -232,21 +338,31 @@
@Nullable String name,
@Nullable TestProject testProject,
boolean experimentalMode,
+ boolean minifyEnabled,
+ boolean useJack,
String targetGradleVersion,
boolean captureStdOut,
boolean captureStdErr,
+ @Nullable File sdkDir,
+ @Nullable File ndkDir,
+ @NonNull Collection<String> gradleProperties,
@Nullable String heapSize) {
- sdkDir = SdkHelper.findSdkDir();
- ndkDir = findNdkDir();
String buildDir = System.getenv("PROJECT_BUILD_DIR");
outDir = (buildDir == null) ? new File("build/tests") : new File(buildDir, "tests");
+ testDir = null;
+ buildFile = sourceDir = null;
this.name = (name == null) ? DEFAULT_TEST_PROJECT_NAME : name;
this.experimentalMode = experimentalMode;
+ this.minifyEnabled = minifyEnabled;
+ this.useJack = useJack;
this.targetGradleVersion = targetGradleVersion;
this.testProject = testProject;
stdout = captureStdOut ? new ByteArrayOutputStream() : null;
stderr = captureStdErr ? new ByteArrayOutputStream() : null;
+ this.sdkDir = sdkDir;
+ this.ndkDir = ndkDir;
this.heapSize = heapSize;
+ this.gradleProperties = gradleProperties;
}
/**
@@ -269,7 +385,20 @@
sdkDir = rootProject.sdkDir;
stdout = rootProject.stdout;
stderr = rootProject.stdout;
+ gradleProperties = ImmutableList.of();
testProject = null;
+ experimentalMode = rootProject.isExperimentalMode();
+ targetGradleVersion = rootProject.getTargetGradleVersion();
+ minifyEnabled = false;
+ useJack = false;
+ }
+
+ String getTargetGradleVersion() {
+ return targetGradleVersion;
+ }
+
+ boolean isExperimentalMode() {
+ return experimentalMode;
}
public static Builder builder() {
@@ -366,6 +495,7 @@
}
createLocalProp(testDir, sdkDir, ndkDir);
+ createGradleProp();
base.evaluate();
}
};
@@ -523,11 +653,43 @@
* @param tasks Variadic list of tasks to execute.
*/
public void execute(String ... tasks) {
- execute(Collections.<String>emptyList(), false, false, tasks);
+ execute(Collections.<String>emptyList(), false, false, ExpectedBuildResult.SUCCESS, tasks);
}
public void execute(@NonNull List<String> arguments, String ... tasks) {
- execute(arguments, false, false, tasks);
+ execute(arguments, false, false, ExpectedBuildResult.SUCCESS, tasks);
+ }
+
+ public void executeWithBenchmark(
+ @NonNull String benchmarkName,
+ @NonNull BenchmarkMode benchmarkMode,
+ String ... tasks) {
+ List<String> arguments = ImmutableList.of(
+ "-P" + RECORD_BENCHMARK_NAME + "=" + benchmarkName,
+ "-P" + RECORD_BENCHMARK_MODE + "=" + benchmarkMode.name().toLowerCase(Locale.US)
+ );
+ execute(arguments, false, false, ExpectedBuildResult.SUCCESS, tasks);
+ }
+
+ public void executeExpectingFailure(String... tasks) {
+ executeExpectingFailure(Collections.<String>emptyList(), tasks);
+ }
+
+ public void executeExpectingFailure(@NonNull List<String> arguments, String... tasks) {
+ execute(
+ arguments,
+ false /*returnModel*/,
+ false /*emulateStudio_1_0*/,
+ ExpectedBuildResult.FAILURE,
+ tasks);
+ }
+
+ public void executeConnectedCheck() {
+ executeConnectedCheck(Collections.<String>emptyList());
+ }
+
+ public void executeConnectedCheck(List<String> arguments) {
+ execute(arguments, REMOTE_TEST_PROVIDER == null ? "connectedCheck" : "deviceCheck");
}
/**
@@ -553,7 +715,8 @@
@NonNull
public AndroidProject executeAndReturnModel(boolean emulateStudio_1_0, String ... tasks) {
//noinspection ConstantConditions
- return execute(Collections.<String>emptyList(), true, emulateStudio_1_0, tasks);
+ return execute(Collections.<String>emptyList(), true, emulateStudio_1_0,
+ ExpectedBuildResult.SUCCESS, tasks);
}
/**
@@ -582,9 +745,10 @@
public Map<String, AndroidProject> executeAndReturnMultiModel(boolean emulateStudio_1_0, String ... tasks) {
ProjectConnection connection = getProjectConnection();
try {
- executeBuild(Collections.<String>emptyList(), connection, tasks);
+ executeBuild(Collections.<String>emptyList(), connection, tasks,
+ ExpectedBuildResult.SUCCESS);
- return buildModel(connection, new GetAndroidModelAction(), emulateStudio_1_0);
+ return buildModel(connection, new GetAndroidModelAction(), emulateStudio_1_0, null, null);
} finally {
connection.close();
@@ -649,7 +813,9 @@
Map<String, AndroidProject> modelMap = buildModel(
connection,
new GetAndroidModelAction(),
- emulateStudio_1_0);
+ emulateStudio_1_0,
+ null,
+ null);
// ensure there was only one project
assertEquals("Quering GradleTestProject.getModel() with multi-project settings",
@@ -671,13 +837,28 @@
*/
@NonNull
public Map<String, AndroidProject> getAllModels() {
- Map<String, AndroidProject> allModels = getAllModels(new GetAndroidModelAction(), false);
+ return getAllModelsWithBenchmark(null, null);
+ }
+
+ /**
+ * Returns a project model for each sub-project without building generating a
+ * @param benchmarkName optional benchmark name to pass to Gradle
+ * @param benchmarkMode optional benchmark mode to pass to gradle.
+
+ * {@link AssertionError} if any sync issue is raised during the model loading.
+ */
+ @NonNull
+ public Map<String, AndroidProject> getAllModelsWithBenchmark(
+ @Nullable String benchmarkName,
+ @Nullable BenchmarkMode benchmarkMode) {
+ Map<String, AndroidProject> allModels = getAllModels(new GetAndroidModelAction(), false, benchmarkName, benchmarkMode);
for (AndroidProject project : allModels.values()) {
assertNoSyncIssues(project);
}
return allModels;
}
+
private static void assertNoSyncIssues(AndroidProject project) {
if (!project.getSyncIssues().isEmpty()) {
StringBuilder msg = new StringBuilder();
@@ -699,7 +880,7 @@
*/
@NonNull
public Map<String, AndroidProject> getAllModelsIgnoringSyncIssues() {
- return getAllModels(new GetAndroidModelAction(), false);
+ return getAllModels(new GetAndroidModelAction(), false, null, null);
}
/**
@@ -709,7 +890,7 @@
*/
@NonNull
public <K, V> Map<K, V> getAllModels(@NonNull BuildAction<Map<K, V>> action) {
- return getAllModels(action, false);
+ return getAllModels(action, false, null, null);
}
/**
@@ -717,30 +898,32 @@
*
* @param action the build action to gather the model
* @param emulateStudio_1_0 whether to emulate an older IDE (studio 1.0) querying the model.
+ * @param benchmarkName optional benchmark name to pass to Gradle
+ * @param benchmarkMode optional benchmark mode to pass to gradle.
*/
@NonNull
public <K, V> Map<K, V> getAllModels(
@NonNull BuildAction<Map<K, V>> action,
- boolean emulateStudio_1_0) {
+ boolean emulateStudio_1_0,
+ @Nullable String benchmarkName,
+ @Nullable BenchmarkMode benchmarkMode) {
ProjectConnection connection = getProjectConnection();
try {
- return buildModel(connection, action, emulateStudio_1_0);
+ return buildModel(connection, action, emulateStudio_1_0, benchmarkName, benchmarkMode);
} finally {
connection.close();
}
}
- public void evaluate() {
- getAllModels(new GetEmptyModelAction(), false);
- }
-
/**
* Runs gradle on the project. Throws exception on failure.
*
* @param arguments List of arguments for the gradle command.
* @param returnModel whether the model should be queried and returned.
* @param emulateStudio_1_0 whether to emulate an older IDE (studio 1.0) querying the model.
+ * @param expectedBuildResult the expected result. If the build status does not match the
+ * expected failure, then an exception will be thrown.
* @param tasks Variadic list of tasks to execute.
*
* @return the model, if <var>returnModel</var> was true, null otherwise
@@ -750,16 +933,19 @@
@NonNull List<String> arguments,
boolean returnModel,
boolean emulateStudio_1_0,
+ ExpectedBuildResult expectedBuildResult,
@NonNull String ... tasks) {
ProjectConnection connection = getProjectConnection();
try {
- executeBuild(arguments, connection, tasks);
+ executeBuild(arguments, connection, tasks, expectedBuildResult);
if (returnModel) {
Map<String, AndroidProject> modelMap = buildModel(
connection,
new GetAndroidModelAction(),
- emulateStudio_1_0);
+ emulateStudio_1_0,
+ null,
+ null);
// ensure there was only one project
assertEquals("Quering GradleTestProject.getModel() with multi-project settings",
@@ -774,25 +960,28 @@
return null;
}
- private void executeBuild(List<String> arguments, ProjectConnection connection,
- String[] tasks) {
- List<String> args = Lists.newArrayListWithCapacity(3 + arguments.size());
+ private enum ExpectedBuildResult {
+ SUCCESS,
+ FAILURE
+ }
+
+ private void executeBuild(final List<String> arguments, ProjectConnection connection,
+ final String[] tasks, ExpectedBuildResult expectedBuildResult) {
+ List<String> args = Lists.newArrayListWithCapacity(5 + arguments.size());
args.add("-i");
args.add("-u");
+ args.add("-Pcom.android.build.gradle.integratonTest.useJack=" + Boolean.toString(useJack));
+ args.add("-Pcom.android.build.gradle.integratonTest.minifyEnabled=" +
+ Boolean.toString(minifyEnabled));
+ args.add("-Pcom.android.build.gradle.integratonTest.useComponentModel=" +
+ Boolean.toString(experimentalMode));
args.addAll(arguments);
+ BuildLauncher launcher = connection.newBuild()
+ .forTasks(tasks)
+ .withArguments(Iterables.toArray(args, String.class));
- List<String> jvmArguments = getJvmArguments();
-
- if (JacocoAgent.isJacocoEnabled()) {
- jvmArguments.add(JacocoAgent.getJvmArg());
- }
- if (!jvmArguments.isEmpty()) {
- args.add("-Dorg.gradle.jvmargs=" + Joiner.on(' ').join(jvmArguments));
- }
-
- BuildLauncher launcher = connection.newBuild().forTasks(tasks)
- .withArguments(args.toArray(new String[args.size()]));
+ setJvmArguments(launcher);
if (stdout != null) {
launcher.setStandardOutput(stdout);
@@ -804,55 +993,89 @@
} else {
launcher.setStandardError(System.err);
}
- launcher.run();
+ if (expectedBuildResult == ExpectedBuildResult.SUCCESS) {
+ launcher.run();
+ } else {
+ launcher.run(new ResultHandler<Void>() {
+ @Override
+ public void onComplete(Void aVoid) {
+ throw new AssertionError(
+ String.format(
+ "Expecting build to fail:\n" +
+ " Tasks: %s\n" +
+ " Arguments: %s",
+ Joiner.on(' ').join(tasks),
+ Joiner.on(' ').join(arguments)));
+ }
+
+ @Override
+ public void onFailure(GradleConnectionException e) {
+ // Ignore, the test expects this build to fail.
+ }
+ });
+ }
}
- private List<String> getJvmArguments() {
+ private void setJvmArguments(LongRunningOperation launcher) {
List<String> jvmArguments = new ArrayList<String>();
+
if (!Strings.isNullOrEmpty(heapSize)) {
jvmArguments.add("-Xmx" + heapSize);
}
+
jvmArguments.add("-XX:MaxPermSize=1024m");
+
String debugIntegrationTest = System.getenv("DEBUG_INNER_TEST");
if (!Strings.isNullOrEmpty(debugIntegrationTest)) {
- jvmArguments.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005");
+ jvmArguments.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5006");
}
- return jvmArguments;
+
+ if (JacocoAgent.isJacocoEnabled()) {
+ jvmArguments.add(JacocoAgent.getJvmArg());
+ }
+
+ launcher.setJvmArguments(Iterables.toArray(jvmArguments, String.class));
}
/**
* Returns a project model for each sub-project without building.
- *
* @param connection the opened ProjectConnection
* @param action the build action to gather the model
* @param emulateStudio_1_0 whether to emulate an older IDE (studio 1.0) querying the model.
+ * @param benchmarkName optional benchmark name to pass to Gradle
+ * @param benchmarkMode optional benchmark mode to pass to gradle.
*/
@NonNull
private <K,V> Map<K, V> buildModel(
@NonNull ProjectConnection connection,
@NonNull BuildAction<Map<K, V>> action,
- boolean emulateStudio_1_0) {
+ boolean emulateStudio_1_0,
+ @Nullable String benchmarkName,
+ @Nullable BenchmarkMode benchmarkMode) {
- BuildActionExecuter<Map<K, V>> executer = connection.action(action);
+ BuildActionExecuter<Map<K, V>> executor = connection.action(action);
- List<String> arguments = Lists.newArrayListWithCapacity(emulateStudio_1_0 ? 2 : 3);
+ List<String> arguments = Lists.newArrayListWithCapacity(5);
arguments.add("-P" + AndroidProject.PROPERTY_BUILD_MODEL_ONLY + "=true");
arguments.add("-P" + AndroidProject.PROPERTY_INVOKED_FROM_IDE + "=true");
if (!emulateStudio_1_0) {
arguments.add("-P" + AndroidProject.PROPERTY_BUILD_MODEL_ONLY_ADVANCED + "=true");
}
-
- List<String> debugJvmArguments = getJvmArguments();
- if (!debugJvmArguments.isEmpty()) {
- arguments.add("-Dorg.gradle.jvmargs=" + Joiner.on(' ').join(debugJvmArguments));
+ if (benchmarkName != null) {
+ arguments.add("-P" + RECORD_BENCHMARK_NAME + "=" + benchmarkName);
+ if (benchmarkMode != null) {
+ arguments.add("-P" + RECORD_BENCHMARK_MODE + "=" + benchmarkMode.name().toLowerCase(Locale.US));
+ }
}
- executer.withArguments(Iterables.toArray(arguments, String.class));
+ setJvmArguments(executor);
- executer.setStandardOutput(System.out);
- executer.setStandardError(System.err);
+ executor.withArguments(Iterables.toArray(arguments, String.class));
- return executer.run();
+ executor.setStandardOutput(System.out);
+ executor.setStandardError(System.err);
+
+ return executor.run();
}
/**
@@ -930,4 +1153,54 @@
return (File) localProp.getFile();
}
+
+ private void createGradleProp() throws IOException {
+ if (gradleProperties.isEmpty()) {
+ return;
+ }
+ File propertyFile = file("gradle.properties");
+ Files.write(Joiner.on('\n').join(gradleProperties), propertyFile, Charset.defaultCharset());
+ }
+
+
+ public static void assumeLocalDevice() {
+ Assume.assumeTrue(
+ "Install task not run against device provider",
+ GradleTestProject.REMOTE_TEST_PROVIDER == null);
+ }
+
+ public static void assumeBuildToolsAtLeast(int major) {
+ assumeBuildToolsAtLeast(
+ major, FullRevision.IMPLICIT_MINOR_REV, FullRevision.IMPLICIT_MICRO_REV);
+ }
+
+ public static void assumeBuildToolsAtLeast(int major, int minor, int micro) {
+ FullRevision currentVersion = FullRevision.parseRevision(DEFAULT_BUILD_TOOL_VERSION);
+ Assume.assumeTrue("Test is only applicable to build tools > " + major,
+ new FullRevision(major, minor, micro).compareTo(currentVersion) < 0);
+ }
+
+ /**
+ * Replace a line from a file with another line.
+ * @param relativePath the relative path of the file from the root of the project
+ * @param lineNumber the line number, starting at 1
+ * @param line the line to replace with.
+ * @throws IOException
+ */
+ public void replaceLine(
+ String relativePath,
+ int lineNumber,
+ String line) throws IOException {
+ File file = new File(testDir, relativePath.replace("/", File.separator));
+
+ List<String> lines = Files.readLines(file, Charsets.UTF_8);
+
+ lines.add(lineNumber, line);
+ lines.remove(lineNumber - 1);
+
+ Files.write(
+ Joiner.on(System.getProperty("line.separator")).join(lines),
+ file,
+ Charsets.UTF_8);
+ }
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/TemporaryProjectModification.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/TemporaryProjectModification.java
new file mode 100644
index 0000000..316361e
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/TemporaryProjectModification.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.common.fixture;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.annotations.NonNull;
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.io.Files;
+
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.MultipleFailureException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import groovy.lang.Closure;
+
+/**
+ * Allows project files to be modified, but stores their original content, so it can be restored for
+ * the next test.
+ */
+public class TemporaryProjectModification {
+
+ private final GradleTestProject mTestProject;
+
+ private final Map<String, String> mFileContentToRestore = new HashMap<String, String>();
+
+ private TemporaryProjectModification(GradleTestProject testProject) {
+ mTestProject = testProject;
+ }
+
+ /**
+ * Runs a test that mutates the project in a reversible way, and returns the project to its
+ * original state after the callback has been run.
+ *
+ * @param project The project to modify.
+ * @param test The test to run.
+ * @throws InitializationError if the project modification infrastructure fails.
+ * @throws Exception passed through if the test throws an exception.
+ */
+ public static void doTest(GradleTestProject project, ModifiedProjectTest test) throws
+ Exception {
+ TemporaryProjectModification modifiedProject = new TemporaryProjectModification(project);
+ try {
+ test.runTest(modifiedProject);
+ } finally {
+ modifiedProject.close();
+ }
+ }
+
+ public interface ModifiedProjectTest {
+ void runTest(TemporaryProjectModification modifiedProject) throws Exception;
+ }
+
+ public void replaceFile(
+ @NonNull String relativePath,
+ @NonNull final String content) throws InitializationError {
+ modifyFile(relativePath, new Function<String, String>() {
+ @Override
+ public String apply(String input) {
+ return content;
+ }
+ });
+ }
+
+ public void replaceInFile(
+ @NonNull String relativePath,
+ @NonNull final String search,
+ @NonNull final String replace) throws InitializationError {
+ modifyFile(relativePath, new Function<String, String>() {
+ @Override
+ public String apply(String input) {
+ return input.replaceAll(search, replace);
+ }
+ });
+
+ }
+
+ public void modifyFile(
+ @NonNull String relativePath,
+ @NonNull Function<String, String> modification) throws InitializationError {
+ File file = mTestProject.file(relativePath);
+
+ String currentContent = null;
+ try {
+ currentContent = Files.toString(file, Charsets.UTF_8);
+ } catch (IOException e) {
+ throw new InitializationError(e);
+ }
+
+ // We can modify multiple times, but we only want to store the original.
+ if (!mFileContentToRestore.containsKey(relativePath)) {
+ mFileContentToRestore.put(relativePath, currentContent);
+ }
+
+ String newContent = modification.apply(currentContent);
+
+ if (newContent == null) {
+ assertTrue("File should have been deleted", file.delete());
+ } else {
+ try {
+ Files.write(newContent, file, Charsets.UTF_8);
+ } catch (IOException e) {
+ throw new InitializationError(e);
+ }
+ }
+ }
+
+ /**
+ * Returns the project back to its original state.
+ */
+ public void close() throws InitializationError {
+ try {
+ for (Map.Entry<String, String> entry : mFileContentToRestore.entrySet()) {
+ Files.write(entry.getValue(), mTestProject.file(entry.getKey()), Charsets.UTF_8);
+ }
+ } catch (IOException e) {
+ throw new InitializationError(e);
+ }
+ mFileContentToRestore.clear();
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/AndroidComponentGradleModule.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/AndroidComponentGradleModule.java
index e723a64..4fe46b5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/AndroidComponentGradleModule.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/AndroidComponentGradleModule.java
@@ -46,11 +46,11 @@
return "apply plugin: 'com.android.model.library'\n" +
"\n" +
"model {\n" +
- " android.config {\n" +
- " compileSdkVersion " + DEFAULT_COMPILE_SDK_VERSION + "\n" +
- " buildToolsVersion '" + DEFAULT_BUILD_TOOL_VERSION + "'\n" +
- " defaultConfig {\n" +
- " useJack " + CUSTOM_JACK + "\n" +
+ " android {\n" +
+ " compileSdkVersion = " + DEFAULT_COMPILE_SDK_VERSION + "\n" +
+ " buildToolsVersion = '" + DEFAULT_BUILD_TOOL_VERSION + "'\n" +
+ " defaultConfig.with {\n" +
+ "// useJack " + CUSTOM_JACK + "\n" +
" }\n" +
" }\n" +
"}\n";
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/EmptyAndroidTestApp.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/EmptyAndroidTestApp.groovy
index 71448db..1454877 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/EmptyAndroidTestApp.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/EmptyAndroidTestApp.groovy
@@ -19,5 +19,25 @@
/**
* An empty app.
*/
-class EmptyAndroidTestApp extends AbstractAndroidTestApp {
+class EmptyAndroidTestApp extends AbstractAndroidTestApp implements AndroidTestApp {
+
+ public EmptyAndroidTestApp() {
+
+ }
+
+ public EmptyAndroidTestApp(String packageName) {
+ TestSourceFile manifest =
+ new TestSourceFile("src/main", "AndroidManifest.xml",
+ """<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="$packageName"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <application/>
+</manifest>
+""");
+
+ addFiles(manifest);
+ }
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/GradleModuleFactory.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/GradleModuleFactory.java
index 5b65c8d..d9160ef 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/GradleModuleFactory.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/GradleModuleFactory.java
@@ -19,7 +19,6 @@
import com.android.annotations.NonNull;
import java.io.File;
-import java.lang.reflect.Constructor;
import java.util.List;
/**
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/VariantBuildScriptGenerator.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/VariantBuildScriptGenerator.java
index 7cf61e6..a4fb890 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/VariantBuildScriptGenerator.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/fixture/app/VariantBuildScriptGenerator.java
@@ -1,7 +1,11 @@
package com.android.build.gradle.integration.common.fixture.app;
+import com.google.common.collect.Maps;
+
import java.util.Map;
+import groovy.lang.Closure;
+
/**
* Generator to create build.gradle with arbitrary number of variants.
*
@@ -25,6 +29,8 @@
private final Map<String, Integer> variantCounts;
+ private final Map<String, Closure<String>> variantPostProcessors = Maps.newHashMap();
+
/**
* Create a VariantBuildScriptGenerator
*
@@ -33,27 +39,42 @@
* @param template a template for the build script. Strings in the format "${key}" will be
* replaced if the key exists in variantCounts.
*/
- public VariantBuildScriptGenerator(Map<String, Integer> variantCounts, String template) {
+ public VariantBuildScriptGenerator(
+ Map<String, Integer> variantCounts,
+ String template) {
this.template = template;
this.variantCounts = variantCounts;
}
/**
+ * Add a post processor to customize the output format of a specified variant.
+ *
+ * @param variant Name of the variant type.
+ * @param postProcessor A Closure that accept the variant type name as String and return the
+ * formatted String.
+ */
+ public void addPostProcessor(String variant, Closure<String> postProcessor) {
+ variantPostProcessors.put(variant, postProcessor);
+ }
+
+ /**
* Generate the string for a build.gradle script.
*/
public String createBuildScript() {
String buildScript = template;
- System.out.println(template);
for (Map.Entry<String, Integer> variantCount : variantCounts.entrySet()) {
String variantName = variantCount.getKey();
StringBuilder variants = new StringBuilder();
for (int i = 0; i < variantCount.getValue(); i++) {
- variants.append(variantName);
- variants.append(i);
+ Closure<String> postProcessor = variantPostProcessors.get(variantName);
+ if (postProcessor == null) {
+ variants.append(variantName);
+ variants.append(i);
+ } else {
+ variants.append(postProcessor.call(variantName + i));
+ }
variants.append("\n");
}
- System.out.println(variants.toString());
-
buildScript = buildScript.replace("${" + variantName + "}", variants.toString());
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AarSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AarSubject.java
index ec75b68..8554fae 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AarSubject.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AarSubject.java
@@ -17,64 +17,59 @@
package com.android.build.gradle.integration.common.truth;
import com.android.annotations.NonNull;
-import com.android.annotations.VisibleForTesting;
-import com.android.build.gradle.integration.common.utils.ApkHelper;
-import com.android.build.gradle.integration.common.utils.SdkHelper;
-import com.android.builder.core.ApkInfoParser;
-import com.android.ide.common.process.DefaultProcessExecutor;
import com.android.ide.common.process.ProcessException;
-import com.android.ide.common.process.ProcessExecutor;
-import com.android.ide.common.process.ProcessInfoBuilder;
-import com.android.utils.StdLogger;
+import com.google.common.base.Charsets;
+import com.google.common.io.CharStreams;
import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.ListSubject;
-import com.google.common.truth.Subject;
-import com.google.common.truth.Truth;
-
-import junit.framework.Assert;
+import com.google.common.truth.StringSubject;
+import com.google.common.truth.SubjectFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.io.InputStreamReader;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* Truth support for aar files.
*/
-public class AarSubject extends AbstractZipSubject<AarSubject> {
+public class AarSubject extends AbstractAndroidSubject<AarSubject> {
- public AarSubject(FailureStrategy failureStrategy, File subject) {
+ static class Factory extends SubjectFactory<AarSubject, File> {
+ @NonNull
+ public static Factory get() {
+ return new Factory();
+ }
+
+ private Factory() {}
+
+ @Override
+ public AarSubject getSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull File subject) {
+ return new AarSubject(failureStrategy, subject);
+ }
+ }
+
+ public AarSubject(@NonNull FailureStrategy failureStrategy, @NonNull File subject) {
super(failureStrategy, subject);
}
- @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
- public void containsClass(String className) throws IOException, ProcessException {
- if (!checkForClass(className)) {
- failWithRawMessage("'%s' does not contain '%s'", getDisplaySubject(), className);
- }
- }
+ @NonNull
+ public StringSubject textSymbolFile() throws IOException {
+ InputStream stream = getInputStream("R.txt");
- public void doesNotContainClass(String className) throws IOException, ProcessException {
- if (checkForClass(className)) {
- failWithRawMessage("'%s' unexpectedly contains '%s'", getDisplaySubject(), className);
- }
- }
-
- @Override
- protected String getDisplaySubject() {
- String name = (internalCustomName() == null) ? "" : "\"" + internalCustomName() + "\" ";
- return name + "<" + getSubject().getName() + ">";
+ return new StringSubject(failureStrategy,
+ CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8)));
}
/**
* Returns true if the provided class is present in the file.
* @param expectedClassName the class name in the format Lpkg1/pk2/Name;
*/
- private boolean checkForClass(
+ @Override
+ protected boolean checkForClass(
@NonNull String expectedClassName)
throws ProcessException, IOException {
InputStream stream = getInputStream("classes.jar");
@@ -94,4 +89,5 @@
zis.close();
}
}
+
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AarSubjectFactory.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AarSubjectFactory.java
deleted file mode 100644
index cde9e1f..0000000
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AarSubjectFactory.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.integration.common.truth;
-
-import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.SubjectFactory;
-
-import java.io.File;
-
-/**
- * Factory to add truth support for aar files.
- */
-class AarSubjectFactory extends SubjectFactory<AarSubject, File> {
- public static AarSubjectFactory factory() {
- return new AarSubjectFactory();
- }
-
- private AarSubjectFactory() {}
-
- @Override
- public AarSubject getSubject(FailureStrategy failureStrategy, File subject) {
- return new AarSubject(failureStrategy, subject);
- }
-}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AbstractAndroidSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AbstractAndroidSubject.java
new file mode 100644
index 0000000..8c83940
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AbstractAndroidSubject.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.common.truth;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.process.ProcessException;
+import com.google.common.truth.FailureStrategy;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.zip.ZipFile;
+
+/**
+ * Base Truth support for android archives (aar and apk)
+ */
+public abstract class AbstractAndroidSubject<T extends AbstractZipSubject<T>> extends AbstractZipSubject<T> {
+
+ public AbstractAndroidSubject(@NonNull FailureStrategy failureStrategy, @NonNull File subject) {
+ super(failureStrategy, subject);
+ }
+
+ /**
+ * Returns true if the provided class is present in the file.
+ * @param expectedClassName the class name in the format Lpkg1/pk2/Name;
+ */
+ protected abstract boolean checkForClass(
+ @NonNull String expectedClassName)
+ throws ProcessException, IOException;
+
+ @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+ public void containsClass(String className) throws IOException, ProcessException {
+ if (!checkForClass(className)) {
+ failWithRawMessage("'%s' does not contain '%s'", getDisplaySubject(), className);
+ }
+ }
+
+ public void doesNotContainClass(String className) throws IOException, ProcessException {
+ if (checkForClass(className)) {
+ failWithRawMessage("'%s' unexpectedly contains '%s'", getDisplaySubject(), className);
+ }
+ }
+
+ @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+ public void containsResource(String name) throws IOException, ProcessException {
+ if (!checkForResource(name)) {
+ failWithRawMessage("'%s' does not contain resource '%s'", getDisplaySubject(), name);
+ }
+ }
+
+ public void doesNotContainResource(String name) throws IOException, ProcessException {
+ if (checkForResource(name)) {
+ failWithRawMessage("'%s' unexpectedly contains resource '%s'", getDisplaySubject(), name);
+ }
+ }
+
+ @Override
+ protected String getDisplaySubject() {
+ String name = (internalCustomName() == null) ? "" : "\"" + internalCustomName() + "\" ";
+ return name + "<" + getSubject().getName() + ">";
+ }
+
+ private boolean checkForResource(String name) throws IOException {
+ ZipFile zipFile = null;
+ try {
+ zipFile = new ZipFile(getSubject());
+ return zipFile.getEntry("res/" + name) != null;
+ } finally {
+ if (zipFile != null) {
+ zipFile.close();
+ }
+ }
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AbstractZipSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AbstractZipSubject.java
index fea69b4..3566744 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AbstractZipSubject.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/AbstractZipSubject.java
@@ -16,11 +16,13 @@
package com.android.build.gradle.integration.common.truth;
-import static com.google.common.truth.Truth.assertThat;
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat;
+import com.android.annotations.NonNull;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
+import com.google.common.primitives.Bytes;
import com.google.common.truth.FailureStrategy;
import com.google.common.truth.IterableSubject;
import com.google.common.truth.Subject;
@@ -40,12 +42,13 @@
public abstract class AbstractZipSubject<T extends Subject<T, File>> extends Subject<T, File> {
private ZipFile zip;
- public AbstractZipSubject(FailureStrategy failureStrategy, File subject) {
+ public AbstractZipSubject(@NonNull FailureStrategy failureStrategy, @NonNull File subject) {
super(failureStrategy, subject);
+ new FileSubject(failureStrategy, subject).exists();
try {
zip = new ZipFile(subject);
} catch (IOException e) {
- failWithRawMessage("IOException thrown when creating ZipFile: %s.", e.toString());
+ failWithRawMessage("IOException thrown when creating ZipFile: '%s'.", e.toString());
}
}
@@ -53,7 +56,7 @@
* Asserts the zip file contains a file with the specified path.
*/
@SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
- public void contains(String path) {
+ public void contains(@NonNull String path) {
if (zip.getEntry(path) == null) {
failWithRawMessage("'%s' does not contain '%s'", zip.getName(), path);
}
@@ -62,7 +65,7 @@
/**
* Asserts the zip file does not contains a file with the specified path.
*/
- public void doesNotContain(String path) {
+ public void doesNotContain(@NonNull String path) {
if (zip.getEntry(path) != null) {
failWithRawMessage("'%s' unexpectedly contains '%s'", zip.getName(), path);
}
@@ -77,7 +80,7 @@
* @throws IOException of the zip file cannot be opened.
*/
public IterableSubject<? extends IterableSubject<?, String, List<String>>, String, List<String>> entries(
- String conformingTo) throws IOException {
+ @NonNull String conformingTo) throws IOException {
ImmutableList.Builder<String> entries = ImmutableList.builder();
Pattern pattern = Pattern.compile(conformingTo);
@@ -102,19 +105,32 @@
* Content is trimmed when compared.
*/
@SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
- public void containsFileWithContent(String path, String content) {
- assertThat(extractContentAsString(path).trim()).named(path).comparesEqualTo(content.trim());
+ public void containsFileWithContent(@NonNull String path, @NonNull String content) {
+ assertThat(extractContentAsString(path).trim()).named(path).isEqualTo(content.trim());
}
/**
* Asserts the zip file contains a file with the specified byte array content.
*/
@SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
- public void containsFileWithContent(String path, byte[] content) {
+ public void containsFileWithContent(@NonNull String path, @NonNull byte[] content) {
assertThat(extractContentAsByte(path)).named(path).isEqualTo(content);
}
- protected String extractContentAsString(String path) {
+ /**
+ * Asserts the zip file contains a file <b>without</b> the specified byte sequence
+ * <b>anywhere</b> in the file
+ */
+ @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+ public void containsFileWithoutContent(@NonNull String path, @NonNull byte[] sub) {
+ byte[] contents = extractContentAsByte(path);
+ int index = Bytes.indexOf(contents, sub);
+ if (index != -1) {
+ failWithRawMessage("Found byte sequence at " + index + " in class file " + path);
+ }
+ }
+
+ protected String extractContentAsString(@NonNull String path) {
InputStream stream = getInputStream(path);
try {
return new String(ByteStreams.toByteArray(stream), Charsets.UTF_8).trim();
@@ -124,7 +140,7 @@
}
}
- protected byte[] extractContentAsByte(String path) {
+ protected byte[] extractContentAsByte(@NonNull String path) {
InputStream stream = getInputStream(path);
try {
return ByteStreams.toByteArray(stream);
@@ -134,7 +150,7 @@
}
}
- protected InputStream getInputStream(String path) {
+ protected InputStream getInputStream(@NonNull String path) {
ZipEntry entry = zip.getEntry(path);
if (entry == null) {
failWithRawMessage("'%s' does not contain '%s'", zip.getName(), path);
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubject.java
index 91f47f8..291ff13 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubject.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubject.java
@@ -27,23 +27,22 @@
import com.android.ide.common.process.ProcessInfoBuilder;
import com.android.utils.StdLogger;
import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.ListSubject;
-import com.google.common.truth.Subject;
+import com.google.common.truth.IterableSubject;
+import com.google.common.truth.SubjectFactory;
import com.google.common.truth.Truth;
-import junit.framework.Assert;
+import org.junit.Assert;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.zip.ZipFile;
/**
* Truth support for apk files.
*/
-public class ApkSubject extends Subject<ApkSubject, File> {
+public class ApkSubject extends AbstractAndroidSubject<ApkSubject> {
private static final Pattern PATTERN_CLASS_DESC = Pattern.compile(
"^Class descriptor\\W*:\\W*'(L.+;)'$");
@@ -51,12 +50,31 @@
private static final Pattern PATTERN_MAX_SDK_VERSION = Pattern.compile(
"^maxSdkVersion\\W*:\\W*'(.+)'$");
- public ApkSubject(FailureStrategy failureStrategy, File subject) {
+ static class Factory extends SubjectFactory<ApkSubject, File> {
+ @NonNull
+ public static Factory get() {
+ return new Factory();
+ }
+
+ private Factory() {}
+
+ @Override
+ public ApkSubject getSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull File subject) {
+ return new ApkSubject(failureStrategy, subject);
+ }
+ }
+
+
+ public ApkSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull File subject) {
super(failureStrategy, subject);
}
@NonNull
- public ListSubject locales() throws ProcessException {
+ public IterableSubject<? extends IterableSubject<?, String, List<String>>, String, List<String>> locales() throws ProcessException {
File apk = getSubject();
List<String> locales = ApkHelper.getLocales(apk);
@@ -68,32 +86,6 @@
}
@SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
- public void containsClass(String className) throws IOException, ProcessException {
- if (!checkForClass(className)) {
- failWithRawMessage("'%s' does not contain '%s'", getDisplaySubject(), className);
- }
- }
-
- public void doesNotContainClass(String className) throws IOException, ProcessException {
- if (checkForClass(className)) {
- failWithRawMessage("'%s' unexpectedly contains '%s'", getDisplaySubject(), className);
- }
- }
-
- @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
- public void containsResource(String name) throws IOException, ProcessException {
- if (!checkForResource(name)) {
- failWithRawMessage("'%s' does not contain resource '%s'", getDisplaySubject(), name);
- }
- }
-
- public void doesNotContainResource(String name) throws IOException, ProcessException {
- if (checkForResource(name)) {
- failWithRawMessage("'%s' unexpectedly contains resource '%s'", getDisplaySubject(), name);
- }
- }
-
- @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
public void hasVersionCode(int versionCode) throws ProcessException {
File apk = getSubject();
@@ -110,7 +102,7 @@
}
@SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
- public void hasVersionName(String versionName) throws ProcessException {
+ public void hasVersionName(@NonNull String versionName) throws ProcessException {
File apk = getSubject();
ApkInfoParser.ApkInfo apkInfo = getApkInfo(apk);
@@ -143,7 +135,8 @@
* Returns true if the provided class is present in the file.
* @param expectedClassName the class name in the format Lpkg1/pk2/Name;
*/
- private boolean checkForClass(
+ @Override
+ protected boolean checkForClass(
@NonNull String expectedClassName)
throws ProcessException, IOException {
// get the dexdump exec
@@ -170,20 +163,8 @@
return false;
}
- private boolean checkForResource(String name) throws IOException {
- ZipFile zipFile = null;
- try {
- zipFile = new ZipFile(getSubject());
- return zipFile.getEntry("res/" + name) != null;
- } finally {
- if (zipFile != null) {
- zipFile.close();
- }
- }
- }
-
@NonNull
- private static ApkInfoParser.ApkInfo getApkInfo(File apk) throws ProcessException {
+ private static ApkInfoParser.ApkInfo getApkInfo(@NonNull File apk) throws ProcessException {
ProcessExecutor processExecutor = new DefaultProcessExecutor(
new StdLogger(StdLogger.Level.ERROR));
ApkInfoParser parser = new ApkInfoParser(SdkHelper.getAapt(), processExecutor);
@@ -191,7 +172,7 @@
}
@VisibleForTesting
- void checkMaxSdkVersion(List<String> output, int maxSdkVersion) {
+ void checkMaxSdkVersion(@NonNull List<String> output, int maxSdkVersion) {
for (String line : output) {
Matcher m = PATTERN_MAX_SDK_VERSION.matcher(line.trim());
if (m.matches()) {
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubjectFactory.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubjectFactory.java
deleted file mode 100644
index 7664e6f..0000000
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubjectFactory.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.integration.common.truth;
-
-import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.SubjectFactory;
-
-import java.io.File;
-
-/**
- * Factory to add truth support for apk files.
- */
-class ApkSubjectFactory extends SubjectFactory<ApkSubject, File> {
- public static ApkSubjectFactory factory() {
- return new ApkSubjectFactory();
- }
-
- private ApkSubjectFactory() {}
-
- @Override
- public ApkSubject getSubject(FailureStrategy failureStrategy, File subject) {
- return new ApkSubject(failureStrategy, subject);
- }
-}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubjectTest.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubjectTest.java
index 18d6e0c..f1bdca3 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubjectTest.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ApkSubjectTest.java
@@ -35,10 +35,12 @@
FakeFailureStrategy failure = new FakeFailureStrategy();
File file = new File("foo");
ApkSubject subject = new ApkSubject(failure, file);
+ // apk file doesn't exist so the failure gets filled with error. Ignore and reset.
+ failure.reset();
subject.checkMaxSdkVersion(strings, 1);
- assertThat(failure.message).is("maxSdkVersion not found in badging output for <foo>");
+ assertThat(failure.message).isEqualTo("maxSdkVersion not found in badging output for <foo>");
}
@Test
@@ -51,6 +53,8 @@
FakeFailureStrategy failure = new FakeFailureStrategy();
File file = new File("foo");
ApkSubject subject = new ApkSubject(failure, file);
+ // apk file doesn't exist so the failure gets filled with error. Ignore and reset.
+ failure.reset();
subject.checkMaxSdkVersion(strings, 14);
@@ -67,9 +71,11 @@
FakeFailureStrategy failure = new FakeFailureStrategy();
File file = new File("foo");
ApkSubject subject = new ApkSubject(failure, file);
+ // apk file doesn't exist so the failure gets filled with error. Ignore and reset.
+ failure.reset();
subject.checkMaxSdkVersion(strings, 14);
- assertThat(failure.message).is("Not true that <foo> has maxSdkVersion <14>. It is <20>");
+ assertThat(failure.message).isEqualTo("Not true that <foo> has maxSdkVersion <14>. It is <20>");
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ArtifactSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ArtifactSubject.java
new file mode 100644
index 0000000..d2068c1
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ArtifactSubject.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.common.truth;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.AndroidArtifact;
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+
+public class ArtifactSubject extends Subject<ArtifactSubject, AndroidArtifact> {
+
+ static class Factory extends SubjectFactory<ArtifactSubject, AndroidArtifact> {
+ @NonNull
+ public static Factory get() {
+ return new Factory();
+ }
+
+ private Factory() {}
+
+ @Override
+ public ArtifactSubject getSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull AndroidArtifact subject) {
+ return new ArtifactSubject(failureStrategy, subject);
+ }
+ }
+
+ public ArtifactSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull AndroidArtifact subject) {
+ super(failureStrategy, subject);
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/CollectionIssueSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/CollectionIssueSubject.java
deleted file mode 100644
index 329bade..0000000
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/CollectionIssueSubject.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.integration.common.truth;
-
-import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat;
-
-import com.android.builder.model.SyncIssue;
-import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.Subject;
-
-import java.util.Collection;
-
-public class CollectionIssueSubject extends Subject<CollectionIssueSubject, Collection<SyncIssue>> {
-
- public CollectionIssueSubject(FailureStrategy failureStrategy, Collection<SyncIssue> subject) {
- super(failureStrategy, subject);
- }
-
- /**
- * Asserts that the issue collection has only a single element with the given properties.
- * Not specified properties are not tested and could have any value.
- *
- * @param severity the expected severity
- * @param type the expected type
- * @return the found issue for further testing.
- */
- @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
- public SyncIssue hasSingleIssue(int severity, int type) {
- Collection<SyncIssue> subject = getSubject();
-
- assertThat(subject).hasSize(1);
-
- SyncIssue issue = subject.iterator().next();
- assertThat(issue).isNotNull();
- assertThat(issue).hasSeverity(severity);
- assertThat(issue).hasType(type);
-
- return issue;
- }
-
- /**
- * Asserts that the issue collection has only a single element with the given properties.
- * Not specified properties are not tested and could have any value.
- *
- * @param severity the expected severity
- * @param type the expected type
- * @param data the expected data
- * @return the found issue for further testing.
- */
- @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
- public SyncIssue hasSingleIssue(int severity, int type, String data) {
- Collection<SyncIssue> subject = getSubject();
-
- assertThat(subject).hasSize(1);
-
- SyncIssue issue = subject.iterator().next();
- assertThat(issue).isNotNull();
- assertThat(issue).hasSeverity(severity);
- assertThat(issue).hasType(type);
- assertThat(issue).hasData(data);
-
- return issue;
- }
-
- /**
- * Asserts that the issue collection has only a single element with the given properties.
- *
- * @param severity the expected severity
- * @param type the expected type
- * @param data the expected data
- * @param message the expected message
- */
- @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
- public void hasSingleIssue(int severity, int type, String data, String message) {
- Collection<SyncIssue> subject = getSubject();
-
- assertThat(subject).hasSize(1);
-
- SyncIssue issue = subject.iterator().next();
- assertThat(issue).isNotNull();
- assertThat(issue).hasSeverity(severity);
- assertThat(issue).hasType(type);
- assertThat(issue).hasData(data);
- assertThat(issue).hasMessage(message);
- }
-
- /**
- * Asserts that the issue collection has only an element with the given properties.
- * Not specified properties are not tested and could have any value.
- *
- * @param severity the expected severity
- * @param type the expected type
- * @return the found issue for further testing.
- */
- @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
- public SyncIssue has(int severity, int type) {
- Collection<SyncIssue> subject = getSubject();
-
- for (SyncIssue issue : subject) {
- if (severity == issue.getSeverity() &&
- type == issue.getType()) {
- return issue;
- }
- }
-
- failWithRawMessage("'%s' does not contain <%s / %s>", getDisplaySubject(),
- severity, type);
- // won't reach
- return null;
- }
-
- /**
- * Asserts that the issue collection has only an element with the given properties.
- * Not specified properties are not tested and could have any value.
- *
- * @param severity the expected severity
- * @param type the expected type
- * @param data the expected data
- * @return the found issue for further testing.
- */
- @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
- public SyncIssue has(int severity, int type, String data) {
- Collection<SyncIssue> subject = getSubject();
-
- for (SyncIssue issue : subject) {
- if (severity == issue.getSeverity() &&
- type == issue.getType() &&
- data.equals(issue.getData())) {
- return issue;
- }
- }
-
- failWithRawMessage("'%s' does not contain <%s / %s / %s>", getDisplaySubject(),
- severity, type, data);
- // won't reach
- return null;
- }
-}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/DependenciesSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/DependenciesSubject.java
new file mode 100644
index 0000000..b5c04b9
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/DependenciesSubject.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.common.truth;
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.AndroidLibrary;
+import com.android.builder.model.Dependencies;
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+
+import java.util.Collection;
+
+
+public class DependenciesSubject extends Subject<DependenciesSubject, Dependencies> {
+
+ static class Factory extends
+ SubjectFactory<DependenciesSubject, Dependencies> {
+ @NonNull
+ public static Factory get() {
+ return new Factory();
+ }
+
+ private Factory() {}
+
+ @Override
+ public DependenciesSubject getSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull Dependencies subject) {
+ return new DependenciesSubject(failureStrategy, subject);
+ }
+ }
+
+ public DependenciesSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull Dependencies subject) {
+ super(failureStrategy, subject);
+ }
+
+ /**
+ * Checks that the dependencies has a single library.
+ *
+ * @return the library.
+ */
+ @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+ public AndroidLibrary hasOneLibrary() {
+ Collection<AndroidLibrary> libs = getSubject().getLibraries();
+
+ assertThat(libs).hasSize(1);
+
+ return libs.iterator().next();
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FakeFailureStrategy.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FakeFailureStrategy.java
index ff27f07..73a3e1b 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FakeFailureStrategy.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FakeFailureStrategy.java
@@ -16,6 +16,7 @@
package com.android.build.gradle.integration.common.truth;
+import com.google.common.base.Objects;
import com.google.common.truth.FailureStrategy;
/**
@@ -45,4 +46,21 @@
this.expected = expected;
this.actual = actual;
}
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this)
+ .add("message", message)
+ .add("throwable", throwable)
+ .add("expected", expected)
+ .add("actual", actual)
+ .toString();
+ }
+
+ public void reset() {
+ message = null;
+ throwable = null;
+ expected = null;
+ actual = null;
+ }
}
\ No newline at end of file
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FileSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FileSubject.java
new file mode 100644
index 0000000..f9a6975
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FileSubject.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.common.truth;
+
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+
+import java.io.File;
+
+/**
+ * Truth support for validating File.
+ */
+public class FileSubject extends Subject<FileSubject, File> {
+ public FileSubject(FailureStrategy failureStrategy, File subject) {
+ super(failureStrategy, subject);
+ }
+
+ public void exists() {
+ if (!getSubject().exists()) {
+ fail("exists");
+ }
+ }
+
+ public void doesNotExist() {
+ if (getSubject().exists()) {
+ fail("does not exist");
+ }
+ }
+
+ @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+ public void isFile() {
+ if (!getSubject().exists()) {
+ fail("is a file");
+ }
+ }
+
+ @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+ public void isDirectory() {
+ if (!getSubject().isDirectory()) {
+ fail("is a directory");
+ }
+ }
+
+ public FileSubject subFile(String path) {
+ if (!getSubject().isDirectory()) {
+ fail("is a directory");
+ }
+ return new FileSubject(failureStrategy, new File(getSubject(), path));
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FileSubjectFactory.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FileSubjectFactory.java
new file mode 100644
index 0000000..f9a9965
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/FileSubjectFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.common.truth;
+
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.SubjectFactory;
+
+import java.io.File;
+
+/**
+ * Factory to add truth support for File.
+ */
+public class FileSubjectFactory extends SubjectFactory<FileSubject, File> {
+ public static FileSubjectFactory factory() {
+ return new FileSubjectFactory();
+ }
+
+ private FileSubjectFactory() {}
+
+ @Override
+ public FileSubject getSubject(FailureStrategy failureStrategy, File subject) {
+ return new FileSubject(failureStrategy, subject);
+ }
+
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubject.java
index d9e9d11..70b0d76 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubject.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubject.java
@@ -16,15 +16,33 @@
package com.android.build.gradle.integration.common.truth;
+import com.android.annotations.NonNull;
import com.android.builder.model.SyncIssue;
import com.google.common.truth.FailureStrategy;
import com.google.common.truth.Subject;
-
+import com.google.common.truth.SubjectFactory;
public class IssueSubject extends Subject<IssueSubject, SyncIssue> {
- public IssueSubject(FailureStrategy failureStrategy,
- SyncIssue subject) {
+ static class Factory extends SubjectFactory<IssueSubject, SyncIssue> {
+ @NonNull
+ public static Factory get() {
+ return new Factory();
+ }
+
+ private Factory() {}
+
+ @Override
+ public IssueSubject getSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull SyncIssue subject) {
+ return new IssueSubject(failureStrategy, subject);
+ }
+ }
+
+ public IssueSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull SyncIssue subject) {
super(failureStrategy, subject);
}
@@ -40,13 +58,13 @@
}
}
- public void hasData(String data) {
+ public void hasData(@NonNull String data) {
if (!data.equals(getSubject().getData())) {
failWithBadResults("has data", data, "is", getSubject().getData());
}
}
- public void hasMessage(String message) {
+ public void hasMessage(@NonNull String message) {
if (!message.equals(getSubject().getMessage())) {
failWithBadResults("has message", message, "is", getSubject().getMessage());
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubjectFactory.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubjectFactory.java
deleted file mode 100644
index 9ca376e..0000000
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubjectFactory.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.integration.common.truth;
-
-import com.android.builder.model.SyncIssue;
-import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.SubjectFactory;
-
-/**
- * Factory to add truth support for SyncIssue.
- */
-public class IssueSubjectFactory extends SubjectFactory<IssueSubject, SyncIssue> {
- public static IssueSubjectFactory factory() {
- return new IssueSubjectFactory();
- }
-
- private IssueSubjectFactory() {}
-
- @Override
- public IssueSubject getSubject(FailureStrategy failureStrategy, SyncIssue subject) {
- return new IssueSubject(failureStrategy, subject);
- }
-}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubjectTest.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubjectTest.java
index 80ac751..01031ae 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubjectTest.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/IssueSubjectTest.java
@@ -79,7 +79,7 @@
IssueSubject subject = new IssueSubject(failure, issue);
subject.hasSeverity(0);
- assertThat(failure.message).is("Not true that <1|2|> has severity <0>. It is <1>");
+ assertThat(failure.message).isEqualTo("Not true that <1|2|> has severity <0>. It is <1>");
}
@Test
@@ -91,7 +91,7 @@
IssueSubject subject = new IssueSubject(failure, issue);
subject.hasType(0);
- assertThat(failure.message).is("Not true that <1|2|> has type <0>. It is <2>");
+ assertThat(failure.message).isEqualTo("Not true that <1|2|> has type <0>. It is <2>");
}
@Test
@@ -103,7 +103,7 @@
IssueSubject subject = new IssueSubject(failure, issue);
subject.hasData("bar");
- assertThat(failure.message).is("Not true that <1|2|foo> has data <bar>. It is <foo>");
+ assertThat(failure.message).isEqualTo("Not true that <1|2|foo> has data <bar>. It is <foo>");
}
@Test
@@ -115,7 +115,7 @@
IssueSubject subject = new IssueSubject(failure, issue);
subject.hasMessage("robert");
- assertThat(failure.message).is("Not true that <1|2|foo> has message <robert>. It is <bob>");
+ assertThat(failure.message).isEqualTo("Not true that <1|2|foo> has message <robert>. It is <bob>");
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ModelSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ModelSubject.java
index 99b9950..5736717 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ModelSubject.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ModelSubject.java
@@ -16,26 +16,163 @@
package com.android.build.gradle.integration.common.truth;
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat;
+
+import com.android.annotations.NonNull;
import com.android.builder.model.AndroidProject;
-import com.google.common.truth.CollectionSubject;
+import com.android.builder.model.SyncIssue;
import com.google.common.truth.FailureStrategy;
import com.google.common.truth.Subject;
-import com.google.common.truth.Truth;
+import com.google.common.truth.SubjectFactory;
+
+import java.util.Collection;
/**
* Truth support for AndroidProject.
*/
public class ModelSubject extends Subject<ModelSubject, AndroidProject> {
- public ModelSubject(FailureStrategy failureStrategy, AndroidProject subject) {
+ static class Factory extends SubjectFactory<ModelSubject, AndroidProject> {
+
+ @NonNull
+ public static Factory get() {
+ return new Factory();
+ }
+
+ private Factory() {}
+
+ @Override
+ public ModelSubject getSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull AndroidProject subject) {
+ return new ModelSubject(failureStrategy, subject);
+ }
+ }
+
+ public ModelSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull AndroidProject subject) {
super(failureStrategy, subject);
}
- public CollectionIssueSubject issues() {
- return new CollectionIssueSubject(failureStrategy, getSubject().getSyncIssues());
+
+ /**
+ * Asserts that the issue collection has only a single element with the given properties.
+ * Not specified properties are not tested and could have any value.
+ *
+ * @param severity the expected severity
+ * @param type the expected type
+ * @return the found issue for further testing.
+ */
+ @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+ public SyncIssue hasSingleIssue(int severity, int type) {
+ Collection<SyncIssue> subject = getSubject().getSyncIssues();
+
+ assertThat(subject).hasSize(1);
+
+ SyncIssue issue = subject.iterator().next();
+ assertThat(issue).isNotNull();
+ assertThat(issue).hasSeverity(severity);
+ assertThat(issue).hasType(type);
+
+ return issue;
}
- public CollectionSubject issuesAsCollection() {
- return Truth.assertThat(getSubject().getSyncIssues());
+ /**
+ * Asserts that the issue collection has only a single element with the given properties.
+ * Not specified properties are not tested and could have any value.
+ *
+ * @param severity the expected severity
+ * @param type the expected type
+ * @param data the expected data
+ * @return the found issue for further testing.
+ */
+ @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+ public SyncIssue hasSingleIssue(int severity, int type, String data) {
+ Collection<SyncIssue> subject = getSubject().getSyncIssues();
+
+ assertThat(subject).hasSize(1);
+
+ SyncIssue issue = subject.iterator().next();
+ assertThat(issue).isNotNull();
+ assertThat(issue).hasSeverity(severity);
+ assertThat(issue).hasType(type);
+ assertThat(issue).hasData(data);
+
+ return issue;
+ }
+
+ /**
+ * Asserts that the issue collection has only a single element with the given properties.
+ *
+ * @param severity the expected severity
+ * @param type the expected type
+ * @param data the expected data
+ * @param message the expected message
+ */
+ @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+ public void hasSingleIssue(int severity, int type, String data, String message) {
+ Collection<SyncIssue> subject = getSubject().getSyncIssues();
+
+ assertThat(subject).hasSize(1);
+
+ SyncIssue issue = subject.iterator().next();
+ assertThat(issue).isNotNull();
+ assertThat(issue).hasSeverity(severity);
+ assertThat(issue).hasType(type);
+ assertThat(issue).hasData(data);
+ assertThat(issue).hasMessage(message);
+ }
+
+ /**
+ * Asserts that the issue collection has only an element with the given properties.
+ * Not specified properties are not tested and could have any value.
+ *
+ * @param severity the expected severity
+ * @param type the expected type
+ * @return the found issue for further testing.
+ */
+ @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+ public SyncIssue hasIssue(int severity, int type) {
+ Collection<SyncIssue> subject = getSubject().getSyncIssues();
+
+ for (SyncIssue issue : subject) {
+ if (severity == issue.getSeverity() &&
+ type == issue.getType()) {
+ return issue;
+ }
+ }
+
+ failWithRawMessage("'%s' does not contain <%s / %s>", getDisplaySubject(),
+ severity, type);
+ // won't reach
+ return null;
+ }
+
+ /**
+ * Asserts that the issue collection has only an element with the given properties.
+ * Not specified properties are not tested and could have any value.
+ *
+ * @param severity the expected severity
+ * @param type the expected type
+ * @param data the expected data
+ * @return the found issue for further testing.
+ */
+ @SuppressWarnings("NonBooleanMethodNameMayNotStartWithQuestion")
+ public SyncIssue hasIssue(int severity, int type, String data) {
+ Collection<SyncIssue> subject = getSubject().getSyncIssues();
+
+ for (SyncIssue issue : subject) {
+ if (severity == issue.getSeverity() &&
+ type == issue.getType() &&
+ data.equals(issue.getData())) {
+ return issue;
+ }
+ }
+
+ failWithRawMessage("'%s' does not contain <%s / %s / %s>", getDisplaySubject(),
+ severity, type, data);
+ // won't reach
+ return null;
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ModelSubjectFactory.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ModelSubjectFactory.java
deleted file mode 100644
index df189e6..0000000
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ModelSubjectFactory.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.integration.common.truth;
-
-import com.android.builder.model.AndroidProject;
-import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.SubjectFactory;
-
-/**
- * Factory to add truth support for AndroidProject.
- */
-class ModelSubjectFactory extends SubjectFactory<ModelSubject, AndroidProject> {
- public static ModelSubjectFactory factory() {
- return new ModelSubjectFactory();
- }
-
- private ModelSubjectFactory() {}
-
- @Override
- public ModelSubject getSubject(FailureStrategy failureStrategy, AndroidProject subject) {
- return new ModelSubject(failureStrategy, subject);
- }
-}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/TruthHelper.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/TruthHelper.java
index a3567b3..58d992e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/TruthHelper.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/TruthHelper.java
@@ -19,19 +19,32 @@
import static com.google.common.truth.Truth.assert_;
import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidArtifact;
import com.android.builder.model.AndroidProject;
+import com.android.builder.model.Dependencies;
import com.android.builder.model.SyncIssue;
+import com.android.builder.model.Variant;
+import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Optional;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multiset;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Table;
+import com.google.common.truth.BigDecimalSubject;
import com.google.common.truth.BooleanSubject;
import com.google.common.truth.ClassSubject;
-import com.google.common.truth.CollectionSubject;
import com.google.common.truth.ComparableSubject;
import com.google.common.truth.DefaultSubject;
+import com.google.common.truth.DoubleSubject;
import com.google.common.truth.IntegerSubject;
import com.google.common.truth.IterableSubject;
-import com.google.common.truth.ListSubject;
+import com.google.common.truth.ListMultimapSubject;
import com.google.common.truth.LongSubject;
import com.google.common.truth.MapSubject;
+import com.google.common.truth.MultimapSubject;
+import com.google.common.truth.MultisetSubject;
import com.google.common.truth.ObjectArraySubject;
import com.google.common.truth.OptionalSubject;
import com.google.common.truth.PrimitiveBooleanArraySubject;
@@ -41,130 +54,189 @@
import com.google.common.truth.PrimitiveFloatArraySubject;
import com.google.common.truth.PrimitiveIntArraySubject;
import com.google.common.truth.PrimitiveLongArraySubject;
+import com.google.common.truth.SetMultimapSubject;
import com.google.common.truth.StringSubject;
import com.google.common.truth.Subject;
+import com.google.common.truth.TableSubject;
+import com.google.common.truth.TestVerb;
+import com.google.common.truth.ThrowableSubject;
import java.io.File;
-import java.util.Collection;
-import java.util.List;
+import java.math.BigDecimal;
import java.util.Map;
/**
* Helper for custom Truth factories.
*/
public class TruthHelper {
+ @NonNull
+ public static FileSubject assertThat(@NonNull File file) {
+ return assert_().about(FileSubjectFactory.factory()).that(file);
+ }
@NonNull
public static ApkSubject assertThatApk(@NonNull File apk) {
- return assert_().about(ApkSubjectFactory.factory()).that(apk);
+ return assert_().about(ApkSubject.Factory.get()).that(apk);
}
@NonNull
public static AarSubject assertThatAar(@NonNull File aar) {
- return assert_().about(AarSubjectFactory.factory()).that(aar);
+ return assert_().about(AarSubject.Factory.get()).that(aar);
}
@NonNull
public static ZipFileSubject assertThatZip(@NonNull File file) {
- return assert_().about(ZipFileSubjectFactory.factory()).that(file);
+ return assert_().about(ZipFileSubject.Factory.get()).that(file);
}
@NonNull
public static ModelSubject assertThat(@NonNull AndroidProject androidProject) {
- return assert_().about(ModelSubjectFactory.factory()).that(androidProject);
+ return assert_().about(ModelSubject.Factory.get()).that(androidProject);
}
@NonNull
public static IssueSubject assertThat(@NonNull SyncIssue issue) {
- return assert_().about(IssueSubjectFactory.factory()).that(issue);
+ return assert_().about(IssueSubject.Factory.get()).that(issue);
}
+ @NonNull
+ public static VariantSubject assertThat(@NonNull Variant variant) {
+ return assert_().about(VariantSubject.Factory.get()).that(variant);
+ }
+
+ @NonNull
+ public static ArtifactSubject assertThat(@NonNull AndroidArtifact artifact) {
+ return assert_().about(ArtifactSubject.Factory.get()).that(artifact);
+ }
+
+ @NonNull
+ public static DependenciesSubject assertThat(@NonNull Dependencies dependencies) {
+ return assert_().about(DependenciesSubject.Factory.get()).that(
+ dependencies);
+ }
// ---- helper method from com.google.common.truth.Truth
// this to allow a single static import of assertThat
- public static <T extends Comparable<?>> ComparableSubject<?, T> assertThat(T target) {
+ /**
+ * Returns a {@link TestVerb} that will prepend the given message to the failure message in
+ * the event of a test failure.
+ */
+ public static TestVerb assertWithMessage(String messageToPrepend) {
+ return assert_().withFailureMessage(messageToPrepend);
+ }
+
+ public static <T extends Comparable<?>> ComparableSubject<?, T> assertThat(@Nullable T target) {
return assert_().that(target);
}
- public static Subject<DefaultSubject, Object> assertThat(Object target) {
+ public static BigDecimalSubject assertThat(@Nullable BigDecimal target) {
return assert_().that(target);
}
- public static ClassSubject assertThat(Class<?> target) {
+ public static Subject<DefaultSubject, Object> assertThat(@Nullable Object target) {
return assert_().that(target);
}
- public static LongSubject assertThat(Long target) {
+ @GwtIncompatible("ClassSubject.java")
+ public static ClassSubject assertThat(@Nullable Class<?> target) {
return assert_().that(target);
}
- public static IntegerSubject assertThat(Integer target) {
+ public static ThrowableSubject assertThat(@Nullable Throwable target) {
return assert_().that(target);
}
- public static BooleanSubject assertThat(Boolean target) {
+ public static LongSubject assertThat(@Nullable Long target) {
return assert_().that(target);
}
- public static StringSubject assertThat(String target) {
+ public static DoubleSubject assertThat(@Nullable Double target) {
+ return assert_().that(target);
+ }
+
+ public static IntegerSubject assertThat(@Nullable Integer target) {
+ return assert_().that(target);
+ }
+
+ public static BooleanSubject assertThat(@Nullable Boolean target) {
+ return assert_().that(target);
+ }
+
+ public static StringSubject assertThat(@Nullable String target) {
return assert_().that(target);
}
public static <T, C extends Iterable<T>> IterableSubject<? extends IterableSubject<?, T, C>, T, C>
- assertThat(Iterable<T> target) {
+ assertThat(@Nullable Iterable<T> target) {
return assert_().that(target);
}
- public static <T, C extends Collection<T>>
- CollectionSubject<? extends CollectionSubject<?, T, C>, T, C>
- assertThat(Collection<T> target) {
+ public static <T> ObjectArraySubject<T> assertThat(@Nullable T[] target) {
return assert_().that(target);
}
- public static <T, C extends List<T>> ListSubject<? extends ListSubject<?, T, C>, T, C>
- assertThat(List<T> target) {
+ public static PrimitiveBooleanArraySubject assertThat(@Nullable boolean[] target) {
return assert_().that(target);
}
- public static <T> ObjectArraySubject<T> assertThat(T[] target) {
+ public static PrimitiveIntArraySubject assertThat(@Nullable int[] target) {
return assert_().that(target);
}
- public static PrimitiveBooleanArraySubject assertThat(boolean[] target) {
+ public static PrimitiveLongArraySubject assertThat(@Nullable long[] target) {
return assert_().that(target);
}
- public static PrimitiveIntArraySubject assertThat(int[] target) {
+ public static PrimitiveByteArraySubject assertThat(@Nullable byte[] target) {
return assert_().that(target);
}
- public static PrimitiveLongArraySubject assertThat(long[] target) {
+ public static PrimitiveCharArraySubject assertThat(@Nullable char[] target) {
return assert_().that(target);
}
- public static PrimitiveByteArraySubject assertThat(byte[] target) {
+ public static PrimitiveFloatArraySubject assertThat(@Nullable float[] target) {
return assert_().that(target);
}
- public static PrimitiveCharArraySubject assertThat(char[] target) {
+ public static PrimitiveDoubleArraySubject assertThat(@Nullable double[] target) {
return assert_().that(target);
}
- public static PrimitiveFloatArraySubject assertThat(float[] target) {
+ public static <T> OptionalSubject<T> assertThat(@Nullable Optional<T> target) {
return assert_().that(target);
}
- public static PrimitiveDoubleArraySubject assertThat(double[] target) {
+ public static MapSubject assertThat(@Nullable Map<?, ?> target) {
return assert_().that(target);
}
- public static <T> OptionalSubject<T> assertThat(Optional<T> target) {
+ public static <K, V, M extends Multimap<K, V>>
+ MultimapSubject<? extends MultimapSubject<?, K, V, M>, K, V, M> assertThat(
+ @Nullable Multimap<K, V> target) {
return assert_().that(target);
}
- public static <K, V, M extends Map<K, V>> MapSubject<? extends MapSubject<?, K, V, M>, K, V, M>
- assertThat(Map<K, V> target) {
+ public static <K, V, M extends ListMultimap<K, V>>
+ ListMultimapSubject<? extends ListMultimapSubject<?, K, V, M>, K, V, M> assertThat(
+ @Nullable ListMultimap<K, V> target) {
+ return assert_().that(target);
+ }
+
+ public static <K, V, M extends SetMultimap<K, V>>
+ SetMultimapSubject<? extends SetMultimapSubject<?, K, V, M>, K, V, M> assertThat(
+ @Nullable SetMultimap<K, V> target) {
+ return assert_().that(target);
+ }
+
+ public static <E, M extends Multiset<E>>
+ MultisetSubject<? extends MultisetSubject<?, E, M>, E, M> assertThat(
+ @Nullable Multiset<E> target) {
+ return assert_().that(target);
+ }
+
+ public static TableSubject assertThat(@Nullable Table<?, ?, ?> target) {
return assert_().that(target);
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/VariantSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/VariantSubject.java
new file mode 100644
index 0000000..bf4586c
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/VariantSubject.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.common.truth;
+
+import com.android.annotations.NonNull;
+import com.android.builder.model.Variant;
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+
+
+public class VariantSubject extends Subject<VariantSubject, Variant> {
+
+ static class Factory extends SubjectFactory<VariantSubject, Variant> {
+ @NonNull
+ public static Factory get() {
+ return new Factory();
+ }
+
+ private Factory() {}
+
+ @Override
+ public VariantSubject getSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull Variant subject) {
+ return new VariantSubject(failureStrategy, subject);
+ }
+ }
+
+ public VariantSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull Variant subject) {
+ super(failureStrategy, subject);
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ZipFileSubject.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ZipFileSubject.java
index ce3aa3f..c567dec 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ZipFileSubject.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ZipFileSubject.java
@@ -16,7 +16,9 @@
package com.android.build.gradle.integration.common.truth;
+import com.android.annotations.NonNull;
import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.SubjectFactory;
import java.io.File;
@@ -25,7 +27,25 @@
*/
public class ZipFileSubject extends AbstractZipSubject<ZipFileSubject> {
- public ZipFileSubject(FailureStrategy failureStrategy, File subject) {
+ static class Factory extends SubjectFactory<ZipFileSubject, File> {
+ @NonNull
+ public static Factory get() {
+ return new Factory();
+ }
+
+ private Factory() {}
+
+ @Override
+ public ZipFileSubject getSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull File subject) {
+ return new ZipFileSubject(failureStrategy, subject);
+ }
+ }
+
+ public ZipFileSubject(
+ @NonNull FailureStrategy failureStrategy,
+ @NonNull File subject) {
super(failureStrategy, subject);
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ZipFileSubjectFactory.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ZipFileSubjectFactory.java
deleted file mode 100644
index fe262d2..0000000
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/truth/ZipFileSubjectFactory.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.build.gradle.integration.common.truth;
-
-import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.SubjectFactory;
-
-import java.io.File;
-
-/**
- * Factory to add truth support for zip files.
- */
-public class ZipFileSubjectFactory extends SubjectFactory<ZipFileSubject, File> {
- public static ZipFileSubjectFactory factory() {
- return new ZipFileSubjectFactory();
- }
-
- private ZipFileSubjectFactory() {}
-
- @Override
- public ZipFileSubject getSubject(FailureStrategy failureStrategy, File subject) {
- return new ZipFileSubject(failureStrategy, subject);
- }
-}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/utils/FileHelper.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/utils/FileHelper.java
index bc1322e..2967398 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/utils/FileHelper.java
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/common/utils/FileHelper.java
@@ -21,12 +21,10 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.build.gradle.integration.common.fixture.app.TestSourceFile;
import com.google.common.base.Charsets;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
-import com.google.common.truth.Truth;
import java.io.File;
import java.io.IOException;
@@ -49,7 +47,8 @@
new Predicate<File>() {
@Override
public boolean apply(@Nullable File file) {
- return file != null && !file.isDirectory();
+ // we want to skip directories and symlinks, so isFile is the best check.
+ return file != null && file.isFile();
}
})) {
assertThat(file.toString()).startsWith(base.toString());
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AndroidComponentPluginTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AndroidComponentPluginTest.groovy
index b54178e..fb15666 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AndroidComponentPluginTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AndroidComponentPluginTest.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.component
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -25,6 +26,7 @@
/**
* Test AndroidComponentModelPlugin.
*/
+@CompileStatic
class AndroidComponentPluginTest {
@ClassRule
public static GradleTestProject project = GradleTestProject.builder()
@@ -40,11 +42,11 @@
model {
android.buildTypes {
- custom
+ create("custom")
}
android.productFlavors {
- flavor1
- flavor2
+ create("flavor1")
+ create("flavor2")
}
}
"""
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AppComponentPluginTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AppComponentPluginTest.groovy
index f3c73ba..0556cdf 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AppComponentPluginTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/AppComponentPluginTest.groovy
@@ -17,8 +17,11 @@
package com.android.build.gradle.integration.component
import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.category.SmokeTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import groovy.transform.CompileStatic
+import com.android.builder.model.AndroidProject
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -29,12 +32,15 @@
/**
* Basic integration test for AppComponentModelPlugin.
*/
+@Category(SmokeTests.class)
+@CompileStatic
class AppComponentPluginTest {
@Rule
public GradleTestProject project = GradleTestProject.builder()
.fromTestApp(new HelloWorldApp())
.forExpermimentalPlugin(true)
+ .withoutNdk()
.create();
@Before
@@ -43,9 +49,9 @@
apply plugin: "com.android.model.application"
model {
- android.config {
- compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
- buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
}
}
"""
@@ -53,7 +59,12 @@
@Test
public void basicAssemble() {
- project.execute("assemble");
+ AndroidProject model = project.executeAndReturnModel("assemble");
+ assertThat(model).isNotNull();
+ assertThat(model.getName()).isEqualTo(project.name)
+ assertThat(model.getBuildTypes()).hasSize(2)
+ assertThat(model.getProductFlavors()).hasSize(0)
+ assertThat(model.getVariants()).hasSize(2)
}
@Test
@@ -61,11 +72,11 @@
project.buildFile << """
model {
android.buildTypes {
- b1
+ create("b1")
}
android.productFlavors {
- f1
- f2
+ create("f1")
+ create("f2")
}
}
"""
@@ -88,11 +99,17 @@
"assembleF1DebugAndroidTest",
"assembleF2DebugAndroidTest");
+ AndroidProject model = project.executeAndReturnModel("assemble");
+ assertThat(model).isNotNull();
+ assertThat(model.getName()).isEqualTo(project.name)
+ assertThat(model.getBuildTypes()).hasSize(3)
+ assertThat(model.getProductFlavors()).hasSize(2)
+ assertThat(model.getVariants()).hasSize(6)
}
@Test
@Category(DeviceTests.class)
public void connnectedAndroidTest() {
- project.execute("connectedAndroidTest");
+ project.executeConnectedCheck();
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/BasicNdkComponentTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/BasicNdkComponentTest.groovy
index 154a7bb..3f74612 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/BasicNdkComponentTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/BasicNdkComponentTest.groovy
@@ -17,28 +17,28 @@
package com.android.build.gradle.integration.component
import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.category.SmokeTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
import org.junit.experimental.categories.Category
-import java.util.zip.ZipFile
-
-import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
-import static org.junit.Assert.assertNotNull
/**
* Basic integration test for native plugin.
*/
+@Category(SmokeTests.class)
+@CompileStatic
class BasicNdkComponentTest {
@ClassRule
public static GradleTestProject project = GradleTestProject.builder()
- .fromTestApp(new HelloWorldJniApp(jniDir: "cpp", useCppSource: true))
+ .fromTestApp(new HelloWorldJniApp(useCppSource: true))
.forExpermimentalPlugin(true)
.withHeap("20148m")
.create();
@@ -49,17 +49,12 @@
apply plugin: 'com.android.model.application'
model {
- android.config {
- compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
- buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
}
android.ndk {
- moduleName "hello-jni"
- }
- android.buildTypes {
- debug {
- jniDebuggable true
- }
+ moduleName = "hello-jni"
}
}
"""
@@ -72,44 +67,44 @@
@Test
public void assemble() {
- project.execute("assemble");
+ project.execute("assemble")
}
@Test
public void assembleRelease() {
- project.execute("assembleRelease");
+ project.execute("assembleRelease")
// Verify .so are built for all platform.
- File apk = project.getApk("release", "unsigned");
- assertThatZip(apk).contains("lib/x86/libhello-jni.so");
- assertThatZip(apk).contains("lib/mips/libhello-jni.so");
- assertThatZip(apk).contains("lib/armeabi/libhello-jni.so");
- assertThatZip(apk).contains("lib/armeabi-v7a/libhello-jni.so");
+ File apk = project.getApk("release", "unsigned")
+ assertThatZip(apk).contains("lib/x86/libhello-jni.so")
+ assertThatZip(apk).contains("lib/mips/libhello-jni.so")
+ assertThatZip(apk).contains("lib/armeabi/libhello-jni.so")
+ assertThatZip(apk).contains("lib/armeabi-v7a/libhello-jni.so")
}
@Test
public void assembleDebug() {
- project.execute("assembleDebug");
+ project.execute("assembleDebug")
// Verify .so are built for all platform.
- File apk = project.getApk("debug");
- assertThatZip(apk).contains("lib/x86/libhello-jni.so");
- assertThatZip(apk).contains("lib/x86/gdbserver");
- assertThatZip(apk).contains("lib/x86/gdb.setup");
- assertThatZip(apk).contains("lib/mips/libhello-jni.so");
- assertThatZip(apk).contains("lib/mips/gdbserver");
- assertThatZip(apk).contains("lib/mips/gdb.setup");
- assertThatZip(apk).contains("lib/armeabi/libhello-jni.so");
- assertThatZip(apk).contains("lib/armeabi/gdbserver");
- assertThatZip(apk).contains("lib/armeabi/gdb.setup");
- assertThatZip(apk).contains("lib/armeabi-v7a/libhello-jni.so");
- assertThatZip(apk).contains("lib/armeabi-v7a/gdbserver");
- assertThatZip(apk).contains("lib/armeabi-v7a/gdb.setup");
+ File apk = project.getApk("debug")
+ assertThatZip(apk).contains("lib/x86/libhello-jni.so")
+ assertThatZip(apk).contains("lib/x86/gdbserver")
+ assertThatZip(apk).contains("lib/x86/gdb.setup")
+ assertThatZip(apk).contains("lib/mips/libhello-jni.so")
+ assertThatZip(apk).contains("lib/mips/gdbserver")
+ assertThatZip(apk).contains("lib/mips/gdb.setup")
+ assertThatZip(apk).contains("lib/armeabi/libhello-jni.so")
+ assertThatZip(apk).contains("lib/armeabi/gdbserver")
+ assertThatZip(apk).contains("lib/armeabi/gdb.setup")
+ assertThatZip(apk).contains("lib/armeabi-v7a/libhello-jni.so")
+ assertThatZip(apk).contains("lib/armeabi-v7a/gdbserver")
+ assertThatZip(apk).contains("lib/armeabi-v7a/gdb.setup")
}
@Test
@Category(DeviceTests.class)
public void connnectedAndroidTest() {
- project.execute("connectedAndroidTest");
+ project.executeConnectedCheck();
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/ComponentDslTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/ComponentDslTest.groovy
new file mode 100644
index 0000000..f58e310
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/ComponentDslTest.groovy
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.component
+
+import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import com.android.build.gradle.integration.common.truth.TruthHelper
+import com.android.builder.model.AndroidProject
+import groovy.transform.CompileStatic
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.experimental.categories.Category
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+
+/**
+ * Test various options can be set without necessarily using it.
+ */
+@CompileStatic
+public class ComponentDslTest {
+
+ @Rule
+ public GradleTestProject project = GradleTestProject.builder()
+ .fromTestApp(new HelloWorldJniApp())
+ .forExpermimentalPlugin(true)
+ .create();
+
+ @Before
+ public void setUp() {
+ project.file("proguard.txt").createNewFile()
+ project.buildFile << """
+apply plugin: "com.android.model.application"
+
+model {
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ defaultConfig.with {
+ minSdkVersion.apiLevel = 7
+ }
+ }
+ android.ndk {
+ moduleName = "hello-jni"
+ }
+ android.productFlavors {
+ create("f1") {
+ proguardFiles += file("proguard.txt")
+ buildConfigFields.create {
+ type = "String"
+ name = "foo"
+ value = "\\"bar\\""
+ }
+ }
+ create("f2")
+ }
+}
+
+dependencies {
+ compile 'com.android.support:appcompat-v7:22.2.0'
+}
+"""
+ }
+
+ @Test
+ public void assemble() {
+ AndroidProject model = project.executeAndReturnModel("assemble");
+ assertThat(model).isNotNull();
+ assertThat(model.getName()).isEqualTo(project.name)
+ assertThat(model.getBuildTypes()).hasSize(2)
+ assertThat(model.getProductFlavors()).hasSize(2)
+ assertThat(model.getVariants()).hasSize(4)
+ assertThat(project.getApk("f1", "debug")).exists()
+ }
+
+ @Test
+ @Category(DeviceTests.class)
+ public void connnectedAndroidTest() {
+ project.executeConnectedCheck();
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/ComponentSourceSetTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/ComponentSourceSetTest.groovy
index 7346128..0158f17 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/ComponentSourceSetTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/ComponentSourceSetTest.groovy
@@ -20,6 +20,7 @@
import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -30,20 +31,25 @@
/**
* Integration tests for different configuration of source sets.
*/
+@CompileStatic
class ComponentSourceSetTest {
public static AndroidTestApp app = new HelloWorldJniApp()
static {
+ TestSourceFile manifest = app.getFile("AndroidManifest.xml")
+ app.removeFile(manifest)
+ app.addFile(new TestSourceFile("src", manifest.name, manifest.content));
+
// Remove the main hello-jni.c and place it in different directories for different flavors.
// Note that *not* all variant can be built.
TestSourceFile cSource = app.getFile("hello-jni.c");
app.removeFile(cSource);
app.addFile(
- new TestSourceFile("src/release/c", cSource.name, cSource.content))
+ new TestSourceFile("src/release/jni", cSource.name, cSource.content))
app.addFile(
- new TestSourceFile("src/flavor1/c/hello-jni.c", cSource.name, cSource.content))
- app.addFile(new TestSourceFile("src/flavor2Debug/c/hello-jni.c", cSource.name,
+ new TestSourceFile("src/flavor1/jni/hello-jni.c", cSource.name, cSource.content))
+ app.addFile(new TestSourceFile("src/flavor2Debug/jni/hello-jni.c", cSource.name,
cSource.content))
}
@@ -60,29 +66,45 @@
apply plugin: "com.android.model.application"
model {
- android.config {
- compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
- buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
}
android.ndk {
- moduleName "hello-jni"
+ moduleName = "hello-jni"
}
android.productFlavors {
- flavor1
- flavor2
- flavor3
+ create("flavor1")
+ create("flavor2")
+ create("flavor3")
}
android.sources {
- flavor3 {
- c {
+ main {
+ manifest {
source {
- srcDir 'src/flavor1/c'
+ srcDir 'src'
+ }
+ }
+ jni {
+ source {
+ exclude "**/fail.c"
+ }
+ }
+ }
+ flavor3 {
+ jni {
+ source {
+ srcDir 'src/flavor1/jni'
}
}
}
}
}
"""
+ project.file("src/main/jni").mkdirs()
+ project.file("src/main/jni/fail.c") << """
+Un-compilable file to test exclude source works.
+"""
}
@AfterClass
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/LibraryComponentPluginTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/LibraryComponentPluginTest.groovy
index 6bb948c..f34e196 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/LibraryComponentPluginTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/LibraryComponentPluginTest.groovy
@@ -16,20 +16,27 @@
package com.android.build.gradle.integration.component
+import com.android.build.gradle.integration.common.category.SmokeTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
import com.android.build.gradle.integration.common.fixture.app.HelloWorldLibraryApp
import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
+import org.junit.experimental.categories.Category
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatAar
/**
* Basic integration test for LibraryComponentModelPlugin.
*/
+@Category(SmokeTests.class)
+@CompileStatic
class LibraryComponentPluginTest {
- private static testApp = new HelloWorldLibraryApp()
+ private static HelloWorldLibraryApp testApp = new HelloWorldLibraryApp()
static {
AndroidTestApp app = (AndroidTestApp) testApp.getSubproject(":app")
@@ -46,9 +53,9 @@
}
model {
- android.config {
- compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
- buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
}
}
"""))
@@ -58,10 +65,15 @@
"""
apply plugin: "com.android.model.library"
+dependencies {
+ /* Depend on annotations to trigger the creation of the ExtractAnnotations task */
+ compile 'com.android.support:support-annotations:22.2.0'
+}
+
model {
- android.config {
- compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
- buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
}
}
"""))
@@ -73,6 +85,11 @@
.forExpermimentalPlugin(true)
.create();
+ @BeforeClass
+ public static void assemble() {
+ project.execute("assemble")
+ }
+
@AfterClass
static void cleanUp() {
project = null
@@ -80,7 +97,8 @@
}
@Test
- void assemble() {
- project.execute("assemble")
+ void "check build config file is included"() {
+ File releaseAar = project.getSubproject("lib").getAar("release");
+ assertThatAar(releaseAar).containsClass("com/example/helloworld/BuildConfig.class");
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentModelTest.groovy
new file mode 100644
index 0000000..c8d83c7
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentModelTest.groovy
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.component
+
+import com.android.SdkConstants
+import com.android.build.gradle.integration.common.category.SmokeTests
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.NativeLibrary
+import com.android.builder.model.Variant
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.experimental.categories.Category
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+
+/**
+ * Test the return model of the NDK.
+ */
+@Category(SmokeTests.class)
+class NdkComponentModelTest {
+ @Rule
+ public GradleTestProject project = GradleTestProject.builder()
+ .fromTestApp(new HelloWorldJniApp())
+ .forExpermimentalPlugin(true)
+ .create()
+
+ @Before
+ void setUp() {
+ project.buildFile <<
+"""
+apply plugin: 'com.android.model.application'
+
+model {
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ }
+ android.ndk {
+ moduleName = "hello-jni"
+ CFlags += "-DTEST_C_FLAG"
+ cppFlags += "-DTEST_CPP_FLAG"
+ toolchain = "clang"
+ }
+}
+"""
+ }
+
+ @Test
+ void "check native libraries in model"() {
+ checkModel(
+ debug : [
+ SdkConstants.ABI_ARMEABI,
+ SdkConstants.ABI_ARMEABI_V7A,
+ SdkConstants.ABI_ARM64_V8A,
+ SdkConstants.ABI_INTEL_ATOM,
+ SdkConstants.ABI_INTEL_ATOM64,
+ SdkConstants.ABI_MIPS,
+ SdkConstants.ABI_MIPS64
+ ]);
+ }
+
+ @Test
+ void "check native libraries with splits"() {
+ project.buildFile <<
+"""
+model {
+ android {
+ splits.with {
+ abi {
+ enable true
+ reset()
+ include 'x86', 'armeabi-v7a', 'mips'
+ }
+ }
+ }
+}
+"""
+ checkModel(
+ debug: [SdkConstants.ABI_ARMEABI_V7A, SdkConstants.ABI_INTEL_ATOM, SdkConstants.ABI_MIPS]);
+ }
+
+ @Test
+ void "check native libraries with splits and universalApk"() {
+ project.buildFile <<
+ """
+model {
+ android {
+ splits.with {
+ abi {
+ enable true
+ reset()
+ include 'x86', 'armeabi-v7a', 'mips'
+ universalApk true
+ }
+ }
+ }
+}
+"""
+ checkModel(
+ debug : [
+ SdkConstants.ABI_ARMEABI,
+ SdkConstants.ABI_ARMEABI_V7A,
+ SdkConstants.ABI_ARM64_V8A,
+ SdkConstants.ABI_INTEL_ATOM,
+ SdkConstants.ABI_INTEL_ATOM64,
+ SdkConstants.ABI_MIPS,
+ SdkConstants.ABI_MIPS64
+ ]);
+ }
+
+ @Test
+ void "check native libraries with abiFilters"() {
+ project.buildFile <<
+ """
+model {
+ android.productFlavors {
+ create("x86") {
+ ndk.abiFilters += "x86"
+ }
+ create("arm") {
+ ndk.abiFilters += "armeabi-v7a"
+ }
+ create("mips") {
+ ndk.abiFilters +="mips"
+ }
+ }
+}
+"""
+ checkModel(
+ x86Debug : [SdkConstants.ABI_INTEL_ATOM],
+ armDebug : [SdkConstants.ABI_ARMEABI_V7A],
+ mipsDebug : [SdkConstants.ABI_MIPS]);
+ }
+
+ @Test
+ void "check variant specific flags"() {
+ project.buildFile <<
+ """
+model {
+ android.buildTypes {
+ debug {
+ ndk.CFlags += "-DTEST_FLAG_DEBUG"
+ }
+ release {
+ ndk.CFlags += "-DTEST_FLAG_RELEASE"
+ }
+ }
+ android.productFlavors {
+ create("f1") {
+ ndk.CFlags += "-DTEST_FLAG_F1"
+ }
+ create("f2") {
+ ndk.CFlags += "-DTEST_FLAG_F2"
+ }
+ }
+}
+"""
+ AndroidProject model = project.executeAndReturnModel("assembleDebug")
+ NativeLibrary f1Debug = ModelHelper.getVariant(model.getVariants(), "f1Debug").getMainArtifact()
+ .getNativeLibraries().first()
+ assertThat(f1Debug.getCCompilerFlags()).contains("-DTEST_FLAG_DEBUG")
+ assertThat(f1Debug.getCCompilerFlags()).contains("-DTEST_FLAG_F1")
+ NativeLibrary f2Release = ModelHelper.getVariant(model.getVariants(), "f2Release").getMainArtifact()
+ .getNativeLibraries().first()
+ assertThat(f2Release.getCCompilerFlags()).contains("-DTEST_FLAG_RELEASE")
+ assertThat(f2Release.getCCompilerFlags()).contains("-DTEST_FLAG_F2")
+ }
+
+ @Test
+ void "check using add on string for compileSdkVersion"() {
+ project.buildFile <<
+"""
+model {
+ android {
+ compileSdkVersion = "Google Inc.:Google APIs:$GradleTestProject.DEFAULT_COMPILE_SDK_VERSION"
+ }
+}
+"""
+ AndroidProject model = project.executeAndReturnModel("assembleDebug")
+ NativeLibrary lib = ModelHelper.getVariant(model.getVariants(), "debug").getMainArtifact()
+ .getNativeLibraries().first()
+ for (String flag : lib.getCCompilerFlags()) {
+ if (flag.contains("sysroot")) {
+ assertThat(flag).contains("android-${GradleTestProject.DEFAULT_COMPILE_SDK_VERSION}")
+ }
+ }
+ }
+
+ /**
+ * Verify resulting model is as expected.
+ *
+ * @param variantToolchains map of variant name to array of expected toolchains.
+ */
+ private void checkModel(Map variantToolchains) {
+
+ AndroidProject model = project.executeAndReturnModel("assembleDebug")
+
+ Collection<Variant> variants = model.getVariants()
+ for (Map.Entry entry : variantToolchains) {
+ Variant variant = ModelHelper.getVariant(variants, (String) entry.getKey())
+ AndroidArtifact mainArtifact = variant.getMainArtifact()
+
+ assertThat(mainArtifact.getNativeLibraries()).hasSize(((Collection)entry.getValue()).size())
+ for (NativeLibrary nativeLibrary : mainArtifact.getNativeLibraries()) {
+ assertThat(nativeLibrary.getName()).isEqualTo("hello-jni")
+ assertThat(nativeLibrary.getCCompilerFlags()).contains("-DTEST_C_FLAG");
+ assertThat(nativeLibrary.getCCompilerFlags()).contains("-gcc-toolchain"); // check clang specific flags
+ assertThat(nativeLibrary.getCppCompilerFlags()).contains("-DTEST_CPP_FLAG");
+ assertThat(nativeLibrary.getCppCompilerFlags()).contains("-gcc-toolchain"); // check clang specific flags
+ assertThat(nativeLibrary.getCSystemIncludeDirs()).isEmpty();
+ assertThat(nativeLibrary.getCppSystemIncludeDirs()).isNotEmpty();
+ File solibSearchPath = nativeLibrary.getDebuggableLibraryFolders().first()
+ assertThat(new File(solibSearchPath, "libhello-jni.so")).exists()
+ }
+
+ Collection<String> expectedToolchainNames = entry.getValue().collect { "clang-" + it }
+ Collection<String> toolchainNames = model.getNativeToolchains().collect { it.getName() }
+ assertThat(toolchainNames).containsAllIn(expectedToolchainNames)
+ Collection<String> nativeLibToolchains = mainArtifact.getNativeLibraries().
+ collect { it.getToolchainName() }
+ assertThat(nativeLibToolchains).containsExactlyElementsIn(expectedToolchainNames)
+ }
+
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentPluginTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentPluginTest.groovy
index 0d81b31..7cf288d 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentPluginTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentPluginTest.groovy
@@ -16,19 +16,24 @@
package com.android.build.gradle.integration.component
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+
/**
* Tests for NdkComponentModelPlugin
*/
-import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+
/**
* Basic integration test for ndk component plugin.
*/
+@CompileStatic
class NdkComponentPluginTest {
@ClassRule
@@ -44,9 +49,11 @@
apply plugin: NdkComponentModelPlugin
model {
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ }
android.ndk {
- compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
- moduleName "hello-jni"
+ moduleName = "hello-jni"
}
}
"""
@@ -60,5 +67,8 @@
@Test
public void assemble() {
project.execute("assemble");
+ assertThat(project.file("build/intermediates/binaries/debug/obj/x86/libhello-jni.so")).exists()
+ assertThat(project.file("build/intermediates/binaries/debug/lib/x86/libhello-jni.so")).exists()
+ assertThat(project.file("build/intermediates/binaries/release/lib/x86/libhello-jni.so")).exists()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentSplitTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentSplitTest.groovy
index 5d9e347..bc3b282 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentSplitTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentSplitTest.groovy
@@ -15,49 +15,50 @@
*/
package com.android.build.gradle.integration.component
-
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
import org.junit.experimental.categories.Category
-import java.util.zip.ZipFile
-
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
-import static org.junit.Assert.assertNotNull
-import static org.junit.Assert.assertNull
/**
* Integration test of the native plugin with multiple variants.
*/
+@CompileStatic
class NdkComponentSplitTest {
@ClassRule
public static GradleTestProject project = GradleTestProject.builder()
- .fromTestApp(new HelloWorldJniApp(jniDir: "c"))
+ .fromTestApp(new HelloWorldJniApp())
.forExpermimentalPlugin(true)
.create()
@BeforeClass
public static void setUp() {
+ GradleTestProject.assumeBuildToolsAtLeast(21)
project.getBuildFile() << """
apply plugin: 'com.android.model.application'
model {
- android.config {
- compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
- buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
- generatePureSplits true
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ generatePureSplits = true
- defaultConfig {
- minSdkVersion 21
+ defaultConfig.with {
+ minSdkVersion.with {
+ apiLevel = 21
+ }
}
- splits {
+ splits.with {
abi {
enable true
reset()
@@ -66,12 +67,7 @@
}
}
android.ndk {
- moduleName "hello-jni"
- }
- android.buildTypes {
- debug {
- jniDebuggable true
- }
+ moduleName = "hello-jni"
}
}
"""
@@ -84,6 +80,11 @@
@Test
public void assembleDebug() {
+ // Ensure compileDebugSource creates the shared object.
+ project.execute("compileDebugSources");
+ assertThat(project.file("build/intermediates/binaries/debug/lib/x86/libhello-jni.so"))
+ .exists();
+
project.execute("assembleDebug");
// Verify .so are built for all platform.
@@ -111,6 +112,6 @@
@Test
@Category(DeviceTests.class)
public void connectedAndroidTest() {
- project.execute("connectedAndroidTest");
+ project.executeConnectedCheck();
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentVariantTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentVariantTest.groovy
index c1267c1..779d25c 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentVariantTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkComponentVariantTest.groovy
@@ -19,28 +19,28 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import com.android.builder.core.BuilderConstants
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
import org.junit.experimental.categories.Category
-import java.util.zip.ZipFile
-
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
import static com.google.common.truth.Truth.assertThat
-import static org.junit.Assert.assertNotNull
-import static org.junit.Assert.assertNull
/**
* Integration test of the native plugin with multiple variants.
*/
+@CompileStatic
class NdkComponentVariantTest {
@ClassRule
public static GradleTestProject project = GradleTestProject.builder()
- .fromTestApp(new HelloWorldJniApp(jniDir: "c"))
+ .fromTestApp(new HelloWorldJniApp())
.forExpermimentalPlugin(true)
- .create();
+ .create()
@BeforeClass
public static void setUp() {
@@ -49,33 +49,28 @@
apply plugin: 'com.android.model.application'
model {
- android.config {
- compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
- buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
}
android.ndk {
- moduleName "hello-jni"
+ moduleName = "hello-jni"
}
android.buildTypes {
- debug {
- jniDebuggable true
+ create("jniDebug") {
+ ndk.debuggable = true;
}
}
android.productFlavors {
- x86 {
- ndk {
- abiFilter "x86"
- }
+ create("x86") {
+ ndk.abiFilters += "x86"
}
- arm {
- ndk {
- abiFilters "armeabi-v7a", "armeabi"
- }
+ create("arm") {
+ ndk.abiFilters += "armeabi-v7a"
+ ndk.abiFilters += "armeabi"
}
- mips {
- ndk {
- abiFilter "mips"
- }
+ create("mips") {
+ ndk.abiFilters += "mips"
}
}
}
@@ -101,43 +96,73 @@
@Test
public void assembleX86Debug() {
- project.execute("assembleX86Debug");
+ project.execute("assembleX86Debug")
// Verify .so are built for all platform.
- ZipFile apk = new ZipFile(project.getApk("x86", "debug"));
- assertNotNull(apk.getEntry("lib/x86/libhello-jni.so"));
- assertNull(apk.getEntry("lib/mips/libhello-jni.so"));
- assertNull(apk.getEntry("lib/armeabi/libhello-jni.so"));
- assertNull(apk.getEntry("lib/armeabi-v7a/libhello-jni.so"));
+ File apk = project.getApk("x86", "debug")
+ assertThatZip(apk).contains("lib/x86/libhello-jni.so")
+ assertThatZip(apk).doesNotContain("lib/mips/libhello-jni.so")
+ assertThatZip(apk).doesNotContain("lib/armeabi/libhello-jni.so")
+ assertThatZip(apk).doesNotContain("lib/armeabi-v7a/libhello-jni.so")
}
@Test
public void assembleArmDebug() {
- project.execute("assembleArmDebug");
+ project.execute("assembleArmDebug")
// Verify .so are built for all platform.
- ZipFile apk = new ZipFile(project.getApk("arm", "debug"));
- assertNull(apk.getEntry("lib/x86/libhello-jni.so"));
- assertNull(apk.getEntry("lib/mips/libhello-jni.so"));
- assertNotNull(apk.getEntry("lib/armeabi/libhello-jni.so"));
- assertNotNull(apk.getEntry("lib/armeabi-v7a/libhello-jni.so"));
+ File apk = project.getApk("arm", "debug")
+ assertThatZip(apk).doesNotContain("lib/x86/libhello-jni.so")
+ assertThatZip(apk).doesNotContain("lib/mips/libhello-jni.so")
+ assertThatZip(apk).contains("lib/armeabi/libhello-jni.so")
+ assertThatZip(apk).contains("lib/armeabi-v7a/libhello-jni.so")
}
@Test
public void assembleMipsDebug() {
- project.execute("assembleMipsDebug");
+ project.execute("assembleMipsDebug")
// Verify .so are built for all platform.
- ZipFile apk = new ZipFile(project.getApk("mips", "debug"));
- assertNull(apk.getEntry("lib/x86/libhello-jni.so"));
- assertNotNull(apk.getEntry("lib/mips/libhello-jni.so"));
- assertNull(apk.getEntry("lib/armeabi/libhello-jni.so"));
- assertNull(apk.getEntry("lib/armeabi-v7a/libhello-jni.so"));
+ File apk = project.getApk("mips", "debug")
+ assertThatZip(apk).doesNotContain("lib/x86/libhello-jni.so")
+ assertThatZip(apk).contains("lib/mips/libhello-jni.so")
+ assertThatZip(apk).doesNotContain("lib/armeabi/libhello-jni.so")
+ assertThatZip(apk).doesNotContain("lib/armeabi-v7a/libhello-jni.so")
+ }
+
+ @Test
+ public void "check setting isDebuggable generates gdbserver and gdb.setup"() {
+ project.execute("assembleArmJniDebug")
+
+ File apk = project.getApk("arm", "jniDebug", "unsigned")
+ assertThatZip(apk).contains("lib/armeabi/libhello-jni.so")
+ assertThatZip(apk).contains("lib/armeabi/gdbserver")
+ assertThatZip(apk).contains("lib/armeabi/gdb.setup")
+ assertThatZip(apk).contains("lib/armeabi-v7a/libhello-jni.so")
+ assertThatZip(apk).contains("lib/armeabi-v7a/gdbserver")
+ assertThatZip(apk).contains("lib/armeabi-v7a/gdb.setup")
+ }
+
+ @Test
+ public void "check release build does not contain gdbserver and gdb.setup"() {
+ project.execute("assembleArmRelease")
+
+ File apk = project.getApk("arm", "release", "unsigned")
+ assertThatZip(apk).contains("lib/armeabi/libhello-jni.so")
+ assertThatZip(apk).doesNotContain("lib/armeabi/gdbserver")
+ assertThatZip(apk).doesNotContain("lib/armeabi/gdb.setup")
+ assertThatZip(apk).contains("lib/armeabi-v7a/libhello-jni.so")
+ assertThatZip(apk).doesNotContain("lib/armeabi-v7a/gdbserver")
+ assertThatZip(apk).doesNotContain("lib/armeabi-v7a/gdb.setup")
}
@Test
@Category(DeviceTests.class)
public void connectedAndroidTest() {
- project.execute("connectedAndroidTestArmDebug");
+ if (GradleTestProject.DEVICE_PROVIDER_NAME.equals(BuilderConstants.CONNECTED)) {
+ project.execute(GradleTestProject.DEVICE_PROVIDER_NAME + "ArmDebugAndroidTest")
+ } else {
+ project.execute(GradleTestProject.DEVICE_PROVIDER_NAME + "X86DebugAndroidTest")
+ }
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkFlagsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkFlagsTest.groovy
new file mode 100644
index 0000000..d21d8cd
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkFlagsTest.groovy
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.component
+
+import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+import org.junit.experimental.categories.Category
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
+
+/**
+ * Tests C/C++/ld flags in an NDK project.
+ */
+@CompileStatic
+class NdkFlagsTest {
+
+ static AndroidTestApp cApp = new HelloWorldJniApp()
+ static {
+ TestSourceFile orig = cApp.getFile("hello-jni.c")
+ cApp.removeFile(orig)
+ cApp.addFile(new TestSourceFile(orig.path, orig.name,
+ """
+#include <string.h>
+#include <jni.h>
+
+// This is a trivial JNI example where we use a native method
+// to return a new VM String.
+jstring
+Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env, jobject thiz)
+{
+ return (*env)->NewStringUTF(env, HELLO_WORLD EXCLAMATION_MARK);
+}
+"""
+ ))
+ }
+
+
+ @ClassRule
+ public static GradleTestProject cProject = GradleTestProject.builder()
+ .withName("c_project")
+ .fromTestApp(cApp)
+ .forExpermimentalPlugin(true)
+ .create();
+
+ static AndroidTestApp cppApp = new HelloWorldJniApp(useCppSource: true)
+ static {
+ TestSourceFile orig = cppApp.getFile("hello-jni.cpp")
+ cppApp.removeFile(orig)
+ cppApp.addFile(new TestSourceFile(orig.path, orig.name,
+ """
+#include <string.h>
+#include <jni.h>
+
+// This is a trivial JNI example where we use a native method
+// to return a new VM String.
+extern "C"
+jstring
+Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env, jobject thiz)
+{
+ // HELLO_WORLD and EXCLAMATION_MARK must be defined as follows during compilation.
+ // #define HELLO_WORLD "hello world"
+ // #define EXCLAMATION_MARK "!"
+ return env->NewStringUTF(HELLO_WORLD EXCLAMATION_MARK);
+}
+"""
+ ))
+
+ }
+
+ @ClassRule
+ public static GradleTestProject cppProject = GradleTestProject.builder()
+ .withName("cpp_project")
+ .fromTestApp(cppApp)
+ .forExpermimentalPlugin(true)
+ .create();
+
+ static AndroidTestApp ldApp = new HelloWorldJniApp()
+ static {
+ ldApp.addFile(new TestSourceFile("src/main/jni", "log.c",
+ """
+#include <android/log.h>
+
+// Simple function that uses function from an external library. Should fail unless -llog is set
+// when linking.
+void log() {
+ __android_log_print(ANDROID_LOG_INFO, "hello-world", "Hello World!");
+}
+"""))
+ }
+
+ @ClassRule
+ public static GradleTestProject ldProject = GradleTestProject.builder()
+ .withName("ld_project")
+ .fromTestApp(ldApp)
+ .forExpermimentalPlugin(true)
+ .create();
+
+
+ @BeforeClass
+ public static void setUp() {
+ cProject.getBuildFile() << """
+apply plugin: 'com.android.model.application'
+
+model {
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ }
+ android.ndk {
+ moduleName = "hello-jni"
+ CFlags += ['-DHELLO_WORLD="hello world"', '-DEXCLAMATION_MARK="!"']
+ CFlags += ' -DFLAG_WITH_LEADING_SPACE'
+ }
+}
+"""
+
+ cppProject.getBuildFile() << """
+apply plugin: 'com.android.model.application'
+
+model {
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ }
+ android.ndk {
+ moduleName = "hello-jni"
+ cppFlags = ['-DHELLO_WORLD="hello world"', '-DEXCLAMATION_MARK="!"']
+ }
+}
+"""
+
+ ldProject.getBuildFile() << """
+apply plugin: 'com.android.model.application'
+
+model {
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ }
+ android.ndk {
+ moduleName = "hello-jni"
+ ldFlags += "-llog"
+ }
+}
+"""
+ }
+
+ @AfterClass
+ static void cleanUp() {
+ cProject = null
+ }
+
+ @Test
+ public void "assemble C project"() {
+ cProject.execute("assembleDebug")
+ assertThatZip(cProject.getApk("debug")).contains("lib/x86/libhello-jni.so")
+ }
+
+ @Test
+ public void "assemble C++ project"() {
+ cppProject.execute("assembleDebug")
+ assertThatZip(cppProject.getApk("debug")).contains("lib/x86/libhello-jni.so")
+ }
+
+ @Test
+ public void "assemble ld project"() {
+ ldProject.execute("assembleDebug")
+ assertThatZip(ldProject.getApk("debug")).contains("lib/x86/libhello-jni.so")
+ }
+
+ @Test
+ @Category(DeviceTests.class)
+ public void "connectedCheck C project"() {
+ cProject.executeConnectedCheck();
+ }
+
+ @Test
+ @Category(DeviceTests.class)
+ public void "connectedCheck C++ project"() {
+ cppProject.executeConnectedCheck();
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkJniLib2Test.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkJniLib2Test.groovy
index 1e2ac01..caeef48 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkJniLib2Test.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkJniLib2Test.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -29,6 +30,7 @@
* Assemble tests for ndkJniLib2.
*/
@Ignore("Test is disabled until new dependency model is ready.")
+@CompileStatic
class NdkJniLib2Test {
@ClassRule
@@ -54,6 +56,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck");
+ project.executeConnectedCheck();
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkSanAngeles2Test.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkSanAngeles2Test.groovy
index cf40e68..feca3d1 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkSanAngeles2Test.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkSanAngeles2Test.groovy
@@ -16,33 +16,46 @@
package com.android.build.gradle.integration.component
+import com.android.SdkConstants
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.NativeLibrary
+import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
import org.junit.experimental.categories.Category
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+
/**
* Assemble tests for ndkSanAngeles2.
*/
+@CompileStatic
class NdkSanAngeles2Test {
@ClassRule
- static public GradleTestProject project = GradleTestProject.builder()
+ public static GradleTestProject project = GradleTestProject.builder()
.forExpermimentalPlugin(true)
.fromTestProject("ndkSanAngeles2")
.create()
+ private static AndroidProject model;
+
@BeforeClass
static void setUp() {
- project.execute("clean", "assembleDebug");
+ model = project.executeAndReturnModel("clean", "assembleDebug")
}
@AfterClass
static void cleanUp() {
project = null
+ model = null
}
@Test
@@ -51,8 +64,35 @@
}
@Test
+ void "check model"() {
+ Collection<Variant> variants = model.getVariants()
+ assertThat(variants).hasSize(8)
+
+ Variant debugVariant = ModelHelper.getVariant(variants, "x86Debug")
+ AndroidArtifact debugMainArtifact = debugVariant.getMainArtifact()
+ assertThat(debugMainArtifact.getNativeLibraries()).hasSize(1)
+
+ NativeLibrary nativeLibrary = debugMainArtifact.getNativeLibraries().first()
+ assertThat(nativeLibrary.getName()).isEqualTo("sanangeles")
+ assertThat(nativeLibrary.getToolchainName()).isEqualTo("clang-x86")
+ assertThat(nativeLibrary.getCCompilerFlags()).contains("-DDISABLE_IMPORTGL");
+ assertThat(nativeLibrary.getCSystemIncludeDirs()).isEmpty();
+ assertThat(nativeLibrary.getCppSystemIncludeDirs()).isNotEmpty()
+ File solibSearchPath = nativeLibrary.getDebuggableLibraryFolders().first()
+ assertThat(new File(solibSearchPath, "libsanangeles.so")).exists()
+
+ Collection<String> toolchainNames = model.getNativeToolchains().collect { it.getName() }
+ Collection<String> expectedToolchains = [
+ SdkConstants.ABI_INTEL_ATOM,
+ SdkConstants.ABI_ARMEABI_V7A,
+ SdkConstants.ABI_ARMEABI,
+ SdkConstants.ABI_MIPS].collect { "clang-" + it }
+ assertThat(toolchainNames).containsAllIn(expectedToolchains)
+ }
+
+ @Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck");
+ project.executeConnectedCheck();
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStandaloneSoTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStandaloneSoTest.groovy
index 84cf84c..ff8938a 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStandaloneSoTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStandaloneSoTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -29,6 +30,7 @@
* Assemble tests for ndkStandaloneSo.
*/
@Ignore("Test is disabled until new dependency model is ready.")
+@CompileStatic
class NdkStandaloneSoTest {
@ClassRule
@@ -54,6 +56,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck");
+ project.executeConnectedCheck();
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStlTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStlTest.groovy
index 67386bb..dfded44 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStlTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkStlTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import groovy.transform.CompileStatic
import org.gradle.tooling.BuildException
import org.junit.Before
import org.junit.Rule
@@ -36,6 +37,7 @@
* This unit test is parameterized and will be executed for various values of STL.
*/
@RunWith(Parameterized.class)
+@CompileStatic
public class NdkStlTest {
@Parameterized.Parameters
@@ -62,7 +64,7 @@
@Rule
public GradleTestProject project = GradleTestProject.builder()
- .fromTestApp(new HelloWorldJniApp(jniDir: "cpp", useCppSource: true))
+ .fromTestApp(new HelloWorldJniApp(useCppSource: true))
.forExpermimentalPlugin(true)
.create()
@@ -72,12 +74,12 @@
apply plugin: 'com.android.model.application'
model {
- android.config {
- compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
- buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
}
android.ndk {
- moduleName "hello-jni"
+ moduleName = "hello-jni"
}
}
"""
@@ -88,7 +90,7 @@
project.getBuildFile() << """
model {
android.ndk {
- stl "$stl"
+ stl = "$stl"
}
}
"""
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkVariantsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkVariantsTest.groovy
index 5e90883..fe48955 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkVariantsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/component/NdkVariantsTest.groovy
@@ -16,15 +16,19 @@
package com.android.build.gradle.integration.component
+import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
+import org.junit.experimental.categories.Category
/**
* Assemble tests for ndkVariants.
*/
+@CompileStatic
class NdkVariantsTest {
@ClassRule
@@ -47,4 +51,10 @@
void lint() {
project.execute("lint")
}
+
+ @Test
+ @Category(DeviceTests.class)
+ public void connnectedAndroidTest() {
+ project.executeConnectedCheck();
+ }
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithCompileLocalAarTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithCompileLocalAarTest.groovy
index 027fe64..f4d74de 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithCompileLocalAarTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithCompileLocalAarTest.groovy
@@ -63,7 +63,7 @@
@Test
void "check model failed to load"() {
- SyncIssue issue = assertThat(model).issues().hasSingleIssue(
+ SyncIssue issue = assertThat(model).hasSingleIssue(
SyncIssue.SEVERITY_ERROR,
SyncIssue.TYPE_NON_JAR_LOCAL_DEP)
assertThat(new File(issue.getData()).getName()).is('baseLib-1.0.aar')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithJarDependOnLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithJarDependOnLibTest.groovy
index 9600a2b..18cb14e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithJarDependOnLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithJarDependOnLibTest.groovy
@@ -65,7 +65,7 @@
@Test
void "check model failed to load"() {
- assertThat(models.get(':app')).issues().hasSingleIssue(
+ assertThat(models.get(':app')).hasSingleIssue(
SyncIssue.SEVERITY_ERROR,
SyncIssue.TYPE_JAR_DEPEND_ON_AAR,
'projectWithModules:jar:jar:unspecified')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithNonExistentResolutionStrategyForAarTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithNonExistentResolutionStrategyForAarTest.groovy
index 2d047fd..7ddca09 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithNonExistentResolutionStrategyForAarTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithNonExistentResolutionStrategyForAarTest.groovy
@@ -15,20 +15,17 @@
*/
package com.android.build.gradle.integration.dependencies
-
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.builder.model.AndroidProject
import com.android.builder.model.SyncIssue
-import com.google.common.collect.Iterables
import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
-import static org.junit.Assert.assertEquals
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
import static org.junit.Assert.assertTrue
-
/**
* test for flavored dependency on a different package.
*/
@@ -88,10 +85,9 @@
@Test
void "check we received a sync issue"() {
- assertEquals(1, models.get(":app").getSyncIssues().size());
- SyncIssue syncIssue = Iterables.getOnlyElement(models.get(":app").getSyncIssues());
- assertEquals(SyncIssue.SEVERITY_ERROR, syncIssue.getSeverity());
- assertEquals(SyncIssue.TYPE_UNRESOLVED_DEPENDENCY, syncIssue.getType());
- assertTrue(syncIssue.message.contains("org.jdeferred:jdeferred-android-aar:-1.-1.-1"));
+ SyncIssue issue = assertThat(models.get(":app")).hasSingleIssue(
+ SyncIssue.SEVERITY_ERROR,
+ SyncIssue.TYPE_UNRESOLVED_DEPENDENCY)
+ assertTrue(issue.message.contains("org.jdeferred:jdeferred-android-aar:-1.-1.-1"));
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLibTest.groovy
index e6ad2f2..89eff2e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLibTest.groovy
@@ -56,7 +56,7 @@
@Test
void "check model failed to load"() {
- assertThat(models.get(':app')).issues().hasSingleIssue(
+ assertThat(models.get(':app')).hasSingleIssue(
SyncIssue.SEVERITY_ERROR,
SyncIssue.TYPE_NON_JAR_PACKAGE_DEP,
'projectWithModules:library:aar:unspecified')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLocalAarTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLocalAarTest.groovy
index bacd296..c7a7d18 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLocalAarTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithPackageLocalAarTest.groovy
@@ -63,7 +63,7 @@
@Test
void "check model failed to load"() {
- SyncIssue issue = assertThat(model).issues().hasSingleIssue(
+ SyncIssue issue = assertThat(model).hasSingleIssue(
SyncIssue.SEVERITY_ERROR,
SyncIssue.TYPE_NON_JAR_LOCAL_DEP)
assertThat(new File(issue.getData()).getName()).is('baseLib-1.0.aar')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLibTest.groovy
index ff961ac..14ac3c6 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLibTest.groovy
@@ -56,7 +56,7 @@
@Test
void "check model failed to load"() {
- assertThat(models.get(':app')).issues().hasSingleIssue(
+ assertThat(models.get(':app')).hasSingleIssue(
SyncIssue.SEVERITY_ERROR,
SyncIssue.TYPE_NON_JAR_PROVIDED_DEP,
'projectWithModules:library:aar:unspecified')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLocalAarTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLocalAarTest.groovy
index b378f64..ccf23b5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLocalAarTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/AppWithProvidedLocalAarTest.groovy
@@ -63,7 +63,7 @@
@Test
void "check model failed to load"() {
- SyncIssue issue = assertThat(model).issues().hasSingleIssue(
+ SyncIssue issue = assertThat(model).hasSingleIssue(
SyncIssue.SEVERITY_ERROR,
SyncIssue.TYPE_NON_JAR_LOCAL_DEP)
assertThat(new File(issue.getData()).getName()).is('baseLib-1.0.aar')
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/LocalJarInAarInModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/LocalJarInAarInModelTest.groovy
new file mode 100644
index 0000000..efa71de
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/LocalJarInAarInModelTest.groovy
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.dependencies
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.android.build.gradle.integration.common.truth.TruthHelper
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidLibrary
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Dependencies
+import com.android.builder.model.Variant
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+import static org.junit.Assert.assertTrue
+/**
+ * test for the path of the local jars in aars before and after exploding them.
+ */
+class LocalJarInAarInModelTest {
+
+ @Rule
+ public GradleTestProject project = GradleTestProject.builder()
+ .fromTestApp(new HelloWorldApp())
+ .create()
+
+ @Before
+ void setUp() {
+ project.getBuildFile() << """
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+ defaultConfig {
+ minSdkVersion 4
+ }
+}
+
+dependencies {
+ compile 'com.android.support:support-v4:22.1.1'
+}
+"""
+ }
+
+ @After
+ void cleanUp() {
+ project = null
+ }
+
+ @Test
+ void checkModelBeforeBuild() {
+ //clean the project and get the model. The aar won't be exploded for this sync event.
+ AndroidProject model = project.executeAndReturnModel("clean")
+
+ Variant variant = ModelHelper.getVariant(model.getVariants(), "debug")
+ Dependencies dependencies = variant.getMainArtifact().getDependencies()
+ Collection<AndroidLibrary> libraries = dependencies.getLibraries();
+
+ TruthHelper.assertThat(libraries).hasSize(1);
+
+ // now build the project.
+ project.execute("prepareDebugDependencies")
+
+ // now check the model validity
+ AndroidLibrary lib = libraries.iterator().next()
+
+ File jarFile = lib.getJarFile()
+ assertTrue("File doesn't exist: " + jarFile, jarFile.exists());
+ for (File localJar : lib.getLocalJars()) {
+ assertTrue("File doesn't exist: " + localJar, localJar.exists());
+ }
+ }
+
+ @Test
+ void checkModelAfterBuild() {
+ //build the project and get the model. The aar is exploded for this sync event.
+ AndroidProject model = project.executeAndReturnModel("clean", "prepareDebugDependencies")
+
+ Variant variant = ModelHelper.getVariant(model.getVariants(), "debug")
+ Dependencies dependencies = variant.getMainArtifact().getDependencies()
+ Collection<AndroidLibrary> libraries = dependencies.getLibraries();
+
+ TruthHelper.assertThat(libraries).hasSize(1);
+
+ // now check the model validity
+ AndroidLibrary lib = libraries.iterator().next()
+
+ File jarFile = lib.getJarFile()
+ assertTrue("File doesn't exist: " + jarFile, jarFile.exists());
+ for (File localJar : lib.getLocalJars()) {
+ assertTrue("File doesn't exist: " + localJar, localJar.exists());
+ }
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/OptionalAarTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/OptionalAarTest.groovy
new file mode 100644
index 0000000..7faf203
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/OptionalAarTest.groovy
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.dependencies
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidLibrary
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Dependencies
+import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatAar
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
+/**
+ * test for optional aar (using the provided scope)
+ */
+@CompileStatic
+class OptionalAarTest {
+
+ @ClassRule
+ static public GradleTestProject project = GradleTestProject.builder()
+ .fromTestProject("projectWithModules")
+ .create()
+ static Map<String, AndroidProject> models
+
+ @BeforeClass
+ static void setUp() {
+ project.getSubproject('app').getBuildFile() << """
+
+dependencies {
+ compile project(':library')
+}
+"""
+ project.getSubproject('library').getBuildFile() << """
+
+dependencies {
+ provided project(':library2')
+}
+"""
+ models = project.executeAndReturnMultiModel("clean", ":app:assembleDebug")
+ }
+
+ @AfterClass
+ static void cleanUp() {
+ project = null
+ models = null
+ }
+
+ @Test
+ void "check app doesn't contain provided lib's layout"() {
+ File apk = project.getSubproject('app').getApk("debug")
+
+ assertThatApk(apk).doesNotContainResource("layout/lib2layout.xml")
+ }
+
+ @Test
+ void "check app doesn't contain provided lib's code"() {
+ File apk = project.getSubproject('app').getApk("debug")
+
+ assertThatApk(apk).doesNotContainClass("Lcom/example/android/multiproject/library2/PersonView2;")
+ }
+
+ @Test
+ void "check lib doesn't contain provided lib's layout"() {
+ File aar = project.getSubproject('library').getAar("release")
+
+ assertThatAar(aar).doesNotContainResource("layout/lib2layout.xml")
+ assertThatAar(aar).textSymbolFile().contains("int layout liblayout")
+ assertThatAar(aar).textSymbolFile().doesNotContain("int layout lib2layout")
+ }
+
+ @Test
+ void "check app model doesn't include optional library"() {
+ Collection<Variant> variants = models.get(":app").getVariants()
+
+ // get the main artifact of the debug artifact and its dependencies
+ Variant variant = ModelHelper.getVariant(variants, "debug")
+ AndroidArtifact artifact = variant.getMainArtifact()
+ Dependencies dependencies = artifact.getDependencies()
+ Collection<AndroidLibrary> libs = dependencies.getLibraries();
+
+ assertThat(libs).hasSize(1);
+
+ AndroidLibrary library = libs.first()
+ assertThat(library.getProject()).isEqualTo(":library")
+ assertThat(library.isOptional()).isFalse()
+ }
+
+ @Test
+ void "check library model includes optional library"() {
+ Collection<Variant> variants = models.get(":library").getVariants()
+
+ // get the main artifact of the debug artifact and its dependencies
+ Variant variant = ModelHelper.getVariant(variants, "debug")
+ AndroidArtifact artifact = variant.getMainArtifact()
+ Dependencies dependencies = artifact.getDependencies()
+ Collection<AndroidLibrary> libs = dependencies.getLibraries();
+
+ assertThat(libs).hasSize(1);
+
+ AndroidLibrary library = libs.first()
+ assertThat(library.getProject()).isEqualTo(":library2")
+ assertThat(library.isOptional()).isTrue()
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithMismatchDep.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithMismatchDep.groovy
index b19566a..c32bfe8 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithMismatchDep.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithMismatchDep.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.builder.model.AndroidProject
import com.android.builder.model.SyncIssue
+import groovy.transform.CompileStatic
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -26,10 +27,10 @@
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertTrue
import static org.junit.Assert.fail
-
/**
* Tests the handling of test dependencies.
*/
+@CompileStatic
class TestWithMismatchDep {
@Rule
@@ -55,7 +56,7 @@
// Query the model to get the mismatch dep sync error.
AndroidProject model = project.getSingleModelIgnoringSyncIssues()
- assertThat(model).issues().hasSingleIssue(
+ assertThat(model).hasSingleIssue(
SyncIssue.SEVERITY_ERROR,
SyncIssue.TYPE_MISMATCH_DEP,
'com.google.guava:guava',
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsApp.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsApp.groovy
index 60d5b1f..435b370 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsApp.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsApp.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.dependencies
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Tests the handling of test dependency.
*/
+@CompileStatic
class TestWithSameDepAsApp {
@ClassRule
@@ -59,6 +61,6 @@
@Test
@Category(DeviceTests.class)
void "run tests on devices"() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsAppWithProguard.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsAppWithProguard.groovy
index 7eddff6..eaea1ad 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsAppWithProguard.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/TestWithSameDepAsAppWithProguard.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -26,6 +27,7 @@
/**
* Tests the handling of test dependency.
*/
+@CompileStatic
class TestWithSameDepAsAppWithProguard {
private static AndroidTestApp testApp = new HelloWorldApp()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/VariantDependencyTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/VariantDependencyTest.groovy
index d827983..36b2e20 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/VariantDependencyTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dependencies/VariantDependencyTest.groovy
@@ -33,6 +33,7 @@
import com.android.ide.common.process.ProcessExecutor
import com.android.utils.StdLogger
import com.google.common.collect.Sets
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -44,6 +45,7 @@
import static org.junit.Assert.assertNotNull
import static org.junit.Assert.assertTrue
+@CompileStatic
class VariantDependencyTest {
@ClassRule
public static GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/DslTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/DslTest.groovy
index d1fddd8..24f84bc 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/DslTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/DslTest.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle.integration.dsl
-
import com.android.build.gradle.integration.application.BuildConfigTest
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
@@ -26,7 +25,6 @@
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull
-
/**
* General DSL tests
*/
@@ -135,7 +133,7 @@
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "";
public static final int VERSION_CODE = 1;
- public static final String VERSION_NAME = "";
+ public static final String VERSION_NAME = "1.0";
// Fields from default config.
public static final String test2 = "Ä…";
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/TestedVariantTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/TestedVariantTest.groovy
index a2880ec..25e0250 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/TestedVariantTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/dsl/TestedVariantTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import groovy.transform.CompileStatic
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -25,6 +26,7 @@
/**
* Test that the test variant returns what it should.
*/
+@CompileStatic
class TestedVariantTest {
@Rule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/BasicTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/BasicTest.groovy
new file mode 100644
index 0000000..ae60782
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/BasicTest.groovy
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.googleservices
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.EmptyAndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Variant
+import com.google.common.base.Joiner
+import com.google.common.io.Files
+import com.google.common.truth.Truth
+import groovy.json.internal.Charsets
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.Assert
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+/**
+ * Basic test with gcm + ga
+ */
+@CompileStatic
+class BasicTest {
+
+ public static final AndroidTestApp helloWorldApp = new EmptyAndroidTestApp("com.example.app.free")
+
+ private static final File resDataFolder = new File(GradleTestProject.TEST_RES_DIR, "basic")
+ static {
+ File source = new File(resDataFolder, "example.json")
+ helloWorldApp.addFile(new TestSourceFile(
+ "",
+ TestHelper.JSON_FILE_NAME,
+ Files.toString(source, Charsets.UTF_8)))
+ }
+
+ @ClassRule
+ public static GradleTestProject project = GradleTestProject.builder()
+ .fromTestApp(helloWorldApp)
+ .create()
+
+ public static AndroidProject model
+ private static File generatedResFolder
+
+ @BeforeClass
+ public static void setUp() {
+
+ project.getBuildFile() << """
+apply plugin: 'com.android.application'
+apply plugin: 'com.google.gms.google-services'
+
+android {
+ compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+}
+"""
+ model = project.executeAndReturnModel("clean", "assembleDebug")
+
+ generatedResFolder = new File(project.getTestDir(),
+ Joiner.on(File.separator).join("build", "generated", "res", "google-services", "debug"))
+ }
+
+ @AfterClass
+ public static void cleanUp() {
+ project = null
+ model = null
+ }
+
+ @Test
+ public void "test values res file is generated"() {
+ File valuesFolder = new File(generatedResFolder, "values")
+ File values = new File(valuesFolder, "values.xml")
+ Assert.assertTrue(values.isFile())
+
+ File goldenFile = new File(resDataFolder, "values.xml")
+ Truth.assert_().that(Files.toString(values, Charsets.UTF_8).trim())
+ .isEqualTo(Files.toString(goldenFile, Charsets.UTF_8).trim());
+ }
+
+ @Test
+ public void "test ga res file is generated"() {
+ File xmlFolder = new File(generatedResFolder, "xml")
+ File global_tracker = new File(xmlFolder, "global_tracker.xml")
+ Assert.assertTrue(global_tracker.isFile())
+
+ File goldenFile = new File(resDataFolder, "global_tracker.xml")
+ Truth.assert_().that(Files.toString(global_tracker, Charsets.UTF_8).trim())
+ .isEqualTo(Files.toString(goldenFile, Charsets.UTF_8).trim());
+ }
+
+ @Test
+ public void "test generated res folder is in model"() {
+ Variant debugVariant = ModelHelper.getVariant(model.getVariants(), "debug");
+ Collection<File> generatedResFolders = debugVariant.getMainArtifact().getGeneratedResourceFolders()
+
+ Truth.assert_().that(generatedResFolders).contains(generatedResFolder.getCanonicalFile())
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/DisabledServiceTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/DisabledServiceTest.groovy
new file mode 100644
index 0000000..055690e
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/DisabledServiceTest.groovy
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.googleservices
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.EmptyAndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Variant
+import com.google.common.base.Joiner
+import com.google.common.io.Files
+import com.google.common.truth.Truth
+import groovy.json.internal.Charsets
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.Assert
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+/**
+ * Test for mostly empty json file.
+ */
+@CompileStatic
+class DisabledServiceTest {
+
+ public static final AndroidTestApp helloWorldApp = new EmptyAndroidTestApp("com.example.app.free")
+
+ private static final File resDataFolder = new File(GradleTestProject.TEST_RES_DIR, "disabledservice")
+ static {
+ File source = new File(resDataFolder, "example.json")
+ helloWorldApp.addFile(new TestSourceFile(
+ "",
+ TestHelper.JSON_FILE_NAME,
+ Files.toString(source, Charsets.UTF_8)))
+ }
+
+ @ClassRule
+ public static GradleTestProject project = GradleTestProject.builder()
+ .fromTestApp(helloWorldApp)
+ .create()
+
+ public static AndroidProject model
+ private static File generatedResFolder
+
+ @BeforeClass
+ public static void setUp() {
+
+ project.getBuildFile() << """
+apply plugin: 'com.android.application'
+apply plugin: 'com.google.gms.google-services'
+
+android {
+ compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+}
+"""
+ model = project.executeAndReturnModel("clean", "assembleDebug")
+ generatedResFolder = new File(project.getTestDir(),
+ Joiner.on(File.separator).join("build", "generated", "res", "google-services", "debug"))
+ }
+
+ @AfterClass
+ public static void cleanUp() {
+ project = null
+
+ }
+ @Test
+ public void "test values res file is generated"() {
+ File valuesFolder = new File(generatedResFolder, "values")
+ File values = new File(valuesFolder, "values.xml")
+ Assert.assertTrue(values.isFile())
+
+ File goldenFile = new File(resDataFolder, "values.xml")
+ Truth.assert_().that(Files.toString(values, Charsets.UTF_8).trim())
+ .isEqualTo(Files.toString(goldenFile, Charsets.UTF_8).trim());
+ }
+
+ @Test
+ public void "test generated res folder is in model"() {
+ Variant debugVariant = ModelHelper.getVariant(model.getVariants(), "debug");
+ Collection<File> generatedResFolders = debugVariant.getMainArtifact().getGeneratedResourceFolders()
+
+ Truth.assert_().that(generatedResFolders).contains(generatedResFolder.getCanonicalFile())
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/FlavorTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/FlavorTest.groovy
new file mode 100644
index 0000000..4e727b0
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/FlavorTest.groovy
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.googleservices
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.EmptyAndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Variant
+import com.google.common.base.Joiner
+import com.google.common.io.Files
+import com.google.common.truth.Truth
+import groovy.json.internal.Charsets
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.Assert
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+/**
+ * Basic test with gcm + ga
+ */
+@CompileStatic
+class FlavorTest {
+
+ public static final AndroidTestApp helloWorldApp = new EmptyAndroidTestApp("com.example.app")
+
+ private static final File resDataFolder = new File(GradleTestProject.TEST_RES_DIR, "flavor")
+ static {
+ File source = new File(resDataFolder, "example.json")
+ helloWorldApp.addFile(new TestSourceFile(
+ "",
+ TestHelper.JSON_FILE_NAME,
+ Files.toString(source, Charsets.UTF_8)))
+ }
+
+ @ClassRule
+ public static GradleTestProject project = GradleTestProject.builder()
+ .fromTestApp(helloWorldApp)
+ .create()
+
+ public static AndroidProject model
+ private static File generatedFreeResFolder
+ private static File generatedPaidResFolder
+
+ @BeforeClass
+ public static void setUp() {
+
+ project.getBuildFile() << """
+apply plugin: 'com.android.application'
+apply plugin: 'com.google.gms.google-services'
+
+android {
+ compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+ productFlavors {
+ free {
+ applicationId 'com.example.app.free'
+ }
+ paid {
+ applicationId 'com.example.app.paid'
+ }
+ }
+
+}
+"""
+ model = project.executeAndReturnModel("clean", "assembleDebug")
+
+ generatedFreeResFolder = new File(project.getTestDir(),
+ Joiner.on(File.separator).join("build", "generated", "res", "google-services", "free", "debug"))
+ generatedPaidResFolder = new File(project.getTestDir(),
+ Joiner.on(File.separator).join("build", "generated", "res", "google-services", "paid", "debug"))
+ }
+
+ @AfterClass
+ public static void cleanUp() {
+ project = null
+ model = null
+ }
+
+ @Test
+ public void "test values res file is generated"() {
+ checkResValuesFile(generatedFreeResFolder, "free.values.xml")
+ checkResValuesFile(generatedPaidResFolder, "paid.values.xml")
+ }
+
+ private static void checkResValuesFile(File generatedResFolder, String goldenFileName) {
+ File valuesFolder = new File(generatedResFolder, "values")
+ File values = new File(valuesFolder, "values.xml")
+ Assert.assertTrue(values.isFile())
+
+ File goldenFile = new File(resDataFolder, goldenFileName)
+ Truth.assert_().that(Files.toString(values, Charsets.UTF_8).trim())
+ .isEqualTo(Files.toString(goldenFile, Charsets.UTF_8).trim());
+ }
+
+ @Test
+ public void "test ga res file is generated"() {
+ checkGlobalTracker(generatedFreeResFolder, "free.global_tracker.xml")
+ checkGlobalTracker(generatedPaidResFolder, "paid.global_tracker.xml")
+ }
+
+ private static void checkGlobalTracker(File generatedResFolder, String goldenFileName) {
+ File xmlFolder = new File(generatedResFolder, "xml")
+ File global_tracker = new File(xmlFolder, "global_tracker.xml")
+ Assert.assertTrue(global_tracker.isFile())
+
+ File goldenFile = new File(resDataFolder, goldenFileName)
+ Truth.assert_().that(Files.toString(global_tracker, Charsets.UTF_8).trim())
+ .isEqualTo(Files.toString(goldenFile, Charsets.UTF_8).trim());
+ }
+
+ @Test
+ public void "test generated res folder is in model"() {
+ checkModel("freeDebug", generatedFreeResFolder)
+ checkModel("paidDebug", generatedPaidResFolder)
+ }
+
+ private static void checkModel(String variantName, File generatedResFolder) {
+ Variant freeDebugVariant = ModelHelper.getVariant(model.getVariants(), variantName);
+ Collection<File> generatedResFolders = freeDebugVariant.getMainArtifact().getGeneratedResourceFolders()
+
+ Truth.assert_().that(generatedResFolders).contains(generatedResFolder.getCanonicalFile())
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/NoClientTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/NoClientTest.groovy
new file mode 100644
index 0000000..7419b44
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/NoClientTest.groovy
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.googleservices
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.EmptyAndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import com.google.common.io.Files
+import com.google.common.truth.Truth
+import groovy.json.internal.Charsets
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+/**
+ * Test with a mismatch json file vs the app package name.
+ */
+@CompileStatic
+class NoClientTest {
+
+ public static final AndroidTestApp helloWorldApp = new EmptyAndroidTestApp("com.example.app.typo")
+
+ static {
+ File source = new File(new File(GradleTestProject.TEST_RES_DIR, "basic"), "example.json")
+ helloWorldApp.addFile(new TestSourceFile(
+ "",
+ TestHelper.JSON_FILE_NAME,
+ Files.toString(source, Charsets.UTF_8)))
+ }
+
+ @ClassRule
+ public static GradleTestProject project = GradleTestProject.builder()
+ .fromTestApp(helloWorldApp)
+ .captureStdOut(true)
+ .create()
+
+ @BeforeClass
+ public static void setUp() {
+
+ project.getBuildFile() << """
+apply plugin: 'com.android.application'
+apply plugin: 'com.google.gms.google-services'
+
+android {
+ compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+}
+"""
+ project.execute("clean", "assembleDebug")
+ }
+
+ @AfterClass
+ public static void cleanUp() {
+ project = null
+ }
+
+ @Test
+ public void "test warning is output"() {
+ ByteArrayOutputStream stream = project.getStdout()
+
+ Truth.assert_().that(stream.toString("UTF-8")).contains(
+ "No matching client found for package name 'com.example.app.typo'")
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/NoServiceTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/NoServiceTest.groovy
new file mode 100644
index 0000000..5772f19
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/NoServiceTest.groovy
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.googleservices
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.EmptyAndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Variant
+import com.google.common.base.Joiner
+import com.google.common.io.Files
+import com.google.common.truth.Truth
+import groovy.json.internal.Charsets
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.Assert
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+
+/**
+ * Test for mostly empty json file.
+ */
+@CompileStatic
+class NoServiceTest {
+
+ public static final AndroidTestApp helloWorldApp = new EmptyAndroidTestApp("com.example.app.free")
+
+ private static final File resDataFolder = new File(GradleTestProject.TEST_RES_DIR, "noservice")
+ static {
+ File source = new File(resDataFolder, "no_services.json")
+ helloWorldApp.addFile(new TestSourceFile(
+ "",
+ TestHelper.JSON_FILE_NAME,
+ Files.toString(source, Charsets.UTF_8)))
+ }
+
+ @ClassRule
+ public static GradleTestProject project = GradleTestProject.builder()
+ .fromTestApp(helloWorldApp)
+ .create()
+
+ public static AndroidProject model
+ private static File generatedResFolder
+
+ @BeforeClass
+ public static void setUp() {
+
+ project.getBuildFile() << """
+apply plugin: 'com.android.application'
+apply plugin: 'com.google.gms.google-services'
+
+android {
+ compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+
+}
+"""
+ model = project.executeAndReturnModel("clean", "assembleDebug")
+ generatedResFolder = new File(project.getTestDir(),
+ Joiner.on(File.separator).join("build", "generated", "res", "google-services", "debug"))
+ }
+
+ @AfterClass
+ public static void cleanUp() {
+ project = null
+
+ }
+ @Test
+ public void "test values res file is generated"() {
+ File valuesFolder = new File(generatedResFolder, "values")
+ File values = new File(valuesFolder, "values.xml")
+ Assert.assertTrue(values.isFile())
+
+ File goldenFile = new File(resDataFolder, "values.xml")
+ Truth.assert_().that(Files.toString(values, Charsets.UTF_8).trim())
+ .isEqualTo(Files.toString(goldenFile, Charsets.UTF_8).trim());
+ }
+
+ @Test
+ public void "test generated res folder is in model"() {
+ Variant debugVariant = ModelHelper.getVariant(model.getVariants(), "debug");
+ Collection<File> generatedResFolders = debugVariant.getMainArtifact().getGeneratedResourceFolders()
+
+ Truth.assert_().that(generatedResFolders).contains(generatedResFolder.getCanonicalFile())
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/TestHelper.java b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/TestHelper.java
new file mode 100644
index 0000000..0ec42b8
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/googleservices/TestHelper.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.googleservices;
+
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+
+import org.junit.Assert;
+
+import java.io.File;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.CodeSource;
+import java.util.List;
+
+/**
+ */
+public class TestHelper {
+
+ public static final String JSON_FILE_NAME = "google-services.json";
+
+ public static File getResourceFolder2(String... pathSegments) {
+ CodeSource source = TestHelper.class.getProtectionDomain().getCodeSource();
+ if (source != null) {
+ URL location = source.getLocation();
+ try {
+ File dir = new File(location.toURI());
+ Assert.assertTrue(dir.getPath(), dir.exists());
+
+ dir = dir.getParentFile().getParentFile().getParentFile();
+
+ List<String> segments = Lists.newArrayList("src", "test", "resources");
+ if (pathSegments != null) {
+ segments.addAll(Lists.newArrayList(pathSegments));
+ }
+
+ return new File(
+ dir,
+ Joiner.on(File.separator).join(segments));
+ } catch (URISyntaxException e) {
+ fail(e.getLocalizedMessage());
+ }
+ }
+
+ fail("Fail to get the test resource folder");
+
+ return null;
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AidlTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AidlTest.groovy
index 288c688..9ccd2d6 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AidlTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AidlTest.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.library
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -25,6 +26,7 @@
/**
* Assemble tests for aidl.
*/
+@CompileStatic
class AidlTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApiTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApiTest.groovy
index 1f9c389..fc61bb2 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApiTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApiTest.groovy
@@ -18,15 +18,20 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.utils.FileUtils
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
import org.junit.experimental.categories.Category
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+
/**
* Assemble tests for api.
*/
+@CompileStatic
class ApiTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +56,16 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
+ }
+
+ @Test
+ public void backwardsCompatible() throws Exception {
+ // ATTENTION Author and Reviewers - please make sure required changes to the build file
+ // are backwards compatible before updating this test.
+ assertThat(FileUtils.sha1(project.file("app/build.gradle")))
+ .isEqualTo("e20b70879b449c222ce9c0f9f17cb808d6899b06")
+ assertThat(FileUtils.sha1(project.file("lib/build.gradle")))
+ .isEqualTo("fbdd1f6d0d0e190412db885a1c281efee4ab0cac")
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApplibtestTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApplibtestTest.groovy
index 54c09db..dcc0f24 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApplibtestTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ApplibtestTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for applibtest.
*/
+@CompileStatic
class ApplibtestTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AssetsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AssetsTest.groovy
index 38b4a8d..fae6db3 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AssetsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AssetsTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for assets.
*/
+@CompileStatic
class AssetsTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AttrOrderTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AttrOrderTest.groovy
index 0c981ac..f4445eb 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AttrOrderTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/AttrOrderTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for attrOrder.
*/
+@CompileStatic
class AttrOrderTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/DslTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/DslTest.groovy
index 6f0a676..5e9802c 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/DslTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/DslTest.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.library
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import groovy.transform.CompileStatic
import org.gradle.tooling.BuildException
import org.junit.Before
import org.junit.Rule
@@ -25,6 +26,7 @@
import static org.junit.Assert.assertEquals
import static org.junit.Assert.fail
+@CompileStatic
class DslTest {
@Rule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavoredlibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavoredlibTest.groovy
index ccf8ff3..55a81f5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavoredlibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavoredlibTest.groovy
@@ -23,6 +23,7 @@
import com.android.builder.model.Dependencies
import com.android.builder.model.ProductFlavorContainer
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -36,6 +37,7 @@
/**
* Assemble tests for flavoredlib.
*/
+@CompileStatic
class FlavoredlibTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -111,6 +113,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavorlibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavorlibTest.groovy
index 7acb0a2..b12a565 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavorlibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/FlavorlibTest.groovy
@@ -24,6 +24,7 @@
import com.android.builder.model.Dependencies
import com.android.builder.model.ProductFlavorContainer
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.Assert
import org.junit.BeforeClass
@@ -39,6 +40,7 @@
/**
* Assemble tests for flavorlib.
*/
+@CompileStatic
class FlavorlibTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -118,6 +120,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/GenerateAnnotationsClassPathTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/GenerateAnnotationsClassPathTest.groovy
index fd569fd..cd37449 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/GenerateAnnotationsClassPathTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/GenerateAnnotationsClassPathTest.groovy
@@ -16,6 +16,7 @@
package com.android.build.gradle.integration.library
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -23,6 +24,7 @@
import static org.junit.Assert.assertFalse
+@CompileStatic
class GenerateAnnotationsClassPathTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyJarDepTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyJarDepTest.groovy
index 1f9e364..e072ed0 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyJarDepTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyJarDepTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for libMinifyJarDep.
*/
+@CompileStatic
class LibMinifyJarDepTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyLibDepTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyLibDepTest.groovy
index 89d4356..06f97fa 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyLibDepTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyLibDepTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for libMinifyLibDep.
*/
+@CompileStatic
class LibMinifyLibDepTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyTest.groovy
index b29df0e..f2d552d 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibMinifyTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.utils.FileHelper
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -26,6 +27,7 @@
/**
* Assemble tests for libMinify.
*/
+@CompileStatic
class LibMinifyTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibProguardConsumerFilesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibProguardConsumerFilesTest.groovy
index 5db0cac..7c85dc8 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibProguardConsumerFilesTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibProguardConsumerFilesTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.utils.FileHelper
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -28,6 +29,7 @@
/**
* Assemble tests for libProguarConsumerFiles.
*/
+@CompileStatic
class LibProguardConsumerFilesTest {
@ClassRule
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibTestDepTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibTestDepTest.groovy
index 9464e16..591fe2c 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibTestDepTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibTestDepTest.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle.integration.library
-
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.utils.ModelHelper
@@ -24,6 +23,7 @@
import com.android.builder.model.Dependencies
import com.android.builder.model.JavaLibrary
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -34,12 +34,11 @@
import static com.android.builder.model.AndroidProject.ARTIFACT_ANDROID_TEST
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull
-import static org.junit.Assert.assertThat
import static org.junit.Assert.assertTrue
-
/**
* Assemble tests for libTestDep.
*/
+@CompileStatic
class LibTestDepTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -91,6 +90,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibsTestTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibsTestTest.groovy
index 9f64770..8372aac 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibsTestTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LibsTestTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for libsTest.
*/
+@CompileStatic
class LibsTestTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalAarTestTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalAarTestTest.groovy
index 3a1b343..6d19d2f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalAarTestTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalAarTestTest.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.library
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -25,6 +26,7 @@
/**
* Assemble tests for localAarTest.
*/
+@CompileStatic
class LocalAarTestTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalJarsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalJarsTest.groovy
index 8627580..9133eef 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalJarsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/LocalJarsTest.groovy
@@ -21,6 +21,7 @@
import com.android.builder.model.Dependencies
import com.android.builder.model.JavaLibrary
import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -31,6 +32,7 @@
/**
* Assemble tests for localJars.
*/
+@CompileStatic
class LocalJarsTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MinifyLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MinifyLibTest.groovy
index 27ce57e..8f10a52 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MinifyLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MinifyLibTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for minifyLib.
*/
+@CompileStatic
class MinifyLibTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiDexWithLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiDexWithLibTest.groovy
index acf86f6..3ff336f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiDexWithLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiDexWithLibTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for multiDexWithLib.
*/
+@CompileStatic
class MultiDexWithLibTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -35,6 +37,7 @@
@BeforeClass
static void setUp() {
+ GradleTestProject.assumeBuildToolsAtLeast(21)
project.execute("clean", "assembleDebug")
}
@@ -51,6 +54,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiprojectTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiprojectTest.groovy
index d8a604b..662043e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiprojectTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/MultiprojectTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for multiproject.
*/
+@CompileStatic
class MultiprojectTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,9 @@
@Test
@Category(DeviceTests.class)
void connectedCheckAndReport() {
- project.execute("connectedCheck", "mergeAndroidReports")
+ project.executeConnectedCheck()
+ // android-reporting plugin currently executes connected tasks.
+ GradleTestProject.assumeLocalDevice();
+ project.execute("mergeAndroidReports")
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ProguardAarPackagingTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ProguardAarPackagingTest.groovy
index 8420836..68a8600 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ProguardAarPackagingTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/ProguardAarPackagingTest.groovy
@@ -1,5 +1,4 @@
package com.android.build.gradle.integration.library
-
import com.android.SdkConstants
import com.android.annotations.NonNull
import com.android.build.gradle.integration.common.fixture.GradleTestProject
@@ -22,7 +21,6 @@
import static org.junit.Assert.assertNotNull
import static org.junit.Assert.assertNull
import static org.junit.Assert.assertTrue
-
/**
* Integration test to check that libraries included directly as jar files are correctly handled
* when using proguard.
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/RenderscriptInLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/RenderscriptInLibTest.groovy
index b39d8a1..ac08c8e 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/RenderscriptInLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/RenderscriptInLibTest.groovy
@@ -17,6 +17,7 @@
package com.android.build.gradle.integration.library
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -25,6 +26,7 @@
/**
* Assemble tests for renderscriptInLib.
*/
+@CompileStatic
class RenderscriptInLibTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/SameNamedLibsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/SameNamedLibsTest.groovy
index 6340ef7..ab4fccf 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/SameNamedLibsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/library/SameNamedLibsTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,6 +28,7 @@
/**
* Assemble tests for sameNamedLibs.
*/
+@CompileStatic
class SameNamedLibsTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
@@ -51,6 +53,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkConnectedCheckTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkConnectedCheckTest.groovy
new file mode 100644
index 0000000..264d5aa
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkConnectedCheckTest.groovy
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.ndk
+
+import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.TestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+import org.junit.experimental.categories.Category
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
+
+/**
+ * Test AndroidTest with NDK.
+ */
+class NdkConnectedCheckTest {
+
+ private static AndroidTestApp app = new HelloWorldJniApp();
+ static {
+ app.addFile(new TestSourceFile("src/androidTest/jni", "hello-jni-test.c",
+"""
+#include <string.h>
+#include <jni.h>
+
+jstring
+Java_com_example_hellojni_HelloJniTest_expectedString(JNIEnv* env, jobject thiz)
+{
+ return (*env)->NewStringUTF(env, "hello world!");
+}
+"""));
+ app.addFile(new TestSourceFile("src/androidTest/java/com/example/hellojni", "HelloJniTest.java",
+"""
+package com.example.hellojni;
+
+import android.test.ActivityInstrumentationTestCase;
+
+public class HelloJniTest extends ActivityInstrumentationTestCase<HelloJni> {
+
+ public HelloJniTest() {
+ super("com.example.hellojni", HelloJni.class);
+ }
+
+ // Get expected string from JNI.
+ public native String expectedString();
+
+ static {
+ System.loadLibrary("hello-jni_test");
+ }
+
+ public void testJniName() {
+ final HelloJni a = getActivity();
+ // ensure a valid handle to the activity has been returned
+ assertNotNull(a);
+
+ assertTrue(expectedString().equals(a.stringFromJNI()));
+ }
+}
+"""));
+ }
+
+ @ClassRule
+ public static GradleTestProject project = GradleTestProject.builder()
+ .fromTestApp(app)
+ .addGradleProperties("android.useDeprecatedNdk=true")
+ .create()
+
+
+ @BeforeClass
+ static void setUp() {
+ project.getBuildFile() <<
+"""
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ defaultConfig {
+ ndk {
+ moduleName "hello-jni"
+ }
+ }
+}
+"""
+ project.execute("clean", "assembleAndroidTest");
+ }
+
+
+ @Test
+ void "check test lib is packaged"() {
+ File apk = project.getApk("debug", "androidTest", "unaligned")
+ assertThatZip(apk).contains("lib/x86/libhello-jni_test.so")
+ }
+
+ @Test
+ @Category(DeviceTests.class)
+ void connectedCheck() {
+ project.executeConnectedCheck()
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkJniLibTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkJniLibTest.groovy
index 06f2019..8561323 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkJniLibTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkJniLibTest.groovy
@@ -33,6 +33,7 @@
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
.fromTestProject("ndkJniLib")
+ .addGradleProperties("android.useDeprecatedNdk=true")
.create()
@BeforeClass
@@ -67,6 +68,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkLibPrebuiltsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkLibPrebuiltsTest.groovy
index 6aa62a6..ca42727 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkLibPrebuiltsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkLibPrebuiltsTest.groovy
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -27,10 +28,12 @@
/**
* Assemble tests for ndkLibPrebuilts.
*/
+@CompileStatic
class NdkLibPrebuiltsTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
.fromTestProject("ndkLibPrebuilts")
+ .addGradleProperties("android.useDeprecatedNdk=true")
.create()
@BeforeClass
@@ -51,6 +54,6 @@
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkModelTest.groovy
new file mode 100644
index 0000000..b261142
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkModelTest.groovy
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.ndk
+
+import com.android.SdkConstants
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.NativeLibrary
+import com.android.builder.model.Variant
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+
+/**
+ * Test the return model of the NDK.
+ */
+class NdkModelTest {
+ @Rule
+ public GradleTestProject project = GradleTestProject.builder()
+ .fromTestApp(new HelloWorldJniApp())
+ .addGradleProperties("android.useDeprecatedNdk=true")
+ .create()
+
+ @Before
+ void setUp() {
+ project.buildFile <<
+"""
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ defaultConfig {
+ ndk {
+ moduleName "hello-jni"
+ cFlags = "-DTEST_FLAG"
+ }
+ }
+}
+"""
+ }
+
+ @Test
+ void "check native libraries in model"() {
+ checkModel(
+ debug : [
+ SdkConstants.ABI_ARMEABI,
+ SdkConstants.ABI_ARMEABI_V7A,
+ SdkConstants.ABI_ARM64_V8A,
+ SdkConstants.ABI_INTEL_ATOM,
+ SdkConstants.ABI_INTEL_ATOM64,
+ SdkConstants.ABI_MIPS,
+ SdkConstants.ABI_MIPS64
+ ]);
+ }
+
+ @Test
+ void "check native libraries with splits"() {
+ project.buildFile <<
+"""
+android {
+ splits {
+ abi {
+ enable true
+ reset()
+ include 'x86', 'armeabi-v7a', 'mips'
+ }
+ }
+}
+"""
+ checkModel(
+ debug: [SdkConstants.ABI_ARMEABI_V7A, SdkConstants.ABI_INTEL_ATOM, SdkConstants.ABI_MIPS]);
+ }
+
+ @Test
+ void "check native libraries with splits and universalApk"() {
+ project.buildFile <<
+ """
+android {
+ splits {
+ abi {
+ enable true
+ reset()
+ include 'x86', 'armeabi-v7a', 'mips'
+ universalApk true
+ }
+ }
+}
+"""
+ checkModel(
+ debug : [
+ SdkConstants.ABI_ARMEABI,
+ SdkConstants.ABI_ARMEABI_V7A,
+ SdkConstants.ABI_ARM64_V8A,
+ SdkConstants.ABI_INTEL_ATOM,
+ SdkConstants.ABI_INTEL_ATOM64,
+ SdkConstants.ABI_MIPS,
+ SdkConstants.ABI_MIPS64
+ ]);
+ }
+
+ @Test
+ void "check native libraries with abiFilters"() {
+ project.buildFile <<
+ """
+android {
+ productFlavors {
+ x86 {
+ ndk {
+ abiFilter "x86"
+ }
+ }
+ arm {
+ ndk {
+ abiFilters "armeabi-v7a"
+ }
+ }
+ mips {
+ ndk {
+ abiFilter "mips"
+ }
+ }
+ }
+}
+"""
+ checkModel(
+ x86Debug : [SdkConstants.ABI_INTEL_ATOM],
+ armDebug : [SdkConstants.ABI_ARMEABI_V7A],
+ mipsDebug : [SdkConstants.ABI_MIPS]);
+ }
+
+ @Test
+ void "check using add on string for compileSdkVersion"() {
+ project.buildFile <<
+"""
+android {
+ compileSdkVersion "Google Inc.:Google APIs:$GradleTestProject.DEFAULT_COMPILE_SDK_VERSION"
+}
+"""
+ AndroidProject model = project.executeAndReturnModel("assembleDebug")
+ NativeLibrary lib = ModelHelper.getVariant(model.getVariants(), "debug").getMainArtifact()
+ .getNativeLibraries().first()
+ for (String flag : lib.getCCompilerFlags()) {
+ if (flag.contains("sysroot")) {
+ assertThat(flag).contains("android-${GradleTestProject.DEFAULT_COMPILE_SDK_VERSION}")
+ }
+ }
+ }
+
+ /**
+ * Verify resulting model is as expected.
+ *
+ * @param variantToolchains map of variant name to array of expected toolchains.
+ */
+ private void checkModel(Map variantToolchains) {
+
+ AndroidProject model = project.executeAndReturnModel("assembleDebug")
+
+ Collection<Variant> variants = model.getVariants()
+ for (Map.Entry entry : variantToolchains) {
+ Variant variant = ModelHelper.getVariant(variants, (String) entry.getKey())
+ AndroidArtifact mainArtifact = variant.getMainArtifact()
+
+ assertThat(mainArtifact.getNativeLibraries()).hasSize(((Collection)entry.getValue()).size())
+ for (NativeLibrary nativeLibrary : mainArtifact.getNativeLibraries()) {
+ assertThat(nativeLibrary.getName()).isEqualTo("hello-jni")
+ assertThat(nativeLibrary.getCCompilerFlags()).contains("-DTEST_FLAG");
+ assertThat(nativeLibrary.getCppCompilerFlags()).contains("-DTEST_FLAG");
+ assertThat(nativeLibrary.getCSystemIncludeDirs()).isEmpty();
+ assertThat(nativeLibrary.getCppSystemIncludeDirs()).isNotEmpty();
+ File solibSearchPath = nativeLibrary.getDebuggableLibraryFolders().first()
+ assertThat(new File(solibSearchPath, "libhello-jni.so")).exists()
+ }
+
+ Collection<String> expectedToolchainNames = entry.getValue().collect { "gcc-" + it }
+ Collection<String> toolchainNames = model.getNativeToolchains().collect { it.getName() }
+ assertThat(toolchainNames).containsAllIn(expectedToolchainNames)
+ Collection<String> nativeLibToolchains = mainArtifact.getNativeLibraries().
+ collect { it.getToolchainName() }
+ assertThat(nativeLibToolchains).containsExactlyElementsIn(expectedToolchainNames)
+ }
+
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkPrebuiltsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkPrebuiltsTest.groovy
index 4dc9015..e8b283f 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkPrebuiltsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkPrebuiltsTest.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle.integration.ndk
-
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.builder.model.AndroidProject
import com.android.builder.model.Variant
@@ -29,7 +28,6 @@
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull
import static org.junit.Assert.assertTrue
-
/**
* Assemble tests for ndkPrebuilts.
*/
@@ -37,6 +35,7 @@
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
.fromTestProject("ndkPrebuilts")
+ .addGradleProperties("android.useDeprecatedNdk=true")
.create()
static AndroidProject model
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkSanAngelesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkSanAngelesTest.groovy
index e9bba65..7c2952b 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkSanAngelesTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NdkSanAngelesTest.groovy
@@ -16,6 +16,7 @@
package com.android.build.gradle.integration.ndk
+import com.android.SdkConstants
import com.android.build.FilterData
import com.android.build.OutputFile
import com.android.build.gradle.integration.common.category.DeviceTests
@@ -24,14 +25,17 @@
import com.android.builder.model.AndroidArtifact
import com.android.builder.model.AndroidArtifactOutput
import com.android.builder.model.AndroidProject
+import com.android.builder.model.NativeLibrary
import com.android.builder.model.Variant
import com.google.common.collect.Maps
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
import org.junit.experimental.categories.Category
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
import static com.android.builder.core.BuilderConstants.DEBUG
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertNotNull
@@ -40,10 +44,12 @@
/**
* Assemble tests for ndkSanAngeles.
*/
+@CompileStatic
class NdkSanAngelesTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
.fromTestProject("ndkSanAngeles")
+ .addGradleProperties("android.useDeprecatedNdk=true")
.create()
static AndroidProject model
@@ -72,11 +78,11 @@
// get the main artifact of the debug artifact
Variant debugVariant = ModelHelper.getVariant(variants, DEBUG)
assertNotNull("debug Variant null-check", debugVariant)
- AndroidArtifact debugMainArficat = debugVariant.getMainArtifact()
- assertNotNull("Debug main info null-check", debugMainArficat)
+ AndroidArtifact debugMainArtifact = debugVariant.getMainArtifact()
+ assertNotNull("Debug main info null-check", debugMainArtifact)
// get the outputs.
- Collection<AndroidArtifactOutput> debugOutputs = debugMainArficat.getOutputs()
+ Collection<AndroidArtifactOutput> debugOutputs = debugMainArtifact.getOutputs()
assertNotNull(debugOutputs)
assertEquals(3, debugOutputs.size())
@@ -107,10 +113,9 @@
assertTrue(expected.isEmpty())
}
-
@Test
@Category(DeviceTests.class)
void connectedCheck() {
- project.execute("connectedCheck")
+ project.executeConnectedCheck()
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NoSplitNdkVariantsTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NoSplitNdkVariantsTest.groovy
index 37e2da0..85ec5be 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NoSplitNdkVariantsTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/NoSplitNdkVariantsTest.groovy
@@ -19,6 +19,8 @@
import com.android.build.gradle.integration.common.category.DeviceTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import com.android.builder.core.BuilderConstants
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
@@ -33,11 +35,13 @@
/**
* Integration test of the native plugin with multiple variants without using splits.
*/
+@CompileStatic
class NoSplitNdkVariantsTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
.fromTestApp(new HelloWorldJniApp())
+ .addGradleProperties("android.useDeprecatedNdk=true")
.create()
@BeforeClass
@@ -124,6 +128,10 @@
@Test
@Category(DeviceTests.class)
public void connectedAndroidTest() {
- project.execute("connectedAndroidTestArmDebug")
+ if (GradleTestProject.DEVICE_PROVIDER_NAME.equals(BuilderConstants.CONNECTED)) {
+ project.execute(GradleTestProject.DEVICE_PROVIDER_NAME + "ArmDebugAndroidTest")
+ } else {
+ project.execute(GradleTestProject.DEVICE_PROVIDER_NAME + "X86DebugAndroidTest")
+ }
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/Pre21SplitTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/Pre21SplitTest.groovy
index 9ff13a6..9a52db5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/Pre21SplitTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/ndk/Pre21SplitTest.groovy
@@ -16,22 +16,28 @@
package com.android.build.gradle.integration.ndk
+import com.android.build.gradle.integration.common.category.SmokeTests
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldJniApp
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
+import org.junit.experimental.categories.Category
import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
/**
* Test split DSL with API level < 21.
*/
+@Category(SmokeTests.class)
+@CompileStatic
class Pre21SplitTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
.fromTestApp(new HelloWorldJniApp())
+ .addGradleProperties("android.useDeprecatedNdk=true")
.create()
@BeforeClass
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/IOScheduleCodeChangeTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/IOScheduleCodeChangeTest.groovy
new file mode 100644
index 0000000..203d742
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/IOScheduleCodeChangeTest.groovy
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.performance
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.BUILD_FULL
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.BUILD_INC_JAVA
+/**
+ * Performance test for full and incremental build on ioschedule 2014
+ */
+@RunWith(Parameterized.class)
+@CompileStatic
+class IOScheduleCodeChangeTest {
+
+ @Parameterized.Parameters(name="minify={0} jack={1}")
+ public static Collection<Object[]> data() {
+ // returns an array of boolean for all combinations of (proguard, jack).
+ // Right now, only return the (false, false) and (false, true) cases.
+ return [
+// [true, false].toArray(),
+// [true, true].toArray(),
+ [false, false].toArray(),
+ [false, true].toArray(),
+ ];
+ }
+
+ private final boolean proguard
+ private final boolean jack
+
+ IOScheduleCodeChangeTest(boolean proguard, boolean jack) {
+ this.proguard = proguard
+ this.jack = jack
+ }
+
+ @Rule
+ public GradleTestProject project = GradleTestProject.builder()
+ .fromExternalProject("iosched")
+ .withJack(jack)
+ .withMinify(proguard)
+ .create()
+
+ @Before
+ public void setUp() {
+ project.executeWithBenchmark("iosched2014", BUILD_FULL, "clean" , "assembleDebug")
+ }
+
+ @After
+ void cleanUp() {
+ project = null;
+ }
+
+ @Test
+ void "Incremental Build on Java Change"() {
+ project.replaceLine(
+ "android/src/main/java/com/google/samples/apps/iosched/model/ScheduleItem.java",
+ 30,
+ " public long startTime = 1;")
+ project.executeWithBenchmark("iosched2014", BUILD_INC_JAVA, "assembleDebug")
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/IOScheduleResChangeTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/IOScheduleResChangeTest.groovy
new file mode 100644
index 0000000..8f96bd4
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/IOScheduleResChangeTest.groovy
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.performance
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.BUILD_FULL
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.BUILD_INC_RES_ADD
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.BUILD_INC_RES_EDIT
+
+/**
+ * Performance test for full and incremental build on ioschedule 2014
+ */
+@CompileStatic
+class IOScheduleResChangeTest {
+
+ @Rule
+ public GradleTestProject project = GradleTestProject.builder()
+ .fromExternalProject("iosched")
+ .create()
+
+ @Before
+ public void setUp() {
+ project.executeWithBenchmark("iosched2014", BUILD_FULL, "clean" , "assembleDebug")
+ }
+
+ @After
+ void cleanUp() {
+ project = null;
+ }
+
+ @Test
+ void "Incremental Build on Resource Edit Change"() {
+ project.replaceLine(
+ "android/src/main/res/values/strings.xml",
+ 97,
+ " <string name=\"app_name\">Google I/O 2015</string>")
+
+ project.executeWithBenchmark("iosched2014", BUILD_INC_RES_EDIT, "assembleDebug")
+ }
+
+ @Test
+ void "Incremental Build on Resource Add Change"() {
+ project.replaceLine(
+ "android/src/main/res/values/strings.xml",
+ 97,
+ " <string name=\"app_name\">Google I/O 2015</string><string name=\"aaaa\">aaa</string>")
+
+ project.executeWithBenchmark("iosched2014", BUILD_INC_RES_ADD, "assembleDebug")
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidComponentTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidComponentTest.groovy
index 91b57b9..8330926 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidComponentTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidComponentTest.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle.integration.performance
-
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
import com.android.build.gradle.integration.common.fixture.app.VariantBuildScriptGenerator
@@ -24,6 +23,8 @@
import org.junit.ClassRule
import org.junit.Test
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
+
/**
* Performance test on gradle experimantal plugin with a large number of variants
*/
@@ -34,18 +35,19 @@
.forExpermimentalPlugin(true)
.create()
+
@BeforeClass
static void setUp() {
- project.buildFile << new VariantBuildScriptGenerator(
+ VariantBuildScriptGenerator generator = new VariantBuildScriptGenerator(
buildTypes: VariantBuildScriptGenerator.LARGE_NUMBER,
productFlavors: VariantBuildScriptGenerator.LARGE_NUMBER,
"""
apply plugin: "com.android.model.application"
model {
- android.config {
- compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
- buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
}
android.buildTypes {
@@ -56,7 +58,11 @@
\${productFlavors}
}
}
- """.stripIndent()).createBuildScript()
+ """.stripIndent())
+ generator.addPostProcessor("buildTypes") { return (String) "create(\"$it\")" }
+ generator.addPostProcessor("productFlavors") { return (String) "create(\"$it\")" }
+
+ project.buildFile << generator.createBuildScript()
// Execute before performance test to warm up the cache.
project.execute("help");
@@ -69,6 +75,6 @@
@Test
void performanceTest() {
- project.execute("help")
+ project.executeWithBenchmark("LargeVariantAndroid", EVALUATION, "projects")
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidTest.groovy
index b617d14..df629a5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/LargeVariantAndroidTest.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle.integration.performance
-
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
import com.android.build.gradle.integration.common.fixture.app.VariantBuildScriptGenerator
@@ -24,6 +23,8 @@
import org.junit.ClassRule
import org.junit.Test
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
+
/**
* Performance test on gradle plugin with a large number of variants
*/
@@ -31,6 +32,7 @@
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
.fromTestApp(new HelloWorldApp())
+ .useExperimentalGradleVersion(true)
.create()
@BeforeClass
@@ -66,6 +68,6 @@
@Test
void performanceTest() {
- project.execute("help")
+ project.executeWithBenchmark("LargeVariantAndroid", EVALUATION, "projects")
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentEvaluationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentEvaluationTest.groovy
index 7e4c34c..f6a1ffc 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentEvaluationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentEvaluationTest.groovy
@@ -23,6 +23,7 @@
import org.junit.ClassRule
import org.junit.Test
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_BREADTH
import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_DEPTH
@@ -50,6 +51,6 @@
@Test
void "'projects' task run on 120 projects"() {
- project.execute("projects")
+ project.executeWithBenchmark("MediumAndroid", EVALUATION, "projects")
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentModelTest.groovy
index 61e13b4..f2095b2 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentModelTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidComponentModelTest.groovy
@@ -23,8 +23,10 @@
import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.ClassRule
+import org.junit.Ignore
import org.junit.Test
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.SYNC
import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_BREADTH
import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_DEPTH
@@ -50,7 +52,8 @@
}
@Test
+ @Ignore
void "model query for 120 projects"() {
- Map<String, AndroidProject> models = project.getAllModels()
+ Map<String, AndroidProject> models = project.getAllModelsWithBenchmark("MediumAndroid", SYNC)
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidEvaluationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidEvaluationTest.groovy
index abbfa42..e24c2e8 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidEvaluationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidEvaluationTest.groovy
@@ -24,6 +24,7 @@
import org.junit.ClassRule
import org.junit.Test
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_BREADTH
import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_DEPTH
@@ -50,6 +51,6 @@
@Test
void "'projects' task run on 120 projects"() {
- project.execute("projects")
+ project.executeWithBenchmark("MediumAndroid", EVALUATION, "projects")
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidModelTest.groovy
index 077c44e..910d44a 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidModelTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumAndroidModelTest.groovy
@@ -25,6 +25,7 @@
import org.junit.ClassRule
import org.junit.Test
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.SYNC
import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_BREADTH
import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_DEPTH
@@ -51,6 +52,6 @@
@Test
void "model query for 120 projects"() {
- Map<String, AndroidProject> models = project.getAllModels()
+ Map<String, AndroidProject> models = project.getAllModelsWithBenchmark("MediumAndroid", SYNC)
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumJavaEvaluationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumJavaEvaluationTest.groovy
index 1cad88f..e6164c1 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumJavaEvaluationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MediumJavaEvaluationTest.groovy
@@ -24,6 +24,7 @@
import org.junit.ClassRule
import org.junit.Test
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_BREADTH
import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.MEDIUM_DEPTH
@@ -49,6 +50,6 @@
@Test
void "'projects' task run on 120 projects"() {
- project.execute("projects")
+ project.executeWithBenchmark("MediumJava", EVALUATION, "projects")
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidComponentTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidComponentTest.groovy
index 2698c8f..d995e2b 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidComponentTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidComponentTest.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle.integration.performance
-
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.TestProject
import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
@@ -24,26 +23,29 @@
import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
import com.android.build.gradle.integration.common.fixture.app.VariantBuildScriptGenerator
import org.junit.AfterClass
+import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.BUILD_FULL
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
+
/**
- * Performance test on gradle experimantal plugin with multiple subprojects and multiple variants.
+ * Performance test on gradle experimental plugin with multiple sub-projects and multiple variants.
*/
class MultiProjectsAndroidComponentTest {
public static AndroidTestApp app = new HelloWorldApp()
static {
- app.addFile(new TestSourceFile("", "build.gradle",
- new VariantBuildScriptGenerator(
+ VariantBuildScriptGenerator generator = new VariantBuildScriptGenerator(
buildTypes: VariantBuildScriptGenerator.MEDIUM_NUMBER,
productFlavors: VariantBuildScriptGenerator.MEDIUM_NUMBER,
"""
apply plugin: "com.android.model.application"
model {
- android.config {
- compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
- buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ android {
+ compileSdkVersion = $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion = "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
}
android.buildTypes {
@@ -54,8 +56,11 @@
\${productFlavors}
}
}
- """.stripIndent()).createBuildScript())
- )
+ """.stripIndent())
+ generator.addPostProcessor("buildTypes") { return (String) "create(\"$it\")" }
+ generator.addPostProcessor("productFlavors") { return (String) "create(\"$it\")" }
+
+ app.addFile(new TestSourceFile("", "build.gradle", generator.createBuildScript()))
}
public static TestProject baseProject = new MultiModuleTestProject("app", app, 10)
@@ -66,6 +71,12 @@
.forExpermimentalPlugin(true)
.create()
+ @BeforeClass
+ static void setUp() {
+ // Execute before performance test to warm up the cache.
+ project.execute("help");
+ }
+
@AfterClass
static void cleanUp() {
app = null;
@@ -74,7 +85,12 @@
}
@Test
- void performanceTest() {
- project.execute("help")
+ void "performance test - projects"() {
+ project.executeWithBenchmark("MultiProjectsAndroid", EVALUATION, "projects")
+ }
+
+ @Test
+ void "performance test - single variant"() {
+ project.executeWithBenchmark("MultiProjectsAndroid", BUILD_FULL, ":app0:assembleProductFlavor0BuildType0")
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidTest.groovy
index 3fabb69..ede30b5 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/MultiProjectsAndroidTest.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle.integration.performance
-
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.TestProject
import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
@@ -28,6 +27,9 @@
import org.junit.ClassRule
import org.junit.Test
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.BUILD_FULL
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
+
/**
* Performance test on gradle plugin with multiple subprojects and multiple variants.
*/
@@ -62,8 +64,15 @@
@ClassRule
public static GradleTestProject project = GradleTestProject.builder()
.fromTestApp(baseProject)
+ .useExperimentalGradleVersion(true)
.create()
+ @BeforeClass
+ static void setUp() {
+ // Execute before performance test to warm up the cache.
+ project.execute("help");
+ }
+
@AfterClass
static void cleanUp() {
app = null;
@@ -72,7 +81,12 @@
}
@Test
- void performanceTest() {
- project.execute("help")
+ void "performance test - projects"() {
+ project.executeWithBenchmark("MultiProjectsAndroid", EVALUATION, "projects")
+ }
+
+ @Test
+ void "performance test - single variant"() {
+ project.executeWithBenchmark("MultiProjectsAndroid", BUILD_FULL, ":app0:assembleProductFlavor0BuildType0")
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentEvaluationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentEvaluationTest.groovy
index 53c855f..bd2a5c2 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentEvaluationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentEvaluationTest.groovy
@@ -23,6 +23,7 @@
import org.junit.ClassRule
import org.junit.Test
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.SMALL_BREADTH
import static com.android.build.gradle.integration.common.fixture.app.LargeTestProject.SMALL_DEPTH
@@ -49,6 +50,6 @@
@Test
void "'projects' task run on 30 projects"() {
- project.execute("projects")
+ project.executeWithBenchmark("SmallAndroid", EVALUATION, "projects")
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentModelTest.groovy
index 59a6933..b29fcb8 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentModelTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidComponentModelTest.groovy
@@ -22,7 +22,11 @@
import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.ClassRule
+import org.junit.Ignore
import org.junit.Test
+
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.SYNC
+
/**
* test with ~30 projects that queries the IDE model
*/
@@ -45,7 +49,8 @@
}
@Test
+ @Ignore
void "model query for 30 projects"() {
- Map<String, AndroidProject> models = project.getAllModels()
+ Map<String, AndroidProject> models = project.getAllModelsWithBenchmark("SmallAndroid", SYNC)
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidEvaluationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidEvaluationTest.groovy
index 1217529..d463454 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidEvaluationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidEvaluationTest.groovy
@@ -22,6 +22,9 @@
import org.junit.AfterClass
import org.junit.ClassRule
import org.junit.Test
+
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
+
/**
* test with ~30 projects that evaluates the projects
*/
@@ -44,6 +47,6 @@
@Test
void "'projects' task run on 30 projects"() {
- project.execute("projects")
+ project.executeWithBenchmark("SmallAndroid", EVALUATION, "projects")
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidModelTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidModelTest.groovy
index 9feedc0..6b01591 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidModelTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallAndroidModelTest.groovy
@@ -23,6 +23,9 @@
import org.junit.AfterClass
import org.junit.ClassRule
import org.junit.Test
+
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.SYNC
+
/**
* test with ~30 projects that queries the IDE model
*/
@@ -45,6 +48,6 @@
@Test
void "model query for 30 projects"() {
- Map<String, AndroidProject> models = project.getAllModels()
+ Map<String, AndroidProject> models = project.getAllModelsWithBenchmark("SmallAndroid", SYNC)
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallJavaEvaluationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallJavaEvaluationTest.groovy
index 1e20066..8b4e6a2 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallJavaEvaluationTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/performance/SmallJavaEvaluationTest.groovy
@@ -22,6 +22,9 @@
import org.junit.AfterClass
import org.junit.ClassRule
import org.junit.Test
+
+import static com.android.build.gradle.integration.common.fixture.GradleTestProject.BenchmarkMode.EVALUATION
+
/**
* test with ~30 projects that evaluates the projects
*/
@@ -44,6 +47,6 @@
@Test
void "'projects' task run on 30 projects"() {
- project.execute("projects")
+ project.executeWithBenchmark("MediumJava", EVALUATION, "projects")
}
}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/DuplicateModuleNameImportTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/DuplicateModuleNameImportTest.groovy
new file mode 100644
index 0000000..dbbcbd2
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/DuplicateModuleNameImportTest.groovy
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.test
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
+import org.gradle.api.tasks.StopExecutionException
+import org.junit.AfterClass
+import org.junit.ClassRule
+import org.junit.Test
+
+import static org.junit.Assert.assertTrue
+import static org.junit.Assert.fail
+
+/**
+ * Regression test for a infinite loop error in DependencyManager when importing a project
+ * with the same local name as the requester.
+ */
+@CompileStatic
+class DuplicateModuleNameImportTest {
+
+ @ClassRule
+ static public GradleTestProject project = GradleTestProject.builder()
+ .fromTestProject("duplicateNameImport")
+ .create()
+
+ @AfterClass
+ static void cleanUp() {
+ project = null
+ }
+
+ @Test
+ void "check build"() throws Exception {
+ // building should generate an exception
+ try {
+ project.execute("clean", "assemble")
+ fail("build succeeded while it should have failed")
+ } catch (Exception expected) {
+ // look for the root exception which has been thrown by the gradle build process
+ Throwable rootException = expected;
+ while (rootException.getCause() != null) {
+ rootException = rootException.getCause();
+ }
+ assertTrue("Exception did not contain expected message",
+ rootException.getMessage().contains(
+"""Your project contains 2 or more modules with the same identification a.b.c:Project
+at ":A:Project" and ":B:Project".
+You must use different identification (either name or group) for each modules."""))
+ }
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestModuleTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestModuleTest.groovy
index ecceacf..412d870 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestModuleTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestModuleTest.groovy
@@ -15,26 +15,17 @@
*/
package com.android.build.gradle.integration.test
-
import com.android.build.gradle.integration.common.fixture.GradleTestProject
-import com.android.build.gradle.integration.common.utils.ModelHelper
-import com.android.builder.model.AndroidArtifact
import com.android.builder.model.AndroidProject
-import com.android.builder.model.Dependencies
-import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Test
-
-import static com.android.builder.core.BuilderConstants.DEBUG
-import static com.android.builder.model.AndroidProject.ARTIFACT_ANDROID_TEST
-import static org.junit.Assert.assertEquals
-import static org.junit.Assert.assertNotNull
-
/**
* Test for setup with 2 modules: app and test-app
*/
+@CompileStatic
class SeparateTestModuleTest {
@ClassRule
static public GradleTestProject project = GradleTestProject.builder()
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithAarDependencyTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithAarDependencyTest.groovy
new file mode 100644
index 0000000..db30337
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithAarDependencyTest.groovy
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.test
+
+import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.utils.ModelHelper
+import com.android.builder.model.AndroidArtifact
+import com.android.builder.model.AndroidProject
+import com.android.builder.model.Dependencies
+import com.android.builder.model.Variant
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+import org.junit.experimental.categories.Category
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
+/**
+ * Test separate test module testing an app with aar dependencies.
+ */
+@CompileStatic
+public class SeparateTestWithAarDependencyTest {
+
+ @ClassRule
+ static public GradleTestProject project = GradleTestProject.builder()
+ .fromTestProject("separateTestModule")
+ .create()
+
+ static Map<String, AndroidProject> models
+
+ @BeforeClass
+ static void setup() {
+ project.getSubproject("app").getBuildFile() << """
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion = rootProject.ext.buildToolsVersion
+
+ publishNonDefault true
+
+ defaultConfig {
+ minSdkVersion 8
+ }
+}
+dependencies {
+ compile 'com.android.support:appcompat-v7:22.1.0'
+}
+ """
+
+ models = project.executeAndReturnMultiModel("clean", "assemble")
+ }
+
+ @AfterClass
+ static void cleanUp() {
+ project = null
+ models = null
+ }
+
+ @Test
+ void "check app doesn't contain test app's code"() {
+ File apk = project.getSubproject('test').getApk("debug")
+ assertThatApk(apk).doesNotContainClass("Lcom/android/tests/basic/Main;")
+ }
+
+ @Test
+ void "check app doesn't contain test app's layout"() {
+ File apk = project.getSubproject('test').getApk("debug")
+ assertThatApk(apk).doesNotContainResource("layout/main.xml")
+ }
+
+ @Test
+ void "check app doesn't contain test app's dependency lib's code"() {
+ File apk = project.getSubproject('test').getApk("debug")
+ assertThatApk(apk).doesNotContainClass("Landroid/support/v7/app/ActionBar;")
+ }
+
+ @Test
+ void "check app doesn't contain test app's dependency lib's resources"() {
+ File apk = project.getSubproject('test').getApk("debug")
+ assertThatApk(apk).doesNotContainResource("layout/abc_action_bar_title_item.xml")
+ }
+
+ @Test
+ void "check test model includes the tested app"() {
+ Collection<Variant> variants = models.get(":test").getVariants()
+
+ // get the main artifact of the debug artifact and its dependencies
+ Variant variant = ModelHelper.getVariant(variants, "debug")
+ AndroidArtifact artifact = variant.getMainArtifact()
+ Dependencies dependencies = artifact.getDependencies()
+
+ // check the app project shows up as a project dependency
+ Collection<String> projects = dependencies.getProjects()
+ assertThat(projects).containsExactly(":app")
+
+ // and that nothing else shows up.
+ // TODO: fix this.
+// Collection<JavaLibrary> javaLibs = dependencies.getJavaLibraries();
+// assertThat(javaLibs).hasSize(0);
+// Collection<AndroidLibrary> libs = dependencies.getLibraries();
+// assertThat(libs).hasSize(0);
+ }
+
+ @Test
+ @Category(DeviceTests)
+ void "connected check"() {
+ GradleTestProject.assumeLocalDevice()
+ project.execute("clean",":test:connectedCheck");
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithDependenciesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithDependenciesTest.groovy
new file mode 100644
index 0000000..a4d3577
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithDependenciesTest.groovy
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.test;
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk;
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject;
+import com.android.builder.model.AndroidProject;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.Map;
+
+import groovy.transform.CompileStatic;
+
+/**
+ * Test separate test module that tests an application with some complicated dependencies :
+ * - the app imports a library importing a jar file itself.
+ * - use minification.
+ */
+@CompileStatic
+public class SeparateTestWithDependenciesTest {
+
+ @ClassRule
+ public static GradleTestProject project = GradleTestProject.builder()
+ .fromTestProject("separateTestModuleWithDependencies")
+ .create()
+
+ @BeforeClass
+ static void setup() {
+ project.execute("clean", "assemble")
+ }
+ @Test
+ void "check app contains all dependent clases"() {
+ project.execute("clean", "assemble")
+ File apk = project.getSubproject('app').getApk("debug")
+ assertThatApk(apk).containsClass("Lcom/android/tests/jarDep/JarDependencyUtil;")
+ }
+
+
+ @Test
+ void "check test app does not contain any minified application's dependent classes"() {
+ project.execute("clean", "assemble")
+ File apk = project.getSubproject('test').getApk("debug")
+ assertThatApk(apk).doesNotContainClass("Lcom/android/tests/jarDep/JarDependencyUtil;")
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithMinificationButNoObfuscationTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithMinificationButNoObfuscationTest.groovy
new file mode 100644
index 0000000..c8dae31
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithMinificationButNoObfuscationTest.groovy
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.test
+
+import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
+import org.junit.AfterClass
+import org.junit.ClassRule
+import org.junit.Test
+import org.junit.experimental.categories.Category
+
+/**
+ * Test for a separate test module that has minification turned on but no obfuscation
+ * (no mapping.txt file produced)
+ */
+@CompileStatic
+class SeparateTestWithMinificationButNoObfuscationTest {
+ @ClassRule
+ static public GradleTestProject project = GradleTestProject.builder()
+ .fromTestProject("separateTestWithMinificationButNoObfuscation")
+ .create()
+
+ @Test
+ void "test building"() {
+ // just building fine is enough to test the regression.
+ project.execute("clean", "assemble")
+ }
+
+ @AfterClass
+ static void cleanUp() {
+ project = null
+ }
+
+ @Test
+ @Category(DeviceTests)
+ void "connected check"() {
+ GradleTestProject.assumeLocalDevice()
+ project.execute(":test:connectedAndroidTest");
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithoutMinificationWithDependenciesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithoutMinificationWithDependenciesTest.groovy
new file mode 100644
index 0000000..07f007d
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/test/SeparateTestWithoutMinificationWithDependenciesTest.groovy
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.test;
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk;
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject;
+
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.io.File;
+
+/**
+ * Test separate test module that tests an application with some complicated dependencies :
+ * - the app imports a library importing a jar file itself.
+ */
+public class SeparateTestWithoutMinificationWithDependenciesTest {
+ @ClassRule
+ public static GradleTestProject project = GradleTestProject.builder()
+ .fromTestProject("separateTestModuleWithDependencies")
+ .create()
+
+
+ @BeforeClass
+ static void setup() {
+ project.getSubproject("test").getBuildFile() << """
+ apply plugin: 'com.android.test'
+
+ android {
+ compileSdkVersion 19
+ buildToolsVersion = rootProject.buildToolsVersion
+
+ targetProjectPath ':app'
+ targetVariant 'debug'
+ }
+ """
+ project.execute("clean", "assemble")
+ }
+ @Test
+ void "check app contains all dependent clases"() {
+ File apk = project.getSubproject('app').getApk("debug")
+ assertThatApk(apk).containsClass("Lcom/android/tests/jarDep/JarDependencyUtil;")
+ }
+
+
+ @Test
+ void "check test app does not contain any application's dependent classes"() {
+ File apk = project.getSubproject('test').getApk("debug")
+ assertThatApk(apk).doesNotContainClass("Lcom/android/tests/jarDep/JarDependencyUtil;")
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/JUnitResults.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/JUnitResults.groovy
index 1c814df..9f9cd5d 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/JUnitResults.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/JUnitResults.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle.integration.testing
-
/**
* Helper class for inspecting JUnit XML files.
*/
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/TestingSupportLibraryTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/TestingSupportLibraryTest.groovy
new file mode 100644
index 0000000..03124c8
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/TestingSupportLibraryTest.groovy
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.testing
+
+import com.android.build.gradle.integration.common.category.DeviceTests
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.app.AndroidTestApp
+import com.android.build.gradle.integration.common.fixture.app.HelloWorldApp
+import com.android.build.gradle.integration.common.fixture.app.TestSourceFile
+import groovy.transform.CompileStatic
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.experimental.categories.Category
+
+import static com.android.build.gradle.integration.common.truth.TruthHelper.assertThatZip
+
+/**
+ * Test project to cover the Android Gradle plugin's interaction with the testing support library.
+ */
+@CompileStatic
+class TestingSupportLibraryTest {
+
+ public static final AndroidTestApp helloWorldApp = new HelloWorldApp();
+ static {
+ /* Junit 4 now maps tests annotated with @Ignore and tests that throw
+ AssumptionFailureExceptions as skipped. */
+ helloWorldApp.addFile(new TestSourceFile("src/androidTest/java/com/example/helloworld", "FailureAssumptionTest.java",
+ """
+package com.example.helloworld;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class FailureAssumptionTest {
+ @Test
+ public void checkAssumptionIsSkipped() {
+ assumeTrue(false);
+ fail("Tests with failing assumptions should be skipped");
+ }
+
+ @Test
+ @Ignore
+ public void checkIgnoreTestsArePossible() {
+ fail("Tests with @Ignore annotation should be skipped");
+ }
+
+ @Test
+ public void checkThisTestPasses() {
+ System.err.println("Test executed");
+ }
+}
+"""))
+ helloWorldApp.addFile(new TestSourceFile("src/main", "AndroidManifest.xml",
+ """<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.helloworld"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk android:minSdkVersion="18" />
+ <application android:label="@string/app_name">
+ <activity android:name=".HelloWorld"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
+"""))
+ }
+
+ @Rule
+ public GradleTestProject project = GradleTestProject.builder()
+ .fromTestApp(helloWorldApp)
+ .create()
+
+ @Before
+ public void setUp() {
+ project
+ project.getBuildFile() << """
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion $GradleTestProject.DEFAULT_COMPILE_SDK_VERSION
+ buildToolsVersion "$GradleTestProject.DEFAULT_BUILD_TOOL_VERSION"
+ defaultConfig {
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ dependencies {
+ androidTestCompile 'com.android.support.test:runner:0.3'
+ androidTestCompile 'com.android.support.test:rules:0.3'
+ }
+}
+"""
+ }
+
+ @Test
+ public void "check compile"() {
+ project.execute("assembleDebugAndroidTest")
+ }
+
+ @Test
+ @Category(DeviceTests.class)
+ public void "test ignored tests are not run"() {
+ project.executeConnectedCheck()
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingComplexProjectTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingComplexProjectTest.groovy
new file mode 100644
index 0000000..67e0db9
--- /dev/null
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingComplexProjectTest.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.build.gradle.integration.testing
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import org.junit.AfterClass
+import org.junit.ClassRule
+import org.junit.Test
+/**
+ * Runs tests in a big, complicated project.
+ */
+class UnitTestingComplexProjectTest {
+ @ClassRule
+ public static GradleTestProject project = GradleTestProject.builder()
+ .fromTestProject("unitTestingComplexProject")
+ .create()
+
+ @AfterClass
+ public static void freeResources() throws Exception {
+ project = null
+ }
+
+ @Test
+ public void appProject() throws Exception {
+ project.execute("clean", "test")
+ }
+}
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingDefaultValuesTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingDefaultValuesTest.groovy
index d844105..661ca2c 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingDefaultValuesTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingDefaultValuesTest.groovy
@@ -16,6 +16,7 @@
package com.android.build.gradle.integration.testing
import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import groovy.transform.CompileStatic
import org.junit.ClassRule
import org.junit.Test
diff --git a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingFlavorsSupportTest.groovy b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingFlavorsSupportTest.groovy
index 1e6758a..da07294 100644
--- a/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingFlavorsSupportTest.groovy
+++ b/build-system/integration-test/src/test/groovy/com/android/build/gradle/integration/testing/UnitTestingFlavorsSupportTest.groovy
@@ -15,7 +15,6 @@
*/
package com.android.build.gradle.integration.testing
-
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.google.common.base.Throwables
import org.gradle.tooling.BuildException
@@ -25,7 +24,6 @@
import static com.android.build.gradle.integration.testing.JUnitResults.Outcome.FAILED
import static com.android.build.gradle.integration.testing.JUnitResults.Outcome.PASSED
import static org.junit.Assert.fail
-
/**
* Meta-level tests for the app-level unit testing support.
*/
diff --git a/build-system/integration-test/src/test/resources/basic/example.json b/build-system/integration-test/src/test/resources/basic/example.json
new file mode 100644
index 0000000..6bad0d3
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/basic/example.json
@@ -0,0 +1,90 @@
+{
+
+"project_info": {
+ "project_id": "red-ant-1",
+ "project_number": "1234567890",
+ "name": "My Awesome App"
+},
+
+"client": [{
+ "client_info": {
+ "client_type": 1,
+ "android_client_info": {
+ "package_name": "com.example.app.free"
+ }
+ },
+
+ "oauth_client": [{
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.example.app.free",
+ "certificate_hash": "IUW99pi4cVA5vQ6D8gab7UNawhw="
+ }
+ },
+ {
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.example.app.free",
+ "certificate_hash": "AFDD9pi4klj5vQ6D8gab7UNawhw="
+ }
+ }],
+
+ "services": {
+ "analytics_service": {
+ "status": 2,
+ "analytics_property": {
+ "tracking_id": "UA-123456789"
+ }
+ },
+ "cloud_messaging_service": {
+ "status": 2
+ },
+ "appinvite_service": {
+ "status": 2
+ },
+ "google_signin_service": {
+ "status": 2
+ },
+ "ads_service": {
+ "status": 2,
+ "test_banner_ad_unit_id": "ca-app-pub-3940256099942544\/1033173712",
+ "test_interstitial_ad_unit_id": "ca-app-pub-3940256099942544\/6300978111"
+ }
+ }
+},
+
+{
+ "client_info": {
+ "client_type": 1,
+ "android_client_info": {
+ "package_name": "com.example.app.paid"
+ }
+ },
+
+ "oauth_client": [{
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.example.app.paid",
+ "certificate_hash": "IUW99pi4cVA5vQ6D8gab7UNawhw="
+ }
+ }],
+ "services": {
+ "analytics_service": {
+ "status": 2,
+ "analytics_property": {
+ "tracking_id": "UA-1923912413"
+ }
+ },
+ "cloud_messaging_service": {
+ "status": 2
+ },
+ "appinvite_service": {
+ "status": 2
+ },
+ "google_signin_service": {
+ "status": 2
+ }
+ }
+}]
+
+}
diff --git a/build-system/integration-test/src/test/resources/basic/global_tracker.xml b/build-system/integration-test/src/test/resources/basic/global_tracker.xml
new file mode 100644
index 0000000..eda13b3
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/basic/global_tracker.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="ga_trackingId">UA-123456789</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/basic/values.xml b/build-system/integration-test/src/test/resources/basic/values.xml
new file mode 100644
index 0000000..8ec1e20
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/basic/values.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="ga_trackingId">UA-123456789</string>
+ <string name="gcm_defaultSenderId">1234567890</string>
+ <string name="test_banner_ad_unit_id">ca-app-pub-3940256099942544/1033173712</string>
+ <string name="test_interstitial_ad_unit_id">ca-app-pub-3940256099942544/6300978111</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/disabledservice/example.json b/build-system/integration-test/src/test/resources/disabledservice/example.json
new file mode 100644
index 0000000..9d58065
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/disabledservice/example.json
@@ -0,0 +1,90 @@
+{
+
+"project_info": {
+ "project_id": "red-ant-1",
+ "project_number": "1234567890",
+ "name": "My Awesome App"
+},
+
+"client": [{
+ "client_info": {
+ "client_type": 1,
+ "android_client_info": {
+ "package_name": "com.example.app.free"
+ }
+ },
+
+ "oauth_client": [{
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.example.app.free",
+ "certificate_hash": "IUW99pi4cVA5vQ6D8gab7UNawhw="
+ }
+ },
+ {
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.example.app.free",
+ "certificate_hash": "AFDD9pi4klj5vQ6D8gab7UNawhw="
+ }
+ }],
+
+ "services": {
+ "analytics_service": {
+ "status": 2,
+ "analytics_property": {
+ "tracking_id": "UA-123456789"
+ }
+ },
+ "cloud_messaging_service": {
+ "status": 2
+ },
+ "appinvite_service": {
+ "status": 2
+ },
+ "google_signin_service": {
+ "status": 2
+ },
+ "ads_service": {
+ "status": 1,
+ "test_banner_ad_unit_id": "ca-app-pub-3940256099942544\/1033173712",
+ "test_interstitial_ad_unit_id": "ca-app-pub-3940256099942544\/6300978111"
+ }
+ }
+},
+
+{
+ "client_info": {
+ "client_type": 1,
+ "android_client_info": {
+ "package_name": "com.example.app.paid"
+ }
+ },
+
+ "oauth_client": [{
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.example.app.paid",
+ "certificate_hash": "IUW99pi4cVA5vQ6D8gab7UNawhw="
+ }
+ }],
+ "services": {
+ "analytics_service": {
+ "status": 2,
+ "analytics_property": {
+ "tracking_id": "UA-1923912413"
+ }
+ },
+ "cloud_messaging_service": {
+ "status": 2
+ },
+ "appinvite_service": {
+ "status": 2
+ },
+ "google_signin_service": {
+ "status": 2
+ }
+ }
+}]
+
+}
diff --git a/build-system/integration-test/src/test/resources/disabledservice/global_tracker.xml b/build-system/integration-test/src/test/resources/disabledservice/global_tracker.xml
new file mode 100644
index 0000000..eda13b3
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/disabledservice/global_tracker.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="ga_trackingId">UA-123456789</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/disabledservice/values.xml b/build-system/integration-test/src/test/resources/disabledservice/values.xml
new file mode 100644
index 0000000..e43783c
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/disabledservice/values.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="ga_trackingId">UA-123456789</string>
+ <string name="gcm_defaultSenderId">1234567890</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/flavor/example.json b/build-system/integration-test/src/test/resources/flavor/example.json
new file mode 100644
index 0000000..877c44b
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/flavor/example.json
@@ -0,0 +1,85 @@
+{
+
+"project_info": {
+ "project_id": "red-ant-1",
+ "project_number": "1234567890",
+ "name": "My Awesome App"
+},
+
+"client": [{
+ "client_info": {
+ "client_type": 1,
+ "android_client_info": {
+ "package_name": "com.example.app.free"
+ }
+ },
+
+ "oauth_client": [{
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.example.app.free",
+ "certificate_hash": "IUW99pi4cVA5vQ6D8gab7UNawhw="
+ }
+ },
+ {
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.example.app.free",
+ "certificate_hash": "AFDD9pi4klj5vQ6D8gab7UNawhw="
+ }
+ }],
+
+ "services": {
+ "analytics_service": {
+ "status": 2,
+ "analytics_property": {
+ "tracking_id": "UA-123456789"
+ }
+ },
+ "cloud_messaging_service": {
+ "status": 2
+ },
+ "appinvite_service": {
+ "status": 2
+ },
+ "google_signin_service": {
+ "status": 2
+ }
+ }
+},
+
+{
+ "client_info": {
+ "client_type": 1,
+ "android_client_info": {
+ "package_name": "com.example.app.paid"
+ }
+ },
+
+ "oauth_client": [{
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.example.app.paid",
+ "certificate_hash": "IUW99pi4cVA5vQ6D8gab7UNawhw="
+ }
+ }],
+ "services": {
+ "analytics_service": {
+ "status": 2,
+ "analytics_property": {
+ "tracking_id": "UA-1923912413"
+ }
+ },
+ "cloud_messaging_service": {
+ "status": 2
+ },
+ "appinvite_service": {
+ "status": 2
+ },
+ "google_signin_service": {
+ "status": 2
+ }
+ }
+}]
+
+}
diff --git a/build-system/integration-test/src/test/resources/flavor/free.global_tracker.xml b/build-system/integration-test/src/test/resources/flavor/free.global_tracker.xml
new file mode 100644
index 0000000..eda13b3
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/flavor/free.global_tracker.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="ga_trackingId">UA-123456789</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/flavor/free.values.xml b/build-system/integration-test/src/test/resources/flavor/free.values.xml
new file mode 100644
index 0000000..e43783c
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/flavor/free.values.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="ga_trackingId">UA-123456789</string>
+ <string name="gcm_defaultSenderId">1234567890</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/flavor/paid.global_tracker.xml b/build-system/integration-test/src/test/resources/flavor/paid.global_tracker.xml
new file mode 100644
index 0000000..b17f063
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/flavor/paid.global_tracker.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="ga_trackingId">UA-1923912413</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/flavor/paid.values.xml b/build-system/integration-test/src/test/resources/flavor/paid.values.xml
new file mode 100644
index 0000000..ac047c1
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/flavor/paid.values.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="ga_trackingId">UA-1923912413</string>
+ <string name="gcm_defaultSenderId">1234567890</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/noservice/global_tracker.xml b/build-system/integration-test/src/test/resources/noservice/global_tracker.xml
new file mode 100644
index 0000000..eda13b3
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/noservice/global_tracker.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="ga_trackingId">UA-123456789</string>
+</resources>
diff --git a/build-system/integration-test/src/test/resources/noservice/no_services.json b/build-system/integration-test/src/test/resources/noservice/no_services.json
new file mode 100644
index 0000000..e3ad31e
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/noservice/no_services.json
@@ -0,0 +1,11 @@
+{
+
+"project_info": {
+ "project_id": "red-ant-1",
+ "project_number": "1234567890",
+ "name": "My Awesome App"
+},
+
+"client": []
+
+}
diff --git a/build-system/integration-test/src/test/resources/noservice/values.xml b/build-system/integration-test/src/test/resources/noservice/values.xml
new file mode 100644
index 0000000..77697c1
--- /dev/null
+++ b/build-system/integration-test/src/test/resources/noservice/values.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="gcm_defaultSenderId">1234567890</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/3rdPartyTests/app/build.gradle b/build-system/integration-test/test-projects/3rdPartyTests/app/build.gradle
index 77efb73..5e2ea74 100644
--- a/build-system/integration-test/test-projects/3rdPartyTests/app/build.gradle
+++ b/build-system/integration-test/test-projects/3rdPartyTests/app/build.gradle
@@ -20,7 +20,7 @@
}
project.afterEvaluate {
- configure(fakeAndroidTest) {
+ configure(fakeDebugAndroidTest) {
doLast {
String error = project.fakeProvider.isValid()
if (error != null) {
diff --git a/build-system/integration-test/test-projects/3rdPartyTests/buildSrc/build.gradle b/build-system/integration-test/test-projects/3rdPartyTests/buildSrc/build.gradle
index 63051c5..82e0baa 100644
--- a/build-system/integration-test/test-projects/3rdPartyTests/buildSrc/build.gradle
+++ b/build-system/integration-test/test-projects/3rdPartyTests/buildSrc/build.gradle
@@ -1,7 +1,11 @@
apply plugin: 'java'
apply plugin: 'idea'
-def env = new Object() { String gradleVersion };
+def env = new Object() {
+ String gradleVersion
+ String experimentalGradleVersion
+};
+
apply from: "../../commonGradlePluginVersion.gradle", to: env
apply from: "../../commonLocalRepo.gradle"
diff --git a/build-system/integration-test/test-projects/3rdPartyTests/lib/build.gradle b/build-system/integration-test/test-projects/3rdPartyTests/lib/build.gradle
index 755a6ab..ea70169 100644
--- a/build-system/integration-test/test-projects/3rdPartyTests/lib/build.gradle
+++ b/build-system/integration-test/test-projects/3rdPartyTests/lib/build.gradle
@@ -20,7 +20,7 @@
}
project.afterEvaluate {
- configure(fakeAndroidTest) {
+ configure(fakeDebugAndroidTest) {
doLast {
String error = project.fakeProvider.isValid()
if (error != null) {
diff --git a/build-system/integration-test/test-projects/api/app/build.gradle b/build-system/integration-test/test-projects/api/app/build.gradle
index 782e191..c6c66c8 100644
--- a/build-system/integration-test/test-projects/api/app/build.gradle
+++ b/build-system/integration-test/test-projects/api/app/build.gradle
@@ -1,3 +1,7 @@
+// ATTENTION -- hash value of this file is checked in the corresponding
+// integration test. Please make sure any changes you make here are
+// backwards compatible.
+
apply plugin: 'com.android.application'
android {
@@ -32,4 +36,4 @@
if (android.testVariants.size() != 1) {
throw new GradleException("Wrong number of test variants!")
}
-}
\ No newline at end of file
+}
diff --git a/build-system/integration-test/test-projects/api/lib/build.gradle b/build-system/integration-test/test-projects/api/lib/build.gradle
index 69c65cd..29843fd 100644
--- a/build-system/integration-test/test-projects/api/lib/build.gradle
+++ b/build-system/integration-test/test-projects/api/lib/build.gradle
@@ -1,3 +1,7 @@
+// ATTENTION -- hash value of this file is checked in the corresponding
+// integration test. Please make sure any changes you make here are
+// backwards compatible.
+
apply plugin: 'com.android.library'
android {
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/build.gradle b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/build.gradle
new file mode 100644
index 0000000..35636ce
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/build.gradle
@@ -0,0 +1,33 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion "21.1.0"
+
+ defaultConfig {
+ applicationId "com.example.manifest_merger_example"
+ minSdkVersion 15
+ targetSdkVersion 21
+ versionCode 1
+ versionName "1.0"
+ }
+ productFlavors {
+ flavor {
+ applicationId "com.example.manifest_merger_example.flavor"
+ minSdkVersion 15
+ targetSdkVersion 21
+ versionCode 1
+ versionName "1.0"
+ }
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ }
+ }
+}
+
+dependencies {
+ compile project(':examplelibrary')
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+}
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/flavor/AndroidManifest.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/flavor/AndroidManifest.xml
new file mode 100644
index 0000000..45541ac
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/flavor/AndroidManifest.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <application
+ android:allowBackup="true"
+ android:theme="@style/AppTheme">
+
+ </application>
+
+</manifest>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..67f82bd
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/AndroidManifest.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest
+ package="com.example.manifest_merger_example"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <application
+ android:allowBackup="true"
+ android:theme="@style/AppTheme">
+
+ </application>
+
+</manifest>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/drawable-mdpi/ic_launcher.png b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/layout/activity_main.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..7445603
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,16 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".MainActivity">
+
+ <TextView
+ android:text="@string/hello_world"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+</RelativeLayout>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/menu/menu_main.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..db961b9
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,8 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:context=".MainActivity">
+ <item android:id="@+id/action_settings"
+ android:title="@string/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never"/>
+</menu>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/dimens.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..acf94cc
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..56c7ab1
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">manifest-merger-example</string>
+ <string name="hello_world">Hello world!</string>
+ <string name="action_settings">Settings</string>
+
+</resources>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/styles.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..cf2867a
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/app/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ </style>
+
+</resources>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/build.gradle b/build-system/integration-test/test-projects/applicationIdInLibsTest/build.gradle
new file mode 100644
index 0000000..3268a26
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/build.gradle
@@ -0,0 +1,5 @@
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
+
+
+apply plugin: 'android-reporting'
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/build.gradle b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/build.gradle
new file mode 100644
index 0000000..fbf7f29
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/build.gradle
@@ -0,0 +1,18 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion "21.1.0"
+
+ defaultConfig {
+ minSdkVersion 15
+ targetSdkVersion 21
+ versionCode 1
+ versionName "1.0"
+ }
+}
+
+android.testVariants.all {
+ it.mergedFlavor.manifestPlaceholders = [ localApplicationId:"com.example.manifest_merger_example.flavor"]
+}
+
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/androidTest/java/com/example/examplelibrary/ApplicationTest.java b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/androidTest/java/com/example/examplelibrary/ApplicationTest.java
new file mode 100644
index 0000000..fd8c006
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/androidTest/java/com/example/examplelibrary/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.example.examplelibrary;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..db8c460
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.mylibrary" >
+
+ <!-- Permissions for Google Cloud Messaging -->
+ <permission
+ android:name="${applicationId}.permission.C2D_MESSAGE"
+ android:protectionLevel="signature"/>
+
+ <uses-permission android:name="${applicationId}.permission.C2D_MESSAGE"/>
+ <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name" >
+ <activity
+ android:name="com.example.examplelibrary.LoginActivity"
+ android:label="@string/app_name"
+ android:windowSoftInputMode="adjustResize|stateHidden" >
+ </activity>
+ </application>
+
+</manifest>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/java/com/example/examplelibrary/LoginActivity.java b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/java/com/example/examplelibrary/LoginActivity.java
new file mode 100644
index 0000000..50fa184
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/java/com/example/examplelibrary/LoginActivity.java
@@ -0,0 +1,13 @@
+package com.example.examplelibrary;
+
+import android.app.Activity;
+
+/**
+ * A fake login activity.
+ */
+public class LoginActivity extends Activity {
+
+}
+
+
+
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/res/drawable-mdpi/ic_launcher.png b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/res/values/strings.xml
new file mode 100644
index 0000000..0c9f82a
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/examplelibrary/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">ExampleLibrary</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/applicationIdInLibsTest/settings.gradle b/build-system/integration-test/test-projects/applicationIdInLibsTest/settings.gradle
new file mode 100644
index 0000000..f52177d
--- /dev/null
+++ b/build-system/integration-test/test-projects/applicationIdInLibsTest/settings.gradle
@@ -0,0 +1 @@
+include ':app', ':examplelibrary'
diff --git a/build-system/integration-test/test-projects/artifactApi/build.gradle b/build-system/integration-test/test-projects/artifactApi/build.gradle
index 545ee34..f8edf2d 100644
--- a/build-system/integration-test/test-projects/artifactApi/build.gradle
+++ b/build-system/integration-test/test-projects/artifactApi/build.gradle
@@ -1,3 +1,7 @@
+// ATTENTION -- hash value of this file is checked in the corresponding
+// integration test. Please make sure any changes you make here are
+// backwards compatible.
+
apply from: "../commonHeader.gradle"
buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
diff --git a/build-system/integration-test/test-projects/basic/build.gradle b/build-system/integration-test/test-projects/basic/build.gradle
index e40ecdf..d2af2bd 100644
--- a/build-system/integration-test/test-projects/basic/build.gradle
+++ b/build-system/integration-test/test-projects/basic/build.gradle
@@ -39,6 +39,8 @@
targetSdkVersion 16
testInstrumentationRunner "android.test.InstrumentationTestRunner"
+ testInstrumentationRunnerArgument "size", "medium"
+
testHandleProfiling false
buildConfigField "boolean", "DEFAULT", "true"
diff --git a/build-system/integration-test/test-projects/basic/src/androidTest/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/basic/src/androidTest/java/com/android/tests/basic/MainTest.java
index 7712fbf..312d15a 100644
--- a/build-system/integration-test/test-projects/basic/src/androidTest/java/com/android/tests/basic/MainTest.java
+++ b/build-system/integration-test/test-projects/basic/src/androidTest/java/com/android/tests/basic/MainTest.java
@@ -2,8 +2,15 @@
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
import android.widget.TextView;
+import java.lang.RuntimeException;
+
+/**
+ * NB: All tests not annotated with @MediumTest will be ignored as the InstrumentationTestRunner
+ * is passed the custom argument of "size medium".
+ */
public class MainTest extends ActivityInstrumentationTestCase2<Main> {
private TextView mTextView;
@@ -40,5 +47,11 @@
public void testBuildConfig() {
assertEquals("bar", BuildConfig.FOO);
}
+
+ @SmallTest
+ public void testSmallTestsShouldNotBeRun() {
+ throw new RuntimeException("Should have been excluded by custom test instrumentation "
+ + "runner argument.");
+ }
}
diff --git a/build-system/integration-test/test-projects/commonBuildScript.gradle b/build-system/integration-test/test-projects/commonBuildScript.gradle
index 59cf7c2..8ebc12f 100644
--- a/build-system/integration-test/test-projects/commonBuildScript.gradle
+++ b/build-system/integration-test/test-projects/commonBuildScript.gradle
@@ -14,11 +14,18 @@
* limitations under the License.
*/
-def env = new Object() { String gradleVersion };
+def env = new Object() {
+ String gradleVersion
+ String experimentalGradleVersion
+};
apply from: "commonGradlePluginVersion.gradle", to: env
apply from: "commonLocalRepo.gradle"
dependencies {
classpath "com.android.tools.build:gradle:$env.gradleVersion"
+ classpath "com.google.gms:google-services:$env.gradleVersion"
+ if (System.env.TEST_CLASSPATH_DEPENDENCY) {
+ classpath System.env.TEST_CLASSPATH_DEPENDENCY
+ }
}
diff --git a/build-system/integration-test/test-projects/commonBuildScriptExperimental.gradle b/build-system/integration-test/test-projects/commonBuildScriptExperimental.gradle
index c39c427..31b14e9 100644
--- a/build-system/integration-test/test-projects/commonBuildScriptExperimental.gradle
+++ b/build-system/integration-test/test-projects/commonBuildScriptExperimental.gradle
@@ -13,11 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-def env = new Object() { String gradleVersion };
+def env = new Object() {
+ String gradleVersion
+ String experimentalGradleVersion
+};
+
apply from: "commonGradlePluginVersion.gradle", to: env
apply from: "commonLocalRepo.gradle"
dependencies {
- classpath "com.android.tools.build:gradle-experimental:$env.gradleVersion"
+ classpath "com.android.tools.build:gradle-experimental:$env.experimentalGradleVersion"
+ if (System.env.TEST_CLASSPATH_DEPENDENCY) {
+ classpath System.env.TEST_CLASSPATH_DEPENDENCY
+ }
}
diff --git a/build-system/integration-test/test-projects/commonGradlePluginVersion.gradle b/build-system/integration-test/test-projects/commonGradlePluginVersion.gradle
index d194490..5f38f00 100644
--- a/build-system/integration-test/test-projects/commonGradlePluginVersion.gradle
+++ b/build-system/integration-test/test-projects/commonGradlePluginVersion.gradle
@@ -15,6 +15,7 @@
*/
gradleVersion = System.env.CUSTOM_GRADLE
+experimentalGradleVersion = System.env.CUSTOM_EXPERIMENTAL_GRADLE
if (gradleVersion == null && System.env.CUSTOM_REPO != null) {
// Extract the version from the top level buildSrc, relative to CUSTOM_REPO.
@@ -25,11 +26,13 @@
String buildVersion
String baseVersion
String apiVersion
+ String experimentalVersion
}
}
}
apply from: "$System.env.CUSTOM_REPO/../../tools/buildSrc/base/version.gradle", to: env
gradleVersion = env.project.ext.buildVersion
+ experimentalGradleVersion = env.project.ext.experimentalVersion
}
if (gradleVersion == null) {
diff --git a/build-system/integration-test/test-projects/commonHeader.gradle b/build-system/integration-test/test-projects/commonHeader.gradle
index 9a6afe6..1499c89 100644
--- a/build-system/integration-test/test-projects/commonHeader.gradle
+++ b/build-system/integration-test/test-projects/commonHeader.gradle
@@ -18,7 +18,32 @@
buildToolsVersion = [System.env.CUSTOM_BUILDTOOLS, project.properties["CUSTOM_BUILDTOOLS"], '22.0.1'].find()
latestCompileSdk = 21
- useJack = System.env.CUSTOM_JACK || project.properties["CUSTOM_JACK"]
+ useJack = System.env.CUSTOM_JACK ||
+ Boolean.valueOf((String) project.properties[
+ "com.android.build.gradle.integratonTest.useJack"])
+
+ minifyEnabled = System.env.CUSTOM_MINIFY ||
+ Boolean.valueOf((String) project.properties[
+ "com.android.build.gradle.integratonTest.minifyEnabled"])
+
+ useComponentModel = Boolean.valueOf((String) project.properties[
+ "com.android.build.gradle.integratonTest.useComponentModel"])
+
+ def remoteTestProvider = System.env.REMOTE_TEST_PROVIDER
+ if (remoteTestProvider != null) {
+ plugins.withId('com.android.application') {
+ apply plugin: remoteTestProvider
+ }
+ plugins.withId('com.android.library') {
+ apply plugin: remoteTestProvider
+ }
+ plugins.withId('com.android.model.application') {
+ apply plugin: remoteTestProvider
+ }
+ plugins.withId('com.android.model.library') {
+ apply plugin: remoteTestProvider
+ }
+ }
}
plugins.withId("com.android.application") { plugin ->
diff --git a/build-system/integration-test/test-projects/commonLocalRepo.gradle b/build-system/integration-test/test-projects/commonLocalRepo.gradle
index c25169d..0374bb1 100644
--- a/build-system/integration-test/test-projects/commonLocalRepo.gradle
+++ b/build-system/integration-test/test-projects/commonLocalRepo.gradle
@@ -22,4 +22,7 @@
} else {
throw new RuntimeException("Neither CUSTOM_REPO nor USE_EXTERNAL_REPO set.")
}
+ if (System.env.ADDITIONAL_TEST_CUSTOM_REPO != null) {
+ maven { url System.env.ADDITIONAL_TEST_CUSTOM_REPO }
+ }
}
diff --git a/build-system/integration-test/test-projects/componentModel/build.gradle b/build-system/integration-test/test-projects/componentModel/build.gradle
index ef3f5e4..55fb3e4 100644
--- a/build-system/integration-test/test-projects/componentModel/build.gradle
+++ b/build-system/integration-test/test-projects/componentModel/build.gradle
@@ -26,8 +26,8 @@
buildToolsVersion '19.1.0'
}
android.productFlavors {
- f1 {}
- f2 {}
+ create { name = "f1" }
+ create { name = "f2" }
}
}
diff --git a/build-system/integration-test/test-projects/densitySplit/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/densitySplit/src/main/AndroidManifest.xml
index 7eb0a67..1422948 100644
--- a/build-system/integration-test/test-projects/densitySplit/src/main/AndroidManifest.xml
+++ b/build-system/integration-test/test-projects/densitySplit/src/main/AndroidManifest.xml
@@ -4,7 +4,7 @@
<application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
<activity android:name=".Main"
android:label="@string/app_name"
- android:icon="@drawable/other">
+ android:icon="@drawable/ic_launcher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/build-system/integration-test/test-projects/duplicateNameImport/A/Project/build.gradle b/build-system/integration-test/test-projects/duplicateNameImport/A/Project/build.gradle
new file mode 100644
index 0000000..1bb4b9c
--- /dev/null
+++ b/build-system/integration-test/test-projects/duplicateNameImport/A/Project/build.gradle
@@ -0,0 +1,2 @@
+
+apply plugin: 'android-library'
diff --git a/build-system/integration-test/test-projects/duplicateNameImport/A/Project/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/duplicateNameImport/A/Project/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b6a45ff
--- /dev/null
+++ b/build-system/integration-test/test-projects/duplicateNameImport/A/Project/src/main/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="A.Project" >
+
+ <application />
+
+</manifest>
diff --git a/build-system/integration-test/test-projects/duplicateNameImport/B/Project/build.gradle b/build-system/integration-test/test-projects/duplicateNameImport/B/Project/build.gradle
new file mode 100644
index 0000000..95e0116
--- /dev/null
+++ b/build-system/integration-test/test-projects/duplicateNameImport/B/Project/build.gradle
@@ -0,0 +1,5 @@
+apply plugin: 'android-library'
+
+dependencies {
+ compile project(':A:Project')
+}
diff --git a/build-system/integration-test/test-projects/duplicateNameImport/B/Project/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/duplicateNameImport/B/Project/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..01c142f
--- /dev/null
+++ b/build-system/integration-test/test-projects/duplicateNameImport/B/Project/src/main/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="B.Project" >
+
+ <application />
+
+</manifest>
diff --git a/build-system/integration-test/test-projects/duplicateNameImport/build.gradle b/build-system/integration-test/test-projects/duplicateNameImport/build.gradle
new file mode 100644
index 0000000..47e52c3
--- /dev/null
+++ b/build-system/integration-test/test-projects/duplicateNameImport/build.gradle
@@ -0,0 +1,26 @@
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
+
+allprojects {
+
+ version = '0.1'
+ group = 'a.b.c'
+
+ plugins.withId('android-library') {
+
+ android {
+ buildToolsVersion rootProject.ext.buildToolsVersion
+ compileSdkVersion 22
+
+ defaultConfig {
+ minSdkVersion 22
+ targetSdkVersion 22
+ }
+
+ }
+ }
+}
+
+
+
+
diff --git a/build-system/integration-test/test-projects/duplicateNameImport/settings.gradle b/build-system/integration-test/test-projects/duplicateNameImport/settings.gradle
new file mode 100644
index 0000000..faaad8c
--- /dev/null
+++ b/build-system/integration-test/test-projects/duplicateNameImport/settings.gradle
@@ -0,0 +1,6 @@
+
+include ':A:Project'
+include ':B:Project'
+
+
+
diff --git a/build-system/integration-test/test-projects/extractAnnotations/src/main/java/com/android/tests/extractannotations/ExtractTest.java b/build-system/integration-test/test-projects/extractAnnotations/src/main/java/com/android/tests/extractannotations/ExtractTest.java
index 1fac08e..3236a4d 100644
--- a/build-system/integration-test/test-projects/extractAnnotations/src/main/java/com/android/tests/extractannotations/ExtractTest.java
+++ b/build-system/integration-test/test-projects/extractAnnotations/src/main/java/com/android/tests/extractannotations/ExtractTest.java
@@ -58,15 +58,14 @@
@IntDef(flag=false, value={0, Constants.CONSTANT_1, Constants.CONSTANT_3})
@Retention(RetentionPolicy.SOURCE)
- private @interface NonMask {}
+ protected @interface NonMaskType {}
- public void testNonMask(@NonMask int mask) {
+ public void testNonMask(@NonMaskType int mask) {
}
- /** @hide */
@IntDef({VISIBLE, INVISIBLE, GONE, 5, 7 + 10, Constants.CONSTANT_1})
@Retention(RetentionPolicy.SOURCE)
- private @interface Visibility {}
+ public @interface Visibility {}
public static final int VISIBLE = 0x00000000;
public static final int INVISIBLE = 0x00000004;
diff --git a/build-system/integration-test/test-projects/genFolderApi/build.gradle b/build-system/integration-test/test-projects/genFolderApi/build.gradle
index bc14b03..b61482b 100644
--- a/build-system/integration-test/test-projects/genFolderApi/build.gradle
+++ b/build-system/integration-test/test-projects/genFolderApi/build.gradle
@@ -1,3 +1,7 @@
+// ATTENTION -- hash value of this file is checked in the corresponding
+// integration test. Please make sure any changes you make here are
+// backwards compatible.
+
apply from: "../commonHeader.gradle"
buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
diff --git a/build-system/integration-test/test-projects/genFolderApi2/build.gradle b/build-system/integration-test/test-projects/genFolderApi2/build.gradle
index d9279b2..6840b4c 100644
--- a/build-system/integration-test/test-projects/genFolderApi2/build.gradle
+++ b/build-system/integration-test/test-projects/genFolderApi2/build.gradle
@@ -1,3 +1,7 @@
+// ATTENTION -- hash value of this file is checked in the corresponding
+// integration test. Please make sure any changes you make here are
+// backwards compatible.
+
apply from: "../commonHeader.gradle"
buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/build.gradle b/build-system/integration-test/test-projects/jarjarIntegration/build.gradle
new file mode 100644
index 0000000..08e0805
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/build.gradle
@@ -0,0 +1,89 @@
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
+
+
+if (buildToolsVersion < '21.0.0') {
+ println ("Warning : this sample requires build-tools version 21 or above")
+}
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion = rootProject.ext.buildToolsVersion
+
+ defaultConfig {
+ versionCode 12
+ minSdkVersion 16
+ targetSdkVersion 20
+ }
+}
+
+repositories {
+ maven { url System.env.CUSTOM_REPO }
+}
+
+dependencies {
+ compile 'com.google.code.gson:gson:2.3'
+}
+
+// JarJar tasks and related configurations\
+configurations {
+ // create new configuration for JarJar
+ jarjar
+}
+
+dependencies {
+ // add jarjar artifact to jarjar configuration
+ jarjar 'com.googlecode.jarjar:jarjar:1.3'
+}
+
+android.applicationVariants.all { variant ->
+ // create task name
+ String jarJarTaskName = "jarJar${variant.name.capitalize()}";
+
+ def workingDir = project.file('build/intermediates/jarjar');
+ // input file are the compiled files and the gson lib.
+ def inputDir = variant.javaCompiler.destinationDir
+ def outputLibrary = new File(workingDir, 'classes.jar').getCanonicalFile()
+
+ // define task
+ def jarJarTask = task("${jarJarTaskName}") {
+ logger.info "${jarJarTaskName} inputDir: ${inputDir}"
+ logger.info "${jarJarTaskName} outputLibrary: ${outputLibrary}"
+
+ inputs.file inputDir
+ outputs.file outputLibrary
+
+ doLast {
+ // in Ant
+ project.ant {
+ // define jarjar task, for classpath is used path from jarjar configuration
+ taskdef name: 'jarjar',
+ classname: 'com.tonicsystems.jarjar.JarJarTask',
+ classpath: configurations.jarjar.asPath
+
+ // start jarjar task
+ jarjar(jarfile: outputLibrary) {
+ // input is our inputDir
+ fileset(dir: inputDir)
+ // this project has only ONE dependency, gson so this is easy but obviously, this is not
+ // the best way to include compile dependencies.
+ zipFileset(src: variant.compileLibraries.toArray()[0])
+ // rule to repackage gson to new package
+ rule pattern: 'com.google.gson.**', result: 'com.google.repacked.gson.@1'
+ }
+ }
+
+ // replace jar generated by compile with jar generated by JarJar
+ variant.dex.inputFiles = [outputLibrary]
+ // and remove our preDex'ed libraries.
+ variant.dex.libraries = []
+ }
+ }
+
+ // plan task to be started between Compile task and Dex task
+ // if the task was using proguard, we would need to reset the proguard inputs rather than dex.
+ variant.dex.dependsOn jarJarTask
+ jarJarTask.dependsOn variant.javaCompiler
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/proguard-rules.pro b/build-system/integration-test/test-projects/jarjarIntegration/proguard-rules.pro
new file mode 100644
index 0000000..f5df2a5
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/proguard-rules.pro
@@ -0,0 +1 @@
+-keep public class com.android.tests.basic.IndirectlyReferencedClass
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/androidTest/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/jarjarIntegration/src/androidTest/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..b7cbd6d
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/androidTest/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,47 @@
+package com.android.tests.basic;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.TextView;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import com.android.tests.basic.Main;
+
+public class MainTest extends ActivityInstrumentationTestCase2<Main> {
+
+ private TextView mTextView;
+
+ /**
+ * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link Main} activity.
+ */
+ public MainTest() {
+ super(Main.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ final Main a = getActivity();
+ // ensure a valid handle to the activity has been returned
+ assertNotNull(a);
+ mTextView = (TextView) a.findViewById(R.id.text);
+ }
+
+ /**
+ * The name 'test preconditions' is a convention to signal that if this
+ * test doesn't pass, the test case was not set up properly and it might
+ * explain any and all failures in other tests. This is not guaranteed
+ * to run before other tests, as junit uses reflection to find the tests.
+ */
+ @MediumTest
+ public void testPreconditions() {
+ assertNotNull(mTextView);
+ Gson gson = new Gson();
+ String jsonString = gson.toJson(Main.STRINGS);
+ // assert that our gson strings is correct.
+ assertEquals(jsonString, mTextView.getText());
+ }
+}
+
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/jarjarIntegration/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7eb0a67
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.basic">
+ <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+ <activity android:name=".Main"
+ android:label="@string/app_name"
+ android:icon="@drawable/other">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/java/com/android/tests/basic/Main.java b/build-system/integration-test/test-projects/jarjarIntegration/src/main/java/com/android/tests/basic/Main.java
new file mode 100644
index 0000000..5c1e17d
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/java/com/android/tests/basic/Main.java
@@ -0,0 +1,26 @@
+package com.android.tests.basic;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+import com.google.gson.Gson;
+
+import java.lang.String;
+
+public class Main extends Activity
+{
+ static final String[] STRINGS = { "some", "radom", "strings"};
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ TextView tv = (TextView) findViewById(R.id.text);
+ Gson gson = new Gson();
+ String jsonString = gson.toJson(STRINGS);
+ tv.setText(jsonString);
+ }
+}
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-hdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-mdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-mdpi/other.png b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-mdpi/other.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-mdpi/other.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-xhdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-xxhdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/layout/main.xml b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/layout/main.xml
new file mode 100644
index 0000000..b199751
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/layout/main.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+<TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Test App - Basic"
+ android:id="@+id/text"
+ />
+</LinearLayout>
+
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/values/strings.xml
new file mode 100644
index 0000000..6d2c9ba
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">JarJarTest</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/jarjarIntegration/src/release/res/values/strings.xml b/build-system/integration-test/test-projects/jarjarIntegration/src/release/res/values/strings.xml
new file mode 100644
index 0000000..532909c
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarIntegration/src/release/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">_Test-Basic-Release</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/build.gradle b/build-system/integration-test/test-projects/jarjarWithJack/build.gradle
new file mode 100644
index 0000000..8cefaf9
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/build.gradle
@@ -0,0 +1,37 @@
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
+
+
+if (buildToolsVersion < '21.0.0') {
+ println ("Warning : this sample requires build-tools version 21 or above")
+}
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion = rootProject.ext.buildToolsVersion
+
+ defaultConfig {
+ versionCode 12
+ minSdkVersion 16
+ targetSdkVersion 20
+ useJack true
+ jarJarRuleFile 'jarjar.rules'
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android.txt')
+ }
+ }
+}
+
+repositories {
+ maven { url System.env.CUSTOM_REPO }
+}
+
+dependencies {
+ compile 'com.google.code.gson:gson:2.3'
+}
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/jarjar.rules b/build-system/integration-test/test-projects/jarjarWithJack/jarjar.rules
new file mode 100644
index 0000000..f64843f
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/jarjar.rules
@@ -0,0 +1 @@
+rule com.google.gson.** com.google.repacked.gson.@1
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/proguard-rules.pro b/build-system/integration-test/test-projects/jarjarWithJack/proguard-rules.pro
new file mode 100644
index 0000000..f5df2a5
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/proguard-rules.pro
@@ -0,0 +1 @@
+-keep public class com.android.tests.basic.IndirectlyReferencedClass
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/androidTest/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/jarjarWithJack/src/androidTest/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..7cf7329
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/androidTest/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,38 @@
+package com.android.tests.basic;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.TextView;
+
+public class MainTest extends ActivityInstrumentationTestCase2<Main> {
+
+ private TextView mTextView;
+
+ /**
+ * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link Main} activity.
+ */
+ public MainTest() {
+ super(Main.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ final Main a = getActivity();
+ // ensure a valid handle to the activity has been returned
+ assertNotNull(a);
+ mTextView = (TextView) a.findViewById(R.id.text);
+ }
+
+ /**
+ * The name 'test preconditions' is a convention to signal that if this
+ * test doesn't pass, the test case was not set up properly and it might
+ * explain any and all failures in other tests. This is not guaranteed
+ * to run before other tests, as junit uses reflection to find the tests.
+ */
+ @MediumTest
+ public void testPreconditions() {
+ assertNotNull(mTextView);
+ }
+}
+
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/jarjarWithJack/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7eb0a67
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.basic">
+ <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+ <activity android:name=".Main"
+ android:label="@string/app_name"
+ android:icon="@drawable/other">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/java/com/android/tests/basic/Main.java b/build-system/integration-test/test-projects/jarjarWithJack/src/main/java/com/android/tests/basic/Main.java
new file mode 100644
index 0000000..f201320
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/java/com/android/tests/basic/Main.java
@@ -0,0 +1,26 @@
+package com.android.tests.basic;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+import com.google.gson.Gson;
+
+import java.lang.String;
+
+public class Main extends Activity
+{
+ private static final String[] strings = { "some", "radom", "strings"};
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ TextView tv = (TextView) findViewById(R.id.text);
+ Gson gson = new Gson();
+ String jsonString = gson.toJson(strings);
+ tv.setText(jsonString);
+ }
+}
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-hdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-mdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-mdpi/other.png b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-mdpi/other.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-mdpi/other.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-xhdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-xxhdpi/ic_launcher.png b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/layout/main.xml b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/layout/main.xml
new file mode 100644
index 0000000..b199751
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/layout/main.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+<TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Test App - Basic"
+ android:id="@+id/text"
+ />
+</LinearLayout>
+
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/values/strings.xml
new file mode 100644
index 0000000..bf812b2
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">JarJarWithJackTest</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/jarjarWithJack/src/release/res/values/strings.xml b/build-system/integration-test/test-projects/jarjarWithJack/src/release/res/values/strings.xml
new file mode 100644
index 0000000..532909c
--- /dev/null
+++ b/build-system/integration-test/test-projects/jarjarWithJack/src/release/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">_Test-Basic-Release</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/minify/src/androidTest/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/minify/src/androidTest/java/com/android/tests/basic/MainTest.java
index 46a3375..5719946 100644
--- a/build-system/integration-test/test-projects/minify/src/androidTest/java/com/android/tests/basic/MainTest.java
+++ b/build-system/integration-test/test-projects/minify/src/androidTest/java/com/android/tests/basic/MainTest.java
@@ -3,6 +3,8 @@
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.MediumTest;
import android.widget.TextView;
+import android.test.UiThreadTest;
+import android.os.Looper;
public class MainTest extends ActivityInstrumentationTestCase2<Main> {
@@ -56,5 +58,10 @@
assertEquals("com.android.tests.basic.UsedTestClass", UsedTestClass.class.getName());
}
+
+ @UiThreadTest
+ public void testAnnotationNotStripped() {
+ assertTrue("Should be running on UI thread", Looper.myLooper() == Looper.getMainLooper());
+ }
}
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/build.gradle b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/build.gradle
new file mode 100644
index 0000000..20b0991
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/build.gradle
@@ -0,0 +1,39 @@
+apply plugin: 'com.android.application'
+
+dependencies {
+ compile project(':lib')
+}
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion = rootProject.ext.buildToolsVersion
+
+ signingConfigs {
+ myConfig {
+ storeFile file("debug.keystore")
+ storePassword "android"
+ keyAlias "androiddebugkey"
+ keyPassword "android"
+ }
+ }
+
+ defaultConfig {
+ versionCode 12
+ versionName "2.0"
+ minSdkVersion 16
+ targetSdkVersion 16
+ useJack rootProject.ext.useJack
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ signingConfig signingConfigs.myConfig
+ proguardFile getDefaultProguardFile('proguard-android.txt')
+ }
+ }
+
+ dexOptions {
+ incremental false
+ }
+}
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/debug.keystore b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/debug.keystore
new file mode 100644
index 0000000..389278e
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/debug.keystore
Binary files differ
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/androidTest/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/androidTest/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..ccae828
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/androidTest/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,43 @@
+package com.android.tests.basic;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.TextView;
+
+public class MainTest extends ActivityInstrumentationTestCase2<Main> {
+
+ private TextView mTextView;
+
+ /**
+ * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link Main} activity.
+ */
+ public MainTest() {
+ super(Main.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ final Main a = getActivity();
+ // ensure a valid handle to the activity has been returned
+ assertNotNull(a);
+ mTextView = (TextView) a.findViewById(R.id.dateText);
+ }
+
+ /**
+ * The name 'test preconditions' is a convention to signal that if this
+ * test doesn't pass, the test case was not set up properly and it might
+ * explain any and all failures in other tests. This is not guaranteed
+ * to run before other tests, as junit uses reflection to find the tests.
+ */
+ @MediumTest
+ public void testPreconditions() {
+ assertNotNull(mTextView);
+ }
+
+ public void testTextViewContent() {
+ assertEquals("AppNameFromResources-RandomApp_\"Success : Valid number : 123\"",
+ mTextView.getText().toString());
+ }
+}
+
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a34d937
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.basic">
+ <application android:label="@string/app_name" android:icon="@drawable/icon">
+ <activity android:name=".Main"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/java/com/android/tests/basic/Main.java b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/java/com/android/tests/basic/Main.java
new file mode 100644
index 0000000..3bc69ad
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/java/com/android/tests/basic/Main.java
@@ -0,0 +1,60 @@
+package com.android.tests.basic;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.DocumentBuilder;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Node;
+import org.w3c.dom.Element;
+
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import com.android.tests.basic.StringProvider;
+import com.android.tests.util.AppStringProvider;
+
+public class Main extends Activity {
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ TextView tv = (TextView) findViewById(R.id.dateText);
+
+ String message;
+ try {
+ // load untouched xml file.
+ InputStream inputStream = null;
+ try {
+ inputStream = getClass().getResourceAsStream("/com/android/tests/other/some.xml");
+ if (inputStream == null) {
+ message = "Cannot load some.xml";
+ } else {
+ DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
+ Document doc = dBuilder.parse(inputStream);
+ String value = doc.getDocumentElement().getAttribute("value");
+ message = StringProvider.getString(Integer.parseInt(value));
+ }
+ } finally {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+
+ String appName = AppStringProvider.getProperty("app.name");
+ String appString = AppStringProvider.getProperty("app.string");
+
+
+ tv.setText(appName + '-' + appString + '_' + message);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/java/com/android/tests/util/AppStringProvider.java b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/java/com/android/tests/util/AppStringProvider.java
new file mode 100644
index 0000000..22be315
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/java/com/android/tests/util/AppStringProvider.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.util;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.RuntimeException;
+import java.util.Properties;
+import java.util.Enumeration;
+import java.net.URL;
+
+/**
+ * String provider getting the string format from a co-bundled resources.properties file.
+ */
+public class AppStringProvider {
+
+ private final static Properties properties = new Properties();
+
+ static {
+ try {
+ InputStream inputStream = null;
+ try {
+ inputStream = AppStringProvider.class.getResourceAsStream("resources.properties");
+ if (inputStream == null) {
+ properties.put("app.name", "Error, cannot find resources.properties for %d");
+ } else {
+ properties.load(inputStream);
+ }
+ } finally {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+ try {
+ // load the second resource file, using absolute path.
+ inputStream = AppStringProvider.class.getResourceAsStream("/com/android/tests/util/another.properties");
+ if (inputStream == null) {
+ properties.put("app.string", "Error, cannot load another.properties %s %d");
+ } else {
+ properties.load(inputStream);
+ }
+ } finally {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+
+ } catch(IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String getProperty(String key) {
+ return properties.getProperty(key);
+ }
+}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/res/drawable/icon.png
similarity index 100%
copy from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png
copy to build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/res/drawable/icon.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/res/layout/main.xml b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/res/layout/main.xml
new file mode 100644
index 0000000..89ab091
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/res/layout/main.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+<TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Test App - Basic"
+ android:id="@+id/text"
+ />
+<TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:id="@+id/dateText"
+ />
+</LinearLayout>
+
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..60ea2d0
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">_Test-Basic</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/resources/com/android/tests/util/another.properties b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/resources/com/android/tests/util/another.properties
new file mode 100644
index 0000000..001a110
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/resources/com/android/tests/util/another.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+app.string=RandomApp
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/resources/com/android/tests/util/resources.properties b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/resources/com/android/tests/util/resources.properties
new file mode 100644
index 0000000..8caea57
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/app/src/main/resources/com/android/tests/util/resources.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+app.name=AppNameFromResources
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/build.gradle b/build-system/integration-test/test-projects/minifyLibWithJavaRes/build.gradle
new file mode 100644
index 0000000..bba07fc
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/build.gradle
@@ -0,0 +1,2 @@
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/build.gradle b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/build.gradle
new file mode 100644
index 0000000..6f194df
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/build.gradle
@@ -0,0 +1,14 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion = rootProject.ext.buildToolsVersion
+
+ defaultConfig {
+ versionCode 12
+ versionName "2.0"
+ minSdkVersion 16
+ targetSdkVersion 16
+ consumerProguardFiles 'config.pro'
+ }
+}
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/config.pro b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/config.pro
new file mode 100644
index 0000000..a4e4e35
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/config.pro
@@ -0,0 +1 @@
+-adaptresourcefilenames !**another.properties,**.properties
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/androidTest/java/com/android/tests/basic/StringProviderTest.java b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/androidTest/java/com/android/tests/basic/StringProviderTest.java
new file mode 100644
index 0000000..8195e4d
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/androidTest/java/com/android/tests/basic/StringProviderTest.java
@@ -0,0 +1,10 @@
+package com.android.tests.basic;
+
+import junit.framework.TestCase;
+
+public class StringProviderTest extends TestCase {
+
+ public void testGetString() {
+ assertEquals("\"Success : Valid number : 123\"", StringProvider.getString(123));
+ }
+}
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..45f6ffd
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.basic.lib">
+</manifest>
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/java/com/android/tests/basic/StringProvider.java b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/java/com/android/tests/basic/StringProvider.java
new file mode 100644
index 0000000..a9bad9b
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/java/com/android/tests/basic/StringProvider.java
@@ -0,0 +1,23 @@
+package com.android.tests.basic;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.RuntimeException;
+import java.util.Properties;
+import java.util.Enumeration;
+import java.net.URL;
+
+import com.android.tests.other.PropertiesProvider;
+
+/**
+ * String provider getting the string format from a co-bundled resources.properties file.
+ */
+public class StringProvider {
+
+ public static String getString(int aNumber) {
+ return String.format(
+ PropertiesProvider.getProperty("the.format"),
+ PropertiesProvider.getProperty("the.string"),
+ aNumber);
+ }
+}
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/java/com/android/tests/other/PropertiesProvider.java b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/java/com/android/tests/other/PropertiesProvider.java
new file mode 100644
index 0000000..2174f8d
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/java/com/android/tests/other/PropertiesProvider.java
@@ -0,0 +1,54 @@
+package com.android.tests.other;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.RuntimeException;
+import java.util.Properties;
+import java.util.Enumeration;
+import java.net.URL;
+
+/**
+ * String provider getting the string format from a co-bundled resources.properties file.
+ */
+public class PropertiesProvider {
+
+ private final static Properties properties = new Properties();
+
+ static {
+ try {
+ InputStream inputStream = null;
+ try {
+ inputStream = PropertiesProvider.class.getResourceAsStream("resources.properties");
+ if (inputStream == null) {
+ properties.put("the.string", "Error, cannot find resources.properties for %d");
+ } else {
+ properties.load(inputStream);
+ }
+ } finally {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+ try {
+ // load the second resource file, using absolute path.
+ inputStream = PropertiesProvider.class.getResourceAsStream("/com/android/tests/other/another.properties");
+ if (inputStream == null) {
+ properties.put("the.format", "Error, cannot load another.properties %s %d");
+ } else {
+ properties.load(inputStream);
+ }
+ } finally {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+
+ } catch(IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String getProperty(String key) {
+ return properties.getProperty(key);
+ }
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/another.properties b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/another.properties
new file mode 100644
index 0000000..b239ae8
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/another.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+the.format="Success : %s : %d"
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/resources.properties b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/resources.properties
new file mode 100644
index 0000000..ee55e43
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/resources.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+the.string=Valid number
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/some.xml b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/some.xml
new file mode 100644
index 0000000..e6046b7
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/lib/src/main/resources/com/android/tests/other/some.xml
@@ -0,0 +1,17 @@
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<xml value="123"/>
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/minifyLibWithJavaRes/settings.gradle b/build-system/integration-test/test-projects/minifyLibWithJavaRes/settings.gradle
new file mode 100644
index 0000000..eedb2a1
--- /dev/null
+++ b/build-system/integration-test/test-projects/minifyLibWithJavaRes/settings.gradle
@@ -0,0 +1,2 @@
+include 'app'
+include 'lib'
diff --git a/build-system/integration-test/test-projects/ndkJniLib2/lib/build.gradle b/build-system/integration-test/test-projects/ndkJniLib2/lib/build.gradle
index 7db669d..9e66cb0 100644
--- a/build-system/integration-test/test-projects/ndkJniLib2/lib/build.gradle
+++ b/build-system/integration-test/test-projects/ndkJniLib2/lib/build.gradle
@@ -1,12 +1,12 @@
apply plugin: 'com.android.model.library'
model {
- android.config {
+ android {
compileSdkVersion 19
buildToolsVersion = rootProject.ext.buildToolsVersion
}
android.ndk {
- moduleName "hello-jni"
+ moduleName = "hello-jni"
}
}
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/build.gradle b/build-system/integration-test/test-projects/ndkSanAngeles2/build.gradle
index 59773de..bb0bb79 100644
--- a/build-system/integration-test/test-projects/ndkSanAngeles2/build.gradle
+++ b/build-system/integration-test/test-projects/ndkSanAngeles2/build.gradle
@@ -5,63 +5,43 @@
apply plugin: 'com.android.model.application'
model {
- android.config {
- compileSdkVersion 19
+ android {
+ compileSdkVersion = 19
buildToolsVersion = rootProject.ext.buildToolsVersion
- defaultConfig {
+ defaultConfig.with {
// This actual the app version code. Giving ourselves 1,000,000 values
versionCode = 123
}
-
- // make per-variant version code
- applicationVariants.all { variant ->
- // get the single flavor
- def flavorVersion = variant.productFlavors.get(0).versionCode
-
- // set the composite code
- variant.mergedFlavor.versionCode = flavorVersion * 1000000 + defaultConfig.versionCode
- }
- }
- android.buildTypes {
- debug {
- debuggable true
- }
}
android.ndk {
- compileSdkVersion 19
- moduleName "sanangeles"
- cFlags "-DDISABLE_IMPORTGL"
- ldLibs "GLESv1_CM", "dl", "log"
- stl "stlport_static"
- toolchain "clang"
+ moduleName = "sanangeles"
+ CFlags += "-DDISABLE_IMPORTGL"
+ ldLibs += "GLESv1_CM"
+ ldLibs += "dl"
+ ldLibs += "log"
+ stl = "stlport_static"
+ toolchain = "clang"
}
android.productFlavors {
- x86 {
- ndk {
- abiFilter "x86"
- }
-
+ create("x86") {
+ ndk.abiFilters += "x86"
// this is the flavor part of the version code.
// It must be higher than the arm one for devices supporting
// both, as x86 is preferred.
versionCode = 3
}
- arm {
- ndk {
- abiFilter "armeabi-v7a"
- }
+ create("arm") {
+ ndk.abiFilters += "armeabi-v7a"
versionCode = 2
}
- mips {
- ndk {
- abiFilter "mips"
- }
+ create("mips") {
+ ndk.abiFilters += "mips"
versionCode = 1
}
- fat {
+ create("fat") {
// fat binary, lowest version code to be
// the last option
versionCode = 0
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/app-android.c b/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/app-android.c
similarity index 100%
rename from build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/app-android.c
rename to build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/app-android.c
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/app.h b/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/app.h
similarity index 100%
rename from build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/app.h
rename to build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/app.h
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/cams.h b/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/cams.h
similarity index 100%
rename from build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/cams.h
rename to build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/cams.h
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/demo.c b/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/demo.c
similarity index 100%
rename from build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/demo.c
rename to build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/demo.c
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/importgl.c b/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/importgl.c
similarity index 100%
rename from build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/importgl.c
rename to build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/importgl.c
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/importgl.h b/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/importgl.h
similarity index 100%
rename from build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/importgl.h
rename to build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/importgl.h
diff --git a/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/shapes.h b/build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/shapes.h
similarity index 100%
rename from build-system/integration-test/test-projects/ndkSanAngeles2/src/main/c/shapes.h
rename to build-system/integration-test/test-projects/ndkSanAngeles2/src/main/jni/shapes.h
diff --git a/build-system/integration-test/test-projects/ndkStandaloneSo/lib/build.gradle b/build-system/integration-test/test-projects/ndkStandaloneSo/lib/build.gradle
index b2ee58e..219efc9 100644
--- a/build-system/integration-test/test-projects/ndkStandaloneSo/lib/build.gradle
+++ b/build-system/integration-test/test-projects/ndkStandaloneSo/lib/build.gradle
@@ -2,7 +2,7 @@
model {
android.ndk {
- compileSdkVersion 19
- moduleName "hello-jni"
+ compileSdkVersion = 19
+ moduleName = "hello-jni"
}
}
diff --git a/build-system/integration-test/test-projects/ndkVariants/build.gradle b/build-system/integration-test/test-projects/ndkVariants/build.gradle
index 53607e0..535e44b 100644
--- a/build-system/integration-test/test-projects/ndkVariants/build.gradle
+++ b/build-system/integration-test/test-projects/ndkVariants/build.gradle
@@ -1,26 +1,34 @@
apply from: "../commonHeader.gradle"
buildscript { apply from: "../commonBuildScriptExperimental.gradle", to: buildscript }
+// This test ensures each variant compiles the correct source set with the appropriate NDK settings.
apply plugin: 'com.android.model.application'
model {
- android.config {
- compileSdkVersion 19
+ android {
+ compileSdkVersion = 19
buildToolsVersion = rootProject.ext.buildToolsVersion
-
}
android.ndk {
- moduleName "simple-jni"
+ moduleName = "simple-jni"
// TODO: Include a way to set include directories the DSL.
- cppFlags "-I$rootDir/src/include"
- stl "stlport_static"
+ cppFlags += "-I$rootDir/src/include".toString()
+ stl = "stlport_static"
}
android.productFlavors {
- free
- premium
+ create("free")
+ create("premium")
+ }
+
+ // Set binary specific C++ flags.
+ components.android {
+ binaries.afterEach { binary ->
+ binary.mergedNdkConfig.cppFlags.add("-DVARIANT=\"" + binary.name + "\"")
+ }
}
}
+
diff --git a/build-system/integration-test/test-projects/ndkVariants/src/main/java/com/example/simplejni/SimpleJni.java b/build-system/integration-test/test-projects/ndkVariants/src/main/java/com/example/simplejni/SimpleJni.java
index 1b5b9d5..255b0b1 100644
--- a/build-system/integration-test/test-projects/ndkVariants/src/main/java/com/example/simplejni/SimpleJni.java
+++ b/build-system/integration-test/test-projects/ndkVariants/src/main/java/com/example/simplejni/SimpleJni.java
@@ -29,13 +29,10 @@
* the text is retrieved by calling a native
* function.
*/
- StringBuilder sb = new StringBuilder(
- productFlavorFromJni().equals("free") ?
- "Free as in beer!\n" :
- "Show me the money!\n");
- sb.append("(");
- sb.append(buildTypeFromJni());
- sb.append(")");
+ StringBuilder sb = new StringBuilder();
+ sb.append("productFlavor: " + productFlavorFromJni() + "\n");
+ sb.append("buildType: " + buildTypeFromJni() + "\n");
+ sb.append("variant: " + variantFromJni() + "\n");
TextView tv = new TextView(this);
tv.setText(sb.toString());
@@ -49,6 +46,9 @@
public native String productFlavorFromJni();
+ /* Native method that expected the VARIANT macro to be defined. */
+ public native String variantFromJni();
+
/* Load the 'simple-jni' library on application startup. */
static {
System.loadLibrary("simple-jni");
diff --git a/build-system/integration-test/test-projects/ndkVariants/src/main/jni/simple-jni.cpp b/build-system/integration-test/test-projects/ndkVariants/src/main/jni/simple-jni.cpp
index ac3ffd5..fff200e 100644
--- a/build-system/integration-test/test-projects/ndkVariants/src/main/jni/simple-jni.cpp
+++ b/build-system/integration-test/test-projects/ndkVariants/src/main/jni/simple-jni.cpp
@@ -39,3 +39,10 @@
{
return env->NewStringUTF(PRODUCT_FLAVOR);
}
+
+extern "C"
+jstring
+Java_com_example_simplejni_SimpleJni_variantFromJni(JNIEnv* env, jobject thiz)
+{
+ return env->NewStringUTF(VARIANT);
+}
diff --git a/build-system/integration-test/test-projects/packagingOptions/build.gradle b/build-system/integration-test/test-projects/packagingOptions/build.gradle
index bc56782..55c0861 100644
--- a/build-system/integration-test/test-projects/packagingOptions/build.gradle
+++ b/build-system/integration-test/test-projects/packagingOptions/build.gradle
@@ -10,6 +10,9 @@
packagingOptions {
exclude 'excluded.txt'
pickFirst 'first_pick.txt'
+ merge 'merge.txt'
+
+ pickFirst 'lib/x86/libdummy.so'
}
}
diff --git a/build-system/integration-test/test-projects/packagingOptions/jar1.jar b/build-system/integration-test/test-projects/packagingOptions/jar1.jar
index 41e058b..c4701d9 100644
--- a/build-system/integration-test/test-projects/packagingOptions/jar1.jar
+++ b/build-system/integration-test/test-projects/packagingOptions/jar1.jar
Binary files differ
diff --git a/build-system/integration-test/test-projects/packagingOptions/jar2.jar b/build-system/integration-test/test-projects/packagingOptions/jar2.jar
index 7ff93d1..acbf286 100644
--- a/build-system/integration-test/test-projects/packagingOptions/jar2.jar
+++ b/build-system/integration-test/test-projects/packagingOptions/jar2.jar
Binary files differ
diff --git a/build-system/integration-test/test-projects/packagingOptions/jars/jar1/src/main/resources/merge.txt b/build-system/integration-test/test-projects/packagingOptions/jars/jar1/src/main/resources/merge.txt
new file mode 100644
index 0000000..ef600d9
--- /dev/null
+++ b/build-system/integration-test/test-projects/packagingOptions/jars/jar1/src/main/resources/merge.txt
@@ -0,0 +1 @@
+merge1
diff --git a/build-system/integration-test/test-projects/packagingOptions/jars/jar2/src/main/resources/merge.txt b/build-system/integration-test/test-projects/packagingOptions/jars/jar2/src/main/resources/merge.txt
new file mode 100644
index 0000000..0a040d9
--- /dev/null
+++ b/build-system/integration-test/test-projects/packagingOptions/jars/jar2/src/main/resources/merge.txt
@@ -0,0 +1 @@
+merge2
diff --git a/build-system/integration-test/test-projects/packagingOptions/src/debug/jniLibs/x86/libdummy.so b/build-system/integration-test/test-projects/packagingOptions/src/debug/jniLibs/x86/libdummy.so
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build-system/integration-test/test-projects/packagingOptions/src/debug/jniLibs/x86/libdummy.so
diff --git a/build-system/integration-test/test-projects/packagingOptions/src/main/jniLibs/x86/libdummy.so b/build-system/integration-test/test-projects/packagingOptions/src/main/jniLibs/x86/libdummy.so
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build-system/integration-test/test-projects/packagingOptions/src/main/jniLibs/x86/libdummy.so
diff --git a/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/layout/main_layout.xml b/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/layout/main_layout.xml
new file mode 100644
index 0000000..0844dda
--- /dev/null
+++ b/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/layout/main_layout.xml
@@ -0,0 +1,5 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/shared_name"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/public.xml b/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/public.xml
index 694459d..0e702a9 100644
--- a/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/public.xml
+++ b/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/public.xml
@@ -1,4 +1,6 @@
<resources>
<public name="mylib_app_name" type="string"/>
<public name="mylib_public_string" type="string"/>
+ <public name="shared_name" type="string"/>
+ <public name="shared_name" type="id"/>
</resources>
diff --git a/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/strings.xml
index 7a19f7e..606f0b8 100644
--- a/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/strings.xml
+++ b/build-system/integration-test/test-projects/privateResources/mylibrary/src/main/res/values/strings.xml
@@ -2,4 +2,5 @@
<string name="mylib_app_name">My Library</string>
<string name="mylib_public_string">Public String in Library</string>
<string name="mylib_private_string">Private String in Library</string>
+ <string name="shared_name">String that has the same name as an ID</string>
</resources>
diff --git a/build-system/integration-test/test-projects/projectWithModules/library/src/main/res/layout/liblayout.xml b/build-system/integration-test/test-projects/projectWithModules/library/src/main/res/layout/liblayout.xml
new file mode 100644
index 0000000..25c0817
--- /dev/null
+++ b/build-system/integration-test/test-projects/projectWithModules/library/src/main/res/layout/liblayout.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+</LinearLayout>
diff --git a/build-system/integration-test/test-projects/projectWithModules/library2/src/main/java/com/example/android/multiproject/library2/PersonView2.java b/build-system/integration-test/test-projects/projectWithModules/library2/src/main/java/com/example/android/multiproject/library2/PersonView2.java
index 6c740b7..5dd2ed0 100644
--- a/build-system/integration-test/test-projects/projectWithModules/library2/src/main/java/com/example/android/multiproject/library2/PersonView2.java
+++ b/build-system/integration-test/test-projects/projectWithModules/library2/src/main/java/com/example/android/multiproject/library2/PersonView2.java
@@ -4,7 +4,7 @@
import android.widget.TextView;
class PersonView2 extends TextView {
- public PersonView(Context context, String name) {
+ public PersonView2(Context context, String name) {
super(context);
setTextSize(20);
setText(name);
diff --git a/build-system/integration-test/test-projects/projectWithModules/library2/src/main/res/layout/lib2layout.xml b/build-system/integration-test/test-projects/projectWithModules/library2/src/main/res/layout/lib2layout.xml
new file mode 100644
index 0000000..25c0817
--- /dev/null
+++ b/build-system/integration-test/test-projects/projectWithModules/library2/src/main/res/layout/lib2layout.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+</LinearLayout>
diff --git a/build-system/integration-test/test-projects/renderscriptInLib/app/src/main/java/com/example/android/rs/balls/BallsView.java b/build-system/integration-test/test-projects/renderscriptInLib/app/src/main/java/com/example/android/rs/balls/BallsView.java
index 47d28fc..8015970 100644
--- a/build-system/integration-test/test-projects/renderscriptInLib/app/src/main/java/com/example/android/rs/balls/BallsView.java
+++ b/build-system/integration-test/test-projects/renderscriptInLib/app/src/main/java/com/example/android/rs/balls/BallsView.java
@@ -46,6 +46,7 @@
@Override
protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
if(mRS != null) {
mRS = null;
destroyRenderScriptGL();
diff --git a/build-system/integration-test/test-projects/renderscriptMultiSrc/src/main/java/com/example/android/rs/balls/BallsView.java b/build-system/integration-test/test-projects/renderscriptMultiSrc/src/main/java/com/example/android/rs/balls/BallsView.java
index 47d28fc..8015970 100644
--- a/build-system/integration-test/test-projects/renderscriptMultiSrc/src/main/java/com/example/android/rs/balls/BallsView.java
+++ b/build-system/integration-test/test-projects/renderscriptMultiSrc/src/main/java/com/example/android/rs/balls/BallsView.java
@@ -46,6 +46,7 @@
@Override
protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
if(mRS != null) {
mRS = null;
destroyRenderScriptGL();
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/build.gradle
new file mode 100644
index 0000000..3686af6
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/build.gradle
@@ -0,0 +1,25 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion = rootProject.ext.buildToolsVersion
+
+ publishNonDefault true
+
+ defaultConfig {
+ minSdkVersion 16
+ }
+
+ buildTypes {
+ minified.initWith(buildTypes.debug)
+ minified {
+ minifyEnabled true
+ useJack rootProject.ext.useJack
+ proguardFiles getDefaultProguardFile('proguard-android.txt')
+ }
+ }
+}
+
+dependencies {
+ compile project(':lib')
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/androidTest/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/androidTest/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..7c2efd4
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/androidTest/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,39 @@
+package com.android.tests.basic;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.TextView;
+
+public class MainTest extends ActivityInstrumentationTestCase2<Main> {
+
+ private TextView mTextView;
+
+ /**
+ * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link Main} activity.
+ */
+ public MainTest() {
+ super(Main.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ final Main a = getActivity();
+ // ensure a valid handle to the activity has been returned
+ assertNotNull(a);
+ mTextView = (TextView) a.findViewById(R.id.text);
+
+ }
+
+ /**
+ * The name 'test preconditions' is a convention to signal that if this
+ * test doesn't pass, the test case was not set up properly and it might
+ * explain any and all failures in other tests. This is not guaranteed
+ * to run before other tests, as junit uses reflection to find the tests.
+ */
+ @MediumTest
+ public void testPreconditions() {
+ assertNotNull(mTextView);
+ }
+}
+
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a34d937
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.basic">
+ <application android:label="@string/app_name" android:icon="@drawable/icon">
+ <activity android:name=".Main"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/java/com/android/tests/basic/Main.java b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/java/com/android/tests/basic/Main.java
new file mode 100644
index 0000000..2b0e698
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/java/com/android/tests/basic/Main.java
@@ -0,0 +1,15 @@
+package com.android.tests.basic;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class Main extends Activity
+{
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/res/drawable/icon.png
similarity index 100%
copy from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png
copy to build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/res/drawable/icon.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/res/layout/main.xml b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/res/layout/main.xml
new file mode 100644
index 0000000..b199751
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/res/layout/main.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+<TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Test App - Basic"
+ android:id="@+id/text"
+ />
+</LinearLayout>
+
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..60ea2d0
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">_Test-Basic</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/release/res/values/strings.xml b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/release/res/values/strings.xml
new file mode 100644
index 0000000..532909c
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/app/src/release/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">_Test-Basic-Release</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/build.gradle
new file mode 100644
index 0000000..bba07fc
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/build.gradle
@@ -0,0 +1,2 @@
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/jar/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/jar/build.gradle
new file mode 100644
index 0000000..bbfeb03
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/jar/build.gradle
@@ -0,0 +1 @@
+apply plugin: 'java'
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/jar/src/main/java/com/android/tests/jarDep/JarDependencyUtil.java b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/jar/src/main/java/com/android/tests/jarDep/JarDependencyUtil.java
new file mode 100644
index 0000000..e0d9826
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/jar/src/main/java/com/android/tests/jarDep/JarDependencyUtil.java
@@ -0,0 +1,7 @@
+package com.android.tests.jarDep;
+
+public class JarDependencyUtil {
+ public static String getString() {
+ return "jar dependency string";
+ }
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/build.gradle
new file mode 100644
index 0000000..af57e6f
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/build.gradle
@@ -0,0 +1,17 @@
+apply plugin: 'com.android.library'
+
+dependencies {
+ compile project(':jar')
+}
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion = rootProject.ext.buildToolsVersion
+
+ defaultConfig {
+ versionCode 12
+ versionName "2.0"
+ minSdkVersion 16
+ targetSdkVersion 16
+ }
+}
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/androidTest/java/com/android/tests/basic/StringGetterTest.java b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/androidTest/java/com/android/tests/basic/StringGetterTest.java
new file mode 100644
index 0000000..ac09526
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/androidTest/java/com/android/tests/basic/StringGetterTest.java
@@ -0,0 +1,48 @@
+package com.android.tests.basic;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.Method;
+
+public class StringGetterTest extends TestCase {
+
+ public void testNonObfuscatedMethod1() {
+ // this should not be obfuscated
+ String className = "com.android.tests.basic.StringGetter";
+ String methodName = "getString";
+
+ searchMethod(className, methodName, true /*shouldExist*/);
+ }
+
+ public void testNonObfuscatedMethod2() {
+ // this should not be obfuscated
+ String className = "com.android.tests.basic.StringGetter";
+ String methodName = "getString2";
+ searchMethod(className, methodName, true /*shouldExist*/);
+ }
+
+ public void testObduscatedMethod() {
+ String className = "com.android.tests.basic.StringGetter";
+ String methodName = "getStringInternal";
+
+ searchMethod(className, methodName, false /*shouldExist*/);
+ }
+
+ private void searchMethod(String className, String methodName, boolean shouldExist) {
+ try {
+ Class<?> theClass = Class.forName(className);
+ Method method = theClass.getDeclaredMethod(methodName, int.class);
+ if (!shouldExist) {
+ fail("Found " + className + "." + methodName);
+ }
+ } catch (ClassNotFoundException e) {
+ fail("Failed to find com.android.tests.basic.StringGetter");
+ } catch (NoSuchMethodException e) {
+ if (shouldExist) {
+ fail("Did not find " + className + "." + methodName);
+ }
+ }
+ }
+
+}
+
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8c2efe0
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.basic.lib">
+ <application />
+</manifest>
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/main/java/com/android/tests/basic/StringGetter.java b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/main/java/com/android/tests/basic/StringGetter.java
new file mode 100644
index 0000000..d4ffa1b
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/lib/src/main/java/com/android/tests/basic/StringGetter.java
@@ -0,0 +1,31 @@
+package com.android.tests.basic;
+
+import com.android.tests.jarDep.JarDependencyUtil;
+
+public class StringGetter{
+
+ /**
+ * Public method that will not get obfuscated
+ */
+ public static String getString(int foo) {
+ return getStringInternal(foo);
+ }
+
+ /**
+ * Public method that will get obfuscated by the app project.
+ */
+ public static String getString2(int foo) {
+ return getStringInternal(foo);
+ }
+
+ /**
+ * method that will get obfuscated by the library.
+ */
+ public static String getStringInternal(int foo) {
+ return Integer.toString(foo);
+ }
+
+ public static String getStringFromJarDep() {
+ return JarDependencyUtil.getString();
+ }
+}
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/settings.gradle b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/settings.gradle
new file mode 100644
index 0000000..9a46a17
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/settings.gradle
@@ -0,0 +1,4 @@
+include 'app'
+include 'test'
+include 'lib'
+include 'jar'
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/build.gradle
new file mode 100644
index 0000000..4edf874
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/build.gradle
@@ -0,0 +1,9 @@
+apply plugin: 'com.android.test'
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion = rootProject.buildToolsVersion
+
+ targetProjectPath ':app'
+ targetVariant 'minified'
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..21a998c
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/src/main/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.basic.test">
+
+ <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.tests.basic"
+ android:handleProfiling="false"
+ android:functionalTest="false"
+ android:label="Tests for com.android.tests.basic"/>
+</manifest>
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/src/main/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/src/main/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..7c2efd4
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestModuleWithDependencies/test/src/main/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,39 @@
+package com.android.tests.basic;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.TextView;
+
+public class MainTest extends ActivityInstrumentationTestCase2<Main> {
+
+ private TextView mTextView;
+
+ /**
+ * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link Main} activity.
+ */
+ public MainTest() {
+ super(Main.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ final Main a = getActivity();
+ // ensure a valid handle to the activity has been returned
+ assertNotNull(a);
+ mTextView = (TextView) a.findViewById(R.id.text);
+
+ }
+
+ /**
+ * The name 'test preconditions' is a convention to signal that if this
+ * test doesn't pass, the test case was not set up properly and it might
+ * explain any and all failures in other tests. This is not guaranteed
+ * to run before other tests, as junit uses reflection to find the tests.
+ */
+ @MediumTest
+ public void testPreconditions() {
+ assertNotNull(mTextView);
+ }
+}
+
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/build.gradle
index 3bf0fa0..79e4473 100644
--- a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/build.gradle
+++ b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/build.gradle
@@ -16,6 +16,12 @@
apply plugin: 'com.android.application'
+apply from: "../../commonLocalRepo.gradle"
+
+dependencies {
+ compile 'com.google.code.findbugs:jsr305:1.3.9'
+}
+
android {
compileSdkVersion 21
buildToolsVersion = rootProject.ext.buildToolsVersion
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/src/main/java/com/android/tests/basic/Main.java b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/src/main/java/com/android/tests/basic/Main.java
index de80134..f1ae741 100644
--- a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/src/main/java/com/android/tests/basic/Main.java
+++ b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/app/src/main/java/com/android/tests/basic/Main.java
@@ -21,6 +21,8 @@
import android.os.Bundle;
import android.widget.TextView;
+import javax.annotation.Nullable;
+
public class Main extends Activity
{
/** Called when the activity is first created. */
@@ -29,7 +31,12 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView mTextView = (TextView) findViewById(R.id.text);
- mTextView.setText(getUtility().getValue());
+ mTextView.setText(getString());
+ }
+
+ @Nullable
+ private String getString() {
+ return getUtility().getValue();
}
public Utility getUtility() {
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/build.gradle
index 2d7fffe..df07319 100644
--- a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/build.gradle
+++ b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/build.gradle
@@ -16,3 +16,4 @@
apply from: "../commonHeader.gradle"
buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
+apply from: "../commonLocalRepo.gradle"
diff --git a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/test/build.gradle b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/test/build.gradle
index 07230d0..5b31aa0 100644
--- a/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/test/build.gradle
+++ b/build-system/integration-test/test-projects/separateTestModuleWithMinifiedApp/test/build.gradle
@@ -16,6 +16,8 @@
apply plugin: 'com.android.test'
+apply from: "../../commonLocalRepo.gradle"
+
android {
compileSdkVersion 19
buildToolsVersion = rootProject.buildToolsVersion
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/build.gradle b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/build.gradle
new file mode 100644
index 0000000..5509d41
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/build.gradle
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.application'
+
+apply from: "../../commonLocalRepo.gradle"
+
+dependencies {
+ compile 'com.google.code.findbugs:jsr305:1.3.9'
+}
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion = rootProject.ext.buildToolsVersion
+
+ publishNonDefault true
+
+ defaultConfig {
+ minSdkVersion 8
+ }
+
+ buildTypes {
+ minified.initWith(buildTypes.debug)
+ minified {
+ minifyEnabled true
+ useJack rootProject.ext.useJack
+ proguardFiles 'proguard.txt'
+ }
+ }
+}
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/proguard.txt b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/proguard.txt
new file mode 100644
index 0000000..30ccaf5
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/proguard.txt
@@ -0,0 +1,2 @@
+-dontobfuscate
+
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/androidTest/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/androidTest/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..54b6471
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/androidTest/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.basic;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.TextView;
+
+public class MainTest extends ActivityInstrumentationTestCase2<Main> {
+
+ private TextView mTextView;
+
+ /**
+ * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link Main} activity.
+ */
+ public MainTest() {
+ super(Main.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ final Main a = getActivity();
+ // ensure a valid handle to the activity has been returned
+ assertNotNull(a);
+ mTextView = (TextView) a.findViewById(R.id.text);
+
+ }
+
+ /**
+ * The name 'test preconditions' is a convention to signal that if this
+ * test doesn't pass, the test case was not set up properly and it might
+ * explain any and all failures in other tests. This is not guaranteed
+ * to run before other tests, as junit uses reflection to find the tests.
+ */
+ @MediumTest
+ public void testPreconditions() {
+ assertNotNull(mTextView);
+ }
+}
+
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..e65a22f
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.basic">
+ <application android:label="@string/app_name" android:icon="@drawable/icon">
+ <activity android:name=".Main"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/java/com/android/tests/basic/Main.java b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/java/com/android/tests/basic/Main.java
new file mode 100644
index 0000000..f1ae741
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/java/com/android/tests/basic/Main.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.basic;
+
+import com.android.tests.utils.Utility;
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+import javax.annotation.Nullable;
+
+public class Main extends Activity
+{
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ TextView mTextView = (TextView) findViewById(R.id.text);
+ mTextView.setText(getString());
+ }
+
+ @Nullable
+ private String getString() {
+ return getUtility().getValue();
+ }
+
+ public Utility getUtility() {
+ return new Utility();
+ }
+}
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/java/com/android/tests/utils/Utility.java b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/java/com/android/tests/utils/Utility.java
new file mode 100644
index 0000000..e3569bf
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/java/com/android/tests/utils/Utility.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.utils;
+
+public class Utility {
+
+ public String getValue() {
+ return this.getClass().getName() + " someValue";
+ }
+}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/drawable/icon.png
similarity index 100%
copy from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png
copy to build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/drawable/icon.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/layout/main.xml b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/layout/main.xml
new file mode 100644
index 0000000..2754a4c
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/layout/main.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+<TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Test App - Basic"
+ android:id="@+id/text"
+ />
+</LinearLayout>
+
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..026c1fc
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/main/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <string name="app_name">_Test-Basic</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/release/res/values/strings.xml b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/release/res/values/strings.xml
new file mode 100644
index 0000000..80a41e2
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/app/src/release/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <string name="app_name">_Test-Basic-Release</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/build.gradle b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/build.gradle
new file mode 100644
index 0000000..df07319
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/build.gradle
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
+apply from: "../commonLocalRepo.gradle"
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/settings.gradle b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/settings.gradle
new file mode 100644
index 0000000..6b1c0f8
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/settings.gradle
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+include 'app'
+include 'test'
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/build.gradle b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/build.gradle
new file mode 100644
index 0000000..5b31aa0
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/build.gradle
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.test'
+
+apply from: "../../commonLocalRepo.gradle"
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion = rootProject.buildToolsVersion
+
+ targetProjectPath ':app'
+ targetVariant 'minified'
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7d5d647
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/src/main/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.basic.test">
+
+
+ <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.tests.basic"
+ android:handleProfiling="false"
+ android:functionalTest="false"
+ android:label="Tests for com.android.tests.basic"/>
+
+</manifest>
diff --git a/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/src/main/java/com/android/tests/basic/MainTest.java b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/src/main/java/com/android/tests/basic/MainTest.java
new file mode 100644
index 0000000..42c4e26
--- /dev/null
+++ b/build-system/integration-test/test-projects/separateTestWithMinificationButNoObfuscation/test/src/main/java/com/android/tests/basic/MainTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.basic;
+
+import com.android.tests.utils.Utility;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.TextView;
+
+public class MainTest extends ActivityInstrumentationTestCase2<Main> {
+
+ private TextView mTextView;
+ private Main mMain;
+
+ /**
+ * Creates an {@link ActivityInstrumentationTestCase2} that tests the {@link Main} activity.
+ */
+ public MainTest() {
+ super(Main.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mMain = getActivity();
+ // ensure a valid handle to the activity has been returned
+ assertNotNull(mMain);
+ mTextView = (TextView) mMain.findViewById(R.id.text);
+
+ }
+
+ /**
+ * The name 'test preconditions' is a convention to signal that if this
+ * test doesn't pass, the test case was not set up properly and it might
+ * explain any and all failures in other tests. This is not guaranteed
+ * to run before other tests, as junit uses reflection to find the tests.
+ */
+ @MediumTest
+ public void testPreconditions() {
+ assertNotNull(mTextView);
+ assertEquals(mMain.getUtility().getValue(), mTextView.getText());
+ }
+}
+
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/build.gradle b/build-system/integration-test/test-projects/unitTestingComplexProject/app/build.gradle
new file mode 100644
index 0000000..66afaca
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/build.gradle
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.application'
+
+apply from: "../../commonLocalRepo.gradle"
+
+android {
+ // We need an android.jar that contains Java 6 bytecode, since Jenkins runs on Java 6.
+ compileSdkVersion 19
+ buildToolsVersion = rootProject.buildToolsVersion
+
+ testOptions {
+ unitTests.all {
+ systemProperty 'foo', 'bar'
+ }
+ }
+}
+
+dependencies {
+ compile project(':lib')
+
+ testCompile 'junit:junit:4.12'
+ testCompile 'org.mockito:mockito-core:1.9.5'
+ testCompile 'org.jdeferred:jdeferred-android-aar:1.2.3'
+ testCompile 'commons-logging:commons-logging:1.1.1'
+}
\ No newline at end of file
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..867ae14
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests">
+</manifest>
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/java/com/android/tests/Foo.java b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/java/com/android/tests/Foo.java
new file mode 100644
index 0000000..ff506f2
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/java/com/android/tests/Foo.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests;
+
+import com.android.tests.lib.LibFoo;
+
+public class Foo {
+ public String foo() {
+ return "production code";
+ }
+
+ public String callLibFoo() {
+ LibFoo libFoo = new LibFoo();
+ return libFoo.foo();
+ }
+}
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/java/com/android/tests/MainActivity.java b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/java/com/android/tests/MainActivity.java
new file mode 100644
index 0000000..8c1a956
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/java/com/android/tests/MainActivity.java
@@ -0,0 +1,5 @@
+package com.android.tests;
+
+import android.app.Activity;
+
+public class MainActivity extends Activity {}
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..01f019e
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">Unit tests</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/resources/prod_resource_file.txt b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/resources/prod_resource_file.txt
new file mode 100644
index 0000000..b551bb0
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/main/resources/prod_resource_file.txt
@@ -0,0 +1 @@
+prod
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/test/java/com/android/tests/UnitTest.java b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/test/java/com/android/tests/UnitTest.java
new file mode 100644
index 0000000..236751c
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/test/java/com/android/tests/UnitTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import com.android.tests.lib.LibFoo;
+
+import android.app.Activity;
+import android.app.Application;
+import android.bluetooth.BluetoothAdapter;
+import android.content.SyncResult;
+import android.content.SyncStats;
+import android.util.ArrayMap;
+import android.os.AsyncTask;
+import android.os.Debug;
+import android.os.PowerManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.io.InputStream;
+import java.net.URL;
+
+public class UnitTest {
+ @Test
+ public void referenceProductionCode() {
+ // Reference production code:
+ Foo foo = new Foo();
+ assertEquals("production code", foo.foo());
+ }
+
+ @Test
+ public void mockFinalMethod() {
+ Activity activity = mock(Activity.class);
+ Application app = mock(Application.class);
+ when(activity.getApplication()).thenReturn(app);
+
+ assertSame(app, activity.getApplication());
+
+ verify(activity).getApplication();
+ verifyNoMoreInteractions(activity);
+ }
+
+ @Test
+ public void mockFinalClass() {
+ BluetoothAdapter adapter = mock(BluetoothAdapter.class);
+ when(adapter.isEnabled()).thenReturn(true);
+
+ assertTrue(adapter.isEnabled());
+
+ verify(adapter).isEnabled();
+ verifyNoMoreInteractions(adapter);
+ }
+
+ @Test
+ public void mockInnerClass() throws Exception {
+ PowerManager.WakeLock wakeLock = mock(PowerManager.WakeLock.class);
+ when(wakeLock.isHeld()).thenReturn(true);
+ assertTrue(wakeLock.isHeld());
+ }
+
+ @Test
+ public void aarDependencies() throws Exception {
+ org.jdeferred.Deferred<Integer, Integer, Integer> deferred =
+ new org.jdeferred.impl.DeferredObject<Integer, Integer, Integer>();
+ org.jdeferred.Promise promise = deferred.promise();
+ deferred.resolve(42);
+ assertTrue(promise.isResolved());
+ }
+
+ @Test
+ public void exceptions() {
+ try {
+ ArrayMap map = new ArrayMap();
+ map.isEmpty();
+ fail();
+ } catch (RuntimeException e) {
+ assertEquals(RuntimeException.class, e.getClass());
+ assertTrue(e.getMessage().contains("isEmpty"));
+ assertTrue(e.getMessage().contains("not mocked"));
+ assertTrue(e.getMessage().contains("androidstudio/not-mocked"));
+ }
+
+ try {
+ Debug.getThreadAllocCount();
+ fail();
+ } catch (RuntimeException e) {
+ assertEquals(RuntimeException.class, e.getClass());
+ assertTrue(e.getMessage().contains("getThreadAllocCount"));
+ assertTrue(e.getMessage().contains("not mocked"));
+ assertTrue(e.getMessage().contains("androidstudio/not-mocked"));
+ }
+
+ }
+
+ @Test
+ public void enums() throws Exception {
+ assertNotNull(AsyncTask.Status.RUNNING);
+ assertNotEquals(AsyncTask.Status.RUNNING, AsyncTask.Status.FINISHED);
+
+ assertEquals(AsyncTask.Status.FINISHED, AsyncTask.Status.valueOf("FINISHED"));
+ assertEquals(1, AsyncTask.Status.PENDING.ordinal());
+ assertEquals("RUNNING", AsyncTask.Status.RUNNING.name());
+
+ assertEquals(AsyncTask.Status.RUNNING, Enum.valueOf(AsyncTask.Status.class, "RUNNING"));
+
+ AsyncTask.Status[] values = AsyncTask.Status.values();
+ assertEquals(3, values.length);
+ assertEquals(AsyncTask.Status.FINISHED, values[0]);
+ assertEquals(AsyncTask.Status.PENDING, values[1]);
+ assertEquals(AsyncTask.Status.RUNNING, values[2]);
+ }
+
+ @Test
+ public void instanceFields() throws Exception {
+ SyncResult result = mock(SyncResult.class);
+ Field statsField = result.getClass().getField("stats");
+ SyncStats syncStats = mock(SyncStats.class);
+ statsField.set(result, syncStats);
+
+ syncStats.numDeletes = 42;
+ assertEquals(42, result.stats.numDeletes);
+ }
+
+ @Test
+ public void javaResourcesOnClasspath() throws Exception {
+ URL url = UnitTest.class.getClassLoader().getResource("resource_file.txt");
+ assertNotNull(url);
+
+ InputStream stream = UnitTest.class.getClassLoader().getResourceAsStream("resource_file.txt");
+ assertNotNull(stream);
+ byte[] line = new byte[1024];
+ assertTrue("Expected >0 bytes read from input stream", stream.read(line) > 0);
+ String s = new String(line, "UTF-8").trim();
+ assertEquals("success", s);
+ }
+
+ @Test
+ public void prodJavaResourcesOnClasspath() throws Exception {
+ URL url = UnitTest.class.getClassLoader().getResource("prod_resource_file.txt");
+ assertNotNull(url);
+
+ InputStream stream = UnitTest.class.getClassLoader().getResourceAsStream("prod_resource_file.txt");
+ assertNotNull(stream);
+ byte[] line = new byte[1024];
+ assertTrue("Expected >0 bytes read from input stream", stream.read(line) > 0);
+ String s = new String(line, "UTF-8").trim();
+ assertEquals("prod", s);
+ }
+
+ @Test
+ public void libJavaResourcesOnClasspath() throws Exception {
+ URL url = UnitTest.class.getClassLoader().getResource("lib_prod_resource_file.txt");
+ assertNotNull(url);
+
+ InputStream stream = UnitTest.class.getClassLoader().getResourceAsStream("lib_prod_resource_file.txt");
+ assertNotNull(stream);
+ byte[] line = new byte[1024];
+ assertTrue("Expected >0 bytes read from input stream", stream.read(line) > 0);
+ String s = new String(line, "UTF-8").trim();
+ assertEquals("lib prod", s);
+ }
+
+ @Test
+ public void testLibJavaResourcesNotOnClasspath() throws Exception {
+ URL url = UnitTest.class.getClassLoader().getResource("lib_resource_file.txt");
+ assertNull(url);
+
+ InputStream stream = UnitTest.class.getClassLoader().getResourceAsStream("lib_resource_file.txt");
+ assertNull(stream);
+ }
+
+ @Test
+ public void prodRClass() {
+ int id = R.string.app_name;
+ assertTrue(id > 0);
+ }
+
+ @Test
+ @Ignore
+ public void thisIsIgnored() {
+ // Just excercise more JUnit features.
+ }
+
+ @Test
+ public void taskConfiguration() {
+ // This property is set in build.gradle:
+ assertEquals("bar", System.getProperty("foo"));
+ }
+
+ @Test
+ public void commonsLogging() {
+ Log log = LogFactory.getLog(getClass());
+ log.info("I can use commons-logging!");
+ }
+
+ @Test
+ public void libraryCode() {
+ LibFoo libFoo = new LibFoo();
+ assertEquals("library code", libFoo.foo());
+
+ Foo foo = new Foo();
+ assertEquals("library code", foo.callLibFoo());
+ }
+}
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/test/resources/resource_file.txt b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/test/resources/resource_file.txt
new file mode 100644
index 0000000..2e9ba47
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/app/src/test/resources/resource_file.txt
@@ -0,0 +1 @@
+success
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/build.gradle b/build-system/integration-test/test-projects/unitTestingComplexProject/build.gradle
new file mode 100644
index 0000000..ea987b9
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/build.gradle
@@ -0,0 +1,3 @@
+apply from: "../commonHeader.gradle"
+buildscript { apply from: "../commonBuildScript.gradle", to: buildscript }
+
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/lib/build.gradle b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/build.gradle
new file mode 100644
index 0000000..27abd09
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/build.gradle
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.library'
+
+apply from: "../../commonLocalRepo.gradle"
+
+android {
+ // We need an android.jar that contains Java 6 bytecode, since Jenkins runs on Java 6.
+ compileSdkVersion 19
+ buildToolsVersion = rootProject.buildToolsVersion
+
+ testOptions {
+ unitTests.all {
+ systemProperty 'foo', 'bar'
+ }
+ }
+}
+
+dependencies {
+ testCompile 'junit:junit:4.12'
+ testCompile 'org.mockito:mockito-core:1.9.5'
+ testCompile 'org.jdeferred:jdeferred-android-aar:1.2.3'
+ testCompile 'commons-logging:commons-logging:1.1.1'
+}
+
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4577858
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.lib">
+</manifest>
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/java/com/android/tests/lib/LibFoo.java b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/java/com/android/tests/lib/LibFoo.java
new file mode 100644
index 0000000..3cdccde
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/java/com/android/tests/lib/LibFoo.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.lib;
+
+public class LibFoo {
+ public String foo() {
+ return "library code";
+ }
+}
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/res/values/strings.xml
new file mode 100644
index 0000000..01f019e
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">Unit tests</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/resources/lib_prod_resource_file.txt b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/resources/lib_prod_resource_file.txt
new file mode 100644
index 0000000..9537601
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/main/resources/lib_prod_resource_file.txt
@@ -0,0 +1 @@
+lib prod
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/test/java/com/android/tests/lib/UnitTest.java b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/test/java/com/android/tests/lib/UnitTest.java
new file mode 100644
index 0000000..68f968c
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/test/java/com/android/tests/lib/UnitTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.lib;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.app.Activity;
+import android.app.Application;
+import android.bluetooth.BluetoothAdapter;
+import android.content.SyncResult;
+import android.content.SyncStats;
+import android.util.ArrayMap;
+import android.os.AsyncTask;
+import android.os.Debug;
+import android.os.PowerManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.io.InputStream;
+import java.net.URL;
+
+public class UnitTest {
+ @Test
+ public void referenceProductionCode() {
+ // Reference production code:
+ LibFoo foo = new LibFoo();
+ assertEquals("library code", foo.foo());
+ }
+
+ @Test
+ public void mockFinalMethod() {
+ Activity activity = mock(Activity.class);
+ Application app = mock(Application.class);
+ when(activity.getApplication()).thenReturn(app);
+
+ assertSame(app, activity.getApplication());
+
+ verify(activity).getApplication();
+ verifyNoMoreInteractions(activity);
+ }
+
+ @Test
+ public void mockFinalClass() {
+ BluetoothAdapter adapter = mock(BluetoothAdapter.class);
+ when(adapter.isEnabled()).thenReturn(true);
+
+ assertTrue(adapter.isEnabled());
+
+ verify(adapter).isEnabled();
+ verifyNoMoreInteractions(adapter);
+ }
+
+ @Test
+ public void mockInnerClass() throws Exception {
+ PowerManager.WakeLock wakeLock = mock(PowerManager.WakeLock.class);
+ when(wakeLock.isHeld()).thenReturn(true);
+ assertTrue(wakeLock.isHeld());
+ }
+
+ @Test
+ public void aarDependencies() throws Exception {
+ org.jdeferred.Deferred<Integer, Integer, Integer> deferred =
+ new org.jdeferred.impl.DeferredObject<Integer, Integer, Integer>();
+ org.jdeferred.Promise promise = deferred.promise();
+ deferred.resolve(42);
+ assertTrue(promise.isResolved());
+ }
+
+ @Test
+ public void exceptions() {
+ try {
+ ArrayMap map = new ArrayMap();
+ map.isEmpty();
+ fail();
+ } catch (RuntimeException e) {
+ assertEquals(RuntimeException.class, e.getClass());
+ assertTrue(e.getMessage().contains("isEmpty"));
+ assertTrue(e.getMessage().contains("not mocked"));
+ assertTrue(e.getMessage().contains("androidstudio/not-mocked"));
+ }
+
+ try {
+ Debug.getThreadAllocCount();
+ fail();
+ } catch (RuntimeException e) {
+ assertEquals(RuntimeException.class, e.getClass());
+ assertTrue(e.getMessage().contains("getThreadAllocCount"));
+ assertTrue(e.getMessage().contains("not mocked"));
+ assertTrue(e.getMessage().contains("androidstudio/not-mocked"));
+ }
+
+ }
+
+ @Test
+ public void enums() throws Exception {
+ assertNotNull(AsyncTask.Status.RUNNING);
+ assertNotEquals(AsyncTask.Status.RUNNING, AsyncTask.Status.FINISHED);
+
+ assertEquals(AsyncTask.Status.FINISHED, AsyncTask.Status.valueOf("FINISHED"));
+ assertEquals(1, AsyncTask.Status.PENDING.ordinal());
+ assertEquals("RUNNING", AsyncTask.Status.RUNNING.name());
+
+ assertEquals(AsyncTask.Status.RUNNING, Enum.valueOf(AsyncTask.Status.class, "RUNNING"));
+
+ AsyncTask.Status[] values = AsyncTask.Status.values();
+ assertEquals(3, values.length);
+ assertEquals(AsyncTask.Status.FINISHED, values[0]);
+ assertEquals(AsyncTask.Status.PENDING, values[1]);
+ assertEquals(AsyncTask.Status.RUNNING, values[2]);
+ }
+
+ @Test
+ public void instanceFields() throws Exception {
+ SyncResult result = mock(SyncResult.class);
+ Field statsField = result.getClass().getField("stats");
+ SyncStats syncStats = mock(SyncStats.class);
+ statsField.set(result, syncStats);
+
+ syncStats.numDeletes = 42;
+ assertEquals(42, result.stats.numDeletes);
+ }
+
+ @Test
+ public void javaResourcesOnClasspath() throws Exception {
+ URL url = UnitTest.class.getClassLoader().getResource("lib_resource_file.txt");
+ assertNotNull(url);
+
+ InputStream stream = UnitTest.class.getClassLoader().getResourceAsStream("lib_resource_file.txt");
+ assertNotNull(stream);
+ byte[] line = new byte[1024];
+ assertTrue("Expected >0 bytes read from input stream", stream.read(line) > 0);
+ String s = new String(line, "UTF-8").trim();
+ assertEquals("success", s);
+ }
+
+ @Test
+ public void prodJavaResourcesOnClasspath() throws Exception {
+ URL url = UnitTest.class.getClassLoader().getResource("lib_prod_resource_file.txt");
+ assertNotNull(url);
+
+ InputStream stream = UnitTest.class.getClassLoader().getResourceAsStream("lib_prod_resource_file.txt");
+ assertNotNull(stream);
+ byte[] line = new byte[1024];
+ assertTrue("Expected >0 bytes read from input stream", stream.read(line) > 0);
+ String s = new String(line, "UTF-8").trim();
+ assertEquals("lib prod", s);
+ }
+
+ @Test
+ public void prodRClass() {
+ int id = R.string.app_name;
+ assertTrue(id > 0);
+ }
+
+ @Test
+ @Ignore
+ public void thisIsIgnored() {
+ // Just excercise more JUnit features.
+ }
+
+ @Test
+ public void taskConfiguration() {
+ // This property is set in build.gradle:
+ assertEquals("bar", System.getProperty("foo"));
+ }
+
+ @Test
+ public void commonsLogging() {
+ Log log = LogFactory.getLog(getClass());
+ log.info("I can use commons-logging!");
+ }
+}
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/test/resources/lib_resource_file.txt b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/test/resources/lib_resource_file.txt
new file mode 100644
index 0000000..2e9ba47
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/lib/src/test/resources/lib_resource_file.txt
@@ -0,0 +1 @@
+success
diff --git a/build-system/integration-test/test-projects/unitTestingComplexProject/settings.gradle b/build-system/integration-test/test-projects/unitTestingComplexProject/settings.gradle
new file mode 100644
index 0000000..2f4a882
--- /dev/null
+++ b/build-system/integration-test/test-projects/unitTestingComplexProject/settings.gradle
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+include 'app'
+include 'lib'
diff --git a/build-system/integration-test/test-projects/vectorDrawables/build.gradle b/build-system/integration-test/test-projects/vectorDrawables/build.gradle
index 238f69a..10a58e8 100644
--- a/build-system/integration-test/test-projects/vectorDrawables/build.gradle
+++ b/build-system/integration-test/test-projects/vectorDrawables/build.gradle
@@ -13,5 +13,11 @@
minSdkVersion 19
}
- preprocessResources = true
+ preprocessingOptions {
+ densities = ["hdpi"]
+ densities += "xhdpi"
+ }
+
+ // Don't modify files when merging.
+ aaptOptions.cruncherEnabled = false
}
diff --git a/build-system/integration-test/test-projects/vectorDrawables/gradle.properties b/build-system/integration-test/test-projects/vectorDrawables/gradle.properties
new file mode 100644
index 0000000..bbf0dc8
--- /dev/null
+++ b/build-system/integration-test/test-projects/vectorDrawables/gradle.properties
@@ -0,0 +1 @@
+com.android.build.gradle.experimentalPreprocessResources=true
diff --git a/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-fr/french_heart.xml b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-fr/french_heart.xml
new file mode 100644
index 0000000..8419d57
--- /dev/null
+++ b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-fr/french_heart.xml
@@ -0,0 +1,17 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="256dp"
+ android:width="256dp"
+ android:viewportWidth="32"
+ android:viewportHeight="32">
+
+ <!-- draw a path -->
+ <path
+ android:fillColor="#00ff00"
+ android:pathData="M20.5,9.5
+ c-1.965,0,-3.83,1.268,-4.5,3
+ c-0.17,-1.732,-2.547,-3,-4.5,-3
+ C8.957,9.5,7,11.432,7,14
+ c0,3.53,3.793,6.257,9,11.5
+ c5.207,-5.242,9,-7.97,9,-11.5
+ C25,11.432,23.043,9.5,20.5,9.5z" />
+</vector>
diff --git a/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-hdpi/special_heart.png b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-hdpi/special_heart.png
new file mode 100644
index 0000000..954fb1c
--- /dev/null
+++ b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-hdpi/special_heart.png
Binary files differ
diff --git a/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-v16/modern_heart.xml b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-v16/modern_heart.xml
new file mode 100644
index 0000000..5af0e5a
--- /dev/null
+++ b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-v16/modern_heart.xml
@@ -0,0 +1,17 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="256dp"
+ android:width="256dp"
+ android:viewportWidth="32"
+ android:viewportHeight="32">
+
+ <!-- draw a path -->
+ <path
+ android:fillColor="#ff0000"
+ android:pathData="M20.5,9.5
+ c-1.965,0,-3.83,1.268,-4.5,3
+ c-0.17,-1.732,-2.547,-3,-4.5,-3
+ C8.957,9.5,7,11.432,7,14
+ c0,3.53,3.793,6.257,9,11.5
+ c5.207,-5.242,9,-7.97,9,-11.5
+ C25,11.432,23.043,9.5,20.5,9.5z" />
+</vector>
diff --git a/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-v22/no_need.xml b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-v22/no_need.xml
new file mode 100644
index 0000000..5af0e5a
--- /dev/null
+++ b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable-v22/no_need.xml
@@ -0,0 +1,17 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="256dp"
+ android:width="256dp"
+ android:viewportWidth="32"
+ android:viewportHeight="32">
+
+ <!-- draw a path -->
+ <path
+ android:fillColor="#ff0000"
+ android:pathData="M20.5,9.5
+ c-1.965,0,-3.83,1.268,-4.5,3
+ c-0.17,-1.732,-2.547,-3,-4.5,-3
+ C8.957,9.5,7,11.432,7,14
+ c0,3.53,3.793,6.257,9,11.5
+ c5.207,-5.242,9,-7.97,9,-11.5
+ C25,11.432,23.043,9.5,20.5,9.5z" />
+</vector>
diff --git a/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable/special_heart.xml b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable/special_heart.xml
new file mode 100644
index 0000000..5af0e5a
--- /dev/null
+++ b/build-system/integration-test/test-projects/vectorDrawables/src/main/res/drawable/special_heart.xml
@@ -0,0 +1,17 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="256dp"
+ android:width="256dp"
+ android:viewportWidth="32"
+ android:viewportHeight="32">
+
+ <!-- draw a path -->
+ <path
+ android:fillColor="#ff0000"
+ android:pathData="M20.5,9.5
+ c-1.965,0,-3.83,1.268,-4.5,3
+ c-0.17,-1.732,-2.547,-3,-4.5,-3
+ C8.957,9.5,7,11.432,7,14
+ c0,3.53,3.793,6.257,9,11.5
+ c5.207,-5.242,9,-7.97,9,-11.5
+ C25,11.432,23.043,9.5,20.5,9.5z" />
+</vector>
diff --git a/build-system/manifest-merger/build.gradle b/build-system/manifest-merger/build.gradle
index 4ba6a01..3ca48a0 100644
--- a/build-system/manifest-merger/build.gradle
+++ b/build-system/manifest-merger/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
+apply plugin: 'jacoco'
apply plugin: 'sdk-java-lib'
evaluationDependsOn(':base:sdklib')
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/ActionRecorder.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/ActionRecorder.java
index a8b815d..a194534 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/ActionRecorder.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/ActionRecorder.java
@@ -21,7 +21,8 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.concurrency.GuardedBy;
-import com.android.utils.PositionXmlParser;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
@@ -57,7 +58,7 @@
* <ul>
* <li>{@link com.android.manifmerger.Actions.ActionType} to identify whether the action
* applies to an attribute or an element.</li>
- * <li>{@link com.android.manifmerger.Actions.ActionLocation} to identify the source xml
+ * <li>{@link com.android.ide.common.blame.SourceFilePosition} to identify the source xml
* location for the node.</li>
* </ul>
*
@@ -123,8 +124,8 @@
mRecords.put(storageKey, nodeDecisionTree);
}
Actions.NodeRecord record = new Actions.NodeRecord(Actions.ActionType.IMPLIED,
- new Actions.ActionLocation(
- xmlElement.getDocument().getSourceLocation(),
+ new SourceFilePosition(
+ xmlElement.getDocument().getSourceFile(),
xmlElement.getDocument().getRootNode().getPosition()),
xmlElement.getOriginalId(),
reason,
@@ -159,8 +160,8 @@
XmlElement targetElement) {
Actions.NodeRecord record = new Actions.NodeRecord(actionType,
- new Actions.ActionLocation(
- targetElement.getDocument().getSourceLocation(),
+ new SourceFilePosition(
+ targetElement.getDocument().getSourceFile(),
targetElement.getPosition()),
targetElement.getOriginalId(),
null, /* reason */
@@ -215,15 +216,15 @@
*/
synchronized void recordAttributeAction(
@NonNull XmlAttribute attribute,
- @NonNull PositionXmlParser.Position attributePosition,
+ @NonNull SourcePosition attributePosition,
@NonNull Actions.ActionType actionType,
@Nullable AttributeOperationType attributeOperationType) {
XmlElement originElement = attribute.getOwnerElement();
Actions.AttributeRecord attributeRecord = new Actions.AttributeRecord(
actionType,
- new Actions.ActionLocation(
- originElement.getDocument().getSourceLocation(),
+ new SourceFilePosition(
+ originElement.getDocument().getSourceFile(),
attributePosition),
attribute.getOriginalId(),
null, /* reason */
@@ -260,8 +261,8 @@
List<Actions.AttributeRecord> attributeRecords = getAttributeRecords(attribute);
Actions.AttributeRecord attributeRecord = new Actions.AttributeRecord(
Actions.ActionType.REJECTED,
- new Actions.ActionLocation(
- implicitAttributeOwner.getDocument().getSourceLocation(),
+ new SourceFilePosition(
+ implicitAttributeOwner.getDocument().getSourceFile(),
implicitAttributeOwner.getPosition()),
attribute.getOriginalId(),
null, /* reason */
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/Actions.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/Actions.java
index 71544a1..79c8064 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/Actions.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/Actions.java
@@ -20,8 +20,10 @@
import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
import com.android.annotations.concurrency.Immutable;
+import com.android.ide.common.blame.MessageJsonSerializer;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
import com.android.utils.ILogger;
-import com.android.utils.PositionXmlParser;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
@@ -33,10 +35,7 @@
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
import org.xml.sax.SAXException;
@@ -192,12 +191,12 @@
public abstract static class Record {
@NonNull protected final ActionType mActionType;
- @NonNull protected final ActionLocation mActionLocation;
+ @NonNull protected final SourceFilePosition mActionLocation;
@NonNull protected final XmlNode.NodeKey mTargetId;
@Nullable protected final String mReason;
private Record(@NonNull ActionType actionType,
- @NonNull ActionLocation actionLocation,
+ @NonNull SourceFilePosition actionLocation,
@NonNull XmlNode.NodeKey targetId,
@Nullable String reason) {
mActionType = Preconditions.checkNotNull(actionType);
@@ -210,7 +209,7 @@
return mActionType;
}
- public ActionLocation getActionLocation() {
+ public SourceFilePosition getActionLocation() {
return mActionLocation;
}
@@ -237,7 +236,7 @@
private final NodeOperationType mNodeOperationType;
NodeRecord(@NonNull ActionType actionType,
- @NonNull ActionLocation actionLocation,
+ @NonNull SourceFilePosition actionLocation,
@NonNull XmlNode.NodeKey targetId,
@Nullable String reason,
@NonNull NodeOperationType nodeOperationType) {
@@ -264,7 +263,7 @@
AttributeRecord(
@NonNull ActionType actionType,
- @NonNull ActionLocation actionLocation,
+ @NonNull SourceFilePosition actionLocation,
@NonNull XmlNode.NodeKey targetId,
@Nullable String reason,
@Nullable AttributeOperationType operationType) {
@@ -285,80 +284,10 @@
}
}
- /**
- * Defines an action location which is composed of a pointer to the source location (e.g. a
- * file) and a position within that source location.
- */
- public static final class ActionLocation {
-
- private final XmlLoader.SourceLocation mSourceLocation;
-
- private final PositionXmlParser.Position mPosition;
-
- public ActionLocation(@NonNull XmlLoader.SourceLocation sourceLocation,
- @NonNull PositionXmlParser.Position position) {
- mSourceLocation = Preconditions.checkNotNull(sourceLocation);
- mPosition = Preconditions.checkNotNull(position);
- }
-
- public PositionXmlParser.Position getPosition() {
- return mPosition;
- }
-
- public XmlLoader.SourceLocation getSourceLocation() {
- return mSourceLocation;
- }
-
- @Override
- public String toString() {
- String toString = mSourceLocation.print(true);
- if (mPosition != null) {
- toString += ":" + mPosition.getLine() + ":" + mPosition.getColumn();
- }
- return toString;
- }
-
- private static final class ActionLocationAdapter
- implements JsonSerializer<ActionLocation>, JsonDeserializer<ActionLocation> {
-
- private static final String POSITION_ELEMENT = "position";
-
- private static final String LINE_ATTRIBUTE = "line";
-
- private static final String COLUMN_ATTRIBUTE = "col";
-
- private static final String OFFSET_ATTRIBUTE = "offset";
-
- @Override
- public ActionLocation deserialize(JsonElement json, Type typeOfT,
- JsonDeserializationContext context) throws JsonParseException {
- XmlLoader.SourceLocation sourceLocation =
- context.deserialize(json.getAsJsonObject().get(POSITION_ELEMENT),
- XmlLoader.SourceLocation.class);
- PositionImpl position = new PositionImpl(
- json.getAsJsonObject().get(LINE_ATTRIBUTE).getAsInt(),
- json.getAsJsonObject().get(COLUMN_ATTRIBUTE).getAsInt(),
- json.getAsJsonObject().get(OFFSET_ATTRIBUTE).getAsInt());
- return new ActionLocation(sourceLocation, position);
- }
-
- @Override
- public JsonElement serialize(ActionLocation src, Type typeOfSrc,
- JsonSerializationContext context) {
- JsonObject jsonObject = new JsonObject();
- jsonObject.add(POSITION_ELEMENT, context.serialize(src.getSourceLocation()));
- jsonObject.addProperty(LINE_ATTRIBUTE, src.getPosition().getLine());
- jsonObject.addProperty(COLUMN_ATTRIBUTE, src.getPosition().getColumn());
- jsonObject.addProperty(OFFSET_ATTRIBUTE, src.getPosition().getOffset());
- return jsonObject;
- }
- }
- }
-
public String persist() throws IOException {
GsonBuilder gson = new GsonBuilder().setPrettyPrinting();
gson.enableComplexMapKeySerialization();
- gson.registerTypeAdapter(ActionLocation.class, new ActionLocation.ActionLocationAdapter());
+ MessageJsonSerializer.registerTypeAdapters(gson);
return gson.create().toJson(this);
}
@@ -392,14 +321,14 @@
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.enableComplexMapKeySerialization();
gsonBuilder.registerTypeAdapter(XmlNode.NodeName.class, new NodeNameDeserializer());
- gsonBuilder.registerTypeAdapter(ActionLocation.class, new ActionLocation.ActionLocationAdapter());
+ MessageJsonSerializer.registerTypeAdapters(gsonBuilder);
return gsonBuilder.create();
}
public ImmutableMultimap<Integer, Record> getResultingSourceMapping(XmlDocument xmlDocument)
throws ParserConfigurationException, SAXException, IOException {
- XmlLoader.SourceLocation inMemory = XmlLoader.UNKNOWN;
+ SourceFile inMemory = SourceFile.UNKNOWN;
XmlDocument loadedWithLineNumbers = XmlLoader.load(
xmlDocument.getSelectors(),
@@ -422,13 +351,13 @@
if (decisionTreeRecord != null) {
Actions.NodeRecord nodeRecord = findNodeRecord(decisionTreeRecord);
if (nodeRecord != null) {
- mappings.put(element.getPosition().getLine(), nodeRecord);
+ mappings.put(element.getPosition().getStartLine(), nodeRecord);
}
for (XmlAttribute xmlAttribute : element.getAttributes()) {
Actions.AttributeRecord attributeRecord = findAttributeRecord(decisionTreeRecord,
xmlAttribute);
if (attributeRecord != null) {
- mappings.put(xmlAttribute.getPosition().getLine(), attributeRecord);
+ mappings.put(xmlAttribute.getPosition().getStartLine(), attributeRecord);
}
}
}
@@ -447,12 +376,12 @@
StringBuilder actualMappings = new StringBuilder();
String line;
- int count = 1;
+ int count = 0;
while ((line = lineReader.readLine()) != null) {
- actualMappings.append(count).append(line).append("\n");
+ actualMappings.append(count + 1).append(line).append("\n");
if (resultingSourceMapping.containsKey(count)) {
for (Record record : resultingSourceMapping.get(count)) {
- actualMappings.append(count).append("-->")
+ actualMappings.append(count + 1).append("-->")
.append(record.getActionLocation().toString())
.append("\n");
}
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger.java
index c4f95e5..4d35de4 100755
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger.java
@@ -482,7 +482,7 @@
pkg = manifest.getAttribute("package");
}
- if (pkg == null || pkg.length() == 0) {
+ if (pkg == null || pkg.isEmpty()) {
// We can't adjust FQCNs if we don't know the root package name.
// It's not a proper manifest if this is missing anyway.
assert manifest != null;
@@ -509,7 +509,7 @@
// We know it's a shortened FQCN if it starts with a dot
// or does not contain any dot.
- if (value != null && value.length() > 0 &&
+ if (value != null && !value.isEmpty() &&
(value.indexOf('.') == -1 || value.charAt(0) == '.')) {
if (value.charAt(0) == '.') {
value = pkg + value;
@@ -540,7 +540,7 @@
pkg = manifest.getAttribute("package");
}
- if (pkg == null || pkg.length() == 0) {
+ if (pkg == null || pkg.isEmpty()) {
return;
}
@@ -602,7 +602,7 @@
for (String attrName : new String[] { "name", "backupAgent" }) {
String libValue = getAttributeValue(libApp, attrName);
- if (libValue == null || libValue.length() == 0) {
+ if (libValue == null || libValue.isEmpty()) {
// Nothing to do if the attribute is not defined in the lib.
continue;
}
@@ -712,7 +712,7 @@
nextSource: for (Element src : findElements(libDoc, path)) {
String name = getAttributeValue(src, keyAttr);
- if (name.length() == 0) {
+ if (name.isEmpty()) {
mLog.error(Severity.ERROR,
xmlFileAndLine(src),
"Undefined '%1$s' attribute in %2$s.",
@@ -832,11 +832,11 @@
for (Element src : findElements(libDoc, path)) {
Attr attr = src.getAttributeNodeNS(NS_URI, keyAttr);
String name = attr == null ? "" : attr.getNodeValue().trim(); //$NON-NLS-1$
- if (name.length() == 0) {
+ if (name.isEmpty()) {
if (alternateKeyAttr != null) {
attr = src.getAttributeNodeNS(NS_URI, alternateKeyAttr);
String s = attr == null ? "" : attr.getNodeValue().trim(); //$NON-NLS-1$
- if (s.length() != 0) {
+ if (!s.isEmpty()) {
// This element lacks the keyAttr but has the alternateKeyAttr. Skip it.
continue;
}
@@ -859,7 +859,7 @@
"Manifest has more than one %1$s[@%2$s=%3$s] element.",
path, keyAttr, name);
}
- if (dests.size() > 0) {
+ if (!dests.isEmpty()) {
attr = src.getAttributeNodeNS(NS_URI, requiredAttr);
String value = attr == null ? "true" : attr.getNodeValue(); //$NON-NLS-1$
@@ -987,7 +987,7 @@
for (Element dest : findElements(mMainDoc, path)) {
Attr attr = dest.getAttributeNodeNS(NS_URI, keyAttr);
String value = attr == null ? "" : attr.getNodeValue().trim(); //$NON-NLS-1$
- if (value.length() != 0) {
+ if (!value.isEmpty()) {
try {
// Note that the value can be an hex number such as 0x00020001 so we
// need Integer.decode instead of Integer.parseInt.
@@ -1030,7 +1030,7 @@
for (Element src : findElements(libDoc, path)) {
Attr attr = src.getAttributeNodeNS(NS_URI, keyAttr);
String value = attr == null ? "" : attr.getNodeValue().trim(); //$NON-NLS-1$
- if (value.length() != 0) {
+ if (!value.isEmpty()) {
try {
// See comment on Long.decode above.
long version = Long.decode(value);
@@ -1195,7 +1195,7 @@
assert s != null;
s = s.trim();
try {
- if (s.length() > 0) {
+ if (!s.isEmpty()) {
destValue.set(Integer.parseInt(s));
destImplied.set(false);
}
@@ -1228,7 +1228,7 @@
assert s != null;
s = s.trim();
try {
- if (s.length() > 0) {
+ if (!s.isEmpty()) {
srcValue.set(Integer.parseInt(s));
srcImplied.set(false);
}
@@ -1277,7 +1277,7 @@
short t = prev.getNodeType();
if (t == Node.TEXT_NODE) {
String text = prev.getNodeValue();
- if (text == null || text.trim().length() != 0) {
+ if (text == null || !text.trim().isEmpty()) {
// Not whitespace, we don't want it.
break;
}
@@ -1322,7 +1322,7 @@
while (target != null) {
if (target.getNodeType() == Node.TEXT_NODE) {
String text = target.getNodeValue();
- if (text == null || text.trim().length() != 0) {
+ if (text == null || !text.trim().isEmpty()) {
// Not whitespace, insert after.
break;
}
@@ -1599,7 +1599,7 @@
root.removeChild(child);
// If there's some whitespace just before that element, clean it up too.
while (prev != null && prev.getNodeType() == Node.TEXT_NODE) {
- if (prev.getNodeValue().trim().length() == 0) {
+ if (prev.getNodeValue().trim().isEmpty()) {
Node prevPrev = prev.getPreviousSibling();
root.removeChild(prev);
prev = prevPrev;
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger2.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger2.java
index 99f0ada..c75e715 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger2.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger2.java
@@ -24,6 +24,8 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.concurrency.Immutable;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
import com.android.utils.ILogger;
import com.android.utils.Pair;
import com.android.utils.SdkUtils;
@@ -122,26 +124,16 @@
loadedMainManifestInfo.getXmlDocument().getPackage();
if (!mainPackageAttribute.isPresent()) {
mergingReportBuilder.addMessage(
- loadedMainManifestInfo.getXmlDocument().getSourceLocation(), 0, 0,
+ loadedMainManifestInfo.getXmlDocument().getSourceFile(),
MergingReport.Record.Severity.ERROR,
String.format(
"Main AndroidManifest.xml at %1$s manifest:package attribute "
+ "is not declared",
- loadedMainManifestInfo.getXmlDocument().getSourceLocation()
+ loadedMainManifestInfo.getXmlDocument().getSourceFile()
.print(true)));
return mergingReportBuilder.build();
}
- // check for placeholders presence.
- Map<String, Object> finalPlaceHolderValues = mPlaceHolderValues;
- if (!mPlaceHolderValues.containsKey(APPLICATION_ID)) {
- finalPlaceHolderValues =
- ImmutableMap.<String, Object>builder().putAll(mPlaceHolderValues)
- .put(PACKAGE_NAME, mainPackageAttribute.get().getValue())
- .put(APPLICATION_ID, mainPackageAttribute.get().getValue())
- .build();
- }
-
// perform system property injection
performSystemPropertiesInjection(mergingReportBuilder,
loadedMainManifestInfo.getXmlDocument());
@@ -187,7 +179,7 @@
packageAttribute.get().getValue(),
mainPackageAttribute.get().getValue(),
mainPackageAttribute.get().printPosition(),
- packageAttribute.get().getSourceLocation().print(true))
+ packageAttribute.get().getSourceFile().print(true))
: String.format(
"Overlay manifest:package attribute declared at %1$s value=(%2$s)\n"
+ "\thas a different value=(%3$s) "
@@ -197,7 +189,7 @@
mainPackageAttribute.get().getValue(),
mainPackageAttribute.get().printPosition());
mergingReportBuilder.addMessage(
- overlayDocument.getXmlDocument().getSourceLocation(), 0, 0,
+ overlayDocument.getXmlDocument().getSourceFile(),
MergingReport.Record.Severity.ERROR,
message);
return mergingReportBuilder.build();
@@ -253,7 +245,7 @@
// been overridden so the problem was transient. However, with the final document
// ready, all placeholders values must have been provided.
KeyBasedValueResolver<String> placeHolderValueResolver =
- new MapBasedKeyBasedValueResolver<String>(finalPlaceHolderValues);
+ new MapBasedKeyBasedValueResolver<String>(mPlaceHolderValues);
PlaceholderHandler placeholderHandler = new PlaceholderHandler();
placeholderHandler.visit(
mMergeType,
@@ -424,7 +416,7 @@
// check for placeholders presence, switch first the packageName and application id if
// it is not explicitly set.
Map<String, Object> finalPlaceHolderValues = mPlaceHolderValues;
- if (!mPlaceHolderValues.containsKey("applicationId")) {
+ if (!mPlaceHolderValues.containsKey(PlaceholderHandler.APPLICATION_ID)) {
String packageName = manifestInfo.getMainManifestPackageName().isPresent()
? manifestInfo.getMainManifestPackageName().get()
: xmlDocument.getPackageName();
@@ -435,9 +427,11 @@
builder.put(entry);
}
}
- finalPlaceHolderValues = builder.put(PlaceholderHandler.PACKAGE_NAME, packageName)
- .put(PlaceholderHandler.APPLICATION_ID, packageName)
- .build();
+ builder.put(PlaceholderHandler.PACKAGE_NAME, packageName);
+ if (mMergeType != MergeType.LIBRARY) {
+ builder.put(PlaceholderHandler.APPLICATION_ID, packageName);
+ }
+ finalPlaceHolderValues = builder.build();
}
KeyBasedValueResolver<String> placeHolderValueResolver =
@@ -460,7 +454,7 @@
.validate(mergingReportBuilder, lowerPriorityDocument.getXmlDocument());
if (validationResult == MergingReport.Result.ERROR) {
mergingReportBuilder.addMessage(
- lowerPriorityDocument.getXmlDocument().getSourceLocation(), 0, 0,
+ lowerPriorityDocument.getXmlDocument().getSourceFile(),
MergingReport.Record.Severity.ERROR,
"Validation failed, exiting");
return Optional.absent();
@@ -634,9 +628,7 @@
to.getXml().getAttributeNode(systemProperty.toCamelCase()), null);
actionRecorder.recordAttributeAction(xmlAttribute, new Actions.AttributeRecord(
Actions.ActionType.INJECTED,
- new Actions.ActionLocation(
- to.getSourceLocation(),
- PositionImpl.UNKNOWN),
+ new SourceFilePosition(to.getSourceFile(), SourcePosition.UNKNOWN),
xmlAttribute.getId(),
null, /* reason */
null /* attributeOperationType */));
@@ -661,9 +653,7 @@
actionRecorder.recordAttributeAction(xmlAttribute,
new Actions.AttributeRecord(
Actions.ActionType.INJECTED,
- new Actions.ActionLocation(
- to.getSourceLocation(),
- PositionImpl.UNKNOWN),
+ new SourceFilePosition(to.getSourceFile(), SourcePosition.UNKNOWN),
xmlAttribute.getId(),
null, /* reason */
null /* attributeOperationType */
@@ -695,8 +685,8 @@
XmlElement xmlElement = new XmlElement(useSdk, document);
Actions.NodeRecord nodeRecord = new Actions.NodeRecord(
Actions.ActionType.INJECTED,
- new Actions.ActionLocation(xmlElement.getSourceLocation(),
- PositionImpl.UNKNOWN),
+ new SourceFilePosition(xmlElement.getSourceFile(),
+ SourcePosition.UNKNOWN),
xmlElement.getId(),
"use-sdk injection requested",
NodeOperationType.STRICT);
@@ -877,7 +867,7 @@
/**
* Do no perform placeholders replacement.
*/
- NO_PLACEHOLDER_REPLACEMENT;
+ NO_PLACEHOLDER_REPLACEMENT
}
/**
@@ -991,8 +981,14 @@
// provide some free placeholders values.
ImmutableMap<SystemProperty, Object> systemProperties = mSystemProperties.build();
if (systemProperties.containsKey(SystemProperty.PACKAGE)) {
+ // if the package is provided, make it available for placeholder replacement.
mPlaceholders.put(PACKAGE_NAME, systemProperties.get(SystemProperty.PACKAGE));
- mPlaceholders.put(APPLICATION_ID, systemProperties.get(SystemProperty.PACKAGE));
+ // as well as applicationId since package system property overrides everything
+ // but not when output is a library since only the final (application)
+ // application Id should be used to replace libraries "applicationId" placeholders.
+ if (mMergeType != MergeType.LIBRARY) {
+ mPlaceholders.put(APPLICATION_ID, systemProperties.get(SystemProperty.PACKAGE));
+ }
}
ManifestMerger2 manifestMerger =
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergerXmlUtils.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergerXmlUtils.java
index 9438dda..8935e28 100755
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergerXmlUtils.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergerXmlUtils.java
@@ -260,7 +260,7 @@
if (node.getNodeType() == Node.TEXT_NODE) {
String text = node.getNodeValue();
- if (text.length() > 0) {
+ if (!text.isEmpty()) {
for (int pos = 0; (pos = text.indexOf('\n', pos)) != -1; pos++) {
++line;
}
@@ -427,7 +427,7 @@
break;
case Node.TEXT_NODE:
String txt = node.getNodeValue().trim();
- if (txt.length() == 0) {
+ if (txt.isEmpty()) {
// Keep this for debugging. TODO make it a flag
// to dump whitespace on debugging. Otherwise ignore it.
// txt = "[whitespace]";
@@ -712,7 +712,7 @@
// If there are, just dump them as-is into the element representation.
// We do trim whitespace and ignore all-whitespace or empty text nodes.
String s = child.getNodeValue().trim();
- if (s.length() > 0) {
+ if (!s.isEmpty()) {
sb.append(s);
hasText = true;
}
@@ -845,7 +845,7 @@
for (int kE = iE-1; kE >= 0; kE--) {
if (!aE[kE].startsWith(p)) {
sE.insert(0, '\n').insert(0, diffReplaceNs(aE[kE], nsPrefixE)).insert(0, " ");
- if (p.length() == 0) {
+ if (p.isEmpty()) {
break;
}
p = diffGetPrefix(aE[kE]);
@@ -872,7 +872,7 @@
if (!aA[kA].startsWith(p)) {
sA.insert(0, '\n').insert(0, diffReplaceNs(aA[kA], nsPrefixA)).insert(0, " ");
p = diffGetPrefix(aA[kA]);
- if (p.length() == 0) {
+ if (p.isEmpty()) {
break;
}
} else if (aA[kA].contains(keyAttr) || kA == 0) {
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergingReport.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergingReport.java
index 0b0bb03..2f2d717 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergingReport.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/MergingReport.java
@@ -16,10 +16,12 @@
package com.android.manifmerger;
-import static com.android.manifmerger.XmlLoader.SourceLocation;
-
import com.android.annotations.NonNull;
+import com.android.annotations.VisibleForTesting;
import com.android.annotations.concurrency.Immutable;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
import com.android.utils.ILogger;
import com.google.common.base.CaseFormat;
import com.google.common.base.Optional;
@@ -72,6 +74,11 @@
}
}
mActions.log(logger);
+
+ if (!mResult.isSuccess()) {
+ logger.warning("\nSee http://g.co/androidstudio/manifest-merger for more information"
+ + " about the manifest merger.\n");
+ }
}
/**
@@ -149,28 +156,21 @@
/**
* Log record. This is used to give users some information about what is happening and
* what might have gone wrong.
- *
- * TODO: need to enhance to add SourceLocation, and make this more machine readable.
*/
public static class Record {
+
public enum Severity {WARNING, ERROR, INFO }
private final Severity mSeverity;
private final String mLog;
- private final SourceLocation mSourceLocation;
- private final int mLineNumber;
- private final int mColumnNumber;
+ private final SourceFilePosition mSourceLocation;
private Record(
- @NonNull SourceLocation sourceLocation,
- int lineNumber,
- int columnNumber,
+ @NonNull SourceFilePosition sourceLocation,
@NonNull Severity severity,
@NonNull String mLog) {
this.mSourceLocation = sourceLocation;
- this.mLineNumber = lineNumber;
- this.mColumnNumber = columnNumber;
this.mSeverity = severity;
this.mLog = mLog;
}
@@ -185,8 +185,8 @@
@Override
public String toString() {
- return mSourceLocation.print(false)
- + ":" + mLineNumber + ":" + mColumnNumber + " "
+ return mSourceLocation.toString() // needs short string.
+ + " "
+ CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, mSeverity.toString())
+ ":\n\t"
+ mLog;
@@ -221,12 +221,31 @@
return this;
}
- Builder addMessage(@NonNull SourceLocation errorLocation,
+ @VisibleForTesting
+ Builder addMessage(@NonNull SourceFile sourceFile,
int line,
int column,
@NonNull Record.Severity severity,
@NonNull String message) {
+ // The line and column used are 1-based, but SourcePosition uses zero-based.
+ return addMessage(
+ new SourceFilePosition(sourceFile, new SourcePosition(line - 1, column -1, -1)),
+ severity,
+ message);
+ }
+ Builder addMessage(@NonNull SourceFile sourceFile,
+ @NonNull Record.Severity severity,
+ @NonNull String message) {
+ return addMessage(
+ new SourceFilePosition(sourceFile, SourcePosition.UNKNOWN),
+ severity,
+ message);
+ }
+
+ Builder addMessage(@NonNull SourceFilePosition sourceFilePosition,
+ @NonNull Record.Severity severity,
+ @NonNull String message) {
switch (severity) {
case ERROR:
mHasErrors = true;
@@ -235,8 +254,7 @@
mHasWarnings = true;
break;
}
- mRecordBuilder.add(new Record(
- errorLocation, line, column, severity, message));
+ mRecordBuilder.add(new Record(sourceFilePosition, severity, message));
return this;
}
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/OrphanXmlElement.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/OrphanXmlElement.java
index e00008c..0f12930 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/OrphanXmlElement.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/OrphanXmlElement.java
@@ -21,7 +21,8 @@
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.utils.PositionXmlParser;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourcePosition;
import com.android.utils.XmlUtils;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
@@ -107,14 +108,14 @@
@NonNull
@Override
- public PositionXmlParser.Position getPosition() {
- return PositionImpl.UNKNOWN;
+ public SourcePosition getPosition() {
+ return SourcePosition.UNKNOWN;
}
@Override
@NonNull
- public XmlLoader.SourceLocation getSourceLocation() {
- return XmlLoader.UNKNOWN;
+ public SourceFile getSourceFile() {
+ return SourceFile.UNKNOWN;
}
}
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PlaceholderHandler.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/PlaceholderHandler.java
index 97f8b4b..8b0356c 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PlaceholderHandler.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/PlaceholderHandler.java
@@ -18,6 +18,7 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourcePosition;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -116,7 +117,7 @@
// record the attribute set
mergingReportBuilder.getActionRecorder().recordAttributeAction(
xmlAttribute,
- PositionImpl.UNKNOWN,
+ SourcePosition.UNKNOWN,
Actions.ActionType.INJECTED,
null /* attributeOperationType */);
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PositionImpl.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/PositionImpl.java
deleted file mode 100644
index 3f4ae57..0000000
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PositionImpl.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.manifmerger;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.utils.PositionXmlParser;
-
-/**
- * Implementation of {@link com.android.utils.PositionXmlParser.Position} capable of initializing
- * from xml definition or with given line, column and offset.
-*/
-final class PositionImpl implements PositionXmlParser.Position {
-
- /**
- * Unknown position on an action happens when the action did not originate from any source file
- * but from environmental factors like placeholder injection, implicit permissions when
- * upgrading, etc...
- */
- static final PositionXmlParser.Position UNKNOWN = new PositionImpl(0, 0, 0);
-
- private final int mLine;
- private final int mColumn;
- private final int mOffset;
-
- PositionImpl(int line, int column, int offset) {
- mLine = line;
- mColumn = column;
- mOffset = offset;
- }
-
- @Nullable
- @Override
- public PositionXmlParser.Position getEnd() {
- return null;
- }
-
- @Override
- public void setEnd(@NonNull PositionXmlParser.Position end) {
-
- }
-
- @Override
- public int getLine() {
- return mLine;
- }
-
- @Override
- public int getOffset() {
- return mOffset;
- }
-
- @Override
- public int getColumn() {
- return mColumn;
- }
-
- @Override
- public String toString() {
- if (mLine == 0 && mColumn ==0) {
- return "(unknown)";
- } else {
- return mLine + ":" + mColumn;
- }
- }
-}
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PostValidator.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/PostValidator.java
index 4522168..59dfed0 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PostValidator.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/PostValidator.java
@@ -181,8 +181,8 @@
"%1$s was tagged at %2$s:%3$d to replace another declaration "
+ "but no other declaration present",
xmlElement.getId(),
- xmlElement.getDocument().getSourceLocation().print(true),
- xmlElement.getPosition().getLine()
+ xmlElement.getDocument().getSourceFile().print(true),
+ xmlElement.getPosition().getStartLine() + 1
));
}
break;
@@ -195,8 +195,8 @@
"%1$s was tagged at %2$s:%3$d to remove other declarations "
+ "but no other declaration present",
xmlElement.getId(),
- xmlElement.getDocument().getSourceLocation().print(true),
- xmlElement.getPosition().getLine()
+ xmlElement.getDocument().getSourceFile().print(true),
+ xmlElement.getPosition().getStartLine() + 1
));
}
break;
@@ -231,8 +231,8 @@
+ " declarations but no other declaration present",
xmlElement.getId(),
attributeOperation.getKey(),
- xmlElement.getDocument().getSourceLocation().print(true),
- xmlElement.getPosition().getLine()
+ xmlElement.getDocument().getSourceFile().print(true),
+ xmlElement.getPosition().getStartLine() + 1
));
}
break;
@@ -245,8 +245,8 @@
+ " declarations but no other declaration present",
xmlElement.getId(),
attributeOperation.getKey(),
- xmlElement.getDocument().getSourceLocation().print(true),
- xmlElement.getPosition().getLine()
+ xmlElement.getDocument().getSourceFile().print(true),
+ xmlElement.getPosition().getStartLine() + 1
));
}
break;
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PreValidator.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/PreValidator.java
index 7e41e84..6199fde 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/PreValidator.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/PreValidator.java
@@ -253,23 +253,25 @@
case REMOVE:
// check we are not provided a new value.
if (attribute.isPresent()) {
+ // Add one to startLine so the first line is displayed as 1.
xmlElement.addMessage(mergingReport, ERROR, String.format(
"tools:remove specified at line:%d for attribute %s, but "
+ "attribute also declared at line:%d, "
+ "do you want to use tools:replace instead ?",
- xmlElement.getLine(),
+ xmlElement.getPosition().getStartLine() + 1,
attributeOperationTypeEntry.getKey(),
- attribute.get().getPosition().getLine()
+ attribute.get().getPosition().getStartLine() + 1
));
}
break;
case REPLACE:
// check we are provided a new value
if (!attribute.isPresent()) {
+ // Add one to startLine so the first line is displayed as 1.
xmlElement.addMessage(mergingReport, ERROR, String.format(
"tools:replace specified at line:%d for attribute %s, but "
+ "no new value specified",
- xmlElement.getLine(),
+ xmlElement.getPosition().getStartLine() + 1,
attributeOperationTypeEntry.getKey()
));
}
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlAttribute.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlAttribute.java
index 7dd00e1..dd457e7 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlAttribute.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlAttribute.java
@@ -19,7 +19,9 @@
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.utils.PositionXmlParser;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
@@ -105,11 +107,11 @@
@NonNull
@Override
- public PositionXmlParser.Position getPosition() {
+ public SourcePosition getPosition() {
try {
return mOwnerElement.getDocument().getNodePosition(this);
} catch(Exception e) {
- return PositionImpl.UNKNOWN;
+ return SourcePosition.UNKNOWN;
}
}
@@ -335,26 +337,41 @@
Actions.AttributeRecord attributeRecord = report.getActionRecorder()
.getAttributeCreationRecord(higherPriority);
- String error = String.format(
- "Attribute %1$s value=(%2$s) from %3$s\n"
- + "\tis also present at %4$s value=(%5$s)\n"
- + "\tSuggestion: add 'tools:replace=\"%6$s\"' to <%7$s> element "
- + "at %8$s to override",
- higherPriority.getId(),
- higherPriority.getValue(),
- attributeRecord != null
- ? attributeRecord.getActionLocation().toString()
- : "(unknown)",
- printPosition(),
- getValue(),
- mXml.getName(),
- getOwnerElement().getType().toXmlName(),
- higherPriority.getOwnerElement().printPosition(true)
- );
+ String error;
+ if (getOwnerElement().getType().getMergeType() == MergeType.MERGE_CHILDREN_ONLY) {
+ error = String.format(
+ "Attribute %1$s value=(%2$s) from %3$s\n"
+ + "\tis also present at %4$s value=(%5$s).\n"
+ + "\tAttributes of <%6$s> elements are not merged.",
+ higherPriority.getId(),
+ higherPriority.getValue(),
+ attributeRecord != null
+ ? attributeRecord.getActionLocation().print(true /*shortFormat*/)
+ : "(unknown)",
+ printPosition(),
+ getValue(),
+ getOwnerElement().getType().toXmlName());
+ } else {
+ error = String.format(
+ "Attribute %1$s value=(%2$s) from %3$s\n"
+ + "\tis also present at %4$s value=(%5$s).\n"
+ + "\tSuggestion: add 'tools:replace=\"%6$s\"' to <%7$s> element "
+ + "at %8$s to override.",
+ higherPriority.getId(),
+ higherPriority.getValue(),
+ attributeRecord != null
+ ? attributeRecord.getActionLocation().print(true /*shortFormat*/)
+ : "(unknown)",
+ printPosition(),
+ getValue(),
+ mXml.getName(),
+ getOwnerElement().getType().toXmlName(),
+ higherPriority.getOwnerElement().printPosition());
+ }
higherPriority.addMessage(report,
attributeRecord != null
? attributeRecord.getActionLocation().getPosition()
- : PositionImpl.UNKNOWN,
+ : SourcePosition.UNKNOWN,
MergingReport.Record.Severity.ERROR, error);
}
@@ -365,18 +382,17 @@
}
void addMessage(MergingReport.Builder report,
- PositionXmlParser.Position position,
+ SourcePosition position,
MergingReport.Record.Severity severity,
String message) {
- report.addMessage(getOwnerElement().getDocument().getSourceLocation(),
- position.getLine(),
- position.getColumn(),
+ report.addMessage(
+ new SourceFilePosition(getOwnerElement().getDocument().getSourceFile(), position),
severity, message);
}
@NonNull
@Override
- public XmlLoader.SourceLocation getSourceLocation() {
- return getOwnerElement().getSourceLocation();
+ public SourceFile getSourceFile() {
+ return getOwnerElement().getSourceFile();
}
}
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlDocument.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlDocument.java
index bf46f20..1ad2c65 100755
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlDocument.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlDocument.java
@@ -24,6 +24,9 @@
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
import com.android.ide.common.xml.XmlFormatPreferences;
import com.android.ide.common.xml.XmlFormatStyle;
import com.android.ide.common.xml.XmlPrettyPrinter;
@@ -75,22 +78,20 @@
private final Element mRootElement;
// this is initialized lazily to avoid un-necessary early parsing.
private final AtomicReference<XmlElement> mRootNode = new AtomicReference<XmlElement>(null);
- private final PositionXmlParser mPositionXmlParser;
- private final XmlLoader.SourceLocation mSourceLocation;
+ private final SourceFile mSourceFile;
private final KeyResolver<String> mSelectors;
private final KeyBasedValueResolver<SystemProperty> mSystemPropertyResolver;
private final Type mType;
private final Optional<String> mMainManifestPackageName;
- public XmlDocument(@NonNull PositionXmlParser positionXmlParser,
- @NonNull XmlLoader.SourceLocation sourceLocation,
+ public XmlDocument(
+ @NonNull SourceFile sourceLocation,
@NonNull KeyResolver<String> selectors,
@NonNull KeyBasedValueResolver<SystemProperty> systemPropertyResolver,
@NonNull Element element,
@NonNull Type type,
@NonNull Optional<String> mainManifestPackageName) {
- this.mPositionXmlParser = Preconditions.checkNotNull(positionXmlParser);
- this.mSourceLocation = Preconditions.checkNotNull(sourceLocation);
+ this.mSourceFile = Preconditions.checkNotNull(sourceLocation);
this.mRootElement = Preconditions.checkNotNull(element);
this.mSelectors = Preconditions.checkNotNull(selectors);
this.mSystemPropertyResolver = Preconditions.checkNotNull(systemPropertyResolver);
@@ -145,8 +146,8 @@
* @return a new {@link com.android.manifmerger.XmlDocument} with up to date information.
*/
public XmlDocument reparse() {
- return new XmlDocument(mPositionXmlParser,
- mSourceLocation,
+ return new XmlDocument(
+ mSourceFile,
mSelectors,
mSystemPropertyResolver,
mRootElement,
@@ -183,59 +184,24 @@
}
/**
- * Returns a {@link XmlNode} position automatically offsetting the line and number
- * columns by one (for PositionXmlParser, document starts at line 0, however for the common
- * understanding, document should start at line 1).
+ * Returns the position of the specified {@link XmlNode}.
*/
@NonNull
- PositionXmlParser.Position getNodePosition(XmlNode node) {
+ static SourcePosition getNodePosition(XmlNode node) {
return getNodePosition(node.getXml());
}
/**
- * Returns a {@link org.w3c.dom.Node} position automatically offsetting the line and number
- * columns by one (for PositionXmlParser, document starts at line 0, however for the common
- * understanding, document should start at line 1).
+ * Returns the position of the specified {@link org.w3c.dom.Node}.
*/
@NonNull
- PositionXmlParser.Position getNodePosition(Node xml) {
-
- final PositionXmlParser.Position position = mPositionXmlParser.getPosition(xml);
- if (position == null) {
- return PositionImpl.UNKNOWN;
- }
- return new PositionXmlParser.Position() {
- @Nullable
- @Override
- public PositionXmlParser.Position getEnd() {
- return position.getEnd();
- }
-
- @Override
- public void setEnd(@NonNull PositionXmlParser.Position end) {
- position.setEnd(end);
- }
-
- @Override
- public int getLine() {
- return position.getLine() + 1;
- }
-
- @Override
- public int getOffset() {
- return position.getOffset();
- }
-
- @Override
- public int getColumn() {
- return position.getColumn() +1;
- }
- };
+ static SourcePosition getNodePosition(Node xml) {
+ return PositionXmlParser.getPosition(xml);
}
@NonNull
- public XmlLoader.SourceLocation getSourceLocation() {
- return mSourceLocation;
+ public SourceFile getSourceFile() {
+ return mSourceFile;
}
public synchronized XmlElement getRootNode() {
@@ -388,9 +354,10 @@
XmlElement usesSdkElement = usesSdk.get();
if (usesSdkElement.getOperationType() != NodeOperationType.MERGE) {
mergingReport
- .addMessage(getSourceLocation(),
- usesSdkElement.getLine(),
- usesSdkElement.getColumn(),
+ .addMessage(
+ new SourceFilePosition(
+ getSourceFile(),
+ usesSdkElement.getPosition()),
MergingReport.Record.Severity.ERROR,
"uses-sdk element cannot have a \"tools:node\" attribute");
return;
@@ -412,13 +379,13 @@
if (!Character.isDigit(libraryTargetSdkVersion.charAt(0))) {
// this is a code name, ensure this document uses the same code name.
if (!libraryTargetSdkVersion.equals(getTargetSdkVersion())) {
- mergingReport.addMessage(getSourceLocation(), 0, 0, MergingReport.Record.Severity.ERROR,
+ mergingReport.addMessage(getSourceFile(), MergingReport.Record.Severity.ERROR,
String.format(
"uses-sdk:targetSdkVersion %1$s cannot be different than version "
+ "%2$s declared in library %3$s",
getTargetSdkVersion(),
libraryTargetSdkVersion,
- lowerPriorityDocument.getSourceLocation().print(false)
+ lowerPriorityDocument.getSourceFile().print(false)
)
);
return;
@@ -430,13 +397,13 @@
if (!Character.isDigit(libraryMinSdkVersion.charAt(0))) {
// this is a code name, ensure this document uses the same code name.
if (!libraryMinSdkVersion.equals(getMinSdkVersion())) {
- mergingReport.addMessage(getSourceLocation(), 0, 0, MergingReport.Record.Severity.ERROR,
+ mergingReport.addMessage(getSourceFile(), MergingReport.Record.Severity.ERROR,
String.format(
"uses-sdk:minSdkVersion %1$s cannot be different than version "
+ "%2$s declared in library %3$s",
getMinSdkVersion(),
libraryMinSdkVersion,
- lowerPriorityDocument.getSourceLocation().print(false)
+ lowerPriorityDocument.getSourceFile().print(false)
)
);
return;
@@ -444,20 +411,23 @@
}
if (!checkUsesSdkMinVersion(lowerPriorityDocument, mergingReport)) {
- mergingReport.addMessage(getSourceLocation(),
- usesSdk.isPresent() ? usesSdk.get().getLine() : 0,
- usesSdk.isPresent() ? usesSdk.get().getColumn() : 0,
- MergingReport.Record.Severity.ERROR,
- String.format(
+ String error = String.format(
"uses-sdk:minSdkVersion %1$s cannot be smaller than version "
+ "%2$s declared in library %3$s\n"
+ "\tSuggestion: use tools:overrideLibrary=\"%4$s\" to force usage",
getMinSdkVersion(),
lowerPriorityDocument.getRawMinSdkVersion(),
- lowerPriorityDocument.getSourceLocation().print(false),
- lowerPriorityDocument.getPackageName()
- )
- );
+ lowerPriorityDocument.getSourceFile().print(false),
+ lowerPriorityDocument.getPackageName());
+ if (usesSdk.isPresent()) {
+ mergingReport.addMessage(
+ new SourceFilePosition(getSourceFile(), usesSdk.get().getPosition()),
+ MergingReport.Record.Severity.ERROR,
+ error);
+ } else {
+ mergingReport.addMessage(
+ getSourceFile(), MergingReport.Record.Severity.ERROR, error);
+ }
return;
}
@@ -479,9 +449,7 @@
addIfAbsent(mergingReport.getActionRecorder(),
USES_PERMISSION,
permission("WRITE_EXTERNAL_STORAGE"),
- lowerPriorityDocument.getPackageName() + " has a targetSdkVersion < 4",
- Pair.of("maxSdkVersion", "18") // permission became implied at 19.
- );
+ lowerPriorityDocument.getPackageName() + " has a targetSdkVersion < 4");
hasWriteToExternalStoragePermission = true;
addIfAbsent(mergingReport.getActionRecorder(),
@@ -499,10 +467,7 @@
addIfAbsent(mergingReport.getActionRecorder(),
USES_PERMISSION,
permission("READ_EXTERNAL_STORAGE"),
- lowerPriorityDocument.getPackageName() + " requested WRITE_EXTERNAL_STORAGE",
- // NOTE TO @xav, where can we find the list of implied permissions at versions X
- Pair.of("maxSdkVersion", "18") // permission became implied at 19, DID IT ???
- );
+ lowerPriorityDocument.getPackageName() + " requested WRITE_EXTERNAL_STORAGE");
}
// Pre-JellyBean call log permission compatibility.
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlElement.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlElement.java
index 3e60364..f14fb94 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlElement.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlElement.java
@@ -19,9 +19,10 @@
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourcePosition;
import com.android.ide.common.res2.MergingException;
import com.android.utils.ILogger;
-import com.android.utils.PositionXmlParser;
import com.android.utils.SdkUtils;
import com.android.utils.XmlUtils;
import com.google.common.base.Joiner;
@@ -116,15 +117,17 @@
OtherOperationType.valueOf(instruction);
break;
} catch (IllegalArgumentException e1) {
+
String errorMessage =
- String.format("[%1$s:%2$s] Invalid instruction '%3$s', "
- + "valid instructions are : %4$s",
- mDocument.getSourceLocation().print(false),
- mDocument.getNodePosition(xml).getLine(),
+ String.format("Invalid instruction '%1$s', "
+ + "valid instructions are : %2$s",
instruction,
Joiner.on(',').join(AttributeOperationType.values())
);
- throw new RuntimeException(new MergingException(errorMessage, e));
+ throw new RuntimeException(MergingException.wrapException(e)
+ .withMessage(errorMessage)
+ .withFile(mDocument.getSourceFile())
+ .withPosition(mDocument.getNodePosition(xml)).build());
}
}
for (String attributeName : Splitter.on(',').trimResults()
@@ -220,16 +223,17 @@
@NonNull
@Override
- public PositionXmlParser.Position getPosition() {
+ public SourcePosition getPosition() {
return mDocument.getNodePosition(this);
}
@NonNull
@Override
- public XmlLoader.SourceLocation getSourceLocation() {
- return getDocument().getSourceLocation();
+ public SourceFile getSourceFile() {
+ return mDocument.getSourceFile();
}
+
/**
* Merge this xml element with a lower priority node.
*
@@ -245,9 +249,7 @@
if (mSelector != null && !mSelector.isResolvable(getDocument().getSelectors())) {
- mergingReport.addMessage(getSourceLocation(),
- getLine(),
- getColumn(),
+ mergingReport.addMessage(getSourceFilePosition(),
MergingReport.Record.Severity.ERROR,
String.format("'tools:selector=\"%1$s\"' is not a valid library identifier, "
+ "valid identifiers are : %2$s",
@@ -871,9 +873,7 @@
void addMessage(MergingReport.Builder mergingReport,
MergingReport.Record.Severity severity,
String message) {
- mergingReport.addMessage(getDocument().getSourceLocation(),
- getLine(),
- getColumn(),
+ mergingReport.addMessage(getSourceFilePosition(),
severity,
message);
}
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlLoader.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlLoader.java
index 99194fb..66b08d4 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlLoader.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlLoader.java
@@ -19,14 +19,11 @@
import static com.android.manifmerger.ManifestMerger2.SystemProperty;
import static com.android.manifmerger.PlaceholderHandler.KeyBasedValueResolver;
-import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourceFile;
import com.android.utils.PositionXmlParser;
import com.google.common.base.Optional;
-import com.google.common.base.Strings;
import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import java.io.BufferedInputStream;
@@ -42,45 +39,6 @@
*/
public final class XmlLoader {
- /**
- * Abstraction for the notion of source location. This is useful for logging and records
- * collection when a origin of an xml declaration is needed.
- */
- public static class SourceLocation {
-
- @Nullable
- private final File mSource;
-
- @Nullable
- private final String mDescription;
-
- /**
- * Build a source location, one of the parameter must not be null.
- */
- public SourceLocation(@Nullable String description, @Nullable File source) {
- if (description == null && source == null) {
- throw new IllegalArgumentException("description and source cannot be both null");
- }
- mDescription = description == null ? source.getName() : description;
- mSource = source;
- }
-
- /**
- * print this source location in a human and machine readable format.
- *
- * @param shortFormat whether or not to use the short format. For instance, for a file, a
- * short format is the file name while the long format is its path.
- * @return the human and machine readable source location.
- */
- String print(boolean shortFormat) {
- return shortFormat
- ? mDescription
- : mSource == null
- ? mDescription :
- mSource.getAbsolutePath();
- }
- }
-
private XmlLoader() {}
/**
@@ -100,11 +58,9 @@
throws IOException, SAXException, ParserConfigurationException {
InputStream inputStream = new BufferedInputStream(new FileInputStream(xmlFile));
- PositionXmlParser positionXmlParser = new PositionXmlParser();
- Document domDocument = positionXmlParser.parse(inputStream);
- return domDocument != null
- ? new XmlDocument(positionXmlParser,
- new SourceLocation(displayName, xmlFile),
+ Document domDocument = PositionXmlParser.parse(inputStream);
+ return domDocument != null ? new XmlDocument(
+ new SourceFile(xmlFile, displayName),
selectors,
systemPropertyResolver,
domDocument.getDocumentElement(),
@@ -117,7 +73,7 @@
/**
* Loads a xml document from its {@link String} representation without doing xml validation and
* return a {@link com.android.manifmerger.XmlDocument}
- * @param sourceLocation the source location to use for logging and record collection.
+ * @param sourceFile the source location to use for logging and record collection.
* @param xml the persisted xml.
* @return the initialized {@link com.android.manifmerger.XmlDocument}
* @throws IOException this should never be thrown.
@@ -127,17 +83,15 @@
public static XmlDocument load(
KeyResolver<String> selectors,
KeyBasedValueResolver<SystemProperty> systemPropertyResolver,
- SourceLocation sourceLocation,
+ SourceFile sourceFile,
String xml,
XmlDocument.Type type,
Optional<String> mainManifestPackageName)
throws IOException, SAXException, ParserConfigurationException {
- PositionXmlParser positionXmlParser = new PositionXmlParser();
- Document domDocument = positionXmlParser.parse(xml);
+ Document domDocument = PositionXmlParser.parse(xml);
return domDocument != null
? new XmlDocument(
- positionXmlParser,
- sourceLocation,
+ sourceFile,
selectors,
systemPropertyResolver,
domDocument.getDocumentElement(),
@@ -145,7 +99,4 @@
mainManifestPackageName)
: null;
}
-
- public static final SourceLocation UNKNOWN =
- new SourceLocation("Unknown location", null /* source */);
}
diff --git a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlNode.java b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlNode.java
index 8b37d31..f1e3feb 100644
--- a/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlNode.java
+++ b/build-system/manifest-merger/src/main/java/com/android/manifmerger/XmlNode.java
@@ -19,7 +19,9 @@
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.concurrency.Immutable;
-import com.android.utils.PositionXmlParser;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
@@ -32,8 +34,6 @@
*/
public abstract class XmlNode {
- private static final String UNKNOWN_POSITION = "Unknown position";
-
protected static final Function<Node, String> NODE_TO_NAME =
new Function<Node, String>() {
@Override
@@ -65,13 +65,20 @@
* Returns the element's position
*/
@NonNull
- public abstract PositionXmlParser.Position getPosition();
+ public abstract SourcePosition getPosition();
/**
* Returns the element's document xml source file location.
*/
@NonNull
- public abstract XmlLoader.SourceLocation getSourceLocation();
+ public abstract SourceFile getSourceFile();
+
+ /**
+ * Returns the element's document xml source file location.
+ */
+ public SourceFilePosition getSourceFilePosition() {
+ return new SourceFilePosition(getSourceFile(), getPosition());
+ }
/**
* Returns the element's xml
@@ -134,36 +141,13 @@
}
/**
- * Return the line number in the original xml file this element or attribute was declared.
- */
- public int getLine() {
- return getPosition().getLine();
- }
-
- /**
- * Return the column number in the original xml file this element or attribute was declared.
- */
- public int getColumn() {
- return getPosition().getColumn();
- }
-
- /**
* Returns the position of this attribute in the original xml file. This may return an invalid
* location as this xml fragment does not exist in any xml file but is the temporary result
* of the merging process.
- * @return a human readable position or {@link #UNKNOWN_POSITION}
+ * @return a human readable position.
*/
public String printPosition() {
- return printPosition(true);
- }
-
- public String printPosition(boolean shortFormat) {
- PositionXmlParser.Position position = getPosition();
- return new StringBuilder()
- .append(getSourceLocation().print(shortFormat))
- .append(":").append(position.getLine())
- .append(":").append(position.getColumn())
- .toString();
+ return getSourceFilePosition().print(true /*shortFormat*/);
}
/**
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionRecorderTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionRecorderTest.java
index 5d0ba59..1949366 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionRecorderTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionRecorderTest.java
@@ -72,7 +72,7 @@
throws ParserConfigurationException, SAXException, IOException {
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(),
+ TestUtils.sourceFile(getClass(),
REFEFENCE_DOCUMENT), REFERENCE);
XmlElement xmlElement = xmlDocument.getRootNode().getNodeByTypeAndKey(
@@ -90,7 +90,7 @@
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(Actions.HEADER)
.append(xmlElement.getId()).append("\n");
- appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, 6, 5);
+ appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, "6:5-8:16");
Mockito.verify(mLoggerMock).verbose(stringBuilder.toString());
Mockito.verifyNoMoreInteractions(mLoggerMock);
@@ -111,11 +111,11 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(),
+ TestUtils.sourceFile(getClass(),
REFEFENCE_DOCUMENT), REFERENCE);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(),
+ TestUtils.sourceFile(getClass(),
"other_document"), other);
XmlElement xmlElement = xmlDocument.getRootNode().getNodeByTypeAndKey(
@@ -141,8 +141,8 @@
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(Actions.HEADER)
.append(xmlElement.getId()).append("\n");
- appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, 6, 5);
- appendNode(stringBuilder, Actions.ActionType.REJECTED, "other_document", 6, 5);
+ appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, "6:5-8:16");
+ appendNode(stringBuilder, Actions.ActionType.REJECTED, "other_document", "6:5-83");
Mockito.verify(mLoggerMock).verbose(stringBuilder.toString());
Mockito.verifyNoMoreInteractions(mLoggerMock);
@@ -152,7 +152,7 @@
throws ParserConfigurationException, SAXException, IOException {
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(),
+ TestUtils.sourceFile(getClass(),
REFEFENCE_DOCUMENT), REFERENCE);
XmlElement xmlElement = xmlDocument.getRootNode().getNodeByTypeAndKey(
@@ -173,13 +173,12 @@
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(Actions.HEADER)
.append(xmlElement.getId()).append("\n");
- appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, 6, 5);
+ appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, "6:5-8:16");
appendAttribute(stringBuilder,
XmlNode.unwrapName(xmlElement.getXml().getAttributeNode("android:name")),
Actions.ActionType.ADDED,
REFEFENCE_DOCUMENT,
- 6,
- 15);
+ "6:15-41");
Mockito.verify(mLoggerMock).verbose(stringBuilder.toString());
Mockito.verifyNoMoreInteractions(mLoggerMock);
@@ -189,7 +188,7 @@
throws ParserConfigurationException, SAXException, IOException {
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(),
+ TestUtils.sourceFile(getClass(),
REFEFENCE_DOCUMENT), REFERENCE);
XmlElement xmlElement = xmlDocument.getRootNode();
@@ -209,13 +208,12 @@
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(Actions.HEADER)
.append(xmlElement.getId()).append("\n");
- appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, 1, 1);
+ appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, "1:1-10:12");
appendAttribute(stringBuilder,
XmlNode.unwrapName(xmlElement.getXml().getAttributeNode("package")),
Actions.ActionType.ADDED,
REFEFENCE_DOCUMENT,
- 4,
- 5);
+ "4:5-31");
Mockito.verify(mLoggerMock).verbose(stringBuilder.toString());
Mockito.verifyNoMoreInteractions(mLoggerMock);
@@ -237,11 +235,11 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(),
+ TestUtils.sourceFile(getClass(),
REFEFENCE_DOCUMENT), REFERENCE);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(),
+ TestUtils.sourceFile(getClass(),
"other_document"), other);
XmlElement activityElement = xmlDocument.getRootNode().getNodeByTypeAndKey(
@@ -272,10 +270,10 @@
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(Actions.HEADER)
.append(activityElement.getId()).append("\n");
- appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, 6, 5);
- appendNode(stringBuilder, Actions.ActionType.REJECTED, "other_document", 6, 5);
+ appendNode(stringBuilder, Actions.ActionType.ADDED, REFEFENCE_DOCUMENT, "6:5-8:16");
+ appendNode(stringBuilder, Actions.ActionType.REJECTED, "other_document", "6:5-82");
stringBuilder.append(applicationElement.getId()).append("\n");
- appendNode(stringBuilder, Actions.ActionType.ADDED, "other_document", 7, 5);
+ appendNode(stringBuilder, Actions.ActionType.ADDED, "other_document", "7:5-49");
Mockito.verify(mLoggerMock).verbose(stringBuilder.toString());
Mockito.verifyNoMoreInteractions(mLoggerMock);
@@ -284,29 +282,27 @@
private void appendNode(StringBuilder out,
Actions.ActionType actionType,
String docString,
- int lineNumber,
- int columnNumber) {
+ String position) {
out.append(actionType.toString())
.append(" from ")
.append(getClass().getSimpleName()).append('#').append(docString)
- .append(":").append(lineNumber).append(":").append(columnNumber).append("\n");
+ .append(':').append(position).append('\n');
}
private void appendAttribute(StringBuilder out,
XmlNode.NodeName attributeName,
Actions.ActionType actionType,
String docString,
- int lineNumber,
- int columnNumber) {
+ String position) {
- out.append("\t")
+ out.append('\t')
.append(attributeName.toString())
.append("\n\t\t")
.append(actionType.toString())
.append(" from ")
.append(getClass().getSimpleName()).append('#').append(docString)
- .append(":").append(lineNumber)
- .append(":").append(columnNumber).append("\n");
+ .append(':').append(position)
+ .append('\n');
}
}
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionsTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionsTest.java
index b748f7f..c015573 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionsTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ActionsTest.java
@@ -16,16 +16,17 @@
package com.android.manifmerger;
-import static com.android.manifmerger.Actions.ActionLocation;
import static com.android.manifmerger.Actions.DecisionTreeRecord;
import static com.android.manifmerger.XmlNode.NodeKey;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.android.SdkConstants;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
import com.android.sdklib.mock.MockLog;
import com.android.utils.ILogger;
-import com.android.utils.PositionXmlParser;
import com.android.utils.StdLogger;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
@@ -73,8 +74,7 @@
DecisionTreeRecord activityDecisionTree = new DecisionTreeRecord();
activityDecisionTree.addNodeRecord(new Actions.NodeRecord(
Actions.ActionType.ADDED,
- new ActionLocation(mock(XmlLoader.SourceLocation.class), mock(
- PositionXmlParser.Position.class)),
+ new SourceFilePosition(new SourceFile("file"), new SourcePosition(1, 2, -1)),
new NodeKey("nodeKey"),
null, /* reason */
NodeOperationType.MERGE));
@@ -98,8 +98,7 @@
DecisionTreeRecord activityDecisionTree = new DecisionTreeRecord();
activityDecisionTree.addNodeRecord(new Actions.NodeRecord(
Actions.ActionType.ADDED,
- new ActionLocation(mock(XmlLoader.SourceLocation.class), mock(
- PositionXmlParser.Position.class)),
+ new SourceFilePosition(new SourceFile("file"), new SourcePosition(1, 2, -1)),
new NodeKey("nodeKey"),
null, /* reason */
NodeOperationType.MERGE));
@@ -107,8 +106,8 @@
activityDecisionTree.mAttributeRecords.put(attributeName,
ImmutableList.of(
new Actions.AttributeRecord(Actions.ActionType.INJECTED,
- new ActionLocation(mock(XmlLoader.SourceLocation.class),
- mock(PositionXmlParser.Position.class)),
+ new SourceFilePosition(
+ new SourceFile("file"), new SourcePosition(1, 2, -1)),
new NodeKey("nodeKey"),
null, /* reason */
AttributeOperationType.STRICT)));
@@ -183,11 +182,11 @@
+ "</manifest>"; // 13
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument firstLibrary = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriorityOne"), lowerPriorityOne);
+ TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
XmlDocument secondLibrary = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
+ TestUtils.sourceFile(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -207,25 +206,25 @@
+ "3 package=\"com.example.lib3\" >\n"
+ "4\n"
+ "5 <permission\n"
- + "5-->ActionsTest#higherPriority:14:5\n"
+ + "5-->ActionsTest#higherPriority:14:5-18:18\n"
+ "6 android:name=\"permissionThree\"\n"
- + "6-->ActionsTest#higherPriority:15:14\n"
+ + "6-->ActionsTest#higherPriority:15:14-44\n"
+ "7 android:protectionLevel=\"signature\" >\n"
- + "7-->ActionsTest#higherPriority:16:14\n"
+ + "7-->ActionsTest#higherPriority:16:14-49\n"
+ "8 </permission>\n"
+ "9 <permission\n"
- + "9-->ActionsTest#lowerPriorityOne:9:5\n"
+ + "9-->ActionsTest#lowerPriorityOne:9:5-11:18\n"
+ "10 android:name=\"permissionTwo\"\n"
- + "10-->ActionsTest#lowerPriorityOne:9:17\n"
+ + "10-->ActionsTest#lowerPriorityOne:9:17-45\n"
+ "11 android:protectionLevel=\"signature\" >\n"
- + "11-->ActionsTest#lowerPriorityOne:10:14\n"
+ + "11-->ActionsTest#lowerPriorityOne:10:14-49\n"
+ "12 </permission>\n"
+ "13 <permission\n"
- + "13-->ActionsTest#lowerPriorityTwo:9:5\n"
+ + "13-->ActionsTest#lowerPriorityTwo:9:5-11:18\n"
+ "14 android:name=\"permissionFour\"\n"
- + "14-->ActionsTest#lowerPriorityTwo:9:17\n"
+ + "14-->ActionsTest#lowerPriorityTwo:9:17-46\n"
+ "15 android:protectionLevel=\"normal\" >\n"
- + "15-->ActionsTest#lowerPriorityTwo:10:14\n"
+ + "15-->ActionsTest#lowerPriorityTwo:10:14-46\n"
+ "16 </permission>\n"
+ "17\n"
+ "18</manifest>\n";
@@ -274,12 +273,7 @@
Actions.NodeRecord nodeRecord,
List<Actions.NodeRecord> nodeRecordList) {
for (Actions.NodeRecord record : nodeRecordList) {
- if (record.getActionLocation().getPosition().getLine()
- == nodeRecord.getActionLocation().getPosition().getLine()
- && record.getActionLocation().getPosition().getColumn()
- == nodeRecord.getActionLocation().getPosition().getColumn()
- && record.getActionLocation().getPosition().getOffset()
- == nodeRecord.getActionLocation().getPosition().getOffset()
+ if (record.getActionLocation().equals(nodeRecord.getActionLocation())
&& record.getActionType() == nodeRecord.getActionType()) {
return true;
}
@@ -294,12 +288,7 @@
for (Actions.AttributeRecord record : attributeRecordList) {
if (record.getOperationType() == attributeRecord.getOperationType()
&& record.getActionType() == attributeRecord.getActionType()
- && record.getActionLocation().getPosition().getLine()
- == attributeRecord.getActionLocation().getPosition().getLine()
- && record.getActionLocation().getPosition().getColumn()
- == attributeRecord.getActionLocation().getPosition().getColumn()
- && record.getActionLocation().getPosition().getOffset()
- == attributeRecord.getActionLocation().getPosition().getOffset()) {
+ && record.getActionLocation().equals(attributeRecord.getActionLocation())) {
return true;
}
}
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ElementsTrimmerTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ElementsTrimmerTest.java
index 83bc78d..9512204 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ElementsTrimmerTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ElementsTrimmerTest.java
@@ -70,8 +70,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testNoUseFeaturesDeclaration"), input);
+ TestUtils.sourceFile(getClass(), "testNoUseFeaturesDeclaration"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
ElementsTrimmer.trim(xmlDocument, mergingReport);
@@ -97,8 +96,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testNothingToTrim"), input);
+ TestUtils.sourceFile(getClass(), "testNothingToTrim"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
ElementsTrimmer.trim(xmlDocument, mergingReport);
@@ -132,8 +130,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testMultipleAboveTwoResults"), input);
+ TestUtils.sourceFile(getClass(), "testMultipleAboveTwoResults"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
ElementsTrimmer.trim(xmlDocument, mergingReport);
@@ -163,8 +160,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testSingleAboveTwoResults"), input);
+ TestUtils.sourceFile(getClass(), "testSingleAboveTwoResults"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
ElementsTrimmer.trim(xmlDocument, mergingReport);
@@ -199,8 +195,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testMultipleBelowTwoResults"), input);
+ TestUtils.sourceFile(getClass(), "testMultipleBelowTwoResults"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
ElementsTrimmer.trim(xmlDocument, mergingReport);
@@ -230,8 +225,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testSingleBelowTwoResults"), input);
+ TestUtils.sourceFile(getClass(), "testSingleBelowTwoResults"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
ElementsTrimmer.trim(xmlDocument, mergingReport);
@@ -277,8 +271,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testMultipleAboveAndBelowTwoResults"), input);
+ TestUtils.sourceFile(getClass(), "testMultipleAboveAndBelowTwoResults"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
ElementsTrimmer.trim(xmlDocument, mergingReport);
@@ -312,8 +305,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testUsesFeatureSplit"), input);
+ TestUtils.sourceFile(getClass(), "testUsesFeatureSplit"), input);
ActionRecorder mockActionRecorder = Mockito.mock(ActionRecorder.class);
MergingReport.Builder mockReport = Mockito.mock(MergingReport.Builder.class);
@@ -354,8 +346,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testUsesFeatureSplit"), input);
+ TestUtils.sourceFile(getClass(), "testUsesFeatureSplit"), input);
ActionRecorder mockActionRecorder = Mockito.mock(ActionRecorder.class);
MergingReport.Builder mockReport = Mockito.mock(MergingReport.Builder.class);
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2SmallTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2SmallTest.java
index 0c14292..8e0432d 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2SmallTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2SmallTest.java
@@ -173,7 +173,7 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testPackageOverride#xml"), xml);
+ TestUtils.sourceFile(getClass(), "testPackageOverride#xml"), xml);
ManifestMerger2.SystemProperty.PACKAGE.addTo(mActionRecorder, refDocument, "com.bar.new");
// verify the package value was overriden.
@@ -189,7 +189,7 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testMissingPackageOverride#xml"), xml);
+ TestUtils.sourceFile(getClass(), "testMissingPackageOverride#xml"), xml);
ManifestMerger2.SystemProperty.PACKAGE.addTo(mActionRecorder, refDocument, "com.bar.new");
// verify the package value was added.
@@ -205,7 +205,7 @@
+ "</manifest>";
XmlDocument document = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(),
+ TestUtils.sourceFile(getClass(),
"testAddingSystemProperties#xml"), xml);
ManifestMerger2.SystemProperty.VERSION_CODE.addTo(mActionRecorder, document, "101");
@@ -241,7 +241,7 @@
+ "</manifest>";
XmlDocument document = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(),
+ TestUtils.sourceFile(getClass(),
"testAddingSystemProperties#xml"), xml
);
@@ -261,7 +261,7 @@
+ "</manifest>";
XmlDocument document = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(),
+ TestUtils.sourceFile(getClass(),
"testAddingSystemProperties#xml"), xml);
// check initial state.
assertEquals("34", document.getXml().getDocumentElement().getAttribute("versionCode"));
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2Test.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2Test.java
index 9233915..13152a7 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2Test.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestMerger2Test.java
@@ -194,7 +194,7 @@
}
XmlDocument expectedResult = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), testFiles.getMain().getName()),
+ TestUtils.sourceFile(getClass(), testFiles.getMain().getName()),
testFiles.getExpectedResult());
Optional<String> comparingMessage =
expectedResult.compareTo(actualResult);
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestModelTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestModelTest.java
index 19e84d2..eae307e 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestModelTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ManifestModelTest.java
@@ -52,8 +52,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testNoUseFeaturesDeclaration"), input);
+ TestUtils.sourceFile(getClass(), "testNoUseFeaturesDeclaration"), input);
XmlElement xmlElement = xmlDocument.getRootNode().getMergeableElements().get(0);
assertEquals("uses-feature",xmlElement.getXml().getNodeName());
@@ -73,8 +72,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testNoUseFeaturesDeclaration"), input);
+ TestUtils.sourceFile(getClass(), "testNoUseFeaturesDeclaration"), input);
XmlElement xmlElement = xmlDocument.getRootNode().getMergeableElements().get(0);
assertEquals("uses-feature",xmlElement.getXml().getNodeName());
@@ -174,8 +172,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testNoUseFeaturesDeclaration"), input);
+ TestUtils.sourceFile(getClass(), "testNoUseFeaturesDeclaration"), input);
XmlElement xmlElement = xmlDocument.getRootNode().getMergeableElements().get(0);
assertEquals("uses-feature",xmlElement.getXml().getNodeName());
@@ -201,8 +198,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testNoUseFeaturesDeclaration"), input);
+ TestUtils.sourceFile(getClass(), "testNoUseFeaturesDeclaration"), input);
XmlElement xmlElement = xmlDocument.getRootNode().getMergeableElements().get(0);
ImmutableList<XmlElement> screenDefinitions = xmlElement.getMergeableElements();
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/MergingReportTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/MergingReportTest.java
index 9758fda..2545f90 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/MergingReportTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/MergingReportTest.java
@@ -18,11 +18,9 @@
import static com.android.manifmerger.MergingReport.Record.Severity;
import static com.android.manifmerger.PlaceholderHandler.KeyBasedValueResolver;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.when;
+import com.android.ide.common.blame.SourceFile;
import com.android.utils.ILogger;
-import com.android.utils.PositionXmlParser;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
@@ -40,7 +38,7 @@
@Mock ILogger mLoggerMock;
@Mock Element mElement;
- @Mock XmlLoader.SourceLocation mSourceLocation;
+ SourceFile mSourceLocation = new SourceFile("location");
@Mock KeyResolver<String> mKeyResolver;
@Mock KeyBasedValueResolver<ManifestMerger2.SystemProperty> mPropertyResolver;
@@ -112,19 +110,20 @@
}
public void testLogging() {
- when(mSourceLocation.print(any(boolean.class))).thenReturn("location");
MergingReport mergingReport = new MergingReport.Builder(mLoggerMock)
- .addMessage(mSourceLocation,0, 0, Severity.INFO, "merging info")
- .addMessage(mSourceLocation,0, 0, Severity.WARNING, "something weird happened")
- .addMessage(mSourceLocation,0, 0, Severity.ERROR, "something bad happened")
+ .addMessage(mSourceLocation,1, 1, Severity.INFO, "merging info")
+ .addMessage(mSourceLocation,1, 1, Severity.WARNING, "something weird happened")
+ .addMessage(mSourceLocation,1, 1, Severity.ERROR, "something bad happened")
.build();
mergingReport.log(mLoggerMock);
- Mockito.verify(mLoggerMock).verbose("location:0:0 Info:\n\tmerging info");
- Mockito.verify(mLoggerMock).warning("location:0:0 Warning:\n\tsomething weird happened");
+ Mockito.verify(mLoggerMock).verbose("location:1:1 Info:\n\tmerging info");
+ Mockito.verify(mLoggerMock).warning("location:1:1 Warning:\n\tsomething weird happened");
Mockito.verify(mLoggerMock).error(null /* throwable */,
- "location:0:0 Error:\n\tsomething bad happened");
+ "location:1:1 Error:\n\tsomething bad happened");
Mockito.verify(mLoggerMock).verbose(Actions.HEADER);
+ Mockito.verify(mLoggerMock).warning("\nSee http://g.co/androidstudio/manifest-merger "
+ + "for more information about the manifest merger.\n");
Mockito.verifyNoMoreInteractions(mLoggerMock);
}
@@ -144,7 +143,7 @@
public void testGetMergedDocument() {
XmlDocument xmlDocument =
- new XmlDocument(new PositionXmlParser(),
+ new XmlDocument(
mSourceLocation,
mKeyResolver,
mPropertyResolver,
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/PlaceholderHandlerTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/PlaceholderHandlerTest.java
index 11a0e29..ab3869d 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/PlaceholderHandlerTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/PlaceholderHandlerTest.java
@@ -18,7 +18,6 @@
import static com.android.manifmerger.PlaceholderHandler.KeyBasedValueResolver;
import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.times;
@@ -26,6 +25,8 @@
import static org.mockito.Mockito.when;
import com.android.annotations.NonNull;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
import com.android.sdklib.mock.MockLog;
import com.google.common.base.Optional;
@@ -82,7 +83,7 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testPlaceholders#xml"), xml);
+ TestUtils.sourceFile(getClass(), "testPlaceholders#xml"), xml);
PlaceholderHandler handler = new PlaceholderHandler();
handler.visit(
@@ -117,7 +118,7 @@
if (!xmlAttribute.getName().toString().contains("name")) {
verify(mActionRecorder).recordAttributeAction(
xmlAttribute,
- PositionImpl.UNKNOWN,
+ SourcePosition.UNKNOWN,
Actions.ActionType.INJECTED,
null);
}
@@ -137,7 +138,7 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testPlaceholders#xml"), xml);
+ TestUtils.sourceFile(getClass(), "testPlaceholders#xml"), xml);
PlaceholderHandler handler = new PlaceholderHandler();
handler.visit(
@@ -180,7 +181,7 @@
if (!xmlAttribute.getName().toString().contains("name")) {
verify(mActionRecorder, times(2)).recordAttributeAction(
xmlAttribute,
- PositionImpl.UNKNOWN,
+ SourcePosition.UNKNOWN,
Actions.ActionType.INJECTED,
null);
@@ -198,13 +199,13 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testPlaceholders#xml"), xml);
+ TestUtils.sourceFile(getClass(), "testPlaceholders#xml"), xml);
PlaceholderHandler handler = new PlaceholderHandler();
handler.visit(ManifestMerger2.MergeType.APPLICATION, refDocument, nullResolver, mBuilder);
// verify the error was recorded.
verify(mBuilder).addMessage(
- any(XmlLoader.SourceLocation.class), anyInt(), anyInt(),
+ any(SourceFilePosition.class),
eq(MergingReport.Record.Severity.ERROR), anyString());
}
@@ -218,13 +219,13 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testPlaceholders#xml"), xml);
+ TestUtils.sourceFile(getClass(), "testPlaceholders#xml"), xml);
PlaceholderHandler handler = new PlaceholderHandler();
handler.visit(ManifestMerger2.MergeType.LIBRARY, refDocument, nullResolver, mBuilder);
// verify the error was recorded.
verify(mBuilder).addMessage(
- any(XmlLoader.SourceLocation.class), anyInt(), anyInt(),
+ any(SourceFilePosition.class),
eq(MergingReport.Record.Severity.INFO), anyString());
}
}
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/PostValidatorTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/PostValidatorTest.java
index b53c93c..cb704dd 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/PostValidatorTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/PostValidatorTest.java
@@ -18,6 +18,7 @@
import com.android.SdkConstants;
import com.android.utils.ILogger;
+import com.google.common.base.Joiner;
import junit.framework.TestCase;
@@ -68,17 +69,16 @@
+ "\n"
+ " <application android:label=\"@string/lib_name\" />\n"
+ "\n"
+ + "\n"
+ " <activity android:name=\"activityOne\"/>"
+ "\n"
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testIncorrectRemoveMain"), main);
+ TestUtils.sourceFile(getClass(), "testIncorrectRemoveMain"), main);
XmlDocument libraryDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testIncorrectRemoveLib"), library);
+ TestUtils.sourceFile(getClass(), "testIncorrectRemoveLib"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -90,7 +90,8 @@
return;
}
}
- fail("No reference to faulty PostValidatorTest#testIncorrectRemoveMain:8 found");
+ fail("No reference to faulty PostValidatorTest#testIncorrectRemoveMain:8 found in: \n" +
+ Joiner.on("\n ").join(mergingReportBuilder.build().getLoggingRecords()));
}
public void testIncorrectReplace()
@@ -123,12 +124,10 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testIncorrectReplaceMain"), main);
+ TestUtils.sourceFile(getClass(), "testIncorrectReplaceMain"), main);
XmlDocument libraryDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testIncorrectReplaceLib"), library);
+ TestUtils.sourceFile(getClass(), "testIncorrectReplaceLib"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -140,7 +139,8 @@
return;
}
}
- fail("No reference to faulty PostValidatorTest#testIncorrectRemoveMain:8 found");
+ fail("No reference to faulty PostValidatorTest#testIncorrectRemoveMain:8 found in: \n" +
+ Joiner.on("\n ").join(mergingReportBuilder.build().getLoggingRecords()));
}
public void testApplicationInvalidOrder()
@@ -161,7 +161,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testApplicationInvalidOrder"), input);
+ TestUtils.sourceFile(getClass(), "testApplicationInvalidOrder"), input);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -193,7 +193,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testApplicationInvalidOrder"), input);
+ TestUtils.sourceFile(getClass(), "testApplicationInvalidOrder"), input);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -227,7 +227,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testApplicationValidOrder"), input);
+ TestUtils.sourceFile(getClass(), "testApplicationValidOrder"), input);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -258,7 +258,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testUsesSdkInvalidOrder"), input);
+ TestUtils.sourceFile(getClass(), "testUsesSdkInvalidOrder"), input);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -290,7 +290,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testUsesSdkInvalidOrder"), input);
+ TestUtils.sourceFile(getClass(), "testUsesSdkInvalidOrder"), input);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -324,7 +324,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testUsesSdkValidOrder"), input);
+ TestUtils.sourceFile(getClass(), "testUsesSdkValidOrder"), input);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -353,7 +353,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testApplicationInvalidOrder"), input);
+ TestUtils.sourceFile(getClass(), "testApplicationInvalidOrder"), input);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -378,7 +378,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testApplicationInvalidOrder"), input);
+ TestUtils.sourceFile(getClass(), "testApplicationInvalidOrder"), input);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
PostValidator.validate(xmlDocument, mergingReportBuilder);
@@ -399,7 +399,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testApplicationInvalidOrder"), input);
+ TestUtils.sourceFile(getClass(), "testApplicationInvalidOrder"), input);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mILogger);
PostValidator.validate(xmlDocument, mergingReportBuilder);
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/PreValidatorTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/PreValidatorTest.java
index 691bf84..010cfa3 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/PreValidatorTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/PreValidatorTest.java
@@ -51,8 +51,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testIncorrectRemove"), input);
+ TestUtils.sourceFile(getClass(), "testIncorrectRemove"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mockLog);
MergingReport.Result validated = PreValidator.validate(mergingReport, xmlDocument);
@@ -78,8 +77,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testIncorrectRemove"), input);
+ TestUtils.sourceFile(getClass(), "testIncorrectRemove"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mockLog);
MergingReport.Result validated = PreValidator.validate(mergingReport, xmlDocument);
@@ -107,8 +105,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testIncorrectRemove"), input);
+ TestUtils.sourceFile(getClass(), "testIncorrectRemove"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mockLog);
MergingReport.Result validated = PreValidator.validate(mergingReport, xmlDocument);
@@ -136,8 +133,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testIncorrectRemove"), input);
+ TestUtils.sourceFile(getClass(), "testIncorrectRemove"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mockLog);
MergingReport.Result validated = PreValidator.validate(mergingReport, xmlDocument);
@@ -166,8 +162,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testIncorrectRemove"), input);
+ TestUtils.sourceFile(getClass(), "testIncorrectRemove"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mockLog);
MergingReport.Result validated = PreValidator.validate(mergingReport, xmlDocument);
@@ -200,8 +195,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testScreenMerging"), input);
+ TestUtils.sourceFile(getClass(), "testScreenMerging"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mockLog);
MergingReport.Result validated = PreValidator.validate(mergingReport, xmlDocument);
@@ -239,8 +233,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testMultipleIntentFilterWithSameKeyValue"), input);
+ TestUtils.sourceFile(getClass(), "testMultipleIntentFilterWithSameKeyValue"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mockLog);
MergingReport.Result validated = PreValidator.validate(mergingReport, xmlDocument);
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/TestUtils.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/TestUtils.java
index e9c6d2e..d509568 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/TestUtils.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/TestUtils.java
@@ -21,11 +21,9 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourceFile;
import com.google.common.base.Optional;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import java.io.IOException;
@@ -61,15 +59,12 @@
}
};
- static class TestSourceLocation extends XmlLoader.SourceLocation {
-
- TestSourceLocation(Class sourceClass, String location) {
- super(sourceClass.getSimpleName() + "#" + location, null);
- }
+ static SourceFile sourceFile(Class sourceClass, String location) {
+ return new SourceFile(sourceClass.getSimpleName() + "#" + location);
}
static XmlDocument xmlDocumentFromString(
- XmlLoader.SourceLocation location,
+ SourceFile location,
String input) throws IOException, SAXException, ParserConfigurationException {
return XmlLoader.load(
@@ -78,7 +73,7 @@
}
static XmlDocument xmlLibraryFromString(
- XmlLoader.SourceLocation location,
+ SourceFile location,
String input) throws IOException, SAXException, ParserConfigurationException {
return XmlLoader.load(
@@ -87,7 +82,7 @@
}
static XmlDocument xmlDocumentFromString(
- XmlLoader.SourceLocation location,
+ SourceFile location,
String input,
XmlDocument.Type type,
Optional<String> mainManifestPackageName) throws IOException, SAXException, ParserConfigurationException {
@@ -97,7 +92,7 @@
static XmlDocument xmlDocumentFromString(
@NonNull KeyResolver<String> selectors,
- @NonNull XmlLoader.SourceLocation location,
+ @NonNull SourceFile location,
String input) throws IOException, SAXException, ParserConfigurationException {
return XmlLoader.load(selectors, NO_PROPERTY_RESOLVER, location, input,
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ToolsInstructionsCleanerTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ToolsInstructionsCleanerTest.java
index 592bd0c..fd35b92 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/ToolsInstructionsCleanerTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/ToolsInstructionsCleanerTest.java
@@ -53,8 +53,7 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testNodeRemoveOperation"), main);
+ TestUtils.sourceFile(getClass(), "testNodeRemoveOperation"), main);
Element rootElement = mainDocument.getRootNode().getXml();
ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog);
@@ -87,8 +86,7 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testNodeRemoveWithChildrenOperation"), main);
+ TestUtils.sourceFile(getClass(), "testNodeRemoveWithChildrenOperation"), main);
Element rootElement = mainDocument.getRootNode().getXml();
ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog);
@@ -117,8 +115,7 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testNodeRemoveOperation"), main);
+ TestUtils.sourceFile(getClass(), "testNodeRemoveOperation"), main);
assertNull(ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog));
}
@@ -136,8 +133,7 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testNodeRemoveOperation"), main);
+ TestUtils.sourceFile(getClass(), "testNodeRemoveOperation"), main);
assertNull(ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog));
}
@@ -158,8 +154,7 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testNodeReplaceOperation"), main);
+ TestUtils.sourceFile(getClass(), "testNodeReplaceOperation"), main);
Element rootElement = mainDocument.getRootNode().getXml();
ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog);
@@ -187,8 +182,7 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testAttributeRemoveOperation"), main);
+ TestUtils.sourceFile(getClass(), "testAttributeRemoveOperation"), main);
Element rootElement = mainDocument.getRootNode().getXml();
ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog);
@@ -220,8 +214,7 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testSelectorRemoval"), main);
+ TestUtils.sourceFile(getClass(), "testSelectorRemoval"), main);
Element rootElement = mainDocument.getRootNode().getXml();
ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog);
@@ -251,8 +244,7 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testNodeReplaceOperation"), main);
+ TestUtils.sourceFile(getClass(), "testNodeReplaceOperation"), main);
Element rootElement = mainDocument.getRootNode().getXml();
ToolsInstructionsCleaner.cleanToolsReferences(mainDocument, mockLog);
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlAttributeTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlAttributeTest.java
index baa38d1..4e9c69c 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlAttributeTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlAttributeTest.java
@@ -116,9 +116,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -149,7 +149,7 @@
Actions.AttributeRecord attributeRecord = attributeRecords.get(0);
assertEquals(Actions.ActionType.REJECTED, attributeRecord.getActionType());
assertEquals(AttributeOperationType.REMOVE, attributeRecord.getOperationType());
- assertEquals(7, attributeRecord.getActionLocation().getPosition().getLine());
+ assertEquals(6, attributeRecord.getActionLocation().getPosition().getStartLine());
}
public void testNamespaceAwareAttributeRemoval()
@@ -177,9 +177,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -226,9 +226,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -287,11 +287,11 @@
+ "</manifest>";
XmlDocument highPriority = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "highPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "highPriority"), higherPriority);
XmlDocument lowPriority = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowPriority"), lowerPriority);
XmlDocument lowestPriority = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowestPriority"), evenLowerPriority);
+ TestUtils.sourceFile(getClass(), "lowestPriority"), evenLowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -329,15 +329,15 @@
assertEquals(Actions.ActionType.REJECTED, attributeRecord.getActionType());
assertEquals(AttributeOperationType.REMOVE, attributeRecord.getOperationType());
assertEquals("XmlAttributeTest#lowPriority",
- attributeRecord.getActionLocation().getSourceLocation().print(true));
- assertEquals(8, attributeRecord.getActionLocation().getPosition().getLine());
+ attributeRecord.getActionLocation().getFile().print(true));
+ assertEquals(7, attributeRecord.getActionLocation().getPosition().getStartLine());
attributeRecord = attributeRecords.get(1);
assertEquals(Actions.ActionType.REJECTED, attributeRecord.getActionType());
assertEquals(AttributeOperationType.REMOVE, attributeRecord.getOperationType());
assertEquals("XmlAttributeTest#lowestPriority",
- attributeRecord.getActionLocation().getSourceLocation().print(true));
- assertEquals(7, attributeRecord.getActionLocation().getPosition().getLine());
+ attributeRecord.getActionLocation().getFile().print(true));
+ assertEquals(6, attributeRecord.getActionLocation().getPosition().getStartLine());
}
public void testDefaultValueIllegalOverriding()
@@ -367,9 +367,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -404,9 +404,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -456,9 +456,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlDocumentTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlDocumentTest.java
index f7ab6c8..8a0d4b8 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlDocumentTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlDocumentTest.java
@@ -74,8 +74,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testMergeableElementsIdentification()"), input);
+ TestUtils.sourceFile(getClass(), "testMergeableElementsIdentification()"), input);
ImmutableList<XmlElement> mergeableElements = xmlDocument.getRootNode().getMergeableElements();
assertEquals(2, mergeableElements.size());
}
@@ -94,8 +93,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testMergeableElementsIdentification()"), input);
+ TestUtils.sourceFile(getClass(), "testMergeableElementsIdentification()"), input);
ImmutableList<XmlElement> mergeableElements = xmlDocument.getRootNode().getMergeableElements();
assertEquals(2, mergeableElements.size());
assertEquals(ManifestModel.NodeTypes.APPLICATION, mergeableElements.get(0).getType());
@@ -119,8 +117,7 @@
+ "</android:manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testMergeableElementsIdentification()"), input);
+ TestUtils.sourceFile(getClass(), "testMergeableElementsIdentification()"), input);
ImmutableList<XmlElement> mergeableElements = xmlDocument.getRootNode().getMergeableElements();
assertEquals(3, mergeableElements.size());
assertEquals(ManifestModel.NodeTypes.APPLICATION, mergeableElements.get(0).getType());
@@ -143,7 +140,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testGetXmlNodeByTypeAndKey()"), input);
+ TestUtils.sourceFile(getClass(), "testGetXmlNodeByTypeAndKey()"), input);
assertTrue(xmlDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne").isPresent());
assertFalse(xmlDocument.getRootNode().getNodeByTypeAndKey(
@@ -170,9 +167,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testSimpleMerge()"), main);
+ TestUtils.sourceFile(getClass(), "testSimpleMerge()"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "testSimpleMerge()"), library);
+ TestUtils.sourceFile(getClass(), "testSimpleMerge()"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -207,9 +204,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff1()"), main);
+ TestUtils.sourceFile(getClass(), "testDiff1()"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff1()"), library);
+ TestUtils.sourceFile(getClass(), "testDiff1()"), library);
assertTrue(mainDocument.compareTo(libraryDocument).isPresent());
}
@@ -236,9 +233,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff2()"), main);
+ TestUtils.sourceFile(getClass(), "testDiff2()"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff2()"), library);
+ TestUtils.sourceFile(getClass(), "testDiff2()"), library);
assertFalse(mainDocument.compareTo(libraryDocument).isPresent());
}
@@ -267,9 +264,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff3()"), main);
+ TestUtils.sourceFile(getClass(), "testDiff3()"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff3()"), library);
+ TestUtils.sourceFile(getClass(), "testDiff3()"), library);
assertFalse(mainDocument.compareTo(libraryDocument).isPresent());
}
@@ -286,7 +283,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testWriting()"), input);
+ TestUtils.sourceFile(getClass(), "testWriting()"), input);
assertEquals(input, xmlDocument.prettyPrint());
}
@@ -321,9 +318,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -403,9 +400,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -461,9 +458,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -499,9 +496,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -541,9 +538,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -588,9 +585,9 @@
+ "</manifest>";
XmlDocument flavorDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "flavor"), flavor);
+ TestUtils.sourceFile(getClass(), "flavor"), flavor);
XmlDocument mainDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
flavorDocument.merge(mainDocument, mergingReportBuilder);
@@ -631,9 +628,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -663,9 +660,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -707,9 +704,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -761,9 +758,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -808,9 +805,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -871,9 +868,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -917,9 +914,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -963,9 +960,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1010,12 +1007,12 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "overlay"),
+ TestUtils.sourceFile(getClass(), "overlay"),
overlay,
XmlDocument.Type.OVERLAY,
Optional.of("com.example.lib3"));
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1054,9 +1051,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1098,9 +1095,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1141,9 +1138,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1183,9 +1180,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1227,9 +1224,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1273,9 +1270,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1310,9 +1307,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1384,9 +1381,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1448,9 +1445,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1518,9 +1515,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1590,9 +1587,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1660,9 +1657,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
@@ -1722,9 +1719,9 @@
+ "</manifest>";
XmlDocument mainDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "main"), main);
+ TestUtils.sourceFile(getClass(), "main"), main);
XmlDocument libraryDocument = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "library"), library);
+ TestUtils.sourceFile(getClass(), "library"), library);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(mLogger);
Optional<XmlDocument> mergedDocument =
mainDocument.merge(libraryDocument, mergingReportBuilder);
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlElementTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlElementTest.java
index b17f79a..d2fe445 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlElementTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlElementTest.java
@@ -72,7 +72,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testToolsNodeInstructions()"), input);
+ TestUtils.sourceFile(getClass(), "testToolsNodeInstructions()"), input);
Optional<XmlElement> activity = xmlDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne");
assertTrue(activity.isPresent());
@@ -106,8 +106,7 @@
try {
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(
- getClass(), "testInvalidNodeInstruction()"), input);
+ TestUtils.sourceFile(getClass(), "testInvalidNodeInstruction()"), input);
xmlDocument.getRootNode();
fail("Exception not thrown");
} catch (IllegalArgumentException expected) {
@@ -152,7 +151,7 @@
// ActivityOne, remove operation.
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testAttributeInstructions()"), input);
+ TestUtils.sourceFile(getClass(), "testAttributeInstructions()"), input);
Optional<XmlElement> activityOptional = xmlDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne");
assertTrue(activityOptional.isPresent());
@@ -229,7 +228,7 @@
// ActivityOne, remove operation.
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testAttributeInstructions()"), input);
+ TestUtils.sourceFile(getClass(), "testAttributeInstructions()"), input);
Optional<XmlElement> activityOptional = xmlDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne");
assertTrue(activityOptional.isPresent());
@@ -254,7 +253,7 @@
// ActivityOne, remove operation.
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testAttributeInstructions()"), input);
+ TestUtils.sourceFile(getClass(), "testAttributeInstructions()"), input);
Optional<XmlElement> activityOptional = xmlDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne");
assertTrue(activityOptional.isPresent());
@@ -282,7 +281,7 @@
try {
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff6()"), input);
+ TestUtils.sourceFile(getClass(), "testDiff6()"), input);
xmlDocument.getRootNode();
fail("Exception not thrown");
} catch (RuntimeException expected) {
@@ -305,7 +304,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testOtherToolsInstruction"), input);
+ TestUtils.sourceFile(getClass(), "testOtherToolsInstruction"), input);
xmlDocument.getRootNode();
}
@@ -335,9 +334,9 @@
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff1()"), reference);
+ TestUtils.sourceFile(getClass(), "testDiff1()"), reference);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff1()"), other);
+ TestUtils.sourceFile(getClass(), "testDiff1()"), other);
assertFalse(refDocument.getRootNode().getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityOne").get()
@@ -372,9 +371,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff2()"), reference);
+ TestUtils.sourceFile(getClass(), "testDiff2()"), reference);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff2()"), other);
+ TestUtils.sourceFile(getClass(), "testDiff2()"), other);
assertTrue(refDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne").get()
@@ -411,9 +410,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff3()"), reference);
+ TestUtils.sourceFile(getClass(), "testDiff3()"), reference);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff3()"), other);
+ TestUtils.sourceFile(getClass(), "testDiff3()"), other);
assertTrue(refDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne").get()
@@ -450,9 +449,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff4()"), reference);
+ TestUtils.sourceFile(getClass(), "testDiff4()"), reference);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff4()"), other);
+ TestUtils.sourceFile(getClass(), "testDiff4()"), other);
assertTrue(refDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne").get()
.compareTo(
@@ -489,9 +488,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff5()"), reference);
+ TestUtils.sourceFile(getClass(), "testDiff5()"), reference);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff5()"), other);
+ TestUtils.sourceFile(getClass(), "testDiff5()"), other);
assertFalse(refDocument.compareTo(otherDocument).isPresent());
}
@@ -525,9 +524,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff6()"), reference);
+ TestUtils.sourceFile(getClass(), "testDiff6()"), reference);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testDiff6()"), other);
+ TestUtils.sourceFile(getClass(), "testDiff6()"), other);
assertTrue(
refDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne").get()
@@ -568,9 +567,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testMerge()"), reference);
+ TestUtils.sourceFile(getClass(), "testMerge()"), reference);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testMerge()"), other);
+ TestUtils.sourceFile(getClass(), "testMerge()"), other);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -618,9 +617,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -669,9 +668,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -717,9 +716,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -772,9 +771,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -827,9 +826,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -875,9 +874,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -929,9 +928,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -976,9 +975,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -1029,9 +1028,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -1078,9 +1077,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -1128,9 +1127,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -1172,9 +1171,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -1216,9 +1215,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -1266,9 +1265,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -1309,9 +1308,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -1348,9 +1347,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -1395,9 +1394,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriority"), lowerPriority);
+ TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -1480,11 +1479,11 @@
KeyResolver<String> keyResolver = (KeyResolver<String>) Mockito.mock(KeyResolver.class);
when(keyResolver.resolve(any(String.class))).thenReturn("valid");
XmlDocument refDocument = TestUtils.xmlDocumentFromString(keyResolver,
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument firstLibrary = TestUtils.xmlDocumentFromString(keyResolver,
- new TestUtils.TestSourceLocation(getClass(), "lowerPriorityOne"), lowerPriorityOne);
+ TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
XmlDocument secondLibrary = TestUtils.xmlDocumentFromString(keyResolver,
- new TestUtils.TestSourceLocation(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
+ TestUtils.sourceFile(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -1547,11 +1546,11 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument firstLibrary = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriorityOne"), lowerPriorityOne);
+ TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
XmlDocument secondLibrary = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
+ TestUtils.sourceFile(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -1601,9 +1600,9 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument firstLibrary = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriorityOne"), lowerPriorityOne);
+ TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -1675,11 +1674,11 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument firstLibrary = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriorityOne"), lowerPriorityOne);
+ TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
XmlDocument secondLibrary = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
+ TestUtils.sourceFile(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
@@ -1756,10 +1755,10 @@
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "higherPriority"), higherPriority);
+ TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument firstLibrary = TestUtils.xmlLibraryFromString(
- new TestUtils.TestSourceLocation(getClass(), "lowerPriorityOne"), lowerPriorityOne);
+ TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlLoaderTest.java b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlLoaderTest.java
index 65032a2..cb5ea4a 100644
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlLoaderTest.java
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/XmlLoaderTest.java
@@ -17,10 +17,10 @@
package com.android.manifmerger;
import com.android.SdkConstants;
+import com.android.ide.common.blame.SourcePosition;
import com.android.ide.common.xml.XmlFormatPreferences;
import com.android.ide.common.xml.XmlFormatStyle;
import com.android.ide.common.xml.XmlPrettyPrinter;
-import com.android.utils.PositionXmlParser;
import com.google.common.base.Optional;
import junit.framework.TestCase;
@@ -51,7 +51,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(),"testToolsPrefix()"), input);
+ TestUtils.sourceFile(getClass(), "testToolsPrefix()"), input);
Optional<XmlElement> applicationOptional = xmlDocument.getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.APPLICATION, null);
assertTrue(applicationOptional.isPresent());
@@ -87,7 +87,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(),"testPrettyPrint()"), input);
+ TestUtils.sourceFile(getClass(), "testPrettyPrint()"), input);
Optional<XmlElement> applicationOptional = xmlDocument.getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.APPLICATION, null);
assertTrue(applicationOptional.isPresent());
@@ -110,7 +110,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(),"testToolsPrefix()"),input);
+ TestUtils.sourceFile(getClass(), "testToolsPrefix()"),input);
Optional<XmlElement> applicationOptional = xmlDocument.getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.APPLICATION, null);
assertTrue(applicationOptional.isPresent());
@@ -123,17 +123,17 @@
assertEquals("replace", tools.getNodeValue());
// check positions.
- PositionXmlParser.Position applicationPosition = applicationOptional.get().getPosition();
+ SourcePosition applicationPosition = applicationOptional.get().getPosition();
assertNotNull(applicationPosition);
- assertEquals(6, applicationPosition.getLine());
- assertEquals(5, applicationPosition.getColumn());
+ assertEquals(6, applicationPosition.getStartLine() + 1);
+ assertEquals(5, applicationPosition.getStartColumn() + 1);
XmlAttribute xmlAttribute =
new XmlAttribute(applicationOptional.get(), tools, null /* AttributeModel */);
- PositionXmlParser.Position toolsPosition = xmlAttribute.getPosition();
+ SourcePosition toolsPosition = xmlAttribute.getPosition();
assertNotNull(toolsPosition);
- assertEquals(6, toolsPosition.getLine());
- assertEquals(51, toolsPosition.getColumn());
+ assertEquals(6, toolsPosition.getStartLine() + 1);
+ assertEquals(51, toolsPosition.getStartColumn() + 1);
}
public void testUnusualPrefixes()
@@ -150,7 +150,7 @@
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
- new TestUtils.TestSourceLocation(getClass(), "testUnusualPrefixes()"), input);
+ TestUtils.sourceFile(getClass(), "testUnusualPrefixes()"), input);
Optional<XmlElement> applicationOptional = xmlDocument.getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.APPLICATION, null);
assertTrue(applicationOptional.isPresent());
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/08_no_library_package_provided.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/08_no_library_package_provided.xml
index 4d2b87a..bbaf28b 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/08_no_library_package_provided.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/08_no_library_package_provided.xml
@@ -205,4 +205,4 @@
</manifest>
@errors
-WARNING:Missing 'package' declaration in manifest at ManifestMerger2Test1_lib1.xml:1:1
\ No newline at end of file
+WARNING:Missing 'package' declaration in manifest at ManifestMerger2Test1_lib1.xml:1:1-12
\ No newline at end of file
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/09b_overlay_package_different.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/09b_overlay_package_different.xml
index 0e94eae..036c1a2 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/09b_overlay_package_different.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/09b_overlay_package_different.xml
@@ -143,5 +143,5 @@
@errors
-ERROR:Overlay manifest:package attribute declared at ManifestMerger2Test0_overlay1.xml:3:5 value=\(suffix.not.allowed.in.overlay\)
- has a different value=\(com.example.app1\) declared in main manifest at ManifestMerger2Test1_main.xml:9:5
\ No newline at end of file
+ERROR:Overlay manifest:package attribute declared at ManifestMerger2Test0_overlay1.xml:3:5-44 value=\(suffix.not.allowed.in.overlay\)
+ has a different value=\(com.example.app1\) declared in main manifest at ManifestMerger2Test1_main.xml:9:5-31
\ No newline at end of file
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/17_fqcn_conflict.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/17_fqcn_conflict.xml
index 83adc41..24e80ce 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/17_fqcn_conflict.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/17_fqcn_conflict.xml
@@ -114,5 +114,5 @@
</manifest>
@errors
-ERROR:Attribute application@name value=\(com.example.app1.TheApp\) from ManifestMerger2Test0_main.xml:8:13
- is also present at ManifestMerger2Test1_lib1_widget.xml:6:18 value=\(com.example.lib1.TheApp\)
\ No newline at end of file
+ERROR:Attribute application@name value=\(com.example.app1.TheApp\) from ManifestMerger2Test0_main.xml:8:13-34
+ is also present at ManifestMerger2Test1_lib1_widget.xml:6:18-39 value=\(com.example.lib1.TheApp\).
\ No newline at end of file
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/18_fqcn_success.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/18_fqcn_success.xml
index bf25232..6850048 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/18_fqcn_success.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/18_fqcn_success.xml
@@ -115,4 +115,4 @@
</manifest>
@errors
-WARNING:Missing 'package' declaration in manifest at ManifestMerger2Test4_lib4_not_package.xml:1:1
\ No newline at end of file
+WARNING:Missing 'package' declaration in manifest at ManifestMerger2Test4_lib4_not_package.xml:1:1-15:12
\ No newline at end of file
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/21_uses_main_errors.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/21_uses_main_errors.xml
index 0a85ae3..eab41c6 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/21_uses_main_errors.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/21_uses_main_errors.xml
@@ -58,6 +58,6 @@
@errors
-ERROR:Attribute uses-library#com.example.SomeLibrary2_RequiredTrue@required at ManifestMerger2Test0_main.xml:27:13 has an illegal value=\(booh!\), expected 'true' or 'false'
-ERROR:Element uses-library#com.example.SomeLibrary3_RequiredFalse at ManifestMerger2Test0_main.xml:35:9 duplicated with element declared at ManifestMerger2Test0_main.xml:30:9
+ERROR:Attribute uses-library#com.example.SomeLibrary2_RequiredTrue@required at ManifestMerger2Test0_main.xml:27:13-37 has an illegal value=\(booh!\), expected 'true' or 'false'
+ERROR:Element uses-library#com.example.SomeLibrary3_RequiredFalse at ManifestMerger2Test0_main.xml:35:9-37:39 duplicated with element declared at ManifestMerger2Test0_main.xml:30:9-32:40
ERROR:Validation failed, exiting
\ No newline at end of file
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/22_uses_lib_errors.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/22_uses_lib_errors.xml
index 25ff7b8..4cda757 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/22_uses_lib_errors.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/22_uses_lib_errors.xml
@@ -100,9 +100,9 @@
@errors
-ERROR:Missing 'name' key attribute on element uses-library at ManifestMerger2Test1_lib1.xml:10:9
-ERROR:Missing 'name' key attribute on element uses-library at ManifestMerger2Test1_lib1.xml:9:9
-ERROR:Missing 'name' key attribute on element uses-library at ManifestMerger2Test1_lib1.xml:8:9
-WARNING:Element uses-library#com.example.SomeLibrary3_RequiredFalse at ManifestMerger2Test0_main.xml:35:9 duplicated with element declared at ManifestMerger2Test0_main.xml:30:9
-ERROR:Attribute uses-library#com.example.SomeLibrary4_RequiredFalse@required at ManifestMerger2Test1_lib1.xml:25:13 has an illegal value=\(foo\), expected 'true' or 'false'
+ERROR:Missing 'name' key attribute on element uses-library at ManifestMerger2Test1_lib1.xml:10:9-49
+ERROR:Missing 'name' key attribute on element uses-library at ManifestMerger2Test1_lib1.xml:9:9-50
+ERROR:Missing 'name' key attribute on element uses-library at ManifestMerger2Test1_lib1.xml:8:9-25
+WARNING:Element uses-library#com.example.SomeLibrary3_RequiredFalse at ManifestMerger2Test0_main.xml:35:9-37:40 duplicated with element declared at ManifestMerger2Test0_main.xml:30:9-32:40
+ERROR:Attribute uses-library#com.example.SomeLibrary4_RequiredFalse@required at ManifestMerger2Test1_lib1.xml:25:13-35 has an illegal value=\(foo\), expected 'true' or 'false'
ERROR:Validation failed, exiting
\ No newline at end of file
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/30_uses_sdk_ok.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/30_uses_sdk_ok.xml
index 3109de4..7fdc779 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/30_uses_sdk_ok.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/30_uses_sdk_ok.xml
@@ -92,12 +92,10 @@
/>
<android:uses-permission
- android:name="android.permission.WRITE_EXTERNAL_STORAGE"
- android:maxSdkVersion="18" />
+ android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<android:uses-permission android:name="android.permission.READ_PHONE_STATE" />
<android:uses-permission
- android:name="android.permission.READ_EXTERNAL_STORAGE"
- android:maxSdkVersion="18" />
+ android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- I should remain last -->
<application android:name="com.example.TheApp" />
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/41_uses_feat_errors.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/41_uses_feat_errors.xml
index 3701656..247cc32 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/41_uses_feat_errors.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/41_uses_feat_errors.xml
@@ -194,10 +194,10 @@
@errors
-ERROR:Missing one of the key attributes 'name,glEsVersion' on element uses-feature at ManifestMerger2Test1_lib1.xml:6:5
-ERROR:Missing one of the key attributes 'name,glEsVersion' on element uses-feature at ManifestMerger2Test1_lib1.xml:7:5
-ERROR:Missing one of the key attributes 'name,glEsVersion' on element uses-feature at ManifestMerger2Test1_lib1.xml:8:5
-ERROR:Attribute uses-feature#com.example.SomeFeature3_RequiredFalse@required at ManifestMerger2Test1_lib1.xml:18:9 has an illegal value=\(booh!\), expected 'true' or 'false'
-ERROR:Attribute uses-feature#com.example.SomeFeature4_RequiredFalse@required at ManifestMerger2Test1_lib1.xml:23:9 has an illegal value=\(foo\), expected 'true' or 'false'
+ERROR:Missing one of the key attributes 'name,glEsVersion' on element uses-feature at ManifestMerger2Test1_lib1.xml:6:5-21
+ERROR:Missing one of the key attributes 'name,glEsVersion' on element uses-feature at ManifestMerger2Test1_lib1.xml:7:5-46
+ERROR:Missing one of the key attributes 'name,glEsVersion' on element uses-feature at ManifestMerger2Test1_lib1.xml:8:5-45
+ERROR:Attribute uses-feature#com.example.SomeFeature3_RequiredFalse@required at ManifestMerger2Test1_lib1.xml:18:9-33 has an illegal value=\(booh!\), expected 'true' or 'false'
+ERROR:Attribute uses-feature#com.example.SomeFeature4_RequiredFalse@required at ManifestMerger2Test1_lib1.xml:23:9-31 has an illegal value=\(foo\), expected 'true' or 'false'
ERROR:Validation failed, exiting
-WARNING:Element uses-feature#com.example.SomeFeature3_RequiredFalse at ManifestMerger2Test0_main.xml:26:5 duplicated with element declared at ManifestMerger2Test0_main.xml:21:5
+WARNING:Element uses-feature#com.example.SomeFeature3_RequiredFalse at ManifestMerger2Test0_main.xml:26:5-28:36 duplicated with element declared at ManifestMerger2Test0_main.xml:21:5-23:36
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/47_uses_feat_gles_conflict.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/47_uses_feat_gles_conflict.xml
index 8b99039..8cd5392 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/47_uses_feat_gles_conflict.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/47_uses_feat_gles_conflict.xml
@@ -151,8 +151,8 @@
@errors
-ERROR:Attribute uses-feature#0@glEsVersion at ManifestMerger2Test2_lib2.xml:21:9 is not a valid hexadecimal 32 bit value, found 0
-ERROR:Attribute uses-feature#0x0000FFFF@glEsVersion at ManifestMerger2Test2_lib2.xml:27:9 is not a valid hexadecimal value, minimum is 0x00010000, maximum is 0x7FFFFFFF, found 0x0000FFFF
-ERROR:Attribute uses-feature#0xFFFFFFFF@glEsVersion at ManifestMerger2Test2_lib2.xml:34:9 is not a valid hexadecimal value, minimum is 0x00010000, maximum is 0x7FFFFFFF, found 0xFFFFFFFF
-ERROR:Attribute uses-feature#0xFFFFFFFFFFFFFFFF@glEsVersion at ManifestMerger2Test2_lib2.xml:40:9 is not a valid hexadecimal 32 bit value, found 0xFFFFFFFFFFFFFFFF
+ERROR:Attribute uses-feature#0@glEsVersion at ManifestMerger2Test2_lib2.xml:21:9-32 is not a valid hexadecimal 32 bit value, found 0
+ERROR:Attribute uses-feature#0x0000FFFF@glEsVersion at ManifestMerger2Test2_lib2.xml:27:9-41 is not a valid hexadecimal value, minimum is 0x00010000, maximum is 0x7FFFFFFF, found 0x0000FFFF
+ERROR:Attribute uses-feature#0xFFFFFFFF@glEsVersion at ManifestMerger2Test2_lib2.xml:34:9-41 is not a valid hexadecimal value, minimum is 0x00010000, maximum is 0x7FFFFFFF, found 0xFFFFFFFF
+ERROR:Attribute uses-feature#0xFFFFFFFFFFFFFFFF@glEsVersion at ManifestMerger2Test2_lib2.xml:40:9-49 is not a valid hexadecimal 32 bit value, found 0xFFFFFFFFFFFFFFFF
ERROR:Validation failed, exiting
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/50_uses_conf_warning.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/50_uses_conf_warning.xml
index 39cbf3d..1d777cc 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/50_uses_conf_warning.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/50_uses_conf_warning.xml
@@ -155,9 +155,9 @@
@errors
-ERROR:Attribute uses-configuration@reqFiveWayNav value=\(true\) from ManifestMerger2Test0_main.xml:18:9
- is also present at ManifestMerger2Test2_lib2.xml:7:9 value=\(false\)
-ERROR:Attribute uses-configuration@reqNavigation value=\(nonav\) from ManifestMerger2Test0_main.xml:21:9
- is also present at ManifestMerger2Test2_lib2.xml:8:9 value=\(trackball\)
-ERROR:Attribute uses-configuration@reqTouchScreen value=\(stylus\) from ManifestMerger2Test0_main.xml:22:9
- is also present at ManifestMerger2Test2_lib2.xml:9:9 value=\(finger\)
\ No newline at end of file
+ERROR:Attribute uses-configuration@reqFiveWayNav value=\(true\) from ManifestMerger2Test0_main.xml:18:9-37
+ is also present at ManifestMerger2Test2_lib2.xml:7:9-38 value=\(false\).
+ERROR:Attribute uses-configuration@reqNavigation value=\(nonav\) from ManifestMerger2Test0_main.xml:21:9-38
+ is also present at ManifestMerger2Test2_lib2.xml:8:9-42 value=\(trackball\).
+ERROR:Attribute uses-configuration@reqTouchScreen value=\(stylus\) from ManifestMerger2Test0_main.xml:22:9-40
+ is also present at ManifestMerger2Test2_lib2.xml:9:9-40 value=\(finger\).
\ No newline at end of file
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/65_override_app.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/65_override_app.xml
index 2ffafe8..3ae1b7b 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/65_override_app.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/65_override_app.xml
@@ -190,4 +190,4 @@
@errors
-WARNING:Missing 'package' declaration in manifest at ManifestMerger2Test4_lib4_not_package.xml:4:1
+WARNING:Missing 'package' declaration in manifest at ManifestMerger2Test4_lib4_not_package.xml:4:1-21:12
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/70_expand_fqcns.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/70_expand_fqcns.xml
index db5ac81..ef23160 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/70_expand_fqcns.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/70_expand_fqcns.xml
@@ -86,12 +86,10 @@
</activity>
</application>
<android:uses-permission
- android:name="android.permission.WRITE_EXTERNAL_STORAGE"
- android:maxSdkVersion="18" />
+ android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<android:uses-permission android:name="android.permission.READ_PHONE_STATE" />
<android:uses-permission
- android:name="android.permission.READ_EXTERNAL_STORAGE"
- android:maxSdkVersion="18" />
+ android:name="android.permission.READ_EXTERNAL_STORAGE" />
</manifest>
diff --git a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/77_app_metadata_conflict.xml b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/77_app_metadata_conflict.xml
index 81b0a42..74f889e 100755
--- a/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/77_app_metadata_conflict.xml
+++ b/build-system/manifest-merger/src/test/java/com/android/manifmerger/data2/77_app_metadata_conflict.xml
@@ -122,5 +122,5 @@
@errors
-ERROR:Attribute meta-data#name.for.backup.api.key@value value=\(the_apps_backup_api_key\) from ManifestMerger2Test0_main.xml:33:13
- is also present at ManifestMerger2Test1_lib1.xml:16:13 value=\(the_library1_backup_api_key\)
\ No newline at end of file
+ERROR:Attribute meta-data#name.for.backup.api.key@value value=\(the_apps_backup_api_key\) from ManifestMerger2Test0_main.xml:33:13-52
+ is also present at ManifestMerger2Test1_lib1.xml:16:13-56 value=\(the_library1_backup_api_key\).
\ No newline at end of file
diff --git a/build-system/profile/build.gradle b/build-system/profile/build.gradle
index cfd3bdd..b339a6c 100644
--- a/build-system/profile/build.gradle
+++ b/build-system/profile/build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'java'
+apply plugin: 'jacoco'
dependencies {
compile project(':base:annotations')
diff --git a/build-system/profile/src/main/java/com/android/builder/profile/ExecutionType.java b/build-system/profile/src/main/java/com/android/builder/profile/ExecutionType.java
index 2b495ae..23330e1 100644
--- a/build-system/profile/src/main/java/com/android/builder/profile/ExecutionType.java
+++ b/build-system/profile/src/main/java/com/android/builder/profile/ExecutionType.java
@@ -38,9 +38,12 @@
VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT(9),
VARIANT_MANAGER_CREATE_LINT_TASKS(10),
VARIANT_MANAGER_CREATE_TESTS_TASKS(11),
+ VARIANT_MANAGER_CREATE_VARIANTS(12),
RESOLVE_DEPENDENCIES(12),
INITIAL_METADATA(100),
FINAL_METADATA(101),
+ GENERAL_CONFIG(102),
+ VARIANT_CONFIG(103),
// ApplicationTaskManager per variant tasks.
APP_TASK_MANAGER_CREATE_MERGE_MANIFEST_TASK(1000),
@@ -55,8 +58,8 @@
APP_TASK_MANAGER_CREATE_NDK_TASK(1009),
APP_TASK_MANAGER_CREATE_SPLIT_TASK(1010),
APP_TASK_MANAGER_CREATE_PACKAGING_TASK(1011),
- APP_TASK_MANAGER_CREATE_PREPROCESS_RESOURCES_TASK(1012),
- APP_TASK_MANAGER_CREATE_BACKPORT_RESOURCES_TASK(1013),
+ @Deprecated APP_TASK_MANAGER_CREATE_PREPROCESS_RESOURCES_TASK(1012),
+ @Deprecated APP_TASK_MANAGER_CREATE_BACKPORT_RESOURCES_TASK(1013),
APP_TASK_MANAGER_CREATE_LINT_TASK(1014),
// LibraryTaskManager per variant tasks.
@@ -76,7 +79,7 @@
LIB_TASK_MANAGER_CREATE_POST_COMPILATION_TASK(2013),
LIB_TASK_MANAGER_CREATE_PROGUARD_TASK(2014),
LIB_TASK_MANAGER_CREATE_PACKAGE_LOCAL_JAR(2015),
- LIB_TASK_MANAGER_CREATE_BACKPORT_RESOURCES_TASK(2016),
+ @Deprecated LIB_TASK_MANAGER_CREATE_BACKPORT_RESOURCES_TASK(2016),
LIB_TASK_MANAGER_CREATE_LINT_TASK(2017),
// TASK_EXECUTION
@@ -105,15 +108,13 @@
TASK_COPY(3022),
TASK_LINT(3023);
-
int getId() {
return id;
}
-
private final int id;
+
ExecutionType(int id) {
this.id = id;
}
-
}
diff --git a/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorder.java b/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorder.java
index 9c26561..c516726 100644
--- a/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorder.java
+++ b/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorder.java
@@ -19,19 +19,16 @@
import com.android.annotations.NonNull;
import com.android.builder.tasks.Job;
import com.android.builder.tasks.JobContext;
-import com.android.builder.tasks.QueueThreadContext;
+import com.android.builder.tasks.QueueThreadContextAdapter;
import com.android.builder.tasks.Task;
import com.android.builder.tasks.WorkQueue;
import com.android.utils.ILogger;
import com.google.gson.Gson;
-import java.io.File;
-import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
/**
* Records all the {@link ExecutionRecord} for a process, in order it was received and sends then
@@ -66,21 +63,13 @@
- private class WorkQueueContext implements QueueThreadContext<ExecutionRecordWriter> {
- @Override
- public void creation(@NonNull Thread t) throws IOException {
- }
-
+ private class WorkQueueContext extends QueueThreadContextAdapter<ExecutionRecordWriter> {
@Override
public void runTask(@NonNull Job<ExecutionRecordWriter> job) throws Exception {
job.runTask(singletonJobContext);
}
@Override
- public void destruction(@NonNull Thread t) throws IOException, InterruptedException {
- }
-
- @Override
public void shutdown() {
try {
singletonJobContext.getPayload().close();
diff --git a/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorderFactory.java b/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorderFactory.java
index 7ab4269..ca42078 100644
--- a/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorderFactory.java
+++ b/build-system/profile/src/main/java/com/android/builder/profile/ProcessRecorderFactory.java
@@ -22,6 +22,8 @@
import com.android.utils.ILogger;
import com.android.utils.StdLogger;
import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
import com.google.common.io.ByteStreams;
import java.io.BufferedInputStream;
@@ -72,7 +74,10 @@
}
}
- public static void initialize(ILogger logger, File out) throws IOException {
+ public static void initialize(
+ @NonNull ILogger logger,
+ @NonNull File out,
+ @NonNull List<Recorder.Property> properties) throws IOException {
synchronized (LOCK) {
if (sINSTANCE.isInitialized() || !isEnabled()) {
@@ -81,19 +86,40 @@
sINSTANCE.setLogger(logger);
sINSTANCE.setOutputFile(out);
sINSTANCE.setRecordWriter(new ProcessRecorder.JsonRecordWriter(new FileWriter(out)));
- publishInitialRecords();
+ sINSTANCE.get(); // Initialize the ProcessRecorder instance
+ publishInitialRecords(properties);
}
}
- public static void publishInitialRecords() {
- ThreadRecorder.get().record(ExecutionType.INITIAL_METADATA, Recorder.EmptyBlock,
- new Recorder.Property("build_id", UUID.randomUUID().toString()),
- new Recorder.Property("os_name", System.getProperty("os.name")),
- new Recorder.Property("os_version", System.getProperty("os.version")),
- new Recorder.Property("java_version", System.getProperty("java.version")),
- new Recorder.Property("java_vm_version", System.getProperty("java.vm.version")),
- new Recorder.Property("max_memory",
- Long.toString(Runtime.getRuntime().maxMemory())));
+ public static void publishInitialRecords(@NonNull List<Recorder.Property> properties) {
+
+ List<Recorder.Property> propertyList = Lists.newArrayListWithExpectedSize(
+ 6 + properties.size());
+
+ propertyList.add(new Recorder.Property(
+ "build_id",
+ UUID.randomUUID().toString()));
+ propertyList.add(new Recorder.Property(
+ "os_name",
+ System.getProperty("os.name")));
+ propertyList.add(new Recorder.Property(
+ "os_version",
+ System.getProperty("os.version")));
+ propertyList.add(new Recorder.Property(
+ "java_version",
+ System.getProperty("java.version")));
+ propertyList.add(new Recorder.Property(
+ "java_vm_version",
+ System.getProperty("java.vm.version")));
+ propertyList.add(new Recorder.Property(
+ "max_memory",
+ Long.toString(Runtime.getRuntime().maxMemory())));
+ propertyList.addAll(properties);
+
+ ThreadRecorder.get().record(
+ ExecutionType.INITIAL_METADATA,
+ Recorder.EmptyBlock,
+ propertyList);
}
private static boolean sENABLED = !Strings.isNullOrEmpty(System.getenv("RECORD_SPANS"));
@@ -115,7 +141,8 @@
ProcessRecorder.resetForTests();
setEnabled(true);
sINSTANCE.setRecordWriter(recordWriter);
- publishInitialRecords();
+ sINSTANCE.get(); // Initialize the ProcessRecorder instance
+ publishInitialRecords(ImmutableList.<Recorder.Property>of());
}
static boolean isEnabled() {
@@ -146,7 +173,7 @@
return sINSTANCE;
}
- private boolean isInitialized() {
+ boolean isInitialized() {
return processRecorder != null;
}
diff --git a/build-system/profile/src/main/java/com/android/builder/profile/Recorder.java b/build-system/profile/src/main/java/com/android/builder/profile/Recorder.java
index b83d419..7d3815c 100644
--- a/build-system/profile/src/main/java/com/android/builder/profile/Recorder.java
+++ b/build-system/profile/src/main/java/com/android/builder/profile/Recorder.java
@@ -20,6 +20,7 @@
import com.android.annotations.Nullable;
import com.google.common.base.Objects;
+import java.util.List;
import java.util.concurrent.Callable;
/**
@@ -113,6 +114,23 @@
<T> T record(@NonNull ExecutionType executionType, @NonNull Block<T> block,
Property... properties);
+
+ /**
+ * Records the time elapsed while executing a {@link Block} and saves the resulting {@link
+ * ExecutionRecord} to {@link ProcessRecorder}.
+ *
+ * @param executionType the task type, so aggregation can be performed.
+ * @param block the block of code to execution and measure.
+ * @param properties optional list of free formed properties to save in the {@link
+ * ExecutionRecord}
+ * @param <T> the type of the returned value from the block.
+ * @return the value returned from the block (including null) or null if the block execution
+ * raised an exception which was subsequently swallowed by {@link Block#handleException(Exception)}
+ */
+ @Nullable
+ <T> T record(@NonNull ExecutionType executionType, @NonNull Block<T> block,
+ @NonNull List<Property> properties);
+
/**
* Allocate a new recordId that can be used to create a {@link ExecutionRecord} and record
* an execution span. This method is useful when the code span to measure cannot be expressed
diff --git a/build-system/profile/src/main/java/com/android/builder/profile/ThreadRecorder.java b/build-system/profile/src/main/java/com/android/builder/profile/ThreadRecorder.java
index 94c8baa..60b8fee 100644
--- a/build-system/profile/src/main/java/com/android/builder/profile/ThreadRecorder.java
+++ b/build-system/profile/src/main/java/com/android/builder/profile/ThreadRecorder.java
@@ -21,9 +21,9 @@
import com.google.common.collect.ImmutableList;
import java.util.ArrayDeque;
+import java.util.Collections;
import java.util.Deque;
import java.util.List;
-import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -44,6 +44,13 @@
@Override
public <T> T record(@NonNull ExecutionType executionType, @NonNull Block<T> block,
Property... properties) {
+ return record(executionType, block, Collections.<Property>emptyList());
+ }
+
+ @Nullable
+ @Override
+ public <T> T record(@NonNull ExecutionType executionType, @NonNull Block<T> block,
+ @NonNull List<Property> properties) {
try {
return block.call();
} catch (Exception e) {
@@ -66,7 +73,7 @@
public static Recorder get() {
- return ProcessRecorderFactory.isEnabled() ? recorder : dummyRecorder;
+ return ProcessRecorderFactory.getFactory().isInitialized() ? recorder : dummyRecorder;
}
private static class PartialRecord {
@@ -118,26 +125,33 @@
ProcessRecorder.get().writeRecord(executionRecord);
}
-
@Nullable
@Override
public <T> T record(@NonNull ExecutionType executionType, @NonNull Block<T> block,
Property... properties) {
+ List<Recorder.Property> propertyList = properties == null
+ ? ImmutableList.<Recorder.Property>of()
+ : ImmutableList.copyOf(properties);
+
+ return record(executionType, block, propertyList);
+ }
+
+ @Nullable
+ @Override
+ public <T> T record(@NonNull ExecutionType executionType, @NonNull Block<T> block,
+ @NonNull List<Property> properties) {
+
long thisRecordId = ProcessRecorder.allocateRecordId();
// am I a child ?
Long parentId = recordStacks.get().peek();
- List<Recorder.Property> propertyList = properties == null
- ? ImmutableList.<Recorder.Property>of()
- : ImmutableList.copyOf(properties);
-
long startTimeInMs = System.currentTimeMillis();
final PartialRecord currentRecord = new PartialRecord(executionType,
thisRecordId, parentId == null ? 0 : parentId,
- startTimeInMs, propertyList);
+ startTimeInMs, properties);
recordStacks.get().push(thisRecordId);
try {
diff --git a/build-system/profile/src/main/java/com/android/builder/tasks/Job.java b/build-system/profile/src/main/java/com/android/builder/tasks/Job.java
index a10ae8d..61c8897 100644
--- a/build-system/profile/src/main/java/com/android/builder/tasks/Job.java
+++ b/build-system/profile/src/main/java/com/android/builder/tasks/Job.java
@@ -16,6 +16,7 @@
package com.android.builder.tasks;
+import com.android.annotations.NonNull;
import com.google.common.base.Objects;
import java.io.IOException;
@@ -42,7 +43,7 @@
return mJobTitle;
}
- public void runTask(JobContext<T> jobContext) throws IOException {
+ public void runTask(@NonNull JobContext<T> jobContext) throws IOException {
mTask.run(this, jobContext);
}
diff --git a/build-system/profile/src/main/java/com/android/builder/tasks/JobContext.java b/build-system/profile/src/main/java/com/android/builder/tasks/JobContext.java
index 969fb77..0bd9ac2 100644
--- a/build-system/profile/src/main/java/com/android/builder/tasks/JobContext.java
+++ b/build-system/profile/src/main/java/com/android/builder/tasks/JobContext.java
@@ -16,6 +16,8 @@
package com.android.builder.tasks;
+import com.android.annotations.Nullable;
+
/**
* Contains contextual (and usually mutable) job data.
*/
@@ -24,10 +26,11 @@
private final T payload;
private final long creationTime = System.currentTimeMillis();
- public JobContext(T payload) {
+ public JobContext(@Nullable T payload) {
this.payload = payload;
}
+ @Nullable
public T getPayload() {
return payload;
}
diff --git a/build-system/profile/src/main/java/com/android/builder/tasks/QueueThreadContextAdapter.java b/build-system/profile/src/main/java/com/android/builder/tasks/QueueThreadContextAdapter.java
new file mode 100644
index 0000000..7a913d4
--- /dev/null
+++ b/build-system/profile/src/main/java/com/android/builder/tasks/QueueThreadContextAdapter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.builder.tasks;
+
+import com.android.annotations.NonNull;
+
+import java.io.IOException;
+
+/**
+ * Adapter for {@link QueueThreadContext} with empty methods implementations.
+ */
+public abstract class QueueThreadContextAdapter<T> implements QueueThreadContext<T> {
+
+ @Override
+ public void creation(@NonNull Thread t) throws IOException {
+ }
+
+ @Override
+ public void runTask(@NonNull Job<T> job) throws Exception {
+ }
+
+ @Override
+ public void destruction(@NonNull Thread t) throws IOException, InterruptedException {
+ }
+
+ @Override
+ public void shutdown() {
+ }
+}
diff --git a/build-system/profile/src/main/java/com/android/builder/tasks/WorkQueue.java b/build-system/profile/src/main/java/com/android/builder/tasks/WorkQueue.java
index 14a64b8..2a09c13 100644
--- a/build-system/profile/src/main/java/com/android/builder/tasks/WorkQueue.java
+++ b/build-system/profile/src/main/java/com/android/builder/tasks/WorkQueue.java
@@ -23,7 +23,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -35,6 +34,8 @@
*/
public class WorkQueue<T> implements Runnable {
+ private static final boolean VERBOSE = System.getenv("GRADLE_WORK_QUEUE_VERBOSE") != null;
+
private final ILogger mLogger;
// queue name as human would understand.
@@ -127,9 +128,9 @@
private synchronized void checkWorkforce() {
if (mWorkThreads.isEmpty()
|| (mPendingJobs.size() / mWorkThreads.size() > mGrowthTriggerRation)) {
- mLogger.verbose("Request to incrementing workforce from %1$d", mWorkThreads.size());
+ verbose("Request to incrementing workforce from %1$d", mWorkThreads.size());
if (mWorkThreads.size() >= MAX_WORKFORCE_SIZE) {
- mLogger.verbose("Already at max workforce %1$d, denied.", MAX_WORKFORCE_SIZE);
+ verbose("Already at max workforce %1$d, denied.", MAX_WORKFORCE_SIZE);
return;
}
for (int i = 0; i < mMWorkforceIncrement; i++) {
@@ -138,12 +139,12 @@
mWorkThreads.add(t);
t.start();
}
- mLogger.verbose("thread-pool size=%1$d", mWorkThreads.size());
+ verbose("thread-pool size=%1$d", mWorkThreads.size());
}
}
private synchronized void reduceWorkforce() throws InterruptedException {
- mLogger.verbose("Decrementing workforce from " + mWorkThreads.size());
+ verbose("Decrementing workforce from " + mWorkThreads.size());
// push a the right number of kiss of death tasks to shutdown threads.
for (int i = 0; i < mMWorkforceIncrement; i++) {
_push(new QueueTask<T>(QueueTask.ActionType.Death, null));
@@ -200,7 +201,7 @@
// this
try {
try {
- mLogger.verbose("Creating a new working thread %1$s", threadName);
+ verbose("Creating a new working thread %1$s", threadName);
mQueueThreadContext.creation(Thread.currentThread());
} catch (IOException e) {
e.printStackTrace();
@@ -208,7 +209,7 @@
while(true) {
final QueueTask<T> queueTask = mPendingJobs.take();
if (queueTask.actionType== QueueTask.ActionType.Death) {
- mLogger.verbose("Thread(%1$s): Death requested", threadName);
+ verbose("Thread(%1$s): Death requested", threadName);
// we are done.
return;
}
@@ -219,7 +220,7 @@
"I got a null pending job out of the priority queue");
return;
}
- mLogger.verbose("Thread(%1$s): scheduling %2$s", threadName, job.getJobTitle());
+ verbose("Thread(%1$s): scheduling %2$s", threadName, job.getJobTitle());
try {
mQueueThreadContext.runTask(job);
@@ -230,18 +231,18 @@
}
// wait for the job completion.
job.await();
- mLogger.verbose("Thread(%1$s): job %2$s finished", threadName, job.getJobTitle());
+ verbose("Thread(%1$s): job %2$s finished", threadName, job.getJobTitle());
// we could potentially reduce the workforce at this point if we have little
// queuing comparatively to the number of worker threads but at this point, the
// overall process (gradle activity) is fairly short lived so skipping at this
// point.
- mLogger.verbose("Thread(%1$s): queue size %2$d", threadName, mPendingJobs.size());
+ verbose("Thread(%1$s): queue size %2$d", threadName, mPendingJobs.size());
}
} catch (InterruptedException e) {
mLogger.error(e, "Thread(%1$s): Interrupted", threadName);
} finally {
try {
- mLogger.verbose("Thread(%1$s): destruction", threadName);
+ verbose("Thread(%1$s): destruction", threadName);
mQueueThreadContext.destruction(Thread.currentThread());
} catch (IOException e) {
mLogger.error(e, "Thread(%1$s): %2$s", threadName, e.getMessage());
@@ -250,4 +251,10 @@
}
}
}
+
+ private void verbose(String format, Object...args) {
+ if (VERBOSE) {
+ mLogger.verbose(format, args);
+ }
+ }
}
diff --git a/build-system/profile/src/test/java/com/android/builder/profile/ProcessRecorderTest.java b/build-system/profile/src/test/java/com/android/builder/profile/ProcessRecorderTest.java
index ceed139..2608ae7 100644
--- a/build-system/profile/src/test/java/com/android/builder/profile/ProcessRecorderTest.java
+++ b/build-system/profile/src/test/java/com/android/builder/profile/ProcessRecorderTest.java
@@ -22,9 +22,9 @@
import com.android.annotations.NonNull;
+import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
@@ -46,12 +46,17 @@
ProcessRecorderFactory.sINSTANCE = new ProcessRecorderFactory();
}
+ @After
+ public void shutdown() throws InterruptedException {
+ ProcessRecorderFactory.shutdown();
+ }
+
@Test
public void testBasicRecord() throws InterruptedException {
StringWriter stringWriter = new StringWriter();
ProcessRecorder.JsonRecordWriter jsonRecordWriter =
new ProcessRecorder.JsonRecordWriter(stringWriter);
- ProcessRecorderFactory.sINSTANCE.setRecordWriter(jsonRecordWriter);
+ ProcessRecorderFactory.initializeForTests(jsonRecordWriter);
ThreadRecorder.get().record(ExecutionType.SOME_RANDOM_PROCESSING,
new Recorder.Block<Integer>() {
@Override
@@ -74,7 +79,7 @@
StringWriter stringWriter = new StringWriter();
ProcessRecorder.JsonRecordWriter jsonRecordWriter =
new ProcessRecorder.JsonRecordWriter(stringWriter);
- ProcessRecorderFactory.sINSTANCE.setRecordWriter(jsonRecordWriter);
+ ProcessRecorderFactory.initializeForTests(jsonRecordWriter);
ThreadRecorder.get().record(ExecutionType.SOME_RANDOM_PROCESSING,
new Recorder.Block<Integer>() {
@Override
@@ -105,7 +110,7 @@
}
};
- ProcessRecorderFactory.sINSTANCE.setRecordWriter(recorderWriter);
+ ProcessRecorderFactory.initializeForTests(recorderWriter);
ThreadRecorder.get().record(ExecutionType.SOME_RANDOM_PROCESSING,
new Recorder.Block<Integer>() {
@Override
@@ -122,6 +127,8 @@
ProcessRecorder.get().finish();
setExecutionRecords(records);
+ // delete the initial metadata record.
+ assertEquals(ExecutionType.INITIAL_METADATA, records.remove(0).type);
assertEquals(2, records.size());
assertTrue(records.get(1).parentId == records.get(0).id);
}
@@ -142,7 +149,8 @@
}
};
- ProcessRecorderFactory.sINSTANCE.setRecordWriter(recorderWriter);
+ ProcessRecorderFactory.initializeForTests(recorderWriter);
+
Integer value = ThreadRecorder.get().record(ExecutionType.SOME_RANDOM_PROCESSING,
new Recorder.Block<Integer>() {
@Override
@@ -197,6 +205,8 @@
assertNotNull(value);
assertEquals(16, value.intValue());
ProcessRecorder.get().finish();
+ // delete the initial metadata record.
+ assertEquals(ExecutionType.INITIAL_METADATA, records.remove(0).type);
assertEquals(6, records.size());
// re-order by event id.
setExecutionRecords(records);
diff --git a/build-system/profile/src/test/java/com/android/builder/profile/ThreadRecorderTest.java b/build-system/profile/src/test/java/com/android/builder/profile/ThreadRecorderTest.java
index f77b5ed..71a9164 100644
--- a/build-system/profile/src/test/java/com/android/builder/profile/ThreadRecorderTest.java
+++ b/build-system/profile/src/test/java/com/android/builder/profile/ThreadRecorderTest.java
@@ -18,7 +18,7 @@
import com.android.annotations.NonNull;
-import junit.framework.Assert;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
diff --git a/chartlib/build.gradle b/chartlib/build.gradle
index 44557c7..24c58d6 100644
--- a/chartlib/build.gradle
+++ b/chartlib/build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'java'
+apply plugin: 'jacoco'
group = 'com.android.tools.chartlib'
archivesBaseName = 'chartlib'
diff --git a/chartlib/src/main/java/com/android/tools/chartlib/AnimatedComponent.java b/chartlib/src/main/java/com/android/tools/chartlib/AnimatedComponent.java
index 3476c59..483e8ad 100644
--- a/chartlib/src/main/java/com/android/tools/chartlib/AnimatedComponent.java
+++ b/chartlib/src/main/java/com/android/tools/chartlib/AnimatedComponent.java
@@ -23,13 +23,22 @@
import java.awt.event.ActionListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
+import java.awt.geom.Path2D;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.Timer;
-public class AnimatedComponent extends JComponent implements ActionListener, HierarchyListener {
+/**
+ * Base class for components that should change their look over time.
+ *
+ * At a minimum, child classes should override {@link #updateData()} and {@link #draw(Graphics2D)},
+ * as well as pay attention to the field {@link #mFrameLength} as it controls the behavior of timed
+ * animations.
+ */
+public abstract class AnimatedComponent extends JComponent
+ implements ActionListener, HierarchyListener {
protected static final Font DEFAULT_FONT = new Font("Sans", Font.PLAIN, 10);
@@ -67,16 +76,16 @@
* @param fraction the interpolation fraction.
* @return the interpolated value.
*/
- protected float lerp(float from, float to, float fraction) {
+ protected final float lerp(float from, float to, float fraction) {
float q = (float) Math.pow(1.0f - fraction, mFrameLength);
return from * q + to * (1.0f - q);
}
- public boolean isDrawDebugInfo() {
+ public final boolean isDrawDebugInfo() {
return mDrawDebugInfo;
}
- public void setDrawDebugInfo(boolean drawDebugInfo) {
+ public final void setDrawDebugInfo(boolean drawDebugInfo) {
mDrawDebugInfo = drawDebugInfo;
}
@@ -103,7 +112,7 @@
g2d.dispose();
}
- protected void addDebugInfo(String format, Object... values) {
+ protected final void addDebugInfo(String format, Object... values) {
if (mDrawDebugInfo) {
mDebugInfo.add(String.format(format, values));
}
@@ -124,17 +133,15 @@
}
/**
- * First step of the animation, this is where the data is read and the current animation
- * values are fixed.
+ * First step of the animation, this is where the data is read and the current animation values
+ * are fixed.
*/
- protected void updateData() {
- }
+ protected abstract void updateData();
/**
* Renders the data constructed in the update phase to the given graphics context.
*/
- protected void draw(Graphics2D g) {
- }
+ protected abstract void draw(Graphics2D g);
/**
* Draws visual debug information.
@@ -144,12 +151,12 @@
@Override
- public void actionPerformed(ActionEvent actionEvent) {
+ public final void actionPerformed(ActionEvent actionEvent) {
repaint();
}
@Override
- public void hierarchyChanged(HierarchyEvent hierarchyEvent) {
+ public final void hierarchyChanged(HierarchyEvent hierarchyEvent) {
if (mTimer.isRunning() && !isShowing()) {
mTimer.stop();
} else if (!mTimer.isRunning() && isShowing()) {
@@ -157,11 +164,39 @@
}
}
- public void setUpdateData(boolean updateData) {
+ /**
+ * If true, this component will animate normally.
+ */
+ public final void setUpdateData(boolean updateData) {
mUpdateData = updateData;
}
- public void step() {
+ /**
+ * Animate this component for a single frame and then pause. Note that this call will have no
+ * effect unless {@link #setUpdateData(boolean)} is set to {@code false} first.
+ */
+ public final void step() {
mStep = true;
}
+
+ protected static void drawArrow(Graphics2D g, float x, float y, float dx, float dy, float len,
+ Color color) {
+ Path2D.Float path = new Path2D.Float();
+ path.moveTo(x, y);
+ path.lineTo(x + dx * len, y + dy * len);
+ path.lineTo(x + dx * (len - 10) + dy * 10, y + dy * (len - 10) - dx * 10);
+ path.lineTo(x + dx * (len - 10) - dy * 10, y + dy * (len - 10) + dx * 10);
+ g.setColor(color);
+ g.draw(path);
+ }
+
+ protected static void drawMarker(Graphics2D g, float x, float y, Color color) {
+ Path2D.Float path = new Path2D.Float();
+ path.moveTo(x - 10, y);
+ path.lineTo(x + 10, y);
+ path.moveTo(x, y - 10);
+ path.lineTo(x, y + 10);
+ g.setColor(color);
+ g.draw(path);
+ }
}
diff --git a/chartlib/src/main/java/com/android/tools/chartlib/SunburstComponent.java b/chartlib/src/main/java/com/android/tools/chartlib/SunburstComponent.java
new file mode 100644
index 0000000..36e71a6
--- /dev/null
+++ b/chartlib/src/main/java/com/android/tools/chartlib/SunburstComponent.java
@@ -0,0 +1,675 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.chartlib;
+
+import com.android.annotations.NonNull;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.RenderingHints;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.geom.Arc2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.tree.TreeNode;
+
+/**
+ * Component which renders a
+ * <a href="https://en.wikipedia.org/wiki/Pie_chart#Ring_chart_.2F_Sunburst_chart_.2F_Multilevel_pie_chart">
+ * sunburst chart</a> that can be unrolled by setting its angle.
+ */
+public final class SunburstComponent extends AnimatedComponent {
+
+ private static final Color[] COLORS = {
+ new Color(0x6baed6),
+ new Color(0xc6dbef),
+ new Color(0xfd8d3c),
+ new Color(0xfdd0a2),
+ new Color(0x74c476),
+ new Color(0xc7e9c0),
+ new Color(0x9e9ac8),
+ new Color(0xdadaeb),
+ new Color(0x969696),
+ new Color(0xd9d9d9),
+ };
+
+ private static final Color[] HIGHLIGHTS = {
+ new Color(0x3182bd),
+ new Color(0x9ecae1),
+ new Color(0xe6550d),
+ new Color(0xfdae6b),
+ new Color(0x31a354),
+ new Color(0xa1d99b),
+ new Color(0x756bb1),
+ new Color(0xbcbddc),
+ new Color(0x636363),
+ new Color(0xbdbdbd),
+ };
+
+ private ValuedTreeNode mData;
+
+ private Slice mSlice;
+
+ private float mGap;
+
+ private float mStart;
+
+ private float mFixed;
+
+ private float mAngle;
+
+ private float mCurrentAngle;
+
+ private float mSeparator;
+
+ private boolean mAutoSize;
+
+ private float mSliceWidth;
+
+ private boolean myUseCount;
+
+ private int mySelectionLevel;
+
+ private int myZoomLevel;
+
+ private Slice mySelection;
+
+ private Slice myZoom;
+
+ private boolean myLockSelection;
+
+ private final List<SliceSelectionListener> mListeners;
+
+ // Calculated values
+ private float mX;
+
+ private float mY;
+
+ private float mMaxDepth;
+
+ private float mMaxSide;
+
+ private float mCenterX;
+
+ private float mCenterY;
+
+ private float mDelta;
+
+ private Point2D.Float mDirection;
+
+ private Map<Color, Path2D.Float> mPaths;
+
+ public SunburstComponent(@NonNull ValuedTreeNode data) {
+ super(30);
+ mData = data;
+ mSlice = new Slice(0.0f);
+ mSliceWidth = 50;
+ mGap = 50;
+ mX = -1;
+ mY = -1;
+ mCurrentAngle = mAngle = 360.0f;
+ mDelta = 0.0f;
+ mFixed = 60.0f;
+ mStart = 180.0f;
+ mSeparator = 1.0f;
+ mySelectionLevel = -1;
+ myLockSelection = false;
+ mPaths = new HashMap<Color, Path2D.Float>();
+ addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ myZoom = mySelection;
+ myZoomLevel = mySelectionLevel;
+ myLockSelection = false;
+ } else {
+ myLockSelection = !myLockSelection && mySelection != null;
+ }
+ }
+ });
+ mListeners = new LinkedList<SliceSelectionListener>();
+ }
+
+ @Override
+ protected void draw(Graphics2D g) {
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ Dimension dim = getSize();
+
+ g.setColor(getBackground());
+ g.fillRect(0, 0, dim.width, dim.height);
+
+ mPaths.clear();
+ drawSlice(g, mSlice, 0.0f, 0.0f, 1.0f);
+ for (Map.Entry<Color, Path2D.Float> entry : mPaths.entrySet()) {
+ g.setColor(entry.getKey());
+ g.fill(entry.getValue());
+ }
+ }
+
+ @Override
+ protected void updateData() {
+
+ Dimension dim = getSize();
+ mX = dim.width * 0.5f;
+ mY = dim.height * 0.5f;
+
+ updateArea();
+ updateStructure(mSlice, mData, false);
+
+ mCurrentAngle = Math.abs(mCurrentAngle - mAngle) < 0.1f ? mAngle
+ : lerp(mCurrentAngle, mAngle, 0.999f);
+ if (mAutoSize) {
+ float full = Math.min(mMaxDepth, mMaxSide) - mGap - 20;
+ float none = mMaxDepth * 2 - mGap - 20;
+ float factor = mCurrentAngle / 360.0f;
+ float depth = full * factor + none * (1 - factor);
+ mFixed = lerp(mFixed, (float) ((mMaxSide - 20) / Math.PI), 0.999f);
+ float width = depth / getMaxDepth(mSlice);
+ mSliceWidth = lerp(mSliceWidth, width, 0.999f);
+ }
+
+ // mDelta is the extra radius needed to keep the same length at the fixes radius
+ // (mDelta + mFixed * Rad(mCurrentAngle) == 2PI * mFixed =>
+ // mDelta + mFixed == 2PI * mFixed / Rad(mCurrentAngle) =>
+ mDelta = 360.0f * mFixed / mCurrentAngle - mFixed;
+ mDirection = new Point2D.Float((float) Math.cos(mStart * Math.PI / 180.0f),
+ -(float) Math.sin(mStart * Math.PI / 180.0f));
+
+ mCenterX = mX + (mDelta + mMaxDepth * (360.0f - mCurrentAngle) / 360.0f) * mDirection.x;
+ mCenterY = mY + (mDelta + mMaxDepth * (360.0f - mCurrentAngle) / 360.0f) * mDirection.y;
+
+ updateSelection();
+ updateSlice(mSlice, 0, myZoom == null);
+ }
+
+ private void updateSelection() {
+ Point mouse = getMousePosition();
+ if (!myLockSelection) {
+ boolean selection = false;
+ if (mouse != null) {
+ float value = 0;
+ float depth = 0;
+ if (mCurrentAngle > 0) {
+ float distance = (float) mouse.distance(mCenterX, mCenterY);
+ depth = (distance - mGap - mDelta) / mSliceWidth;
+ float angle = -(float) Math
+ .toDegrees(Math.atan2(mouse.y - mCenterY, mouse.x - mCenterX));
+ angle = (angle - mStart - 180.0f + mCurrentAngle * 0.5f) % 360.0f;
+ angle = angle < 0 ? angle + 360.0f : angle;
+ value = angle / mCurrentAngle;
+ } else {
+ float length = (float) (Math.PI * 2.0f * mFixed);
+ // Do the dot product of the vector to the mouse with the unit vector to project to:
+ depth = (mouse.x - mX) * mDirection.x + (mouse.y - mY) * mDirection.y - (mMaxDepth - mGap);
+ value = (mouse.x - mX) * mDirection.y + (mouse.y - mY) * -mDirection.x;
+ // Normalize:
+ depth = -depth / mSliceWidth;
+ value = -value / length + 0.5f;
+ }
+ selection = updateSelectedSlice(mSlice, depth, value, 0);
+ }
+ if (!selection) {
+ mySelectionLevel = -1;
+ if (mySelection != null) {
+ mySelection = null;
+ fireSliceSelected(new SliceSelectionEvent(null));
+ }
+ }
+ } else {
+ if (mySelection != null && mySelection.node == null) {
+ myLockSelection = false;
+ mySelectionLevel = -1;
+ mySelection = null;
+ fireSliceSelected(new SliceSelectionEvent(null));
+ }
+ }
+ if (myZoom != null && myZoom.node == null) {
+ resetZoom();
+ }
+ }
+
+ public void resetZoom() {
+ myZoom = null;
+ myZoomLevel = -1;
+ }
+
+ private boolean updateSlice(Slice slice, int level, boolean zoom) {
+ zoom = zoom || slice == myZoom;
+ boolean children = false;
+ for (int i = 0; i < slice.getChildrenCount(); i++) {
+ Slice child = slice.getChild(i);
+ children = updateSlice(child, level + 1, zoom) || children;
+ }
+ zoom = zoom || children;
+
+ slice.selected = lerp(slice.selected, slice == mySelection ? 1.0f : 0.0f, 0.99f);
+ slice.visible = lerp(slice.visible, zoom ? 1.0f : 0.0f, 0.99f);
+ slice.zoom = lerp(slice.zoom,
+ level < myZoomLevel ? (level == myZoomLevel - 1) ? 0.5f : 0.0f : 1.0f, 0.99f);
+
+ return zoom;
+ }
+
+ private boolean updateSelectedSlice(Slice slice, float depth, float value, int level) {
+ if (depth < 0.0f || value < 0.0f || value > 1.0f) {
+ return false;
+ } else if (depth < slice.getDepth()) {
+ mySelectionLevel = level;
+ if (mySelection != slice) {
+ mySelection = slice;
+ fireSliceSelected(new SliceSelectionEvent(slice.node));
+ }
+ return true;
+ } else {
+ depth -= slice.getDepth();
+ float total = 0.0f;
+ for (Slice child : slice.getChildren()) {
+ total += child.getValue();
+ }
+ float current = 0.0f;
+ for (int i = 0; i < slice.getChildrenCount(); i++) {
+ Slice child = slice.getChild(i);
+ float val = child.getValue() / total;
+ if (value < current + val || i == slice.getChildrenCount() - 1) {
+ value = (value - current) / val;
+ return updateSelectedSlice(child, depth, value, level + 1);
+ }
+ current += val;
+
+ }
+ }
+ return false;
+ }
+
+ private void fireSliceSelected(SliceSelectionEvent event) {
+ for (SliceSelectionListener listener : mListeners) {
+ listener.valueChanged(event);
+ }
+ }
+
+ @Override
+ protected void debugDraw(Graphics2D g2d) {
+ addDebugInfo("Total slices: %d", mData.getCount());
+ addDebugInfo("Paths %d", mPaths.size());
+ g2d.setColor(Color.GREEN);
+ drawArrow(g2d, mX, mY, mDirection.x, mDirection.y, mMaxDepth, Color.MAGENTA);
+ drawArrow(g2d, mX, mY, mDirection.y, -mDirection.x, mMaxSide, Color.MAGENTA);
+ if (mCurrentAngle == 0) {
+ Path2D.Float fixed = new Path2D.Float();
+ float length = (float) (Math.PI * 2.0f * mFixed) * 0.5f;
+ fixed.moveTo(
+ mX + (mMaxDepth * (360.0f - mCurrentAngle) / 360.0f - mFixed) * mDirection.x
+ - mDirection.y * length,
+ mY + (mMaxDepth * (360.0f - mCurrentAngle) / 360.0f - mFixed) * mDirection.y
+ + mDirection.x * length);
+ fixed.lineTo(
+ mX + (mMaxDepth * (360.0f - mCurrentAngle) / 360.0f - mFixed) * mDirection.x
+ + mDirection.y * length,
+ mY + (mMaxDepth * (360.0f - mCurrentAngle) / 360.0f - mFixed) * mDirection.y
+ - mDirection.x * length);
+
+ g2d.draw(fixed);
+ } else {
+ drawMarker(g2d, mCenterX, mCenterY, Color.BLUE);
+ Arc2D.Float fixed = new Arc2D.Float();
+ fixed.setArcByCenter(mCenterX, mCenterY, mDelta + mFixed,
+ mStart + (360.0f - mCurrentAngle) * 0.5f, mCurrentAngle, Arc2D.OPEN);
+ g2d.draw(fixed);
+ }
+ }
+
+ private void updateArea() {
+ float angle = (float) Math.toRadians(mStart);
+ float a = (float) Math.cos(angle) * mY;
+ float b = (float) Math.sin(angle) * mX;
+ mMaxDepth = mX * mY / (float) Math.sqrt((a * a) + (b * b));
+ a = (float) Math.cos(angle + Math.PI * 0.5) * mY;
+ b = (float) Math.sin(angle + Math.PI * 0.5) * mX;
+ mMaxSide = mX * mY / (float) Math.sqrt((a * a) + (b * b));
+ }
+
+ float getFraction(ValuedTreeNode node) {
+ TreeNode parent = node.getParent();
+ assert parent == null || parent instanceof ValuedTreeNode;
+ if (myUseCount) {
+ return parent == null ? 1.0f
+ : (float) node.getCount() / ((ValuedTreeNode) parent).getCount();
+ } else {
+ return parent == null ? 1.0f
+ : (float) node.getValue() / ((ValuedTreeNode) parent).getValue();
+ }
+ }
+
+ static ValuedTreeNode getChildAt(ValuedTreeNode node, int i) {
+ TreeNode child = node.getChildAt(i);
+ assert child instanceof ValuedTreeNode;
+ return (ValuedTreeNode) child;
+ }
+
+ private float getMaxDepth(Slice slice) {
+ float depth = 0.0f;
+ for (Slice child : slice.getChildren()) {
+ depth = Math.max(depth, getMaxDepth(child));
+ }
+ return depth + slice.depth;
+ }
+
+ private boolean updateStructure(Slice slice, ValuedTreeNode node, boolean hasSiblings) {
+ if (node == null) {
+ slice.depth = lerp(slice.depth, hasSiblings ? slice.depth : 0.0f, 0.99f);
+ slice.value = lerp(slice.value, hasSiblings ? 0.0f : slice.value, 0.99f);
+ } else {
+ slice.depth = lerp(slice.depth, node.getParent() == null ? 0.0f : 1.0f, 0.99f);
+ slice.value = lerp(slice.value, getFraction(node), 0.99f);
+ }
+ slice.node = node;
+
+ int last = -1;
+ int slices = slice.getChildrenCount();
+ int nodes = node == null ? 0 : node.getChildCount();
+ for (int i = 0; i < slices; i++) {
+ Slice childSlice = slice.getChild(i);
+ ValuedTreeNode childNode = i < nodes ? getChildAt(node, i) : null;
+ if (updateStructure(childSlice, childNode, nodes > 0)) {
+ last = i;
+ }
+ }
+ // Test neighbours with the same color:
+ int c = slices > 0 ? slice.getChild(0).color
+ : ((slice.color + (int) (Math.random() * COLORS.length - 1) + 1) % COLORS.length);
+ for (int i = slices; i < nodes; i++) {
+ ValuedTreeNode childNode = getChildAt(node, i);
+ Slice childSlice = new Slice(slices > 0 ? 0.0f : getFraction(childNode));
+ childSlice.color = c;
+ childSlice.depth = slices > 0 ? 1.0f : 0.0f;
+ slice.addChild(childSlice);
+ if (updateStructure(childSlice, childNode, nodes > 0)) {
+ last = i;
+ }
+ }
+
+ if (last + 1 < slice.getChildrenCount()) {
+ slice.clearSublist(last + 1, slice.getChildrenCount());
+ }
+
+ return node != null || (slice.depth > 0.00001f && slice.value > 0.00001f) || last >= 0;
+ }
+
+ Path2D.Float getPath(Color color) {
+ Path2D.Float path = mPaths.get(color);
+ if (path == null) {
+ path = new Path2D.Float();
+ mPaths.put(color, path);
+ }
+ return path;
+ }
+
+ private void drawSlice(Graphics2D g, Slice slice, float depth, float from, float to) {
+ if (slice.getDepth() > 0.0f) { // Optimization for zero width slices
+ Color c = COLORS[slice.color];
+ float s = slice.selected;
+ Color b = HIGHLIGHTS[slice.color];
+ c = new Color((int) (b.getRed() * s + c.getRed() * (1 - s)),
+ (int) (b.getGreen() * s + c.getGreen() * (1 - s)),
+ (int) (b.getBlue() * s + c.getBlue() * (1 - s)));
+ Path2D.Float path = getPath(c);
+ if (mCurrentAngle == 0) {
+ float length = (float) (Math.PI * 2.0f * mFixed);
+ float delta = mGap + depth * mSliceWidth - mMaxDepth + mSeparator * 0.5f
+ + slice.getBorder() * mSliceWidth;
+ float up = length * (0.5f - from) - mSeparator * 0.5f;
+ float down = length * (0.5f - to) + mSeparator * 0.5f;
+ float size = mSliceWidth * slice.getDepth() - mSeparator
+ - slice.getBorder() * mSliceWidth * 2.0f;
+
+ float deltaX = mDirection.x * delta;
+ float deltaY = mDirection.y * delta;
+ float upX = mDirection.y * up;
+ float upY = -mDirection.x * up;
+ float downX = mDirection.y * down;
+ float downY = -mDirection.x * down;
+ float sizeX = mDirection.x * size;
+ float sizeY = mDirection.y * size;
+
+ if (up > down) {
+ path.moveTo(mX - deltaX + upX, mY - deltaY + upY);
+ path.lineTo(mX - deltaX + upX - sizeX, mY - deltaY + upY - sizeY);
+ path.lineTo(mX - deltaX + downX - sizeX, mY - deltaY + downY - sizeY);
+ path.lineTo(mX - deltaX + downX, mY - deltaY + downY);
+ path.closePath();
+ }
+ } else {
+ float angle = (360.0f - mCurrentAngle) * 0.5f + mCurrentAngle * from + mStart;
+ float arc = mCurrentAngle * (to - from);
+
+ float radius = mSliceWidth * depth + mGap + mDelta;
+
+ float outerLen = (radius + mSliceWidth * slice.getDepth()) * (float) Math.toRadians(
+ arc);
+ if (outerLen < 1) {
+ path = getPath(Color.RED);
+ }
+ float outerRadius = radius + mSliceWidth * slice.getDepth() - mSeparator * 0.5f
+ - slice.getBorder() * mSliceWidth;
+ float innerRadius = radius + mSeparator * 0.5f + slice.getBorder() * mSliceWidth;
+ float outerAngle = (float) Math.toDegrees(Math.asin(mSeparator / outerRadius));
+ if (outerAngle < arc && outerRadius > innerRadius) {
+ Arc2D.Float outer = new Arc2D.Float();
+ outer.setArcByCenter(mCenterX, mCenterY, outerRadius,
+ angle + outerAngle * 0.5f, arc - outerAngle, Arc2D.OPEN);
+ path.append(outer, false);
+
+ float innerAngle = (float) Math.toDegrees(Math.asin(mSeparator / innerRadius));
+ if (innerAngle < arc) {
+ Arc2D.Float inner = new Arc2D.Float();
+ inner.setArcByCenter(mCenterX, mCenterY, innerRadius,
+ angle + innerAngle * 0.5f + arc - innerAngle, -(arc - innerAngle),
+ Arc2D.OPEN);
+ path.append(inner, true);
+ } else {
+ float r = (float) (mSeparator * 0.5f / Math
+ .sin(Math.toRadians(arc * 0.5f)));
+ float dx = (float) (Math.cos(Math.toRadians(angle + arc * 0.5f)) * r);
+ float dy = (float) (Math.sin(Math.toRadians(angle + arc * 0.5f)) * r);
+ path.lineTo(mCenterX + dx, mCenterY - dy);
+ }
+ path.lineTo(outer.getStartPoint().getX(), outer.getStartPoint().getY());
+ }
+ }
+ }
+
+ float total = 0.0f;
+ for (Slice child : slice.getChildren()) {
+ total += child.getValue();
+ }
+ float value = 0.0f;
+ for (Slice child : slice.getChildren()) {
+ float childFrom = from + (value / total) * (to - from);
+ float childTo = from + ((value + child.getValue()) / total) * (to - from);
+
+ drawSlice(g, child, depth + slice.getDepth(), childFrom, childTo);
+
+ value += child.getValue();
+ }
+ }
+
+ public void setGap(float gap) {
+ mGap = gap;
+ }
+
+ public void setSliceWidth(float sliceWidth) {
+ mSliceWidth = sliceWidth;
+ }
+
+ public void setAngle(float angle) {
+ mAngle = angle;
+ }
+
+ public void setStart(float start) {
+ mStart = start;
+ updateArea();
+ }
+
+ public void setFixed(int fixed) {
+ mFixed = fixed;
+ }
+
+ public float getGap() {
+ return mGap;
+ }
+
+ public float getSliceWidth() {
+ return mSliceWidth;
+ }
+
+ public float getStart() {
+ return mStart;
+ }
+
+ public float getFixed() {
+ return mFixed;
+ }
+
+ public float getAngle() {
+ return mAngle;
+ }
+
+ public float getSeparator() {
+ return mSeparator;
+ }
+
+ public void setSeparator(float separator) {
+ mSeparator = separator;
+ }
+
+ public void setData(ValuedTreeNode data) {
+ mData = data;
+ }
+
+ public ValuedTreeNode getData() {
+ return mData;
+ }
+
+ public void setAutoSize(boolean autoSize) {
+ mAutoSize = autoSize;
+ }
+
+ public void setUseCount(boolean useCount) {
+ myUseCount = useCount;
+ }
+
+ public void addSelectionListener(SliceSelectionListener listener) {
+ mListeners.add(listener);
+ }
+
+ public void removeSelectionListener(SliceSelectionListener listener) {
+ mListeners.remove(listener);
+ }
+
+ static class Slice {
+
+ private ArrayList<Slice> children = new ArrayList<Slice>();
+
+ Slice parent;
+
+ float value;
+
+ float depth;
+
+ int color;
+
+ float hover;
+
+ float zoom;
+
+ float selected;
+
+ float visible;
+
+ ValuedTreeNode node;
+
+ public float getValue() {
+ return value * visible;
+ }
+
+ public float getBorder() {
+ return selected * depth * 0.05f;
+ }
+
+ public float getDepth() {
+ return zoom * depth + getBorder() * 2.0f;
+ }
+
+ public Slice(float value) {
+ this.value = value;
+ this.depth = 1.0f;
+ this.hover = 0.0f;
+ this.selected = 0.0f;
+ this.zoom = 1.0f;
+ this.visible = 1.0f;
+ }
+
+ public int getChildrenCount() {
+ return children.size();
+ }
+
+ public void addChild(Slice child) {
+ children.add(child);
+ child.parent = this;
+ }
+
+ public Slice getChild(int i) {
+ return children.get(i);
+ }
+
+ public void clearSublist(int from, int to) {
+ for (int i = from; i < to; i++) {
+ children.get(i).parent = null;
+ }
+ children.subList(from, to).clear();
+ }
+
+ public ArrayList<Slice> getChildren() {
+ return children;
+ }
+ }
+
+ public static class SliceSelectionEvent {
+
+ private final ValuedTreeNode mNode;
+
+ public SliceSelectionEvent(ValuedTreeNode node) {
+ mNode = node;
+ }
+
+ public ValuedTreeNode getNode() {
+ return mNode;
+ }
+ }
+
+ public interface SliceSelectionListener {
+ void valueChanged(SliceSelectionEvent e);
+ }
+}
diff --git a/chartlib/src/main/java/com/android/tools/chartlib/TimelineComponent.java b/chartlib/src/main/java/com/android/tools/chartlib/TimelineComponent.java
index 57f0e31..d90b1b8 100644
--- a/chartlib/src/main/java/com/android/tools/chartlib/TimelineComponent.java
+++ b/chartlib/src/main/java/com/android/tools/chartlib/TimelineComponent.java
@@ -43,7 +43,7 @@
* modifications to it while it's begin rendered, but objects of this class should not be accessed
* from different threads.
*/
-public class TimelineComponent extends AnimatedComponent
+public final class TimelineComponent extends AnimatedComponent
implements ActionListener, HierarchyListener {
private static final Color TEXT_COLOR = new Color(128, 128, 128);
@@ -660,7 +660,7 @@
}
}
- public static enum Style {
+ public enum Style {
NONE,
SOLID,
DASHED
diff --git a/chartlib/src/main/java/com/android/tools/chartlib/ValuedTreeNode.java b/chartlib/src/main/java/com/android/tools/chartlib/ValuedTreeNode.java
new file mode 100644
index 0000000..1838916
--- /dev/null
+++ b/chartlib/src/main/java/com/android/tools/chartlib/ValuedTreeNode.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.chartlib;
+
+import javax.swing.tree.TreeNode;
+
+public interface ValuedTreeNode extends TreeNode {
+ int getCount();
+
+ int getValue();
+}
diff --git a/chartlib/src/test/java/AnimatedComponentVisualTests.java b/chartlib/src/test/java/AnimatedComponentVisualTests.java
index 68cb79b..c16089d 100644
--- a/chartlib/src/test/java/AnimatedComponentVisualTests.java
+++ b/chartlib/src/test/java/AnimatedComponentVisualTests.java
@@ -16,18 +16,24 @@
import com.android.tools.chartlib.AnimatedComponent;
import com.android.tools.chartlib.EventData;
+import com.android.tools.chartlib.SunburstComponent;
import com.android.tools.chartlib.TimelineComponent;
import com.android.tools.chartlib.TimelineData;
+import com.android.tools.chartlib.ValuedTreeNode;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
+import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
+import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.Box;
@@ -42,8 +48,7 @@
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
-
-import sun.awt.VerticalBagLayout;
+import javax.swing.tree.DefaultMutableTreeNode;
public class AnimatedComponentVisualTests extends JDialog {
@@ -53,6 +58,7 @@
JPanel contentPane = new JPanel(new BorderLayout());
JButton close = new JButton("Close");
JTabbedPane tabs = new JTabbedPane();
+ tabs.addTab("PieChart", getPieChartExample());
tabs.addTab("Timeline", getTimelineExample());
contentPane.setPreferredSize(new Dimension(1280, 1024));
@@ -76,18 +82,6 @@
});
controls.add(debug);
- final JCheckBox update = new JCheckBox("Update");
- update.setSelected(true);
- update.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent actionEvent) {
- for (AnimatedComponent component : mComponents) {
- component.setUpdateData(update.isSelected());
- }
- }
- });
- controls.add(update);
-
final JButton step = new JButton("Step");
step.addActionListener(new ActionListener() {
@Override
@@ -97,10 +91,31 @@
}
}
});
+ final JCheckBox update = new JCheckBox("Update");
+ update.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent actionEvent) {
+ for (AnimatedComponent component : mComponents) {
+ component.setUpdateData(update.isSelected());
+ }
+ step.setEnabled(!update.isSelected());
+ }
+ });
+ update.setSelected(true);
+ step.setEnabled(false);
+ final JCheckBox dark = new JCheckBox("Dark");
+ dark.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent actionEvent) {
+ setDarkMode(dark.isSelected());
+ }
+ });
+ controls.add(dark);
+ controls.add(update);
controls.add(step);
-
contentPane.add(controls, BorderLayout.WEST);
+ setDarkMode(false);
setContentPane(contentPane);
setModal(true);
getRootPane().setDefaultButton(close);
@@ -113,10 +128,14 @@
});
}
- static interface Value {
+ private void setDarkMode(boolean dark) {
+ for (AnimatedComponent c : mComponents) {
+ c.setBackground(dark ? new Color(60, 63, 65) : new Color(244, 244, 244));
+ }
+ }
+ interface Value {
void set(int v);
-
int get();
}
@@ -138,6 +157,7 @@
panel.add(slider, BorderLayout.CENTER);
panel.add(new JLabel(name + ": "), BorderLayout.WEST);
panel.add(text, BorderLayout.EAST);
+ panel.setAlignmentX(Component.LEFT_ALIGNMENT);
return panel;
}
@@ -145,19 +165,231 @@
panel.setLayout(new BorderLayout());
mComponents.add(animated);
panel.add(animated, BorderLayout.CENTER);
- JPanel controls = new JPanel(new VerticalBagLayout());
+
+ JPanel controls = new JPanel();
+ LayoutManager manager = new BoxLayout(controls, BoxLayout.Y_AXIS);
+ controls.setLayout(manager);
controls.setPreferredSize(new Dimension(300, 800));
panel.add(controls, BorderLayout.WEST);
return controls;
}
+ static class DataNode extends DefaultMutableTreeNode implements ValuedTreeNode {
+
+ private int mCount;
+ private int mValue;
+
+ public DataNode() {
+ this(0, 0);
+ }
+
+ public DataNode(int count, int value) {
+ mCount = count;
+ mValue = value;
+ }
+
+ @Override
+ public int getCount() {
+ return mCount;
+ }
+
+ @Override
+ public int getValue() {
+ return mValue;
+ }
+
+ public void add(int count, int value) {
+ mCount += count;
+ mValue += value;
+ if (parent instanceof DataNode) {
+ ((DataNode)parent).add(count, value);
+ }
+ }
+
+ public void addDataNode(DataNode dataNode) {
+ super.add(dataNode);
+ add(dataNode.getCount(), dataNode.getValue());
+ }
+ }
+
+ private JPanel getPieChartExample() {
+
+ final DataNode data = new DataNode();
+ data.addDataNode(new DataNode(1, 10));
+
+ final SunburstComponent layout = new SunburstComponent(data);
+
+ JPanel panel = new JPanel();
+ JPanel controls = createControlledPane(panel, layout);
+ final JLabel info = new JLabel("<No information yet>");
+ panel.add(info, BorderLayout.SOUTH);
+
+ controls.add(createVaribleSlider("Gap", 0, 200, new Value() {
+ @Override
+ public void set(int v) {
+ layout.setGap(v);
+ }
+
+ @Override
+ public int get() {
+ return (int) layout.getGap();
+ }
+ }));
+ controls.add(createVaribleSlider("Size", 0, 200, new Value() {
+ @Override
+ public void set(int v) {
+ layout.setSliceWidth(v);
+ }
+
+ @Override
+ public int get() {
+ return (int) layout.getSliceWidth();
+ }
+ }));
+ controls.add(createVaribleSlider("Angle", 0, 360, new Value() {
+ @Override
+ public void set(int v) {
+ layout.setAngle(v);
+ }
+
+ @Override
+ public int get() {
+ return (int) layout.getAngle();
+ }
+ }));
+ controls.add(createVaribleSlider("Start", 0, 360, new Value() {
+ @Override
+ public void set(int v) {
+ layout.setStart(v);
+ }
+
+ @Override
+ public int get() {
+ return (int) layout.getStart();
+ }
+ }));
+ controls.add(createVaribleSlider("Fixed", 1, 100, new Value() {
+ @Override
+ public void set(int v) {
+ layout.setFixed(v);
+ }
+
+ @Override
+ public int get() {
+ return (int) layout.getFixed();
+ }
+ }));
+ controls.add(createVaribleSlider("Separator", 0, 100, new Value() {
+ @Override
+ public void set(int v) {
+ layout.setSeparator(v);
+ }
+
+ @Override
+ public int get() {
+ return (int) layout.getSeparator();
+ }
+ }));
+ controls.add(createButton("Generate", new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent actionEvent) {
+ generateLayoutData((DataNode) layout.getData(), 5);
+ }
+ }));
+ controls.add(createButton("Tree A", new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent actionEvent) {
+ DataNode g = new DataNode();
+ g.addDataNode(createTree(1));
+ g.addDataNode(createValue());
+ g.addDataNode(createTree(1));
+ g.addDataNode(createValue());
+ g.addDataNode(createTree(0));
+ layout.setData(g);
+ }
+ }));
+ controls.add(createButton("Tree B", new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent actionEvent) {
+ DataNode g = new DataNode();
+ g.addDataNode(createValue());
+ g.addDataNode(createValue());
+ g.addDataNode(createTree(0));
+ layout.setData(g);
+ }
+ }));
+ controls.add(createButton("Value", new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent actionEvent) {
+ DataNode g = new DataNode();
+ g.addDataNode(new DataNode(1, (int) (Math.random() * 50)));
+ layout.setData(g);
+ }
+ }));
+ controls.add(createCheckbox("Auto size", new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent itemEvent) {
+ layout.setAutoSize(itemEvent.getStateChange() == ItemEvent.SELECTED);
+ }
+ }));
+ controls.add(
+ new Box.Filler(new Dimension(0, 0), new Dimension(300, Integer.MAX_VALUE),
+ new Dimension(300, Integer.MAX_VALUE)));
+
+ layout.addSelectionListener(new SunburstComponent.SliceSelectionListener() {
+ @Override
+ public void valueChanged(SunburstComponent.SliceSelectionEvent e) {
+ ValuedTreeNode node = e.getNode();
+ info.setText(node == null ? "<No selection>" : String.format("Value %d Count %d",
+ node.getValue(), node.getCount()));
+ }
+ });
+ return panel;
+ }
+
+ private static DataNode createValue() {
+ return new DataNode(1, (int)(Math.random() * 50));
+ }
+
+ private static DataNode createTree(int depth) {
+ DataNode b = depth == 0 ? createValue() : createTree(depth - 1);
+ DataNode c = depth == 0 ? createValue() : createTree(depth - 1);
+ DataNode a = new DataNode();
+ a.addDataNode(b);
+ a.addDataNode(c);
+ return a;
+ }
+
private static Component createButton(String label, ActionListener action) {
JButton button = new JButton(label);
- button.setPreferredSize(button.getPreferredSize());
button.addActionListener(action);
+ button.setMaximumSize(new Dimension(Integer.MAX_VALUE, button.getMaximumSize().height));
return button;
}
+ private static Component createCheckbox(String label, ItemListener action) {
+ JCheckBox button = new JCheckBox(label);
+ button.addItemListener(action);
+ button.setMaximumSize(new Dimension(Integer.MAX_VALUE, button.getMaximumSize().height));
+ return button;
+ }
+
+ private static void generateLayoutData(DataNode data, int maxDepth) {
+ Random random = new Random();
+ int branch = random.nextInt(9) + 1;
+ for (int i = 0; i < branch; i++) {
+ int value = random.nextInt(1024);
+ if (maxDepth > 0 && random.nextInt(4) == 0) {
+ DataNode group = new DataNode();
+ group.add(new DataNode(1, value));
+ generateLayoutData(group, maxDepth - 1);
+ data.addDataNode(group);
+ } else {
+ data.addDataNode(new DataNode(1, value));
+ }
+ }
+ }
+
private JPanel getTimelineExample() {
final TimelineData data = new TimelineData(2, 2000);
final EventData events = new EventData();
@@ -241,6 +473,7 @@
controls.add(createEventButton(1, events, null));
controls.add(createEventButton(2, events, variance));
+ controls.add(new Box.Filler(new Dimension(0, 0), new Dimension(300, Integer.MAX_VALUE), new Dimension(300, Integer.MAX_VALUE)));
panel.add(timeline, BorderLayout.CENTER);
return panel;
}
diff --git a/common/build.gradle b/common/build.gradle
index 231a25e..904c8f9 100644
--- a/common/build.gradle
+++ b/common/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
+apply plugin: 'jacoco'
apply plugin: 'sdk-java-lib'
dependencies {
diff --git a/common/src/main/java/com/android/SdkConstants.java b/common/src/main/java/com/android/SdkConstants.java
index cca12ec..933f652 100644
--- a/common/src/main/java/com/android/SdkConstants.java
+++ b/common/src/main/java/com/android/SdkConstants.java
@@ -34,7 +34,7 @@
* <li><code>EXT_</code> File name extension, without the dot </li>
* </ul>
*/
-@SuppressWarnings("javadoc") // Not documenting all the fields here
+@SuppressWarnings({"javadoc", "unused"}) // Not documenting all the fields here
public final class SdkConstants {
public static final int PLATFORM_UNKNOWN = 0;
public static final int PLATFORM_LINUX = 1;
@@ -273,6 +273,8 @@
public static final String FD_TEST = "androidTest"; //$NON-NLS-1$
/** Default java code folder name, i.e. "java" */
public static final String FD_JAVA = "java"; //$NON-NLS-1$
+ /** Default native code folder name, i.e. "jni" */
+ public static final String FD_JNI = "jni"; //$NON-NLS-1$
/** Default gradle folder name, i.e. "gradle" */
public static final String FD_GRADLE = "gradle"; //$NON-NLS-1$
/** Default gradle wrapper folder name, i.e. "gradle/wrapper" */
@@ -358,6 +360,7 @@
/** Name of the SDK extras folder. */
public static final String FD_EXTRAS = "extras"; //$NON-NLS-1$
public static final String FD_M2_REPOSITORY = "m2repository"; //$NON-NLS-1$
+ public static final String FD_NDK = "ndk-bundle"; //$NON-NLS-1$
/**
* Name of an extra's sample folder.
* Ideally extras should have one {@link #FD_SAMPLES} folder containing
@@ -408,7 +411,7 @@
* Note: if you need an URI specifically for the "android" namespace, consider using
* {@link SdkConstants#NS_RESOURCES} instead.
*/
- public final static String NS_CUSTOM_RESOURCES_S = "http://schemas.android.com/apk/res/%1$s"; //$NON-NLS-1$
+ public static final String NS_CUSTOM_RESOURCES_S = "http://schemas.android.com/apk/res/%1$s"; //$NON-NLS-1$
/** The name of the uses-library that provides "android.test.runner" */
@@ -452,7 +455,7 @@
/** Path of the bin folder of proguard folder relative to the sdk folder.
* This is an OS path, ending with a separator. */
public static final String OS_SDK_TOOLS_PROGUARD_BIN_FOLDER =
- SdkConstants.OS_SDK_TOOLS_FOLDER +
+ OS_SDK_TOOLS_FOLDER +
"proguard" + File.separator + //$NON-NLS-1$
"bin" + File.separator; //$NON-NLS-1$
@@ -582,6 +585,19 @@
/** MockView is part of the layoutlib bridge and used to display classes that have
* no rendering in the graphical layout editor. */
public static final String CLASS_MOCK_VIEW = "com.android.layoutlib.bridge.MockView"; //$NON-NLS-1$
+ public static final String CLASS_LAYOUT_INFLATER = "android.view.LayoutInflater"; //$NON-NLS-1$
+
+ /* Android Design Support Class Constants */
+ public static final String CLASS_COORDINATOR_LAYOUT = "android.support.design.widget.CoordinatorLayout"; //$NON-NLS-1$
+ public static final String CLASS_APP_BAR_LAYOUT = "android.support.design.widget.AppBarLayout"; //$NON-NLS-1$
+ public static final String CLASS_FLOATING_ACTION_BUTTON = "android.support.design.widget.FloatingActionButton"; //$NON-NLS-1$
+ public static final String CLASS_COLLAPSING_TOOLBAR_LAYOUT = "android.support.design.widget.CollapsingToolbarLayout"; //$NON-NLS-1$
+ public static final String CLASS_NAVIGATION_VIEW = "android.support.design.widget.NavigationView"; //$NON-NLS-1$
+ public static final String CLASS_SNACKBAR = "android.support.design.widget.Snackbar"; //$NON-NLS-1$
+ public static final String CLASS_TAB_LAYOUT = "android.support.design.widget.TabLayout"; //$NON-NLS-1$
+ public static final String CLASS_TEXT_INPUT_LAYOUT = "android.support.design.widget.TextInputLayout"; //$NON-NLS-1$
+ public static final String CLASS_NESTED_SCROLL_VIEW = "android.support.v4.widget.NestedScrollView"; //$NON-NLS-1$
+
/** Returns the appropriate name for the 'android' command, which is 'android.exe' for
* Windows and 'android' for all other platforms. */
@@ -759,6 +775,7 @@
public static final String TAG_DECLARE_STYLEABLE = "declare-styleable"; //$NON-NLS-1$
public static final String TAG_EAT_COMMENT = "eat-comment"; //$NON-NLS-1$
public static final String TAG_SKIP = "skip"; //$NON-NLS-1$
+ public static final String TAG_SELECTOR = "selector"; //$NON-NLS-1$
// Tags: XML
public static final String TAG_HEADER = "header"; //$NON-NLS-1$
@@ -817,10 +834,27 @@
public static final String MULTI_AUTO_COMPLETE_TEXT_VIEW = "MultiAutoCompleteTextView"; //$NON-NLS-1$
public static final String AUTO_COMPLETE_TEXT_VIEW = "AutoCompleteTextView"; //$NON-NLS-1$
public static final String CHECKABLE = "Checkable"; //$NON-NLS-1$
+ public static final String TEXTURE_VIEW = "TextureView"; //$NON-NLS-1$
+
+ /* Android Design Support Tag Constants */
+ public static final String COORDINATOR_LAYOUT = CLASS_COORDINATOR_LAYOUT;
+ public static final String APP_BAR_LAYOUT = CLASS_APP_BAR_LAYOUT;
+ public static final String FLOATING_ACTION_BUTTON = CLASS_FLOATING_ACTION_BUTTON;
+ public static final String COLLAPSING_TOOLBAR_LAYOUT = CLASS_COLLAPSING_TOOLBAR_LAYOUT;
+ public static final String NAVIGATION_VIEW = CLASS_NAVIGATION_VIEW;
+ public static final String SNACKBAR = CLASS_SNACKBAR;
+ public static final String TAB_LAYOUT = CLASS_TAB_LAYOUT;
+ public static final String TEXT_INPUT_LAYOUT = CLASS_TEXT_INPUT_LAYOUT;
// Tags: Drawables
public static final String TAG_BITMAP = "bitmap"; //$NON-NLS-1$
+ // Tags: Data-Binding
+ public static final String TAG_LAYOUT = "layout"; //$NON-NLS-1$
+ public static final String TAG_DATA = "data"; //$NON-NLS-1$
+ public static final String TAG_VARIABLE = "variable"; //$NON-NLS-1$
+ public static final String TAG_IMPORT = "import"; //$NON-NLS-1$
+
// Attributes: Manifest
public static final String ATTR_EXPORTED = "exported"; //$NON-NLS-1$
public static final String ATTR_PERMISSION = "permission"; //$NON-NLS-1$
@@ -830,6 +864,8 @@
public static final String ATTR_PACKAGE = "package"; //$NON-NLS-1$
public static final String ATTR_CORE_APP = "coreApp"; //$NON-NLS-1$
public static final String ATTR_THEME = "theme"; //$NON-NLS-1$
+ public static final String ATTR_SCHEME = "scheme"; //$NON_NLS-1$
+ public static final String ATTR_HOST = "host"; //$NON_NLS-1$
public static final String ATTR_PATH = "path"; //$NON-NLS-1$
public static final String ATTR_PATH_PREFIX = "pathPrefix"; //$NON-NLS-1$
public static final String ATTR_PATH_PATTERN = "pathPattern"; //$NON-NLS-1$
@@ -851,6 +887,10 @@
public static final String ATTR_VALUE = "value"; //$NON-NLS-1$
public static final String ATTR_QUANTITY = "quantity"; //$NON-NLS-1$
public static final String ATTR_FORMAT = "format"; //$NON-NLS-1$
+ public static final String ATTR_PREPROCESSING = "preprocessing"; //$NON-NLS-1$
+
+ // Attributes: Data-Binding
+ public static final String ATTR_ALIAS = "alias"; //$NON-NLS-1$
// Attributes: Layout
public static final String ATTR_LAYOUT_RESOURCE_PREFIX = "layout_";//$NON-NLS-1$
@@ -971,6 +1011,12 @@
// Attributes: Drawables
public static final String ATTR_TILE_MODE = "tileMode"; //$NON-NLS-1$
+ // Attributes: CoordinatorLayout
+ public static final String ATTR_LAYOUT_ANCHOR = "layout_anchor"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_ANCHOR_GRAVITY = "layout_anchorGravity"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_BEHAVIOR = "layout_behavior"; //$NON-NLS-1$
+ public static final String ATTR_LAYOUT_KEYLINE = "layout_keyline"; //$NON-NLS-1$
+
// Values: Manifest
public static final String VALUE_SPLIT_ACTION_BAR_WHEN_NARROW = "splitActionBarWhenNarrow"; // NON-NLS-$1
@@ -984,7 +1030,6 @@
public static final String VALUE_SELECTABLE_ITEM_BACKGROUND =
"?android:attr/selectableItemBackground"; //$NON-NLS-1$
-
// Values: Resources
public static final String VALUE_ID = "id"; //$NON-NLS-1$
@@ -1020,6 +1065,7 @@
public static final String RES_FOLDER = "res"; //$NON-NLS-1$
public static final String DOT_XML = ".xml"; //$NON-NLS-1$
+ public static final String DOT_XSD = ".xsd"; //$NON-NLS-1$
public static final String DOT_GIF = ".gif"; //$NON-NLS-1$
public static final String DOT_JPG = ".jpg"; //$NON-NLS-1$
public static final String DOT_JPEG = ".jpeg"; //$NON-NLS-1$
@@ -1134,6 +1180,7 @@
// Resources
public static final String PREFIX_RESOURCE_REF = "@"; //$NON-NLS-1$
public static final String PREFIX_THEME_REF = "?"; //$NON-NLS-1$
+ public static final String PREFIX_BINDING_EXPR = "@{"; //$NON-NLS-1$
public static final String ANDROID_PREFIX = "@android:"; //$NON-NLS-1$
public static final String ANDROID_THEME_PREFIX = "?android:"; //$NON-NLS-1$
public static final String LAYOUT_RESOURCE_PREFIX = "@layout/"; //$NON-NLS-1$
@@ -1148,6 +1195,7 @@
public static final String ANDROID_LAYOUT_RESOURCE_PREFIX = "@android:layout/"; //$NON-NLS-1$
public static final String ANDROID_STYLE_RESOURCE_PREFIX = "@android:style/"; //$NON-NLS-1$
+ public static final String ANDROID_COLOR_RESOURCE_PREFIX = "@android:color/"; //$NON-NLS-1$
public static final String ANDROID_NEW_ID_PREFIX = "@android:+id/"; //$NON-NLS-1$
public static final String ANDROID_ID_PREFIX = "@android:id/"; //$NON-NLS-1$
public static final String ANDROID_DRAWABLE_PREFIX = "@android:drawable/"; //$NON-NLS-1$
@@ -1373,9 +1421,9 @@
public static final String GRADLE_PLUGIN_NAME = "com.android.tools.build:gradle:";
public static final String GRADLE_MINIMUM_VERSION = "2.2.1";
- public static final String GRADLE_LATEST_VERSION = "2.2.1";
+ public static final String GRADLE_LATEST_VERSION = "2.4";
public static final String GRADLE_PLUGIN_MINIMUM_VERSION = "1.0.0";
- public static final String GRADLE_PLUGIN_RECOMMENDED_VERSION = "1.2.0";
+ public static final String GRADLE_PLUGIN_RECOMMENDED_VERSION = "1.3.0";
public static final String GRADLE_PLUGIN_LATEST_VERSION = GRADLE_PLUGIN_RECOMMENDED_VERSION;
public static final String MIN_BUILD_TOOLS_VERSION = "19.1.0";
public static final String SUPPORT_LIB_ARTIFACT = "com.android.support:support-v4";
@@ -1388,4 +1436,11 @@
public static final String TYPE_DEF_VALUE_ATTRIBUTE = "value";
public static final String TYPE_DEF_FLAG_ATTRIBUTE = "flag";
public static final String FN_ANNOTATIONS_ZIP = "annotations.zip";
+
+ // Data Binding MISC
+ public static final String DATA_BINDING_LIB_ARTIFACT = "com.android.databinding:library";
+ public static final String[] TAGS_DATA_BINDING = new String[]{TAG_VARIABLE,
+ TAG_IMPORT, TAG_LAYOUT, TAG_DATA};
+ public static final String[] ATTRS_DATA_BINDING = new String[]{ATTR_NAME,
+ ATTR_TYPE, ATTR_CLASS, ATTR_ALIAS};
}
diff --git a/common/src/main/java/com/android/ide/common/blame/Message.java b/common/src/main/java/com/android/ide/common/blame/Message.java
new file mode 100644
index 0000000..08f109e
--- /dev/null
+++ b/common/src/main/java/com/android/ide/common/blame/Message.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.blame;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.concurrency.Immutable;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+
+import java.io.File;
+import java.util.List;
+
+@Immutable
+public final class Message {
+
+ @NonNull
+ private final Kind mKind;
+
+ @NonNull
+ private final String mText;
+
+ @NonNull
+ private final List<SourceFilePosition> mSourceFilePositions;
+
+ @NonNull
+ private final String mRawMessage;
+
+ /**
+ * Create a new message, which has a {@link Kind}, a String which will be shown to the user and
+ * at least one {@link SourceFilePosition}.
+ *
+ * @param kind the message type.
+ * @param text the text of the message.
+ * @param sourceFilePosition the first source file position the message .
+ * @param sourceFilePositions any additional source file positions, may be empty.
+ */
+ public Message(@NonNull Kind kind,
+ @NonNull String text,
+ @NonNull SourceFilePosition sourceFilePosition,
+ @NonNull SourceFilePosition... sourceFilePositions) {
+ mKind = kind;
+ mText = text;
+ mRawMessage = text;
+ mSourceFilePositions = ImmutableList.<SourceFilePosition>builder()
+ .add(sourceFilePosition).add(sourceFilePositions).build();
+ }
+
+ /**
+ * Create a new message, which has a {@link Kind}, a String which will be shown to the user and
+ * at least one {@link SourceFilePosition}.
+ *
+ * It also has a rawMessage, to store the original string for cases when the message is
+ * constructed by parsing the output from another tool.
+ *
+ * @param kind the message kind.
+ * @param text a human-readable string explaining the issue.
+ * @param rawMessage the original text of the message, usually from an external tool.
+ * @param sourceFilePosition the first source file position.
+ * @param sourceFilePositions any additional source file positions, may be empty.
+ */
+ public Message(@NonNull Kind kind,
+ @NonNull String text,
+ @NonNull String rawMessage,
+ @NonNull SourceFilePosition sourceFilePosition,
+ @NonNull SourceFilePosition... sourceFilePositions) {
+ mKind = kind;
+ mText = text;
+ mRawMessage = rawMessage;
+ mSourceFilePositions = ImmutableList.<SourceFilePosition>builder()
+ .add(sourceFilePosition).add(sourceFilePositions).build();
+ }
+
+ public Message(@NonNull Kind kind,
+ @NonNull String text,
+ @NonNull String rawMessage,
+ @NonNull ImmutableList<SourceFilePosition> positions) {
+ mKind = kind;
+ mText = text;
+ mRawMessage = rawMessage;
+
+ if (positions.isEmpty()) {
+ mSourceFilePositions = ImmutableList.of(SourceFilePosition.UNKNOWN);
+ } else {
+ mSourceFilePositions = positions;
+ }
+ }
+
+ @NonNull
+ public Kind getKind() {
+ return mKind;
+ }
+
+ @NonNull
+ public String getText() {
+ return mText;
+ }
+
+ /**
+ * Returns a list of source positions. Will always contain at least one item.
+ */
+ @NonNull
+ public List<SourceFilePosition> getSourceFilePositions() {
+ return mSourceFilePositions;
+ }
+
+ @NonNull
+ public String getRawMessage() {
+ return mRawMessage;
+ }
+
+ @Nullable
+ public String getSourcePath() {
+ File file = mSourceFilePositions.get(0).getFile().getSourceFile();
+ if (file == null) {
+ return null;
+ }
+ return file.getAbsolutePath();
+ }
+
+ /**
+ * Returns a legacy 1-based line number.
+ */
+ @Deprecated
+ public int getLineNumber() {
+ return mSourceFilePositions.get(0).getPosition().getStartLine() + 1;
+ }
+
+ /**
+ * @return a legacy 1-based column number.
+ */
+ @Deprecated
+ public int getColumn() {
+ return mSourceFilePositions.get(0).getPosition().getStartColumn() + 1;
+ }
+
+ public enum Kind {
+ ERROR, WARNING, INFO, STATISTICS, UNKNOWN, SIMPLE;
+
+ public static Kind findIgnoringCase(String s, Kind defaultKind) {
+ for (Kind kind : values()) {
+ if (kind.toString().equalsIgnoreCase(s)) {
+ return kind;
+ }
+ }
+ return defaultKind;
+ }
+
+ @Nullable
+ public static Kind findIgnoringCase(String s) {
+ return findIgnoringCase(s, null);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Message)) {
+ return false;
+ }
+ Message that = (Message) o;
+ return Objects.equal(mKind, that.mKind) &&
+ Objects.equal(mText, that.mText) &&
+ Objects.equal(mSourceFilePositions, that.mSourceFilePositions);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mKind, mText, mSourceFilePositions);
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this).add("kind", mKind).add("text", mText).add("sources",
+ mSourceFilePositions).toString();
+ }
+}
diff --git a/common/src/main/java/com/android/ide/common/blame/SourceFile.java b/common/src/main/java/com/android/ide/common/blame/SourceFile.java
new file mode 100644
index 0000000..be1264b
--- /dev/null
+++ b/common/src/main/java/com/android/ide/common/blame/SourceFile.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.blame;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.concurrency.Immutable;
+import com.google.common.base.Objects;
+
+import java.io.File;
+
+/**
+ * Represents a source file.
+ */
+@Immutable
+public final class SourceFile {
+
+ public static final SourceFile UNKNOWN = new SourceFile();
+
+ @Nullable
+ private final File mSourceFile;
+
+ /**
+ * A human readable description
+ *
+ * Usually the file name is OK for the short output, but for the manifest merger,
+ * where all of the files will be named AndroidManifest.xml the variant name is more useful.
+ */
+ @Nullable
+ private final String mDescription;
+
+ @SuppressWarnings("NullableProblems")
+ public SourceFile(
+ @NonNull File sourceFile,
+ @NonNull String description) {
+ mSourceFile = sourceFile;
+ mDescription = description;
+ }
+
+ public SourceFile(
+ @SuppressWarnings("NullableProblems") @NonNull File sourceFile) {
+ mSourceFile = sourceFile;
+ mDescription = null;
+ }
+
+ public SourceFile(
+ @SuppressWarnings("NullableProblems") @NonNull String description) {
+ mSourceFile = null;
+ mDescription = description;
+ }
+
+ private SourceFile() {
+ mSourceFile = null;
+ mDescription = null;
+ }
+
+ @Nullable
+ public File getSourceFile() {
+ return mSourceFile;
+ }
+
+ @Nullable
+ public String getDescription() {
+ return mDescription;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof SourceFile)) {
+ return false;
+ }
+ SourceFile other = (SourceFile) obj;
+
+ return Objects.equal(mDescription, other.mDescription) &&
+ Objects.equal(mSourceFile, other.mSourceFile);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mSourceFile, mDescription);
+ }
+
+ @Override
+ public String toString() {
+ return print(false /* shortFormat */);
+ }
+
+ public String print(boolean shortFormat) {
+ if (mSourceFile == null) {
+ if (mDescription == null) {
+ return "Unknown source file";
+ }
+ return mDescription;
+ }
+ String fileName = mSourceFile.getName();
+ String fileDisplayName = shortFormat ? fileName : mSourceFile.getAbsolutePath();
+ if (mDescription == null || mDescription.equals(fileName)) {
+ return fileDisplayName;
+ } else {
+ return String.format("[%1$s] %2$s", mDescription, fileDisplayName);
+ }
+ }
+
+}
diff --git a/common/src/main/java/com/android/ide/common/blame/SourceFilePosition.java b/common/src/main/java/com/android/ide/common/blame/SourceFilePosition.java
new file mode 100644
index 0000000..e5ce048
--- /dev/null
+++ b/common/src/main/java/com/android/ide/common/blame/SourceFilePosition.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.blame;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.concurrency.Immutable;
+import com.google.common.base.Objects;
+
+import java.io.File;
+
+@Immutable
+public final class SourceFilePosition {
+
+ public static final com.android.ide.common.blame.SourceFilePosition UNKNOWN =
+ new SourceFilePosition(SourceFile.UNKNOWN, SourcePosition.UNKNOWN);
+
+ @NonNull
+ private final SourceFile mSourceFile;
+
+ @NonNull
+ private final SourcePosition mSourcePosition;
+
+ public SourceFilePosition(@NonNull SourceFile sourceFile,
+ @NonNull SourcePosition sourcePosition) {
+ mSourceFile = sourceFile;
+ mSourcePosition = sourcePosition;
+ }
+
+ public SourceFilePosition(@NonNull File file,
+ @NonNull SourcePosition sourcePosition) {
+ this(new SourceFile(file), sourcePosition);
+ }
+
+ @NonNull
+ public SourcePosition getPosition() {
+ return mSourcePosition;
+ }
+
+ @NonNull
+ public SourceFile getFile() {
+ return mSourceFile;
+ }
+
+ @Override
+ public String toString() {
+ return print(false);
+ }
+
+ public String print(boolean shortFormat) {
+ if (mSourcePosition.equals(SourcePosition.UNKNOWN)) {
+ return mSourceFile.print(shortFormat);
+ } else {
+ return mSourceFile.print(shortFormat) + ':' + mSourcePosition.toString();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mSourceFile, mSourcePosition);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof SourceFilePosition)) {
+ return false;
+ }
+ SourceFilePosition other = (SourceFilePosition) obj;
+ return Objects.equal(mSourceFile, other.mSourceFile) &&
+ Objects.equal(mSourcePosition, other.mSourcePosition);
+ }
+}
diff --git a/common/src/main/java/com/android/ide/common/blame/SourcePosition.java b/common/src/main/java/com/android/ide/common/blame/SourcePosition.java
index 5790673..de62180 100644
--- a/common/src/main/java/com/android/ide/common/blame/SourcePosition.java
+++ b/common/src/main/java/com/android/ide/common/blame/SourcePosition.java
@@ -16,6 +16,9 @@
package com.android.ide.common.blame;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.concurrency.Immutable;
import com.google.common.base.Objects;
/**
@@ -23,25 +26,15 @@
*
* Positions that are unknown are represented by -1.
*/
-public class SourcePosition {
+@Immutable
+public final class SourcePosition {
public static final SourcePosition UNKNOWN = new SourcePosition();
- private final int mStartLine;
+ private final int mStartLine, mStartColumn, mStartOffset, mEndLine, mEndColumn, mEndOffset;
- private final int mStartColumn;
-
- private final int mStartOffset;
-
- private final int mEndLine;
-
- private final int mEndColumn;
-
- private final int mEndOffset;
-
- public SourcePosition(int startLine, int startColumn, int startOffset, int endLine,
- int endColumn,
- int endOffset) {
+ public SourcePosition(int startLine, int startColumn, int startOffset,
+ int endLine, int endColumn, int endOffset) {
mStartLine = startLine;
mStartColumn = startColumn;
mStartOffset = startOffset;
@@ -83,25 +76,28 @@
*/
@Override
public String toString() {
- StringBuilder sB = new StringBuilder();
- sB.append(mStartLine);
+ if (mStartLine == -1) {
+ return "?";
+ }
+ StringBuilder sB = new StringBuilder(15);
+ sB.append(mStartLine + 1); // Humans think that the first line is line 1.
if (mStartColumn != -1) {
sB.append(':');
- sB.append(mStartColumn);
+ sB.append(mStartColumn + 1);
}
if (mEndLine != -1) {
if (mEndLine == mStartLine) {
if (mEndColumn != -1 && mEndColumn != mStartColumn) {
sB.append('-');
- sB.append(mEndColumn);
+ sB.append(mEndColumn + 1);
}
} else {
sB.append('-');
- sB.append(mEndLine);
+ sB.append(mEndLine + 1);
if (mEndColumn != -1) {
sB.append(':');
- sB.append(mEndColumn);
+ sB.append(mEndColumn + 1);
}
}
}
@@ -110,17 +106,20 @@
@Override
public boolean equals(Object obj) {
- if (obj instanceof SourcePosition) {
- SourcePosition other = (SourcePosition) obj;
-
- return other.mStartLine == mStartLine &&
- other.mStartColumn == mStartColumn &&
- other.mStartOffset == mStartOffset &&
- other.mEndLine == mEndLine &&
- other.mEndColumn == mEndColumn &&
- other.mEndOffset == mEndOffset;
+ if (this == obj) {
+ return true;
}
- return false;
+ if (!(obj instanceof SourcePosition)) {
+ return false;
+ }
+ SourcePosition other = (SourcePosition) obj;
+
+ return other.mStartLine == mStartLine &&
+ other.mStartColumn == mStartColumn &&
+ other.mStartOffset == mStartOffset &&
+ other.mEndLine == mEndLine &&
+ other.mEndColumn == mEndColumn &&
+ other.mEndOffset == mEndOffset;
}
@Override
diff --git a/common/src/main/java/com/android/utils/FileUtils.java b/common/src/main/java/com/android/utils/FileUtils.java
index 52bd87d..897300a 100644
--- a/common/src/main/java/com/android/utils/FileUtils.java
+++ b/common/src/main/java/com/android/utils/FileUtils.java
@@ -16,34 +16,48 @@
package com.android.utils;
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.android.annotations.NonNull;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
+import com.google.common.hash.Hashing;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
public class FileUtils {
- public static boolean deleteFolder(final File folder) {
+ public static void deleteFolder(final File folder) throws IOException {
if (!folder.exists()) {
- return true;
+ return;
}
File[] files = folder.listFiles();
- if (files != null) {
+ if (files != null) { // i.e. is a directory.
for (final File file : files) {
- if (file.isDirectory()) {
- deleteFolder(file);
- } else {
- file.delete();
- }
+ deleteFolder(file);
}
}
- return folder.delete();
+ if (!folder.delete()) {
+ throw new IOException(String.format("Could not delete folder %s", folder));
+ }
+ }
+
+ public static void emptyFolder(final File folder) throws IOException {
+ deleteFolder(folder);
+ if (!folder.mkdirs()) {
+ throw new IOException(String.format("Could not create empty folder %s", folder));
+ }
}
public static void copyFile(File from, File to) throws IOException {
to = new File(to, from.getName());
if (from.isDirectory()) {
if (!to.exists()) {
- to.mkdirs();
+ if (!to.mkdirs()) {
+ throw new IOException(String.format("Could not create directory %s", to));
+ }
}
File[] children = from.listFiles();
@@ -56,4 +70,30 @@
Files.copy(from, to);
}
}
+
+ public static File join(File dir, String... paths) {
+ return new File(dir, Joiner.on(File.separatorChar).join(paths));
+ }
+
+ public static String relativePath(@NonNull File file, @NonNull File dir) {
+ checkArgument(file.isFile(), "%s is not a file.", file.getPath());
+ checkArgument(dir.isDirectory(), "%s is not a directory.", dir.getPath());
+
+ return dir.toURI().relativize(file.toURI()).getPath();
+ }
+
+ public static String sha1(@NonNull File file) throws IOException {
+ return Hashing.sha1().hashBytes(Files.toByteArray(file)).toString();
+ }
+
+ public static String getNamesAsCommaSeparatedList(Iterable<File> files) {
+ return Joiner.on(", ").join(Iterables.transform(files, GET_NAME));
+ }
+
+ private static final Function<File, String> GET_NAME = new Function<File, String>() {
+ @Override
+ public String apply(File file) {
+ return file.getName();
+ }
+ };
}
diff --git a/common/src/main/java/com/android/utils/HtmlBuilder.java b/common/src/main/java/com/android/utils/HtmlBuilder.java
index f799425..085d7e3 100644
--- a/common/src/main/java/com/android/utils/HtmlBuilder.java
+++ b/common/src/main/java/com/android/utils/HtmlBuilder.java
@@ -65,14 +65,14 @@
}
public HtmlBuilder newline() {
- mStringBuilder.append("<BR/>\n");
+ mStringBuilder.append("<BR/>");
return this;
}
public HtmlBuilder newlineIfNecessary() {
- if (!SdkUtils.endsWith(mStringBuilder, "<BR/>\n")) {
- mStringBuilder.append("<BR/>\n");
+ if (!SdkUtils.endsWith(mStringBuilder, "<BR/>")) {
+ mStringBuilder.append("<BR/>");
}
return this;
@@ -223,9 +223,9 @@
public HtmlBuilder endList() {
if (USE_DD_LISTS) {
- mStringBuilder.append("\n</DL>");
+ mStringBuilder.append("</DL>");
} else {
- mStringBuilder.append("\n</UL>");
+ mStringBuilder.append("</UL>");
}
return this;
@@ -233,10 +233,10 @@
public HtmlBuilder listItem() {
if (USE_DD_LISTS) {
- mStringBuilder.append("\n<DD>");
+ mStringBuilder.append("<DD>");
mStringBuilder.append("-&NBSP;");
} else {
- mStringBuilder.append("\n<LI>");
+ mStringBuilder.append("<LI>");
}
return this;
diff --git a/common/src/main/java/com/android/utils/PositionXmlParser.java b/common/src/main/java/com/android/utils/PositionXmlParser.java
index f0b8767..86ce844 100644
--- a/common/src/main/java/com/android/utils/PositionXmlParser.java
+++ b/common/src/main/java/com/android/utils/PositionXmlParser.java
@@ -18,9 +18,9 @@
import static com.android.SdkConstants.UTF_8;
-import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourcePosition;
import org.w3c.dom.Attr;
import org.w3c.dom.Comment;
@@ -40,7 +40,6 @@
import java.io.InputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
-import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
@@ -69,12 +68,15 @@
"http://xml.org/sax/features/xmlns-uris"; //$NON-NLS-1$
/** See http://www.w3.org/TR/REC-xml/#NT-EncodingDecl */
private static final Pattern ENCODING_PATTERN =
- Pattern.compile("encoding=['\"](\\S*)['\"]");//$NON-NLS-1$
+ Pattern.compile("encoding=['\"](\\S*)['\"]"); //$NON-NLS-1$
+ private static final String LOAD_EXTERNAL_DTD =
+ "http://apache.org/xml/features/nonvalidating/load-external-dtd";; //$NON-NLS-1$
/**
* Parses the XML content from the given input stream.
*
* @param input the input stream containing the XML to be parsed
+ * @param checkDtd whether or not download the DTD and validate it
* @return the corresponding document
* @throws ParserConfigurationException if a SAX parser is not available
* @throws SAXException if the document contains a parsing error
@@ -82,8 +84,8 @@
* happen since the input source is known to be constructed from
* a string.
*/
- @Nullable
- public Document parse(@NonNull InputStream input)
+ @NonNull
+ public static Document parse(@NonNull InputStream input, boolean checkDtd)
throws ParserConfigurationException, SAXException, IOException {
// Read in all the data
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -96,13 +98,32 @@
out.write(buf, 0, r);
}
input.close();
- return parse(out.toByteArray());
+ return parse(out.toByteArray(), checkDtd);
+ }
+
+ /**
+ * @see PositionXmlParser#parse(InputStream, boolean)
+ */
+ @NonNull
+ public static Document parse(@NonNull InputStream input)
+ throws IOException, SAXException, ParserConfigurationException {
+ return parse(input, true);
+ }
+
+ /**
+ * @see PositionXmlParser#parse(byte[], boolean)
+ */
+ @NonNull
+ public static Document parse(@NonNull byte[] data)
+ throws ParserConfigurationException, SAXException, IOException {
+ return parse(data, true);
}
/**
* Parses the XML content from the given byte array
*
* @param data the raw XML data (with unknown encoding)
+ * @param checkDtd whether or not download the DTD and validate it
* @return the corresponding document
* @throws ParserConfigurationException if a SAX parser is not available
* @throws SAXException if the document contains a parsing error
@@ -110,12 +131,12 @@
* happen since the input source is known to be constructed from
* a string.
*/
- @Nullable
- public Document parse(@NonNull byte[] data)
- throws ParserConfigurationException, SAXException, IOException {
+ @NonNull
+ public static Document parse(@NonNull byte[] data, boolean checkDtd)
+ throws ParserConfigurationException, SAXException, IOException {
String xml = getXmlString(data);
xml = XmlUtils.stripBom(xml);
- return parse(xml, new InputSource(new StringReader(xml)), true);
+ return parse(xml, new InputSource(new StringReader(xml)), true, checkDtd);
}
/**
@@ -130,21 +151,26 @@
* happen since the input source is known to be constructed from
* a string.
*/
- @Nullable
- public Document parse(@NonNull String xml)
+ @NonNull
+ public static Document parse(@NonNull String xml)
throws ParserConfigurationException, SAXException, IOException {
xml = XmlUtils.stripBom(xml);
- return parse(xml, new InputSource(new StringReader(xml)), true);
+ return parse(xml, new InputSource(new StringReader(xml)), true, true);
}
@NonNull
- private Document parse(@NonNull String xml, @NonNull InputSource input, boolean checkBom)
+ private static Document parse(@NonNull String xml, @NonNull InputSource input, boolean checkBom,
+ boolean checkDtd)
throws ParserConfigurationException, SAXException, IOException {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
- factory.setFeature(NAMESPACE_FEATURE, true);
- factory.setFeature(NAMESPACE_PREFIX_FEATURE, true);
- factory.setFeature(PROVIDE_XMLNS_URIS, true);
+ if (checkDtd) {
+ factory.setFeature(NAMESPACE_FEATURE, true);
+ factory.setFeature(NAMESPACE_PREFIX_FEATURE, true);
+ factory.setFeature(PROVIDE_XMLNS_URIS, true);
+ } else {
+ factory.setFeature(LOAD_EXTERNAL_DTD, false);
+ }
SAXParser parser = factory.newSAXParser();
DomBuilder handler = new DomBuilder(xml);
XMLReader xmlReader = parser.getXMLReader();
@@ -160,7 +186,7 @@
// (see http://en.wikipedia.org/wiki/Byte_order_mark) so here we'll
// just skip those up to the XML prolog beginning character, <
xml = xml.replaceFirst("^([\\W]+)<","<"); //$NON-NLS-1$ //$NON-NLS-2$
- return parse(xml, new InputSource(new StringReader(xml)), false);
+ return parse(xml, new InputSource(new StringReader(xml)), false, checkDtd);
}
throw e;
}
@@ -313,8 +339,8 @@
* @return the position, or null if the node type is not supported for
* position info
*/
- @Nullable
- public Position getPosition(@NonNull Node node) {
+ @NonNull
+ public static SourcePosition getPosition(@NonNull Node node) {
return getPosition(node, -1, -1);
}
@@ -332,8 +358,15 @@
* @return the position, or null if the node type is not supported for
* position info
*/
+
+ @NonNull
+ public static SourcePosition getPosition(@NonNull Node node, int start, int end) {
+ Position p = getPositionHelper(node, start, end);
+ return p == null ? SourcePosition.UNKNOWN : p.toSourcePosition();
+ }
+
@Nullable
- public Position getPosition(@NonNull Node node, int start, int end) {
+ private static Position getPositionHelper(@NonNull Node node, int start, int end) {
// Look up the position information stored while parsing for the given node.
// Note however that we only store position information for elements (because
// there is no SAX callback for individual attributes).
@@ -384,10 +417,10 @@
}
}
- Position attributePosition = createPosition(line, column, index);
+ Position attributePosition = new Position(line, column, index);
// Also set end range for retrieval in getLocation
- attributePosition.setEnd(createPosition(line, column + matcher.end(1) - index,
- matcher.end(1)));
+ attributePosition.setEnd(
+ new Position(line, column + matcher.end(1) - index, matcher.end(1)));
return attributePosition;
} else {
// No regexp match either: just fall back to element position
@@ -465,15 +498,13 @@
column = newColumn;
}
- Position attributePosition = createPosition(line, column,
- offset + textIndex);
+ Position attributePosition = new Position(line, column, offset + textIndex);
// Also set end range for retrieval in getLocation
if (end != -1) {
- attributePosition.setEnd(createPosition(line, column,
- offset + end));
+ attributePosition.setEnd(new Position(line, column, offset + end));
} else {
- attributePosition.setEnd(createPosition(line, column,
- offset + textLength));
+ attributePosition.setEnd(
+ new Position(line, column, offset + textLength));
}
return attributePosition;
} else if (c == '"') {
@@ -498,7 +529,7 @@
* information is attached to the DOM nodes by setting user data with the
* {@link #POS_KEY} key.
*/
- private final class DomBuilder extends DefaultHandler2 {
+ private static final class DomBuilder extends DefaultHandler2 {
private final String mXml;
private final Document mDocument;
private Locator mLocator;
@@ -640,7 +671,7 @@
}
}
- return createPosition(line, column, offset);
+ return new Position(line, column, offset);
}
}
// we did not find it, approximate.
@@ -685,7 +716,7 @@
}
mCurrentColumn = column;
- return createPosition(mCurrentLine, mCurrentColumn, mCurrentOffset);
+ return new Position(mCurrentLine, mCurrentColumn, mCurrentOffset);
}
@Override
@@ -703,50 +734,7 @@
}
}
- /**
- * Creates a position while constructing the DOM document. This method
- * allows a subclass to create a custom implementation of the position
- * class.
- *
- * @param line the line number for the position
- * @param column the column number for the position
- * @param offset the character offset
- * @return a new position
- */
- @NonNull
- protected Position createPosition(int line, int column, int offset) {
- return new DefaultPosition(line, column, offset);
- }
-
- public interface Position {
- /**
- * Linked position: for a begin position this will point to the
- * corresponding end position. For an end position this will be null.
- *
- * @return the end position, or null
- */
- @Nullable
- public Position getEnd();
-
- /**
- * Linked position: for a begin position this will point to the
- * corresponding end position. For an end position this will be null.
- *
- * @param end the end position
- */
- public void setEnd(@NonNull Position end);
-
- /** @return the line number, 0-based */
- public int getLine();
-
- /** @return the offset number, 0-based */
- public int getOffset();
-
- /** @return the column number, 0-based, and -1 if the column number if not known */
- public int getColumn();
- }
-
- protected static class DefaultPosition implements Position {
+ private static class Position {
/** The line number (0-based where the first line is line 0) */
private final int mLine;
private final int mColumn;
@@ -760,35 +748,44 @@
* @param column the 0-based column number, or -1 if unknown
* @param offset the offset, or -1 if unknown
*/
- public DefaultPosition(int line, int column, int offset) {
+ public Position(int line, int column, int offset) {
this.mLine = line;
this.mColumn = column;
this.mOffset = offset;
}
- @Override
public int getLine() {
return mLine;
}
- @Override
public int getOffset() {
return mOffset;
}
- @Override
public int getColumn() {
return mColumn;
}
- @Override
public Position getEnd() {
return mEnd;
}
- @Override
public void setEnd(@NonNull Position end) {
mEnd = end;
}
+
+ public SourcePosition toSourcePosition() {
+ int endLine = mLine, endColumn = mColumn, endOffset = mOffset;
+
+ if (mEnd != null) {
+ endLine = mEnd.getLine();
+ endColumn = mEnd.getColumn();
+ endOffset = mEnd.getOffset();
+ }
+
+ return new SourcePosition(mLine, mColumn, mOffset, endLine, endColumn, endOffset);
+ }
}
+
+ private PositionXmlParser() { }
}
diff --git a/common/src/main/java/com/android/utils/SdkUtils.java b/common/src/main/java/com/android/utils/SdkUtils.java
index 51b0464..68e7354 100644
--- a/common/src/main/java/com/android/utils/SdkUtils.java
+++ b/common/src/main/java/com/android/utils/SdkUtils.java
@@ -18,10 +18,9 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.annotations.VisibleForTesting;
import com.google.common.base.CaseFormat;
import com.google.common.base.Charsets;
-import com.google.common.collect.Lists;
+import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables;
@@ -37,6 +36,7 @@
import java.text.ParseException;
import java.util.List;
+import static com.android.SdkConstants.DOT_WEBP;
import static com.android.SdkConstants.DOT_XML;
import static com.android.SdkConstants.DOT_PNG;
import static com.android.SdkConstants.DOT_GIF;
@@ -489,8 +489,8 @@
return resourceName;
}
- public static final List<String> IMAGE_EXTENSIONS = Lists.newArrayList(
- DOT_PNG, DOT_9PNG, DOT_GIF, DOT_JPEG, DOT_JPG, DOT_BMP);
+ public static final List<String> IMAGE_EXTENSIONS = ImmutableList.of(
+ DOT_PNG, DOT_9PNG, DOT_GIF, DOT_JPEG, DOT_JPG, DOT_BMP, DOT_WEBP);
/**
* Returns true if the given file path points to an image file recognized by
diff --git a/common/src/main/java/com/android/utils/XmlUtils.java b/common/src/main/java/com/android/utils/XmlUtils.java
index d8374de..1159c89 100644
--- a/common/src/main/java/com/android/utils/XmlUtils.java
+++ b/common/src/main/java/com/android/utils/XmlUtils.java
@@ -33,8 +33,6 @@
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.google.common.base.CaseFormat;
-import com.google.common.base.Charsets;
import com.google.common.io.Files;
import org.w3c.dom.Attr;
@@ -48,14 +46,10 @@
import java.io.ByteArrayInputStream;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
-import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Locale;
@@ -493,7 +487,7 @@
* To perform pretty printing, use {@code XmlPrettyPrinter.prettyPrint(node)} in
* {@code sdk-common}.
*/
- public static String toXml(Node node, boolean preserveWhitespace) {
+ public static String toXml(Node node) {
StringBuilder sb = new StringBuilder(1000);
append(sb, node, 0);
return sb.toString();
diff --git a/common/src/main/java/com/android/xml/AndroidManifest.java b/common/src/main/java/com/android/xml/AndroidManifest.java
index b53566b..b06daa1 100644
--- a/common/src/main/java/com/android/xml/AndroidManifest.java
+++ b/common/src/main/java/com/android/xml/AndroidManifest.java
@@ -70,6 +70,8 @@
public static final String ATTRIBUTE_PACKAGE = "package";
public static final String ATTRIBUTE_VERSIONCODE = "versionCode";
public static final String ATTRIBUTE_NAME = "name";
+ public static final String ATTRIBUTE_MIME_TYPE = "mimeType";
+ public static final String ATTRIBUTE_PORT = "port";
public static final String ATTRIBUTE_REQUIRED = "required";
public static final String ATTRIBUTE_GLESVERSION = "glEsVersion";
public static final String ATTRIBUTE_PROCESS = "process";
diff --git a/common/src/test/java/com/android/utils/HtmlBuilderTest.java b/common/src/test/java/com/android/utils/HtmlBuilderTest.java
index f04f3c3..82701df 100644
--- a/common/src/test/java/com/android/utils/HtmlBuilderTest.java
+++ b/common/src/test/java/com/android/utils/HtmlBuilderTest.java
@@ -59,10 +59,10 @@
builder.add("Plain").newline();
builder.beginList().listItem().add("item 1").listItem().add("item 2").endList();
- assertEquals("Plain<BR/>\n" +
- "<DL>\n" +
- "<DD>-&NBSP;item 1\n" +
- "<DD>-&NBSP;item 2\n" +
+ assertEquals("Plain<BR/>" +
+ "<DL>" +
+ "<DD>-&NBSP;item 1" +
+ "<DD>-&NBSP;item 2" +
"</DL>", builder.getHtml());
}
@@ -124,16 +124,16 @@
public void testNewlineIfNecessary() {
HtmlBuilder builder = new HtmlBuilder();
builder.newlineIfNecessary();
- assertEquals("<BR/>\n", builder.getHtml());
+ assertEquals("<BR/>", builder.getHtml());
builder.newlineIfNecessary();
- assertEquals("<BR/>\n", builder.getHtml());
+ assertEquals("<BR/>", builder.getHtml());
builder.add("a");
builder.newlineIfNecessary();
- assertEquals("<BR/>\na<BR/>\n", builder.getHtml());
+ assertEquals("<BR/>a<BR/>", builder.getHtml());
builder.newline();
builder.newlineIfNecessary();
builder.newlineIfNecessary();
builder.newlineIfNecessary();
- assertEquals("<BR/>\na<BR/>\n<BR/>\n", builder.getHtml());
+ assertEquals("<BR/>a<BR/><BR/>", builder.getHtml());
}
}
diff --git a/common/src/test/java/com/android/utils/PositionXmlParserTest.java b/common/src/test/java/com/android/utils/PositionXmlParserTest.java
index 9c1130f..859cf36 100644
--- a/common/src/test/java/com/android/utils/PositionXmlParserTest.java
+++ b/common/src/test/java/com/android/utils/PositionXmlParserTest.java
@@ -16,7 +16,7 @@
package com.android.utils;
-import com.android.utils.PositionXmlParser.Position;
+import com.android.ide.common.blame.SourcePosition;
import junit.framework.TestCase;
@@ -36,7 +36,7 @@
import java.io.OutputStreamWriter;
import java.io.Writer;
-@SuppressWarnings("javadoc")
+@SuppressWarnings({"javadoc", "IOResourceOpenedButNotSafelyClosed"})
public class PositionXmlParserTest extends TestCase {
public void test() throws Exception {
String xml =
@@ -59,12 +59,11 @@
" android:text=\"Button\" />\n" +
"\n" +
"</LinearLayout>\n";
- PositionXmlParser parser = new PositionXmlParser();
File file = File.createTempFile("parsertest", ".xml");
Writer fw = new BufferedWriter(new FileWriter(file));
fw.write(xml);
fw.close();
- Document document = parser.parse(new FileInputStream(file));
+ Document document = PositionXmlParser.parse(new FileInputStream(file));
assertNotNull(document);
// Basic parsing heart beat tests
@@ -79,40 +78,36 @@
// Check attribute positions
Attr attr = linearLayout.getAttributeNodeNS(ANDROID_URI, "layout_width");
assertNotNull(attr);
- Position start = parser.getPosition(attr);
- Position end = start.getEnd();
- assertEquals(2, start.getLine());
- assertEquals(4, start.getColumn());
- assertEquals(xml.indexOf("android:layout_width"), start.getOffset());
- assertEquals(2, end.getLine());
+ SourcePosition position = PositionXmlParser.getPosition(attr);
+ assertEquals(2, position.getStartLine());
+ assertEquals(4, position.getStartColumn());
+ assertEquals(xml.indexOf("android:layout_width"), position.getStartOffset());
+ assertEquals(2, position.getEndLine());
String target = "android:layout_width=\"match_parent\"";
- assertEquals(xml.indexOf(target) + target.length(), end.getOffset());
+ assertEquals(xml.indexOf(target) + target.length(), position.getEndOffset());
// Check element positions
Element button = (Element) buttons.item(0);
- start = parser.getPosition(button);
- end = start.getEnd();
- assertNull(end.getEnd());
- assertEquals(6, start.getLine());
- assertEquals(4, start.getColumn());
- assertEquals(xml.indexOf("<Button"), start.getOffset());
- assertEquals(xml.indexOf("/>") + 2, end.getOffset());
- assertEquals(10, end.getLine());
- int button1End = end.getOffset();
+ position = PositionXmlParser.getPosition(button);
+ assertEquals(6, position.getStartLine());
+ assertEquals(4, position.getStartColumn());
+ assertEquals(xml.indexOf("<Button"), position.getStartOffset());
+ assertEquals(xml.indexOf("/>") + 2, position.getEndOffset());
+ assertEquals(10, position.getEndLine());
+ int button1End = position.getEndOffset();
Element button2 = (Element) buttons.item(1);
- start = parser.getPosition(button2);
- end = start.getEnd();
- assertEquals(12, start.getLine());
- assertEquals(4, start.getColumn());
- assertEquals(xml.indexOf("<Button", button1End), start.getOffset());
- assertEquals(xml.indexOf("/>", start.getOffset()) + 2, end.getOffset());
- assertEquals(16, end.getLine());
+ position = PositionXmlParser.getPosition(button2);
+ assertEquals(12, position.getStartLine());
+ assertEquals(4, position.getStartColumn());
+ assertEquals(xml.indexOf("<Button", button1End), position.getStartOffset());
+ assertEquals(xml.indexOf("/>", position.getStartOffset()) + 2, position.getEndOffset());
+ assertEquals(16, position.getEndLine());
file.delete();
}
- public void testText() throws Exception {
+ public static void testText() throws Exception {
String xml =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
@@ -128,13 +123,12 @@
" some text\n" +
"\n" +
"</LinearLayout>\n";
- PositionXmlParser parser = new PositionXmlParser();
File file = File.createTempFile("parsertest", ".xml");
file.deleteOnExit();
Writer fw = new BufferedWriter(new FileWriter(file));
fw.write(xml);
fw.close();
- Document document = parser.parse(new FileInputStream(file));
+ Document document = PositionXmlParser.parse(new FileInputStream(file));
assertNotNull(document);
// Basic parsing heart beat tests
@@ -152,16 +146,16 @@
assertNotNull(text);
// Check attribute positions
- Position start = parser.getPosition(text);
- assertEquals(11, start.getLine());
- assertEquals(10, start.getColumn());
- assertEquals(xml.indexOf("some text"), start.getOffset());
+ SourcePosition start = PositionXmlParser.getPosition(text);
+ assertEquals(11, start.getStartLine());
+ assertEquals(10, start.getStartColumn());
+ assertEquals(xml.indexOf("some text"), start.getStartOffset());
// Check attribute positions with text node offsets
- start = parser.getPosition(text, 13, 15);
- assertEquals(11, start.getLine());
- assertEquals(12, start.getColumn());
- assertEquals(xml.indexOf("me"), start.getOffset());
+ start = PositionXmlParser.getPosition(text, 13, 15);
+ assertEquals(11, start.getStartLine());
+ assertEquals(12, start.getStartColumn());
+ assertEquals(xml.indexOf("me"), start.getStartOffset());
}
public void testLineEndings() throws Exception {
@@ -172,12 +166,11 @@
"\r" +
"<LinearLayout></LinearLayout>\r\n" +
"</LinearLayout>\r\n";
- PositionXmlParser parser = new PositionXmlParser();
File file = File.createTempFile("parsertest2", ".xml");
Writer fw = new BufferedWriter(new FileWriter(file));
fw.write(xml);
fw.close();
- Document document = parser.parse(new FileInputStream(file));
+ Document document = PositionXmlParser.parse(new FileInputStream(file));
assertNotNull(document);
file.delete();
@@ -211,7 +204,6 @@
lineEnding +
"<bar></bar>" + lineEnding +
"</foo>" + lineEnding);
- PositionXmlParser parser = new PositionXmlParser();
File file = File.createTempFile("parsertest" + encoding + writeBom + writeEncoding,
".xml");
BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(file));
@@ -247,18 +239,18 @@
writer.write(sb.toString());
writer.close();
- Document document = parser.parse(new FileInputStream(file));
+ Document document = PositionXmlParser.parse(new FileInputStream(file));
assertNotNull(document);
Element root = document.getDocumentElement();
assertEquals(file.getPath(), value, root.getAttribute("attr"));
- assertEquals(4, parser.getPosition(root).getLine());
+ assertEquals(4, PositionXmlParser.getPosition(root).getStartLine());
Attr attribute = root.getAttributeNode("attr");
assertNotNull(attribute);
- Position position = parser.getPosition(attribute);
+ SourcePosition position = PositionXmlParser.getPosition(attribute);
assertNotNull(position);
- assertEquals(4, position.getLine());
- assertEquals(startAttrOffset, position.getOffset());
+ assertEquals(4, position.getStartLine());
+ assertEquals(startAttrOffset, position.getStartOffset());
file.delete();
}
@@ -338,13 +330,12 @@
" some text\n" +
"\n" +
"</LinearLayout>\n";
- PositionXmlParser parser = new PositionXmlParser();
File file = File.createTempFile("parsertest", ".xml");
file.deleteOnExit();
Writer fw = new BufferedWriter(new FileWriter(file));
fw.write(xml);
fw.close();
- Document document = parser.parse(new FileInputStream(file));
+ Document document = PositionXmlParser.parse(new FileInputStream(file));
assertNotNull(document);
// Basic parsing heart beat tests
@@ -354,18 +345,18 @@
// first child is a comment.
org.w3c.dom.Node commentNode = linearLayout.getFirstChild().getNextSibling();
assertEquals(Node.COMMENT_NODE, commentNode.getNodeType());
- Position position = parser.getPosition(commentNode);
+ SourcePosition position = PositionXmlParser.getPosition(commentNode);
assertNotNull(position);
- assertEquals(6, position.getLine());
- assertEquals(4, position.getColumn());
- assertEquals(xml.indexOf("<!--"), position.getOffset());
+ assertEquals(6, position.getStartLine());
+ assertEquals(4, position.getStartColumn());
+ assertEquals(xml.indexOf("<!--"), position.getStartOffset());
// ensure that the next siblings' position start at the right location.
Element button = (Element) document.getElementsByTagName("Button").item(0);
- Position buttonPosition = parser.getPosition(button);
+ SourcePosition buttonPosition = PositionXmlParser.getPosition(button);
assertNotNull(buttonPosition);
- assertEquals(7, buttonPosition.getLine());
- assertEquals(4, buttonPosition.getColumn());
+ assertEquals(7, buttonPosition.getStartLine());
+ assertEquals(4, buttonPosition.getStartColumn());
}
public void testMultipleLineComment() throws Exception {
@@ -387,13 +378,12 @@
" some text\n" +
"\n" +
"</LinearLayout>\n";
- PositionXmlParser parser = new PositionXmlParser();
File file = File.createTempFile("parsertest", ".xml");
file.deleteOnExit();
Writer fw = new BufferedWriter(new FileWriter(file));
fw.write(xml);
fw.close();
- Document document = parser.parse(new FileInputStream(file));
+ Document document = PositionXmlParser.parse(new FileInputStream(file));
assertNotNull(document);
// Basic parsing heart beat tests
@@ -403,18 +393,18 @@
// first child is a comment.
Node commentNode = linearLayout.getFirstChild().getNextSibling();
assertEquals(Node.COMMENT_NODE, commentNode.getNodeType());
- Position position = parser.getPosition(commentNode);
+ SourcePosition position = PositionXmlParser.getPosition(commentNode);
assertNotNull(position);
- assertEquals(6, position.getLine());
- assertEquals(4, position.getColumn());
+ assertEquals(6, position.getStartLine());
+ assertEquals(4, position.getStartColumn());
// ensure that the next siblings' position start at the right location.
Element button = (Element) document.getElementsByTagName("Button").item(0);
- Position buttonPosition = parser.getPosition(button);
+ SourcePosition buttonPosition = PositionXmlParser.getPosition(button);
assertNotNull(buttonPosition);
- assertEquals(9, buttonPosition.getLine());
- assertEquals(4, buttonPosition.getColumn());
+ assertEquals(9, buttonPosition.getStartLine());
+ assertEquals(4, buttonPosition.getStartColumn());
}
public void testAttributeWithoutNamespace() throws Exception {
@@ -444,13 +434,12 @@
+ "</LinearLayout>\n"
+ "\n";
- PositionXmlParser parser = new PositionXmlParser();
File file = File.createTempFile("parsertest", ".xml");
file.deleteOnExit();
Writer fw = new BufferedWriter(new FileWriter(file));
fw.write(xml);
fw.close();
- Document document = parser.parse(new FileInputStream(file));
+ Document document = PositionXmlParser.parse(new FileInputStream(file));
assertNotNull(document);
// Basic parsing heart beat tests
@@ -463,33 +452,26 @@
for (int i = 0, n = buttons.getLength(); i < n; i++) {
Element button = (Element)buttons.item(i);
Attr attr;
- Position start;
- Position end;
+ SourcePosition position;
attr = button.getAttributeNode("orientation");
- start = parser.getPosition(attr);
- assertNotNull(start);
- end = start.getEnd();
- assertNotNull(end);
+ position = PositionXmlParser.getPosition(attr);
+ assertNotNull(position);
assertEquals(" orientation=\"true\"",
- xml.substring(start.getOffset() - 1, end.getOffset()));
+ xml.substring(position.getStartOffset() - 1, position.getEndOffset()));
attr = button.getAttributeNodeNS("http://schemas.android.com/apk/res/android",
"orientation");
- start = parser.getPosition(attr);
- assertNotNull(start);
- end = start.getEnd();
- assertNotNull(end);
+ position = PositionXmlParser.getPosition(attr);
+ assertNotNull(position);
assertEquals("android:orientation=\"vertical\"",
- xml.substring(start.getOffset(), end.getOffset()));
+ xml.substring(position.getStartOffset(), position.getEndOffset()));
attr = button.getAttributeNodeNS("http://foo.bar", "orientation");
- start = parser.getPosition(attr);
- assertNotNull(start);
- end = start.getEnd();
- assertNotNull(end);
+ position = PositionXmlParser.getPosition(attr);
+ assertNotNull(position);
assertEquals("other:orientation=\"vertical\"",
- xml.substring(start.getOffset(), end.getOffset()));
+ xml.substring(position.getStartOffset(), position.getEndOffset()));
}
}
@@ -503,12 +485,11 @@
" <ns:SubTag\n" +
" ns:text=\"Button\" />\n" +
"</ns:SomeTag>\n";
- PositionXmlParser parser = new PositionXmlParser();
File file = File.createTempFile("parsertest", ".xml");
Writer fw = new BufferedWriter(new FileWriter(file));
fw.write(XML);
fw.close();
- Document document = parser.parse(new FileInputStream(file));
+ Document document = PositionXmlParser.parse(new FileInputStream(file));
assertNotNull(document);
Element e = document.getDocumentElement();
assertNotNull(e);
@@ -526,11 +507,10 @@
assertEquals("SubTag", subTag.getLocalName());
Attr attr = subTag.getAttributeNodeNS(NAMESPACE_URL, "text");
assertNotNull(attr);
- Position start = parser.getPosition(attr);
- assertNotNull(start);
- Position end = start.getEnd();
- assertNotNull(end);
- assertEquals("ns:text=\"Button\"", XML.substring(start.getOffset(), end.getOffset()));
+ SourcePosition position = PositionXmlParser.getPosition(attr);
+ assertNotNull(position);
+ assertEquals("ns:text=\"Button\"",
+ XML.substring(position.getStartOffset(), position.getEndOffset()));
assertEquals("Button", subTag.getAttributeNS(NAMESPACE_URL, "text"));
assertEquals(NAMESPACE_URL, subTag.getNamespaceURI());
}
diff --git a/common/src/test/java/com/android/utils/SdkUtilsTest.java b/common/src/test/java/com/android/utils/SdkUtilsTest.java
index adb6c9b..53e01e2 100755
--- a/common/src/test/java/com/android/utils/SdkUtilsTest.java
+++ b/common/src/test/java/com/android/utils/SdkUtilsTest.java
@@ -38,6 +38,15 @@
@SuppressWarnings("javadoc")
public class SdkUtilsTest extends TestCase {
+
+ @Override
+ public void setUp() throws Exception {
+ // TODO: Use Files.createTempDir() to avoid this.
+ if (new File("/tmp/foo").isDirectory()) {
+ fail("This test will fail if /tmp/foo exists and is a directory. Please remove it.");
+ }
+ }
+
public void testEndsWithIgnoreCase() {
assertTrue(SdkUtils.endsWithIgnoreCase("foo", "foo"));
assertTrue(SdkUtils.endsWithIgnoreCase("foo", "Foo"));
@@ -319,7 +328,7 @@
Element root = document.getDocumentElement();
assertNotNull(root);
root.appendChild(document.createComment(comment));
- String xml = XmlUtils.toXml(document, false);
+ String xml = XmlUtils.toXml(document);
assertEquals(""
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<root>"
diff --git a/common/src/test/java/com/android/utils/XmlUtilsTest.java b/common/src/test/java/com/android/utils/XmlUtilsTest.java
index 3f97a51..5d3073c 100644
--- a/common/src/test/java/com/android/utils/XmlUtilsTest.java
+++ b/common/src/test/java/com/android/utils/XmlUtilsTest.java
@@ -176,7 +176,7 @@
Node text = doc.createTextNode(" This is my text ");
child3.appendChild(text);
- String xml = XmlUtils.toXml(doc, true);
+ String xml = XmlUtils.toXml(doc);
assertEquals(
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<myroot baz=\"baz\" foo=\"bar\"><mychild/><hasComment><!--This is my comment--></hasComment><hasText> This is my text </hasText></myroot>",
@@ -199,7 +199,7 @@
Document doc = parse(xml);
- String formatted = XmlUtils.toXml(doc, true);
+ String formatted = XmlUtils.toXml(doc);
assertEquals(""
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
@@ -221,7 +221,7 @@
+ "</root>";
Document doc = parse(xml);
- String formatted = XmlUtils.toXml(doc, true);
+ String formatted = XmlUtils.toXml(doc);
assertEquals(xml, formatted);
}
@@ -236,7 +236,7 @@
+ "</resources>";
Document doc = parse(xml);
- String formatted = XmlUtils.toXml(doc, true);
+ String formatted = XmlUtils.toXml(doc);
assertEquals(""
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
@@ -258,7 +258,7 @@
+ "<root/>";
Document doc = parse(xml);
- xml = XmlUtils.toXml(doc, true);
+ xml = XmlUtils.toXml(doc);
assertEquals(""
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<!-- ============== --><!-- Generic styles --><!-- ============== --><root/>",
@@ -273,7 +273,7 @@
+ "</root>";
Document doc = parse(xml);
- String formatted = XmlUtils.toXml(doc, true);
+ String formatted = XmlUtils.toXml(doc);
assertEquals(xml, formatted);
}
@@ -293,7 +293,7 @@
Document doc = parse(xml);
- String formatted = XmlUtils.toXml(doc, true);
+ String formatted = XmlUtils.toXml(doc);
assertEquals(""
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
diff --git a/ddmlib/build.gradle b/ddmlib/build.gradle
index 824196f..fd300c1 100644
--- a/ddmlib/build.gradle
+++ b/ddmlib/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
+apply plugin: 'jacoco'
apply plugin: 'sdk-java-lib'
group = 'com.android.tools.ddms'
diff --git a/ddmlib/src/main/java/com/android/ddmlib/AdbHelper.java b/ddmlib/src/main/java/com/android/ddmlib/AdbHelper.java
index d49544e..b390f93 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/AdbHelper.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/AdbHelper.java
@@ -191,11 +191,9 @@
/**
* Create an ASCII string preceded by four hex digits. The opening "####"
* is the length of the rest of the string, encoded as ASCII hex (case
- * doesn't matter). "port" and "host" are what we want to forward to. If
- * we're on the host side connecting into the device, "addrStr" should be
- * null.
+ * doesn't matter).
*/
- static byte[] formAdbRequest(String req) {
+ public static byte[] formAdbRequest(String req) {
String resultStr = String.format("%04X%s", req.length(), req); //$NON-NLS-1$
byte[] result;
try {
diff --git a/ddmlib/src/main/java/com/android/ddmlib/AdbVersion.java b/ddmlib/src/main/java/com/android/ddmlib/AdbVersion.java
new file mode 100644
index 0000000..92de6ee
--- /dev/null
+++ b/ddmlib/src/main/java/com/android/ddmlib/AdbVersion.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ddmlib;
+
+import com.android.annotations.NonNull;
+
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class AdbVersion implements Comparable<AdbVersion> {
+ public static final AdbVersion UNKNOWN = new AdbVersion(-1, -1, -1);
+
+ /** Matches e.g. ".... 1.0.32" */
+ private static final Pattern ADB_VERSION_PATTERN = Pattern.compile(
+ "^.*(\\d+)\\.(\\d+)\\.(\\d+).*");
+
+ public final int major;
+ public final int minor;
+ public final int micro;
+
+ private AdbVersion(int major, int minor, int micro) {
+ this.major = major;
+ this.minor = minor;
+ this.micro = micro;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(Locale.US, "%1$d.%2$d.%3$d", major, minor, micro);
+ }
+
+ @Override
+ public int compareTo(AdbVersion o) {
+ if (major != o.major) {
+ return major - o.major;
+ }
+
+ if (minor != o.minor) {
+ return minor - o.minor;
+ }
+
+ return micro - o.micro;
+ }
+
+ @NonNull
+ public static AdbVersion parseFrom(@NonNull String input) {
+ Matcher matcher = ADB_VERSION_PATTERN.matcher(input);
+ if (matcher.matches()) {
+ int major = Integer.parseInt(matcher.group(1));
+ int minor = Integer.parseInt(matcher.group(2));
+ int micro = Integer.parseInt(matcher.group(3));
+ return new AdbVersion(major, minor, micro);
+ } else {
+ return UNKNOWN;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ AdbVersion version = (AdbVersion) o;
+
+ if (major != version.major) {
+ return false;
+ }
+ if (minor != version.minor) {
+ return false;
+ }
+ return micro == version.micro;
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = major;
+ result = 31 * result + minor;
+ result = 31 * result + micro;
+ return result;
+ }
+}
diff --git a/ddmlib/src/main/java/com/android/ddmlib/AndroidDebugBridge.java b/ddmlib/src/main/java/com/android/ddmlib/AndroidDebugBridge.java
index ebbcd90..13dec70 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/AndroidDebugBridge.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/AndroidDebugBridge.java
@@ -19,6 +19,9 @@
import com.android.annotations.NonNull;
import com.android.ddmlib.Log.LogLevel;
import com.google.common.base.Joiner;
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
import java.io.BufferedReader;
import java.io.File;
@@ -32,8 +35,8 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
/**
* A connection to the host-side android debug bridge (adb)
@@ -47,12 +50,7 @@
* Minimum and maximum version of adb supported. This correspond to
* ADB_SERVER_VERSION found in //device/tools/adb/adb.h
*/
-
- private static final int ADB_VERSION_MICRO_MIN = 20;
- private static final int ADB_VERSION_MICRO_MAX = -1;
-
- private static final Pattern sAdbVersion = Pattern.compile(
- "^.*(\\d+)\\.(\\d+)\\.(\\d+)$"); //$NON-NLS-1$
+ private static final AdbVersion MIN_ADB_VERSION = AdbVersion.parseFrom("1.0.20");
private static final String ADB = "adb"; //$NON-NLS-1$
private static final String DDMS = "ddms"; //$NON-NLS-1$
@@ -480,6 +478,7 @@
* Returns the devices.
* @see #hasInitialDeviceList()
*/
+ @NonNull
public IDevice[] getDevices() {
synchronized (sLock) {
if (mDeviceMonitor != null) {
@@ -562,7 +561,11 @@
}
mAdbOsLocation = osLocation;
- checkAdbVersion();
+ try {
+ checkAdbVersion();
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
}
/**
@@ -572,10 +575,9 @@
}
/**
- * Queries adb for its version number and checks it against {@link #ADB_VERSION_MICRO_MIN} and
- * {@link #ADB_VERSION_MICRO_MAX}
+ * Queries adb for its version number and checks that it is atleast {@link #MIN_ADB_VERSION}.
*/
- private void checkAdbVersion() {
+ private void checkAdbVersion() throws IOException {
// default is bad check
mVersionCheck = false;
@@ -583,127 +585,78 @@
return;
}
- String[] command = new String[2];
- command[0] = mAdbOsLocation;
- command[1] = "version"; //$NON-NLS-1$
- Log.d(DDMS, String.format("Checking '%1$s version'", mAdbOsLocation));
- Process process = null;
+ File adb = new File(mAdbOsLocation);
+ ListenableFuture<AdbVersion> future = getAdbVersion(adb);
+ AdbVersion version;
try {
- process = Runtime.getRuntime().exec(command);
- } catch (IOException e) {
- boolean exists = new File(mAdbOsLocation).exists();
- String msg;
- if (exists) {
- msg = String.format(
- "Unexpected exception '%1$s' while attempting to get adb version from '%2$s'",
- e.getMessage(), mAdbOsLocation);
- } else {
- msg = "Unable to locate adb: '" + mAdbOsLocation + "'.\n" +
- "Please use SDK Manager and check if Android SDK platform-tools are installed.";
- }
- Log.logAndDisplay(LogLevel.ERROR, ADB, msg);
- return;
- }
-
- ArrayList<String> errorOutput = new ArrayList<String>();
- ArrayList<String> stdOutput = new ArrayList<String>();
- int status;
- try {
- status = grabProcessOutput(process, errorOutput, stdOutput,
- true /* waitForReaders */);
+ version = future.get(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
return;
+ } catch (java.util.concurrent.TimeoutException e) {
+ String msg = "Unable to obtain result of 'adb version'";
+ Log.logAndDisplay(LogLevel.ERROR, ADB, msg);
+ return;
+ } catch (ExecutionException e) {
+ Log.logAndDisplay(LogLevel.ERROR, ADB, e.getCause().getMessage());
+ Throwables.propagateIfInstanceOf(e.getCause(), IOException.class);
+ return;
}
- if (status != 0) {
- StringBuilder builder = new StringBuilder("'adb version' failed!"); //$NON-NLS-1$
- for (String error : errorOutput) {
- builder.append('\n');
- builder.append(error);
- }
- Log.logAndDisplay(LogLevel.ERROR, ADB, builder.toString());
- }
-
- // check both stdout and stderr
- boolean versionFound = false;
- for (String line : stdOutput) {
- versionFound = scanVersionLine(line);
- if (versionFound) {
- break;
- }
- }
- if (!versionFound) {
- for (String line : errorOutput) {
- versionFound = scanVersionLine(line);
- if (versionFound) {
- break;
- }
- }
- }
-
- if (!versionFound) {
- // if we get here, we failed to parse the output.
- StringBuilder builder = new StringBuilder(
- "Failed to parse the output of 'adb version':\n"); //$NON-NLS-1$
- builder.append("Standard Output was:\n"); //$NON-NLS-1$
- for (String line : stdOutput) {
- builder.append(line);
- builder.append('\n');
- }
- builder.append("\nError Output was:\n"); //$NON-NLS-1$
- for (String line : errorOutput) {
- builder.append(line);
- builder.append('\n');
- }
- Log.logAndDisplay(LogLevel.ERROR, ADB, builder.toString());
+ if (version.compareTo(MIN_ADB_VERSION) > 0) {
+ mVersionCheck = true;
+ } else {
+ String message = String.format(
+ "Required minimum version of adb: %1$s."
+ + "Current version is %2$s", MIN_ADB_VERSION, version);
+ Log.logAndDisplay(LogLevel.ERROR, ADB, message);
}
}
- /**
- * Scans a line resulting from 'adb version' for a potential version number.
- * <p/>
- * If a version number is found, it checks the version number against what is expected
- * by this version of ddms.
- * <p/>
- * Returns true when a version number has been found so that we can stop scanning,
- * whether the version number is in the acceptable range or not.
- *
- * @param line The line to scan.
- * @return True if a version number was found (whether it is acceptable or not).
- */
- @SuppressWarnings("all") // With Eclipse 3.6, replace by @SuppressWarnings("unused")
- private boolean scanVersionLine(String line) {
- if (line != null) {
- Matcher matcher = sAdbVersion.matcher(line);
- if (matcher.matches()) {
- int majorVersion = Integer.parseInt(matcher.group(1));
- int minorVersion = Integer.parseInt(matcher.group(2));
- int microVersion = Integer.parseInt(matcher.group(3));
+ public static ListenableFuture<AdbVersion> getAdbVersion(@NonNull final File adb) {
+ final SettableFuture<AdbVersion> future = SettableFuture.create();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ ProcessBuilder pb = new ProcessBuilder(adb.getPath(), "version");
+ pb.redirectErrorStream(true);
- // check only the micro version for now.
- if (microVersion < ADB_VERSION_MICRO_MIN) {
- String message = String.format(
- "Required minimum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$
- + "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$
- majorVersion, minorVersion, ADB_VERSION_MICRO_MIN,
- microVersion);
- Log.logAndDisplay(LogLevel.ERROR, ADB, message);
- } else if (ADB_VERSION_MICRO_MAX != -1 &&
- microVersion > ADB_VERSION_MICRO_MAX) {
- String message = String.format(
- "Required maximum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$
- + "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$
- majorVersion, minorVersion, ADB_VERSION_MICRO_MAX,
- microVersion);
- Log.logAndDisplay(LogLevel.ERROR, ADB, message);
- } else {
- mVersionCheck = true;
+ Process p = null;
+ try {
+ p = pb.start();
+ } catch (IOException e) {
+ future.setException(e);
+ return;
}
- return true;
+ StringBuilder sb = new StringBuilder();
+ BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
+ try {
+ String line;
+ while ((line = br.readLine()) != null) {
+ AdbVersion version = AdbVersion.parseFrom(line);
+ if (version != AdbVersion.UNKNOWN) {
+ future.set(version);
+ return;
+ }
+ sb.append(line);
+ sb.append('\n');
+ }
+ } catch (IOException e) {
+ future.setException(e);
+ return;
+ } finally {
+ try {
+ br.close();
+ } catch (IOException e) {
+ future.setException(e);
+ }
+ }
+
+ future.setException(new RuntimeException(
+ "Unable to detect adb version, adb output: " + sb.toString()));
}
- }
- return false;
+ }, "Obtaining adb version").start();
+ return future;
}
/**
@@ -1134,7 +1087,7 @@
* This includes adding/removing listeners, but also notifying listeners of new bridges,
* devices, and clients.
*/
- static Object getLock() {
+ private static Object getLock() {
return sLock;
}
diff --git a/ddmlib/src/main/java/com/android/ddmlib/Client.java b/ddmlib/src/main/java/com/android/ddmlib/Client.java
index a5dc0dc..d2b1488 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/Client.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/Client.java
@@ -657,7 +657,10 @@
void sendAndConsume(JdwpPacket packet, ChunkHandler replyHandler)
throws IOException {
- if (mChan == null) {
+ // Fix to avoid a race condition on mChan. This should be better synchronized
+ // but just capturing the channel here, avoids a NPE.
+ SocketChannel chan = mChan;
+ if (chan == null) {
// can happen for e.g. THST packets
Log.v("ddms", "Not sending packet -- client is closed");
return;
@@ -672,9 +675,13 @@
addRequestId(packet.getId(), replyHandler);
}
- synchronized (mChan) {
+ // Synchronizing on this variable is still useful as we do not want to threads
+ // reading at the same time from the same channel, and the only change that
+ // can happen to this channel is to be closed and mChan become null.
+ //noinspection SynchronizationOnLocalVariableOrMethodParameter
+ synchronized (chan) {
try {
- packet.writeAndConsume(mChan);
+ packet.writeAndConsume(chan);
}
catch (IOException ioe) {
removeRequestId(packet.getId());
diff --git a/ddmlib/src/main/java/com/android/ddmlib/ClientData.java b/ddmlib/src/main/java/com/android/ddmlib/ClientData.java
index f453353..2362a44 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/ClientData.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/ClientData.java
@@ -332,7 +332,7 @@
}
public static class HprofData {
- public static enum Type {
+ public enum Type {
FILE,
DATA
}
diff --git a/ddmlib/src/main/java/com/android/ddmlib/DdmConstants.java b/ddmlib/src/main/java/com/android/ddmlib/DdmConstants.java
index 6aec91e..f998ef7 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/DdmConstants.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/DdmConstants.java
@@ -29,10 +29,11 @@
*/
public static final int CURRENT_PLATFORM = currentPlatform();
+ public static final String EXTENSION = "trace";
/**
* Extension for Traceview files.
*/
- public static final String DOT_TRACE = ".trace";
+ public static final String DOT_TRACE = "." + EXTENSION;
/** hprof-conv executable (with extension for the current OS) */
public static final String FN_HPROF_CONVERTER = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
diff --git a/ddmlib/src/main/java/com/android/ddmlib/Device.java b/ddmlib/src/main/java/com/android/ddmlib/Device.java
index bd35ded..1cb1a4c 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/Device.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/Device.java
@@ -21,12 +21,14 @@
import com.android.annotations.VisibleForTesting;
import com.android.annotations.concurrency.GuardedBy;
import com.android.ddmlib.log.LogReceiver;
+import com.google.common.base.CharMatcher;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.Atomics;
import java.io.BufferedInputStream;
import java.io.File;
@@ -46,6 +48,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -372,7 +375,7 @@
}
// The full list of features can be obtained from /etc/permissions/features*
- // However, since we only support the "watch" feature, we can determine that by simply
+ // However, the smaller set of features we are interested in can be obtained by
// reading the build characteristics property.
@Override
public boolean supportsFeature(@NonNull HardwareFeature feature) {
@@ -425,11 +428,47 @@
return !value.endsWith("No such file or directory");
}
+ @Nullable
@Override
- public String getMountPoint(String name) {
- return mMountPoints.get(name);
+ public String getMountPoint(@NonNull String name) {
+ String mount = mMountPoints.get(name);
+ if (mount == null) {
+ try {
+ mount = queryMountPoint(name);
+ mMountPoints.put(name, mount);
+ } catch (TimeoutException ignored) {
+ } catch (AdbCommandRejectedException ignored) {
+ } catch (ShellCommandUnresponsiveException ignored) {
+ } catch (IOException ignored) {
+ }
+ }
+ return mount;
}
+ @Nullable
+ private String queryMountPoint(@NonNull final String name)
+ throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
+ IOException {
+
+ final AtomicReference<String> ref = Atomics.newReference();
+ executeShellCommand("echo $" + name, new MultiLineReceiver() { //$NON-NLS-1$
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+
+ @Override
+ public void processNewLines(String[] lines) {
+ for (String line : lines) {
+ if (!line.isEmpty()) {
+ // this should be the only one.
+ ref.set(line);
+ }
+ }
+ }
+ });
+ return ref.get();
+ }
@Override
public String toString() {
@@ -698,17 +737,17 @@
removeClientInfo(client);
}
- /**
- * Sets the client monitoring socket.
- * @param socketChannel the sockets
- */
- void setClientMonitoringSocket(SocketChannel socketChannel) {
+ /** Sets the socket channel on which a track-jdwp command for this device has been sent. */
+ void setClientMonitoringSocket(@NonNull SocketChannel socketChannel) {
mSocketChannel = socketChannel;
}
/**
- * Returns the client monitoring socket.
+ * Returns the channel on which responses to the track-jdwp command will be available if it
+ * has been set, null otherwise. The channel is set via {@link #setClientMonitoringSocket(SocketChannel)},
+ * which is usually invoked when the device goes online.
*/
+ @Nullable
SocketChannel getClientMonitoringSocket() {
return mSocketChannel;
}
@@ -1001,6 +1040,9 @@
return receiver.getSessionId();
}
+ private static final CharMatcher UNSAFE_PM_INSTALL_SESSION_SPLIT_NAME_CHARS =
+ CharMatcher.inRange('a','z').or(CharMatcher.inRange('A','Z'))
+ .or(CharMatcher.anyOf("_-")).negate();
private boolean uploadAPK(final String sessionId, String apkFilePath, int uniqueId) {
Log.d(sessionId, String.format("Uploading APK %1$s ", apkFilePath));
@@ -1017,6 +1059,8 @@
? fileToUpload.getName().substring(0, fileToUpload.getName().lastIndexOf('.'))
: fileToUpload.getName();
+ baseName = UNSAFE_PM_INSTALL_SESSION_SPLIT_NAME_CHARS.replaceFrom(baseName, '_');
+
String command = String.format("pm install-write -S %d %s %d_%s -",
fileToUpload.length(), sessionId, uniqueId, baseName);
diff --git a/ddmlib/src/main/java/com/android/ddmlib/DeviceMonitor.java b/ddmlib/src/main/java/com/android/ddmlib/DeviceMonitor.java
index 0545a2f..4aa0daa 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/DeviceMonitor.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/DeviceMonitor.java
@@ -16,10 +16,19 @@
package com.android.ddmlib;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
import com.android.ddmlib.AdbHelper.AdbResponse;
import com.android.ddmlib.ClientData.DebuggerStatus;
import com.android.ddmlib.DebugPortManager.IDebugPortProvider;
import com.android.ddmlib.IDevice.DeviceState;
+import com.android.ddmlib.utils.DebuggerPorts;
+import com.android.utils.Pair;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Queues;
+import com.google.common.util.concurrent.Uninterruptibles;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@@ -29,63 +38,64 @@
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
-import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
/**
- * A Device monitor. This connects to the Android Debug Bridge and get device and
- * debuggable process information from it.
+ * The {@link DeviceMonitor} monitors devices attached to adb.
+ *
+ * On one thread, it runs the {@link com.android.ddmlib.DeviceMonitor.DeviceListMonitorTask}.
+ * This establishes a socket connection to the adb host, and issues a
+ * {@link #ADB_TRACK_DEVICES_COMMAND}. It then monitors that socket for all changes about device
+ * connection and device state.
+ *
+ * For each device that is detected to be online, it then opens a new socket connection to adb,
+ * and issues a "track-jdwp" command to that device. On this connection, it monitors active
+ * clients on the device. Note: a single thread monitors jdwp connections from all devices.
+ * The different socket connections to adb (one per device) are multiplexed over a single selector.
*/
final class DeviceMonitor {
- private byte[] mLengthBuffer = new byte[4];
- private byte[] mLengthBuffer2 = new byte[4];
+ private static final String ADB_TRACK_DEVICES_COMMAND = "host:track-devices";
+ private static final String ADB_TRACK_JDWP_COMMAND = "track-jdwp";
- private boolean mQuit = false;
+ private final byte[] mLengthBuffer2 = new byte[4];
- private AndroidDebugBridge mServer;
+ private volatile boolean mQuit = false;
- private SocketChannel mMainAdbConnection = null;
- private boolean mMonitoring = false;
- private int mConnectionAttempt = 0;
- private int mRestartAttemptCount = 0;
- private boolean mInitialDeviceListDone = false;
+ private final AndroidDebugBridge mServer;
+ private DeviceListMonitorTask mDeviceListMonitorTask;
private Selector mSelector;
- private final ArrayList<Device> mDevices = new ArrayList<Device>();
-
- private final ArrayList<Integer> mDebuggerPorts = new ArrayList<Integer>();
-
- private final HashMap<Client, Integer> mClientsToReopen = new HashMap<Client, Integer>();
+ private final List<Device> mDevices = Lists.newCopyOnWriteArrayList();
+ private final DebuggerPorts mDebuggerPorts =
+ new DebuggerPorts(DdmPreferences.getDebugPortBase());
+ private final Map<Client, Integer> mClientsToReopen = new HashMap<Client, Integer>();
+ private final BlockingQueue<Pair<SocketChannel,Device>> mChannelsToRegister =
+ Queues.newLinkedBlockingQueue();
/**
* Creates a new {@link DeviceMonitor} object and links it to the running
* {@link AndroidDebugBridge} object.
* @param server the running {@link AndroidDebugBridge}.
*/
- DeviceMonitor(AndroidDebugBridge server) {
+ DeviceMonitor(@NonNull AndroidDebugBridge server) {
mServer = server;
-
- mDebuggerPorts.add(DdmPreferences.getDebugPortBase());
}
/**
* Starts the monitoring.
*/
void start() {
- new Thread("Device List Monitor") { //$NON-NLS-1$
- @Override
- public void run() {
- deviceMonitorLoop();
- }
- }.start();
+ mDeviceListMonitorTask = new DeviceListMonitorTask(mServer, new DeviceListUpdateListener());
+ new Thread(mDeviceListMonitorTask, "Device List Monitor").start(); //$NON-NLS-1$
}
/**
@@ -94,12 +104,8 @@
void stop() {
mQuit = true;
- // wakeup the main loop thread by closing the main connection to adb.
- try {
- if (mMainAdbConnection != null) {
- mMainAdbConnection.close();
- }
- } catch (IOException e1) {
+ if (mDeviceListMonitorTask != null) {
+ mDeviceListMonitorTask.stop();
}
// wake up the secondary loop by closing the selector.
@@ -108,37 +114,38 @@
}
}
-
-
/**
- * Returns if the monitor is currently connected to the debug bridge server.
- * @return
+ * Returns whether the monitor is currently connected to the debug bridge server.
*/
boolean isMonitoring() {
- return mMonitoring;
+ return mDeviceListMonitorTask != null && mDeviceListMonitorTask.isMonitoring();
}
int getConnectionAttemptCount() {
- return mConnectionAttempt;
+ return mDeviceListMonitorTask == null ? 0
+ : mDeviceListMonitorTask.getConnectionAttemptCount();
}
int getRestartAttemptCount() {
- return mRestartAttemptCount;
+ return mDeviceListMonitorTask == null ? 0 : mDeviceListMonitorTask.getRestartAttemptCount();
+ }
+
+ boolean hasInitialDeviceList() {
+ return mDeviceListMonitorTask != null && mDeviceListMonitorTask.hasInitialDeviceList();
}
/**
* Returns the devices.
*/
- Device[] getDevices() {
- synchronized (mDevices) {
- return mDevices.toArray(new Device[mDevices.size()]);
- }
+ @NonNull Device[] getDevices() {
+ // Since this is a copy of write array list, we don't want to do a compound operation
+ // (toArray with an appropriate size) without locking, so we just let the container provide
+ // an appropriately sized array
+ //noinspection ToArrayCallWithZeroLengthArrayArgument
+ return mDevices.toArray(new Device[0]);
}
- boolean hasInitialDeviceList() {
- return mInitialDeviceListDone;
- }
-
+ @NonNull
AndroidDebugBridge getServer() {
return mServer;
}
@@ -146,7 +153,7 @@
void addClientToDropAndReopen(Client client, int port) {
synchronized (mClientsToReopen) {
Log.d("DeviceMonitor",
- "Adding " + client + " to list of client to reopen (" + port +").");
+ "Adding " + client + " to list of client to reopen (" + port + ").");
if (mClientsToReopen.get(client) == null) {
mClientsToReopen.put(client, port);
}
@@ -155,276 +162,65 @@
}
/**
- * Monitors the devices. This connects to the Debug Bridge
- */
- private void deviceMonitorLoop() {
- do {
- try {
- if (mMainAdbConnection == null) {
- Log.d("DeviceMonitor", "Opening adb connection");
- mMainAdbConnection = openAdbConnection();
- if (mMainAdbConnection == null) {
- mConnectionAttempt++;
- Log.e("DeviceMonitor", "Connection attempts: " + mConnectionAttempt);
- if (mConnectionAttempt > 10) {
- if (!mServer.startAdb()) {
- mRestartAttemptCount++;
- Log.e("DeviceMonitor",
- "adb restart attempts: " + mRestartAttemptCount);
- } else {
- Log.i("DeviceMonitor", "adb restarted");
- mRestartAttemptCount = 0;
- }
- }
- waitABit();
- } else {
- Log.d("DeviceMonitor", "Connected to adb for device monitoring");
- mConnectionAttempt = 0;
- }
- }
-
- if (mMainAdbConnection != null && !mMonitoring) {
- mMonitoring = sendDeviceListMonitoringRequest();
- }
-
- if (mMonitoring) {
- // read the length of the incoming message
- int length = readLength(mMainAdbConnection, mLengthBuffer);
-
- if (length >= 0) {
- // read the incoming message
- processIncomingDeviceData(length);
-
- // flag the fact that we have build the list at least once.
- mInitialDeviceListDone = true;
- }
- }
- } catch (AsynchronousCloseException ace) {
- // this happens because of a call to Quit. We do nothing, and the loop will break.
- } catch (TimeoutException ioe) {
- handleExpectionInMonitorLoop(ioe);
- } catch (IOException ioe) {
- handleExpectionInMonitorLoop(ioe);
- }
- } while (!mQuit);
- }
-
- private void handleExpectionInMonitorLoop(Exception e) {
- if (!mQuit) {
- if (e instanceof TimeoutException) {
- Log.e("DeviceMonitor", "Adb connection Error: timeout");
- } else {
- Log.e("DeviceMonitor", "Adb connection Error:" + e.getMessage());
- }
- mMonitoring = false;
- if (mMainAdbConnection != null) {
- try {
- mMainAdbConnection.close();
- } catch (IOException ioe) {
- // we can safely ignore that one.
- }
- mMainAdbConnection = null;
-
- // remove all devices from list
- // because we are going to call mServer.deviceDisconnected which will acquire this
- // lock we lock it first, so that the AndroidDebugBridge lock is always locked
- // first.
- synchronized (AndroidDebugBridge.getLock()) {
- synchronized (mDevices) {
- for (int n = mDevices.size() - 1; n >= 0; n--) {
- Device device = mDevices.get(0);
- removeDevice(device);
- mServer.deviceDisconnected(device);
- }
- }
- }
- }
- }
- }
-
- /**
- * Sleeps for a little bit.
- */
- private void waitABit() {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e1) {
- }
- }
-
- /**
* Attempts to connect to the debug bridge server.
* @return a connect socket if success, null otherwise
*/
- private SocketChannel openAdbConnection() {
- Log.d("DeviceMonitor", "Connecting to adb for Device List Monitoring...");
-
- SocketChannel adbChannel = null;
+ @Nullable
+ private static SocketChannel openAdbConnection() {
try {
- adbChannel = SocketChannel.open(AndroidDebugBridge.getSocketAddress());
+ SocketChannel adbChannel = SocketChannel.open(AndroidDebugBridge.getSocketAddress());
adbChannel.socket().setTcpNoDelay(true);
+ return adbChannel;
} catch (IOException e) {
+ return null;
}
-
- return adbChannel;
}
/**
- *
- * @return
- * @throws IOException
+ * Updates the device list with the new items received from the monitoring service.
*/
- private boolean sendDeviceListMonitoringRequest() throws TimeoutException, IOException {
- byte[] request = AdbHelper.formAdbRequest("host:track-devices"); //$NON-NLS-1$
-
- try {
- AdbHelper.write(mMainAdbConnection, request);
-
- AdbResponse resp = AdbHelper.readAdbResponse(mMainAdbConnection,
- false /* readDiagString */);
-
- if (!resp.okay) {
- // request was refused by adb!
- Log.e("DeviceMonitor", "adb refused request: " + resp.message);
- }
-
- return resp.okay;
- } catch (IOException e) {
- Log.e("DeviceMonitor", "Sending Tracking request failed!");
- mMainAdbConnection.close();
- throw e;
+ private void updateDevices(@NonNull List<Device> newList) {
+ DeviceListComparisonResult result = DeviceListComparisonResult.compare(mDevices, newList);
+ for (IDevice device : result.removed) {
+ removeDevice((Device) device);
+ mServer.deviceDisconnected(device);
}
- }
- /** Processes an incoming device message from the socket */
- private void processIncomingDeviceData(int length) throws IOException {
- ArrayList<Device> list = new ArrayList<Device>();
+ List<Device> newlyOnline = Lists.newArrayListWithExpectedSize(mDevices.size());
- if (length > 0) {
- byte[] buffer = new byte[length];
- String result = read(mMainAdbConnection, buffer);
+ for (Map.Entry<IDevice, DeviceState> entry : result.updated.entrySet()) {
+ Device device = (Device) entry.getKey();
+ device.setState(entry.getValue());
+ device.update(Device.CHANGE_STATE);
- String[] devices = result.split("\n"); //$NON-NLS-1$
+ if (device.isOnline()) {
+ newlyOnline.add(device);
+ }
+ }
- for (String d : devices) {
- String[] param = d.split("\t"); //$NON-NLS-1$
- if (param.length == 2) {
- // new adb uses only serial numbers to identify devices
- Device device = new Device(this, param[0] /*serialnumber*/,
- DeviceState.getState(param[1]));
+ for (IDevice device : result.added) {
+ mDevices.add((Device) device);
+ mServer.deviceConnected(device);
+ if (device.isOnline()) {
+ newlyOnline.add((Device) device);
+ }
+ }
- //add the device to the list
- list.add(device);
+ if (AndroidDebugBridge.getClientSupport()) {
+ for (Device device : newlyOnline) {
+ if (!startMonitoringDevice(device)) {
+ Log.e("DeviceMonitor", "Failed to start monitoring "
+ + device.getSerialNumber());
}
}
}
- // now merge the new devices with the old ones.
- updateDevices(list);
- }
-
- /**
- * Updates the device list with the new items received from the monitoring service.
- */
- private void updateDevices(ArrayList<Device> newList) {
- // because we are going to call mServer.deviceDisconnected which will acquire this lock
- // we lock it first, so that the AndroidDebugBridge lock is always locked first.
- synchronized (AndroidDebugBridge.getLock()) {
- // array to store the devices that must be queried for information.
- // it's important to not do it inside the synchronized loop as this could block
- // the whole workspace (this lock is acquired during build too).
- ArrayList<Device> devicesToQuery = new ArrayList<Device>();
- synchronized (mDevices) {
- // For each device in the current list, we look for a matching the new list.
- // * if we find it, we update the current object with whatever new information
- // there is
- // (mostly state change, if the device becomes ready, we query for build info).
- // We also remove the device from the new list to mark it as "processed"
- // * if we do not find it, we remove it from the current list.
- // Once this is done, the new list contains device we aren't monitoring yet, so we
- // add them to the list, and start monitoring them.
-
- for (int d = 0 ; d < mDevices.size() ;) {
- Device device = mDevices.get(d);
-
- // look for a similar device in the new list.
- int count = newList.size();
- boolean foundMatch = false;
- for (int dd = 0 ; dd < count ; dd++) {
- Device newDevice = newList.get(dd);
- // see if it matches in id and serial number.
- if (newDevice.getSerialNumber().equals(device.getSerialNumber())) {
- foundMatch = true;
-
- // update the state if needed.
- if (device.getState() != newDevice.getState()) {
- device.setState(newDevice.getState());
- device.update(Device.CHANGE_STATE);
-
- // if the device just got ready/online, we need to start
- // monitoring it.
- if (device.isOnline()) {
- if (AndroidDebugBridge.getClientSupport()) {
- if (!startMonitoringDevice(device)) {
- Log.e("DeviceMonitor",
- "Failed to start monitoring "
- + device.getSerialNumber());
- }
- }
-
- if (device.getPropertyCount() == 0) {
- devicesToQuery.add(device);
- }
- }
- }
-
- // remove the new device from the list since it's been used
- newList.remove(dd);
- break;
- }
- }
-
- if (!foundMatch) {
- // the device is gone, we need to remove it, and keep current index
- // to process the next one.
- removeDevice(device);
- mServer.deviceDisconnected(device);
- } else {
- // process the next one
- d++;
- }
- }
-
- // at this point we should still have some new devices in newList, so we
- // process them.
- for (Device newDevice : newList) {
- // add them to the list
- mDevices.add(newDevice);
- mServer.deviceConnected(newDevice);
-
- // start monitoring them.
- if (AndroidDebugBridge.getClientSupport()) {
- if (newDevice.isOnline()) {
- startMonitoringDevice(newDevice);
- }
- }
-
- // look for their build info.
- if (newDevice.isOnline()) {
- devicesToQuery.add(newDevice);
- }
- }
- }
-
- // query the new devices for info.
- for (Device d : devicesToQuery) {
- queryNewDeviceForInfo(d);
- }
+ for (Device device : newlyOnline) {
+ queryAvdName(device);
}
- newList.clear();
}
- private void removeDevice(Device device) {
+ private void removeDevice(@NonNull Device device) {
device.clearClientList();
mDevices.remove(device);
@@ -438,83 +234,16 @@
}
}
- /**
- * Queries a device for its build info.
- * @param device the device to query.
- */
- private void queryNewDeviceForInfo(Device device) {
- // TODO: do this in a separate thread.
- try {
- queryProperties(device);
-
- queryNewDeviceForMountingPoint(device, IDevice.MNT_EXTERNAL_STORAGE);
- queryNewDeviceForMountingPoint(device, IDevice.MNT_DATA);
- queryNewDeviceForMountingPoint(device, IDevice.MNT_ROOT);
-
- // now get the emulator Virtual Device name (if applicable).
- if (device.isEmulator()) {
- EmulatorConsole console = EmulatorConsole.getConsole(device);
- if (console != null) {
- device.setAvdName(console.getAvdName());
- console.close();
- }
- }
- } catch (TimeoutException e) {
- Log.w("DeviceMonitor", String.format("Connection timeout getting info for device %s",
- device.getSerialNumber()));
-
- } catch (AdbCommandRejectedException e) {
- // This should never happen as we only do this once the device is online.
- Log.w("DeviceMonitor", String.format(
- "Adb rejected command to get device %1$s info: %2$s",
- device.getSerialNumber(), e.getMessage()));
-
- } catch (ShellCommandUnresponsiveException e) {
- Log.w("DeviceMonitor", String.format(
- "Adb shell command took too long returning info for device %s",
- device.getSerialNumber()));
-
- } catch (IOException e) {
- Log.w("DeviceMonitor", String.format(
- "IO Error getting info for device %s",
- device.getSerialNumber()));
- } catch (InterruptedException e) {
- Log.w("DeviceMonitor", String.format(
- "Interrupted getting info for device %s",
- device.getSerialNumber()));
- } catch (ExecutionException e) {
- Log.w("DeviceMonitor", String.format(
- "ExecutionException getting info for device %s",
- device.getSerialNumber()));
+ private static void queryAvdName(@NonNull Device device) {
+ if (!device.isEmulator()) {
+ return;
}
- }
- private void queryProperties(Device device) throws InterruptedException, ExecutionException {
- // first attempt to populate the list of properties by querying arbitrary prop
- // TODO: consider removing this call and just let properties be loaded on demand
- Future<String> prop = device.getSystemProperty("ro.build.id");
- prop.get();
- }
-
- private void queryNewDeviceForMountingPoint(final Device device, final String name)
- throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
- IOException {
- device.executeShellCommand("echo $" + name, new MultiLineReceiver() { //$NON-NLS-1$
- @Override
- public boolean isCancelled() {
- return false;
- }
-
- @Override
- public void processNewLines(String[] lines) {
- for (String line : lines) {
- if (!line.isEmpty()) {
- // this should be the only one.
- device.setMountingPoint(name, line);
- }
- }
- }
- });
+ EmulatorConsole console = EmulatorConsole.getConsole(device);
+ if (console != null) {
+ device.setAvdName(console.getAvdName());
+ console.close();
+ }
}
/**
@@ -522,7 +251,7 @@
* @param device the device to monitor.
* @return true if success.
*/
- private boolean startMonitoringDevice(Device device) {
+ private boolean startMonitoringDevice(@NonNull Device device) {
SocketChannel socketChannel = openAdbConnection();
if (socketChannel != null) {
@@ -536,15 +265,14 @@
device.setClientMonitoringSocket(socketChannel);
- synchronized (mDevices) {
- // always wakeup before doing the register. The synchronized block
- // ensure that the selector won't select() before the end of this block.
- // @see deviceClientMonitorLoop
- mSelector.wakeup();
+ socketChannel.configureBlocking(false);
- socketChannel.configureBlocking(false);
- socketChannel.register(mSelector, SelectionKey.OP_READ, device);
+ try {
+ mChannelsToRegister.put(Pair.of(socketChannel, device));
+ } catch (InterruptedException e) {
+ // the queue is unbounded, and isn't going to block
}
+ mSelector.wakeup();
return true;
}
@@ -597,12 +325,6 @@
private void deviceClientMonitorLoop() {
do {
try {
- // This synchronized block stops us from doing the select() if a new
- // Device is being added.
- // @see startMonitoringDevice()
- synchronized (mDevices) {
- }
-
int count = mSelector.select();
if (mQuit) {
@@ -622,7 +344,7 @@
// This is kinda bad, but if we don't wait a bit, the client
// will never answer the second handshake!
- waitABit();
+ Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
int port = mClientsToReopen.get(client);
@@ -638,6 +360,17 @@
}
}
+ // register any new channels
+ while (!mChannelsToRegister.isEmpty()) {
+ try {
+ Pair<SocketChannel, Device> data = mChannelsToRegister.take();
+ data.getFirst().register(mSelector, SelectionKey.OP_READ, data.getSecond());
+ } catch (InterruptedException e) {
+ // doesn't block: this thread is the only reader and it reads only when
+ // there is data
+ }
+ }
+
if (count == 0) {
continue;
}
@@ -668,12 +401,10 @@
socket.close();
// restart the monitoring of that device
- synchronized (mDevices) {
- if (mDevices.contains(device)) {
- Log.d("DeviceMonitor",
- "Restarting monitoring service for " + device);
- startMonitoringDevice(device);
- }
+ if (mDevices.contains(device)) {
+ Log.d("DeviceMonitor",
+ "Restarting monitoring service for " + device);
+ startMonitoringDevice(device);
}
}
}
@@ -687,17 +418,14 @@
} while (!mQuit);
}
- private boolean sendDeviceMonitoringRequest(SocketChannel socket, Device device)
+ private static boolean sendDeviceMonitoringRequest(@NonNull SocketChannel socket,
+ @NonNull Device device)
throws TimeoutException, AdbCommandRejectedException, IOException {
try {
AdbHelper.setDevice(socket, device);
-
- byte[] request = AdbHelper.formAdbRequest("track-jdwp"); //$NON-NLS-1$
-
- AdbHelper.write(socket, request);
-
- AdbResponse resp = AdbHelper.readAdbResponse(socket, false /* readDiagString */);
+ AdbHelper.write(socket, AdbHelper.formAdbRequest(ADB_TRACK_JDWP_COMMAND));
+ AdbResponse resp = AdbHelper.readAdbResponse(socket, false);
if (!resp.okay) {
// request was refused by adb!
@@ -714,8 +442,8 @@
}
}
- private void processIncomingJdwpData(Device device, SocketChannel monitorSocket, int length)
- throws IOException {
+ private void processIncomingJdwpData(@NonNull Device device,
+ @NonNull SocketChannel monitorSocket, int length) throws IOException {
// This methods reads @length bytes from the @monitorSocket channel.
// These bytes correspond to the pids of the current set of processes on the device.
@@ -733,7 +461,7 @@
String result = read(monitorSocket, buffer);
// split each line in its own list and create an array of integer pid
- String[] pids = result.split("\n"); //$NON-NLS-1$
+ String[] pids = result == null ? new String[0] : result.split("\n"); //$NON-NLS-1$
for (String pid : pids) {
try {
@@ -752,9 +480,7 @@
synchronized (clients) {
for (Client c : clients) {
- existingClients.put(
- c.getClientData().getPid(),
- c);
+ existingClients.put(c.getClientData().getPid(), c);
}
}
@@ -781,11 +507,9 @@
}
}
- /**
- * Opens and creates a new client.
- * @return
- */
- private void openClient(Device device, int pid, int port, MonitorThread monitorThread) {
+ /** Opens and creates a new client. */
+ private static void openClient(@NonNull Device device, int pid, int port,
+ @NonNull MonitorThread monitorThread) {
SocketChannel clientSocket;
try {
@@ -815,16 +539,9 @@
createClient(device, pid, clientSocket, port, monitorThread);
}
- /**
- * Creates a client and register it to the monitor thread
- * @param device
- * @param pid
- * @param socket
- * @param debuggerPort the debugger port.
- * @param monitorThread the {@link MonitorThread} object.
- */
- private void createClient(Device device, int pid, SocketChannel socket, int debuggerPort,
- MonitorThread monitorThread) {
+ /** Creates a client and register it to the monitor thread */
+ private static void createClient(@NonNull Device device, int pid, @NonNull SocketChannel socket,
+ int debuggerPort, @NonNull MonitorThread monitorThread) {
/*
* Successfully connected to something. Create a Client object, add
@@ -858,51 +575,15 @@
if (client.isValid()) {
device.addClient(client);
monitorThread.addClient(client);
- } else {
- client = null;
}
}
private int getNextDebuggerPort() {
- // get the first port and remove it
- synchronized (mDebuggerPorts) {
- if (!mDebuggerPorts.isEmpty()) {
- int port = mDebuggerPorts.get(0);
-
- // remove it.
- mDebuggerPorts.remove(0);
-
- // if there's nothing left, add the next port to the list
- if (mDebuggerPorts.isEmpty()) {
- mDebuggerPorts.add(port+1);
- }
-
- return port;
- }
- }
-
- return -1;
+ return mDebuggerPorts.next();
}
void addPortToAvailableList(int port) {
- if (port > 0) {
- synchronized (mDebuggerPorts) {
- // because there could be case where clients are closed twice, we have to make
- // sure the port number is not already in the list.
- if (mDebuggerPorts.indexOf(port) == -1) {
- // add the port to the list while keeping it sorted. It's not like there's
- // going to be tons of objects so we do it linearly.
- int count = mDebuggerPorts.size();
- for (int i = 0 ; i < count ; i++) {
- if (port < mDebuggerPorts.get(i)) {
- mDebuggerPorts.add(i, port);
- break;
- }
- }
- // TODO: check if we can compact the end of the list.
- }
- }
- }
+ mDebuggerPorts.free(port);
}
/**
@@ -911,7 +592,8 @@
* @return the length, or 0 (zero) if no data is available from the socket.
* @throws IOException if the connection failed.
*/
- private int readLength(SocketChannel socket, byte[] buffer) throws IOException {
+ private static int readLength(@NonNull SocketChannel socket, @NonNull byte[] buffer)
+ throws IOException {
String msg = read(socket, buffer);
if (msg != null) {
@@ -920,20 +602,20 @@
} catch (NumberFormatException nfe) {
// we'll throw an exception below.
}
- }
+ }
// we receive something we can't read. It's better to reset the connection at this point.
throw new IOException("Unable to read length");
}
/**
- * Fills a buffer from a socket.
- * @param socket
- * @param buffer
+ * Fills a buffer by reading data from a socket.
* @return the content of the buffer as a string, or null if it failed to convert the buffer.
- * @throws IOException
+ * @throws IOException if there was not enough data to fill the buffer
*/
- private String read(SocketChannel socket, byte[] buffer) throws IOException {
+ @Nullable
+ private static String read(@NonNull SocketChannel socket, @NonNull byte[] buffer)
+ throws IOException {
ByteBuffer buf = ByteBuffer.wrap(buffer, 0, buffer.length);
while (buf.position() != buf.limit()) {
@@ -948,10 +630,255 @@
try {
return new String(buffer, 0, buf.position(), AdbHelper.DEFAULT_ENCODING);
} catch (UnsupportedEncodingException e) {
- // we'll return null below.
+ return null;
}
-
- return null;
}
+ private class DeviceListUpdateListener implements DeviceListMonitorTask.UpdateListener {
+ @Override
+ public void connectionError(@NonNull Exception e) {
+ for (Device device : mDevices) {
+ removeDevice(device);
+ mServer.deviceDisconnected(device);
+ }
+ }
+
+ @Override
+ public void deviceListUpdate(@NonNull Map<String, DeviceState> devices) {
+ List<Device> l = Lists.newArrayListWithExpectedSize(devices.size());
+ for (Map.Entry<String, DeviceState> entry : devices.entrySet()) {
+ l.add(new Device(DeviceMonitor.this, entry.getKey(), entry.getValue()));
+ }
+ // now merge the new devices with the old ones.
+ updateDevices(l);
+ }
+ }
+
+ @VisibleForTesting
+ static class DeviceListComparisonResult {
+ @NonNull public final Map<IDevice,DeviceState> updated;
+ @NonNull public final List<IDevice> added;
+ @NonNull public final List<IDevice> removed;
+
+ private DeviceListComparisonResult(@NonNull Map<IDevice,DeviceState> updated,
+ @NonNull List<IDevice> added,
+ @NonNull List<IDevice> removed) {
+ this.updated = updated;
+ this.added = added;
+ this.removed = removed;
+ }
+
+ @NonNull
+ public static DeviceListComparisonResult compare(@NonNull List<? extends IDevice> previous,
+ @NonNull List<? extends IDevice> current) {
+ current = Lists.newArrayList(current);
+
+ final Map<IDevice,DeviceState> updated = Maps.newHashMapWithExpectedSize(current.size());
+ final List<IDevice> added = Lists.newArrayListWithExpectedSize(1);
+ final List<IDevice> removed = Lists.newArrayListWithExpectedSize(1);
+
+ for (IDevice device : previous) {
+ IDevice currentDevice = find(current, device);
+ if (currentDevice != null) {
+ if (currentDevice.getState() != device.getState()) {
+ updated.put(device, currentDevice.getState());
+ }
+ current.remove(currentDevice);
+ } else {
+ removed.add(device);
+ }
+ }
+
+ added.addAll(current);
+
+ return new DeviceListComparisonResult(updated, added, removed);
+ }
+
+ @Nullable
+ private static IDevice find(@NonNull List<? extends IDevice> devices,
+ @NonNull IDevice device) {
+ for (IDevice d : devices) {
+ if (d.getSerialNumber().equals(device.getSerialNumber())) {
+ return d;
+ }
+ }
+
+ return null;
+ }
+ }
+
+ @VisibleForTesting
+ static class DeviceListMonitorTask implements Runnable {
+ private final byte[] mLengthBuffer = new byte[4];
+
+ private final AndroidDebugBridge mBridge;
+ private final UpdateListener mListener;
+
+ private SocketChannel mAdbConnection = null;
+ private boolean mMonitoring = false;
+ private int mConnectionAttempt = 0;
+ private int mRestartAttemptCount = 0;
+ private boolean mInitialDeviceListDone = false;
+
+ private volatile boolean mQuit;
+
+ private interface UpdateListener {
+ void connectionError(@NonNull Exception e);
+ void deviceListUpdate(@NonNull Map<String,DeviceState> devices);
+ }
+
+ public DeviceListMonitorTask(@NonNull AndroidDebugBridge bridge,
+ @NonNull UpdateListener listener) {
+ mBridge = bridge;
+ mListener = listener;
+ }
+
+ @Override
+ public void run() {
+ do {
+ if (mAdbConnection == null) {
+ Log.d("DeviceMonitor", "Opening adb connection");
+ mAdbConnection = openAdbConnection();
+ if (mAdbConnection == null) {
+ mConnectionAttempt++;
+ Log.e("DeviceMonitor", "Connection attempts: " + mConnectionAttempt);
+ if (mConnectionAttempt > 10) {
+ if (!mBridge.startAdb()) {
+ mRestartAttemptCount++;
+ Log.e("DeviceMonitor",
+ "adb restart attempts: " + mRestartAttemptCount);
+ } else {
+ Log.i("DeviceMonitor", "adb restarted");
+ mRestartAttemptCount = 0;
+ }
+ }
+ Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
+ } else {
+ Log.d("DeviceMonitor", "Connected to adb for device monitoring");
+ mConnectionAttempt = 0;
+ }
+ }
+
+ try {
+ if (mAdbConnection != null && !mMonitoring) {
+ mMonitoring = sendDeviceListMonitoringRequest();
+ }
+
+ if (mMonitoring) {
+ int length = readLength(mAdbConnection, mLengthBuffer);
+
+ if (length >= 0) {
+ // read the incoming message
+ processIncomingDeviceData(length);
+
+ // flag the fact that we have build the list at least once.
+ mInitialDeviceListDone = true;
+ }
+ }
+ } catch (AsynchronousCloseException ace) {
+ // this happens because of a call to Quit. We do nothing, and the loop will break.
+ } catch (TimeoutException ioe) {
+ handleExceptionInMonitorLoop(ioe);
+ } catch (IOException ioe) {
+ handleExceptionInMonitorLoop(ioe);
+ }
+ } while (!mQuit);
+ }
+
+ private boolean sendDeviceListMonitoringRequest() throws TimeoutException, IOException {
+ byte[] request = AdbHelper.formAdbRequest(ADB_TRACK_DEVICES_COMMAND);
+
+ try {
+ AdbHelper.write(mAdbConnection, request);
+ AdbResponse resp = AdbHelper.readAdbResponse(mAdbConnection, false);
+ if (!resp.okay) {
+ // request was refused by adb!
+ Log.e("DeviceMonitor", "adb refused request: " + resp.message);
+ }
+
+ return resp.okay;
+ } catch (IOException e) {
+ Log.e("DeviceMonitor", "Sending Tracking request failed!");
+ mAdbConnection.close();
+ throw e;
+ }
+ }
+
+ private void handleExceptionInMonitorLoop(@NonNull Exception e) {
+ if (!mQuit) {
+ if (e instanceof TimeoutException) {
+ Log.e("DeviceMonitor", "Adb connection Error: timeout");
+ } else {
+ Log.e("DeviceMonitor", "Adb connection Error:" + e.getMessage());
+ }
+ mMonitoring = false;
+ if (mAdbConnection != null) {
+ try {
+ mAdbConnection.close();
+ } catch (IOException ioe) {
+ // we can safely ignore that one.
+ }
+ mAdbConnection = null;
+
+ mListener.connectionError(e);
+ }
+ }
+ }
+
+ /** Processes an incoming device message from the socket */
+ private void processIncomingDeviceData(int length) throws IOException {
+ Map<String, DeviceState> result;
+ if (length <= 0) {
+ result = Collections.emptyMap();
+ } else {
+ String response = read(mAdbConnection, new byte[length]);
+ result = parseDeviceListResponse(response);
+ }
+
+ mListener.deviceListUpdate(result);
+ }
+
+ @VisibleForTesting
+ static Map<String, DeviceState> parseDeviceListResponse(@Nullable String result) {
+ Map<String, DeviceState> deviceStateMap = Maps.newHashMap();
+ String[] devices = result == null ? new String[0] : result.split("\n"); //$NON-NLS-1$
+
+ for (String d : devices) {
+ String[] param = d.split("\t"); //$NON-NLS-1$
+ if (param.length == 2) {
+ // new adb uses only serial numbers to identify devices
+ deviceStateMap.put(param[0], DeviceState.getState(param[1]));
+ }
+ }
+ return deviceStateMap;
+ }
+
+ boolean isMonitoring() {
+ return mMonitoring;
+ }
+
+ boolean hasInitialDeviceList() {
+ return mInitialDeviceListDone;
+ }
+
+ int getConnectionAttemptCount() {
+ return mConnectionAttempt;
+ }
+
+ int getRestartAttemptCount() {
+ return mRestartAttemptCount;
+ }
+
+ public void stop() {
+ mQuit = true;
+
+ // wakeup the main loop thread by closing the main connection to adb.
+ if (mAdbConnection != null) {
+ try {
+ mAdbConnection.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ }
}
diff --git a/ddmlib/src/main/java/com/android/ddmlib/IDevice.java b/ddmlib/src/main/java/com/android/ddmlib/IDevice.java
index b45d436..be45449 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/IDevice.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/IDevice.java
@@ -35,6 +35,8 @@
String PROP_BUILD_VERSION = "ro.build.version.release";
String PROP_BUILD_API_LEVEL = "ro.build.version.sdk";
String PROP_BUILD_CODENAME = "ro.build.version.codename";
+ String PROP_BUILD_TAGS = "ro.build.tags";
+ String PROP_BUILD_TYPE = "ro.build.type";
String PROP_DEVICE_MODEL = "ro.product.model";
String PROP_DEVICE_MANUFACTURER = "ro.product.manufacturer";
String PROP_DEVICE_CPU_ABI_LIST = "ro.product.cpu.abilist";
@@ -64,7 +66,8 @@
/** Device level hardware features. */
enum HardwareFeature {
- WATCH("watch"); // supports feature watch
+ WATCH("watch"),
+ TV("tv");
private final String mCharacteristic;
@@ -239,7 +242,8 @@
* @see #MNT_ROOT
* @see #MNT_DATA
*/
- String getMountPoint(String name);
+ @Nullable
+ String getMountPoint(@NonNull String name);
/**
* Returns if the device is ready.
diff --git a/ddmlib/src/main/java/com/android/ddmlib/MultiLineReceiver.java b/ddmlib/src/main/java/com/android/ddmlib/MultiLineReceiver.java
index 48afd1e..79b3ede 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/MultiLineReceiver.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/MultiLineReceiver.java
@@ -64,16 +64,22 @@
mArray.clear();
int start = 0;
do {
- int index = s.indexOf("\r\n", start); //$NON-NLS-1$
+ int index = s.indexOf('\n', start); //$NON-NLS-1$
- // if \r\n was not found, this is an unfinished line
+ // if \n was not found, this is an unfinished line
// and we store it to be processed for the next packet
if (index == -1) {
mUnfinishedLine = s.substring(start);
break;
}
- // so we found a \r\n;
+ // we found a \n, in older devices, this is preceded by a \r
+ int newlineLength = 1;
+ if (index > 0 && s.charAt(index - 1) == '\r') {
+ index--;
+ newlineLength = 2;
+ }
+
// extract the line
String line = s.substring(start, index);
if (mTrimLines) {
@@ -82,7 +88,7 @@
mArray.add(line);
// move start to after the \r\n we found
- start = index + 2;
+ start = index + newlineLength;
} while (true);
if (!mArray.isEmpty()) {
diff --git a/ddmlib/src/main/java/com/android/ddmlib/NativeStackCallInfo.java b/ddmlib/src/main/java/com/android/ddmlib/NativeStackCallInfo.java
index be365bf..72d7c77 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/NativeStackCallInfo.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/NativeStackCallInfo.java
@@ -24,7 +24,7 @@
* information as one object.
*/
public final class NativeStackCallInfo {
- private static final Pattern SOURCE_NAME_PATTERN = Pattern.compile("^(.+):(\\d+)$");
+ private static final Pattern SOURCE_NAME_PATTERN = Pattern.compile("^(.+):(\\d+)(\\s+\\(discriminator\\s+\\d+\\))?$");
/** address of this stack frame */
private long mAddress;
@@ -65,6 +65,10 @@
} catch (NumberFormatException e) {
// do nothing, the line number will stay at -1
}
+ if (m.groupCount() == 3) {
+ // A discriminator was found, add that in the source file name.
+ mSourceFile += m.group(3);
+ }
} else {
mSourceFile = sourceFile;
}
diff --git a/ddmlib/src/main/java/com/android/ddmlib/PropertyFetcher.java b/ddmlib/src/main/java/com/android/ddmlib/PropertyFetcher.java
index b366d3b..dc83115 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/PropertyFetcher.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/PropertyFetcher.java
@@ -112,7 +112,7 @@
SettableFuture<String> result;
if (mCacheState.equals(CacheState.FETCHING)) {
result = addPendingRequest(name);
- } else if (mCacheState.equals(CacheState.UNPOPULATED) || !isRoProp(name)) {
+ } else if (mDevice.isOnline() && mCacheState.equals(CacheState.UNPOPULATED) || !isRoProp(name)) {
// cache is empty, or this is a volatile prop that requires a query
result = addPendingRequest(name);
mCacheState = CacheState.FETCHING;
diff --git a/ddmlib/src/main/java/com/android/ddmlib/SyncService.java b/ddmlib/src/main/java/com/android/ddmlib/SyncService.java
index 7958cf9..19c3c14 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/SyncService.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/SyncService.java
@@ -16,6 +16,8 @@
package com.android.ddmlib;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
import com.android.ddmlib.AdbHelper.AdbResponse;
import com.android.ddmlib.FileListingService.FileEntry;
import com.android.ddmlib.SyncException.SyncError;
@@ -29,6 +31,7 @@
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
+import java.util.Date;
/**
* Sync service class to push/pull to/from devices/emulators, through the debug bridge.
@@ -109,6 +112,30 @@
void advance(int work);
}
+ public static class FileStat {
+ private final int myMode;
+ private final int mySize;
+ private final Date myLastModified;
+
+ public FileStat(int mode, int size, int lastModifiedSecs) {
+ myMode = mode;
+ mySize = size;
+ myLastModified = new Date((long)(lastModifiedSecs) * 1000);
+ }
+
+ public int getMode() {
+ return myMode;
+ }
+
+ public int getSize() {
+ return mySize;
+ }
+
+ public Date getLastModified() {
+ return myLastModified;
+ }
+ }
+
/**
* A Sync progress monitor that does nothing
*/
@@ -307,10 +334,10 @@
*/
public void pullFile(String remoteFilepath, String localFilename,
ISyncProgressMonitor monitor) throws TimeoutException, IOException, SyncException {
- Integer mode = readMode(remoteFilepath);
- if (mode == null) {
+ FileStat fileStat = statFile(remoteFilepath);
+ if (fileStat == null) {
// attempts to download anyway
- } else if (mode == 0) {
+ } else if (fileStat.getMode() == 0) {
throw new SyncException(SyncError.NO_REMOTE_OBJECT);
}
@@ -626,6 +653,7 @@
byte[] msg;
final int timeOut = DdmPreferences.getTimeOut();
+ File f = new File(localPath);
try {
byte[] remotePathContent = remotePath.getBytes(AdbHelper.DEFAULT_ENCODING);
@@ -634,8 +662,6 @@
throw new SyncException(SyncError.REMOTE_PATH_LENGTH);
}
- File f = new File(localPath);
-
// create the stream to read the file
fis = new FileInputStream(f);
@@ -683,7 +709,7 @@
}
// create the DONE message
- long time = System.currentTimeMillis() / 1000;
+ long time = f.lastModified() / 1000;
msg = createReq(ID_DONE, (int)time);
// and send it.
@@ -727,14 +753,15 @@
}
/**
- * Returns the mode of the remote file.
+ * Returns the stat info of the remote file.
* @param path the remote file
- * @return an Integer containing the mode if all went well or null
+ * @return an FileStat containing the mode, size and last modified info if all went well or null
* otherwise
* @throws IOException
* @throws TimeoutException in case of a timeout reading responses from the device.
*/
- private Integer readMode(String path) throws TimeoutException, IOException {
+ @Nullable
+ public FileStat statFile(@NonNull String path) throws TimeoutException, IOException {
// create the stat request message.
byte[] msg = createFileReq(ID_STAT, path);
@@ -750,8 +777,10 @@
return null;
}
- // we return the mode (2nd int in the array)
- return ArrayHelper.swap32bitFromArray(statResult, 4);
+ final int mode = ArrayHelper.swap32bitFromArray(statResult, 4);
+ final int size = ArrayHelper.swap32bitFromArray(statResult, 8);
+ final int lastModifiedSecs = ArrayHelper.swap32bitFromArray(statResult, 12);
+ return new FileStat(mode, size, lastModifiedSecs);
}
/**
diff --git a/ddmlib/src/main/java/com/android/ddmlib/log/EventContainer.java b/ddmlib/src/main/java/com/android/ddmlib/log/EventContainer.java
index 4f490d8..3607e60 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/log/EventContainer.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/log/EventContainer.java
@@ -390,7 +390,7 @@
}
}
- private final Object getValue(Object data, int valueIndex, boolean recursive) {
+ private Object getValue(Object data, int valueIndex, boolean recursive) {
EventValueType type = getType(data);
switch (type) {
@@ -410,7 +410,7 @@
return null;
}
- private final double getValueAsDouble(Object data, int valueIndex, boolean recursive)
+ private double getValueAsDouble(Object data, int valueIndex, boolean recursive)
throws InvalidTypeException {
EventValueType type = getType(data);
@@ -433,7 +433,7 @@
throw new InvalidTypeException();
}
- private final String getValueAsString(Object data, int valueIndex, boolean recursive)
+ private String getValueAsString(Object data, int valueIndex, boolean recursive)
throws InvalidTypeException {
EventValueType type = getType(data);
diff --git a/ddmlib/src/main/java/com/android/ddmlib/log/GcEventContainer.java b/ddmlib/src/main/java/com/android/ddmlib/log/GcEventContainer.java
index 859e080..9fd8b59 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/log/GcEventContainer.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/log/GcEventContainer.java
@@ -296,7 +296,7 @@
* @param valueIndex the index of the value.
* @throws InvalidTypeException if index is 0 as it is a string value.
*/
- private final long getValueAsLong(int valueIndex) throws InvalidTypeException {
+ private long getValueAsLong(int valueIndex) throws InvalidTypeException {
switch (valueIndex) {
case 0:
throw new InvalidTypeException();
diff --git a/ddmlib/src/main/java/com/android/ddmlib/testrunner/InstrumentationResultParser.java b/ddmlib/src/main/java/com/android/ddmlib/testrunner/InstrumentationResultParser.java
index 6e7bfa2..40c9971 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/testrunner/InstrumentationResultParser.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/testrunner/InstrumentationResultParser.java
@@ -476,6 +476,7 @@
case StatusCodes.IGNORED:
metrics = getAndResetTestMetrics();
for (ITestRunListener listener : mTestListeners) {
+ listener.testStarted(testId);
listener.testIgnored(testId);
listener.testEnded(testId, metrics);
}
diff --git a/ddmlib/src/main/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java b/ddmlib/src/main/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
index de4f4c6..a8f1491 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
@@ -18,7 +18,6 @@
import com.android.annotations.NonNull;
import com.android.ddmlib.AdbCommandRejectedException;
-import com.android.ddmlib.CollectingOutputReceiver;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.IShellEnabledDevice;
import com.android.ddmlib.Log;
@@ -31,8 +30,6 @@
import java.util.Hashtable;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
@@ -252,7 +249,7 @@
public void run(Collection<ITestRunListener> listeners)
throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException,
IOException {
- final String runCaseCommandStr = String.format("am instrument -w %1$s-r %2$s %3$s",
+ final String runCaseCommandStr = String.format("am instrument -w -r %1$s %2$s %3$s",
getRunOptions(), getArgsCommand(), getRunnerPath());
Log.i(LOG_TAG, String.format("Running %1$s on %2$s", runCaseCommandStr,
mRemoteDevice.getName()));
@@ -304,7 +301,7 @@
* See com/android/commands/am/Am.java for full list of options.
*/
public void setRunOptions(@NonNull String options) {
- mRunOptions = options + " ";
+ mRunOptions = options;
}
@Override
diff --git a/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestRunResult.java b/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestRunResult.java
index e2486fa..4707f1a 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestRunResult.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/testrunner/TestRunResult.java
@@ -157,8 +157,7 @@
* Return total number of tests in a failure state (failed, assumption failure)
*/
public int getNumAllFailedTests() {
- return getNumTestsInState(TestStatus.FAILURE) +
- getNumTestsInState(TestStatus.ASSUMPTION_FAILURE);
+ return getNumTestsInState(TestStatus.FAILURE);
}
/**
diff --git a/ddmlib/src/main/java/com/android/ddmlib/testrunner/XmlTestRunListener.java b/ddmlib/src/main/java/com/android/ddmlib/testrunner/XmlTestRunListener.java
index 9a09c7b..36cb4d4 100644
--- a/ddmlib/src/main/java/com/android/ddmlib/testrunner/XmlTestRunListener.java
+++ b/ddmlib/src/main/java/com/android/ddmlib/testrunner/XmlTestRunListener.java
@@ -285,10 +285,12 @@
serializer.attribute(ns, ATTR_TIME, Double.toString((double)elapsedTimeMs / 1000.f));
switch (testResult.getStatus()) {
- case FAILURE: // intentional fall through
- case ASSUMPTION_FAILURE:
+ case FAILURE:
printFailedTest(serializer, FAILURE, testResult.getStackTrace());
break;
+ case ASSUMPTION_FAILURE:
+ printFailedTest(serializer, SKIPPED_TAG, testResult.getStackTrace());
+ break;
case IGNORED:
serializer.startTag(ns, SKIPPED_TAG);
serializer.endTag(ns, SKIPPED_TAG);
diff --git a/ddmlib/src/main/java/com/android/ddmlib/utils/DebuggerPorts.java b/ddmlib/src/main/java/com/android/ddmlib/utils/DebuggerPorts.java
new file mode 100644
index 0000000..ee2eca3
--- /dev/null
+++ b/ddmlib/src/main/java/com/android/ddmlib/utils/DebuggerPorts.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ddmlib.utils;
+
+import com.android.annotations.concurrency.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DebuggerPorts {
+ @GuardedBy("mDebuggerPorts")
+ private final List<Integer> mDebuggerPorts = new ArrayList<Integer>();
+
+ public DebuggerPorts(int basePort) {
+ mDebuggerPorts.add(basePort);
+ }
+
+ public int next() {
+ // get the first port and remove it
+ synchronized (mDebuggerPorts) {
+ if (!mDebuggerPorts.isEmpty()) {
+ int port = mDebuggerPorts.get(0);
+
+ // remove it.
+ mDebuggerPorts.remove(0);
+
+ // if there's nothing left, add the next port to the list
+ if (mDebuggerPorts.isEmpty()) {
+ mDebuggerPorts.add(port+1);
+ }
+
+ return port;
+ }
+ }
+
+ return -1;
+ }
+
+ public void free(int port) {
+ if (port <= 0) {
+ return;
+ }
+
+ synchronized (mDebuggerPorts) {
+ // because there could be case where clients are closed twice, we have to make
+ // sure the port number is not already in the list.
+ if (mDebuggerPorts.indexOf(port) == -1) {
+ // add the port to the list while keeping it sorted. It's not like there's
+ // going to be tons of objects so we do it linearly.
+ int count = mDebuggerPorts.size();
+ for (int i = 0; i < count; i++) {
+ if (port < mDebuggerPorts.get(i)) {
+ mDebuggerPorts.add(i, port);
+ break;
+ }
+ }
+ // TODO: check if we can compact the end of the list.
+ }
+ }
+ }
+}
diff --git a/ddmlib/src/test/java/com/android/ddmlib/AdbVersionTest.java b/ddmlib/src/test/java/com/android/ddmlib/AdbVersionTest.java
new file mode 100644
index 0000000..6f1c51f
--- /dev/null
+++ b/ddmlib/src/test/java/com/android/ddmlib/AdbVersionTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ddmlib;
+
+import junit.framework.TestCase;
+
+public class AdbVersionTest extends TestCase {
+ public void testParser() {
+ AdbVersion version = AdbVersion.parseFrom("Android Debug Bridge version 1.0.32");
+ assertEquals("1.0.32", version.toString());
+
+ version = AdbVersion.parseFrom("Android Debug Bridge version 1.0.32 8b22c293a0e5-android");
+ assertEquals(AdbVersion.parseFrom("1.0.32"), version);
+
+ version = AdbVersion.parseFrom("1.0.unknown");
+ assertEquals(AdbVersion.UNKNOWN, version);
+ }
+
+ public void testComponents() {
+ AdbVersion version = AdbVersion
+ .parseFrom("Android Debug Bridge version 1.23.32 8b22c293a0e5-android");
+ assertEquals(1, version.major);
+ assertEquals(23, version.minor);
+ assertEquals(32, version.micro);
+ }
+
+ public void testComparison() {
+ AdbVersion min = AdbVersion.parseFrom("1.0.20");
+ AdbVersion now = AdbVersion.parseFrom("1.0.32");
+ assertTrue(now.compareTo(min) > 0);
+ assertTrue(min.compareTo(now) < 0);
+ assertTrue(now.compareTo(now) == 0);
+
+ AdbVersion f = AdbVersion.parseFrom("2.0.32");
+ assertTrue(f.compareTo(now) > 0);
+ }
+}
diff --git a/ddmlib/src/test/java/com/android/ddmlib/AndroidDebugBridgeTest.java b/ddmlib/src/test/java/com/android/ddmlib/AndroidDebugBridgeTest.java
index f9aa8a3..85a8f53 100644
--- a/ddmlib/src/test/java/com/android/ddmlib/AndroidDebugBridgeTest.java
+++ b/ddmlib/src/test/java/com/android/ddmlib/AndroidDebugBridgeTest.java
@@ -20,9 +20,12 @@
import java.io.File;
import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
public class AndroidDebugBridgeTest extends TestCase {
private String mAndroidHome;
+ private File mAdbPath;
@Override
protected void setUp() throws Exception {
@@ -31,19 +34,37 @@
"This test requires ANDROID_HOME environment variable to point to a valid SDK",
mAndroidHome);
- AndroidDebugBridge.init(false);
+ mAdbPath = new File(mAndroidHome, "platform-tools" + File.separator + "adb");
+
+ AndroidDebugBridge.initIfNeeded(false);
}
// https://code.google.com/p/android/issues/detail?id=63170
public void testCanRecreateAdb() throws IOException {
- File adbPath = new File(mAndroidHome, "platform-tools" + File.separator + "adb");
-
- AndroidDebugBridge adb = AndroidDebugBridge.createBridge(adbPath.getCanonicalPath(), true);
+ AndroidDebugBridge adb = AndroidDebugBridge.createBridge(mAdbPath.getCanonicalPath(), true);
assertNotNull(adb);
AndroidDebugBridge.terminate();
- adb = AndroidDebugBridge.createBridge(adbPath.getCanonicalPath(), true);
+ adb = AndroidDebugBridge.createBridge(mAdbPath.getCanonicalPath(), true);
assertNotNull(adb);
AndroidDebugBridge.terminate();
}
+
+ // Some consumers of ddmlib rely on adb being on the path, and hence being
+ // able to create a bridge by simply passing in "adb" as the path to adb.
+ // We should be able to create a bridge in such a case as well.
+ // This test will fail if adb is not currently on the path. It is disabled since we
+ // can't enforce that condition (adb on $PATH) very well from a test..
+ public void disabled_testEmptyAdbPath() throws Exception {
+ AdbVersion version = AndroidDebugBridge.getAdbVersion(
+ new File("adb")).get(5, TimeUnit.SECONDS);
+ assertTrue(version.compareTo(AdbVersion.parseFrom("1.0.20")) > 0);
+ }
+
+ public void testAdbVersion() throws Exception {
+ AdbVersion version = AndroidDebugBridge
+ .getAdbVersion(mAdbPath).get(5, TimeUnit.SECONDS);
+ assertNotSame(version, AdbVersion.UNKNOWN);
+ assertTrue(version.compareTo(AdbVersion.parseFrom("1.0.20")) > 0);
+ }
}
diff --git a/ddmlib/src/test/java/com/android/ddmlib/DeviceMonitorTest.java b/ddmlib/src/test/java/com/android/ddmlib/DeviceMonitorTest.java
new file mode 100644
index 0000000..0182e69
--- /dev/null
+++ b/ddmlib/src/test/java/com/android/ddmlib/DeviceMonitorTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ddmlib;
+
+import com.android.annotations.NonNull;
+
+import junit.framework.TestCase;
+
+import org.easymock.EasyMock;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class DeviceMonitorTest extends TestCase {
+ public void testDeviceListMonitor() {
+ Map<String, IDevice.DeviceState> map = DeviceMonitor.DeviceListMonitorTask
+ .parseDeviceListResponse("R32C801BL5K\tdevice\n0079864fd1d150fd\tunauthorized\n");
+
+ assertEquals(IDevice.DeviceState.ONLINE, map.get("R32C801BL5K"));
+ assertEquals(IDevice.DeviceState.UNAUTHORIZED, map.get("0079864fd1d150fd"));
+ }
+
+ public void testDeviceListComparator() {
+ List<IDevice> previous = Arrays.asList(
+ mockDevice("1", IDevice.DeviceState.ONLINE),
+ mockDevice("2", IDevice.DeviceState.BOOTLOADER)
+ );
+ List<IDevice> current = Arrays.asList(
+ mockDevice("2", IDevice.DeviceState.ONLINE),
+ mockDevice("3", IDevice.DeviceState.OFFLINE)
+ );
+
+ DeviceMonitor.DeviceListComparisonResult result = DeviceMonitor.DeviceListComparisonResult
+ .compare(previous, current);
+
+ assertEquals(1, result.updated.size());
+ assertEquals(IDevice.DeviceState.ONLINE, result.updated.get(previous.get(1)));
+
+ assertEquals(1, result.removed.size());
+ assertEquals("1", result.removed.get(0).getSerialNumber());
+
+ assertEquals(1, result.added.size());
+ assertEquals("3", result.added.get(0).getSerialNumber());
+ }
+
+ private IDevice mockDevice(@NonNull String serial, @NonNull IDevice.DeviceState state) {
+ IDevice device = EasyMock.createMock(IDevice.class);
+ EasyMock.expect(device.getSerialNumber()).andStubReturn(serial);
+ EasyMock.expect(device.getState()).andStubReturn(state);
+ EasyMock.replay(device);
+ return device;
+ }
+}
diff --git a/ddmlib/src/test/java/com/android/ddmlib/DeviceTest.java b/ddmlib/src/test/java/com/android/ddmlib/DeviceTest.java
index cb53b14..5de9feb 100644
--- a/ddmlib/src/test/java/com/android/ddmlib/DeviceTest.java
+++ b/ddmlib/src/test/java/com/android/ddmlib/DeviceTest.java
@@ -78,6 +78,7 @@
static IDevice createMockDevice() {
IDevice mockDevice = EasyMock.createMock(IDevice.class);
EasyMock.expect(mockDevice.getSerialNumber()).andStubReturn("serial");
+ EasyMock.expect(mockDevice.isOnline()).andStubReturn(Boolean.TRUE);
return mockDevice;
}
}
diff --git a/ddmlib/src/test/java/com/android/ddmlib/PropertyFetcherTest.java b/ddmlib/src/test/java/com/android/ddmlib/PropertyFetcherTest.java
index 1049bdc..e715a6b 100644
--- a/ddmlib/src/test/java/com/android/ddmlib/PropertyFetcherTest.java
+++ b/ddmlib/src/test/java/com/android/ddmlib/PropertyFetcherTest.java
@@ -26,8 +26,8 @@
import java.util.concurrent.TimeUnit;
public class PropertyFetcherTest extends TestCase {
- final static String GETPROP_RESPONSE =
- "[ro.sf.lcd_density]: [480]\r\n" +
+ static final String GETPROP_RESPONSE =
+ "\n[ro.sf.lcd_density]: [480]\n" +
"[ro.secure]: [1]\r\n";
/**
diff --git a/ddmlib/src/test/java/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java b/ddmlib/src/test/java/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
index 3e4cdf4..409e2c1 100644
--- a/ddmlib/src/test/java/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
+++ b/ddmlib/src/test/java/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
@@ -314,6 +314,112 @@
assertEquals(3, captureMetrics.getValue().size());
}
+ public void testParse_AssumptionFailuresIgnored() {
+ StringBuilder output = new StringBuilder();
+ addLine(output, "INSTRUMENTATION_STATUS: numtests=3");
+ addLine(output, "INSTRUMENTATION_STATUS: stream=");
+ addLine(output, "com.example.helloworld.FailureAssumptionTest:");
+ addLine(output, "INSTRUMENTATION_STATUS: id=AndroidJUnitRunner");
+ addLine(output, "INSTRUMENTATION_STATUS: test=checkIgnoreTestsArePossible");
+ addLine(output, "INSTRUMENTATION_STATUS: class=com.example.helloworld.FailureAssumptionTest");
+ addLine(output, "INSTRUMENTATION_STATUS: current=1");
+ addLine(output, "INSTRUMENTATION_STATUS_CODE: -3");
+ addLine(output, "INSTRUMENTATION_STATUS: numtests=3");
+ addLine(output, "INSTRUMENTATION_STATUS: stream=");
+ addLine(output, "INSTRUMENTATION_STATUS: id=AndroidJUnitRunner");
+ addLine(output, "INSTRUMENTATION_STATUS: test=checkAssumptionIsSkipped");
+ addLine(output, "INSTRUMENTATION_STATUS: class=com.example.helloworld.FailureAssumptionTest");
+ addLine(output, "INSTRUMENTATION_STATUS: current=2");
+ addLine(output, "INSTRUMENTATION_STATUS_CODE: 1");
+ addLine(output, "INSTRUMENTATION_STATUS: numtests=3");
+ addLine(output, "INSTRUMENTATION_STATUS: stream=");
+ addLine(output, "INSTRUMENTATION_STATUS: id=AndroidJUnitRunner");
+ addLine(output, "INSTRUMENTATION_STATUS: test=checkAssumptionIsSkipped");
+ addLine(output, "INSTRUMENTATION_STATUS: class=com.example.helloworld.FailureAssumptionTest");
+ addLine(output, "INSTRUMENTATION_STATUS: stack=org.junit.AssumptionViolatedException: got: <false>, expected: is <true>");
+ addLine(output, "at org.junit.Assume.assumeThat(Assume.java:95)");
+ addLine(output, "at org.junit.Assume.assumeTrue(Assume.java:41)");
+ addLine(output, "at com.example.helloworld.FailureAssumptionTest.checkAssumptionIsSkipped(FailureAssumptionTest.java:19)");
+ addLine(output, "at java.lang.reflect.Method.invoke(Native Method)");
+ addLine(output, "at java.lang.reflect.Method.invoke(Method.java:372)");
+ addLine(output, "at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)");
+ addLine(output, "at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)");
+ addLine(output, "at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)");
+ addLine(output, "at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)");
+ addLine(output, "at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)");
+ addLine(output, "at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)");
+ addLine(output, "at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)");
+ addLine(output, "at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)");
+ addLine(output, "at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)");
+ addLine(output, "at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)");
+ addLine(output, "at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)");
+ addLine(output, "at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)");
+ addLine(output, "at org.junit.runners.ParentRunner.run(ParentRunner.java:363)");
+ addLine(output, "at org.junit.runners.Suite.runChild(Suite.java:128)");
+ addLine(output, "at org.junit.runners.Suite.runChild(Suite.java:27)");
+ addLine(output, "at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)");
+ addLine(output, "at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)");
+ addLine(output, "at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)");
+ addLine(output, "at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)");
+ addLine(output, "at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)");
+ addLine(output, "at org.junit.runners.ParentRunner.run(ParentRunner.java:363)");
+ addLine(output, "at org.junit.runner.JUnitCore.run(JUnitCore.java:137)");
+ addLine(output, "at org.junit.runner.JUnitCore.run(JUnitCore.java:115)");
+ addLine(output, "at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:54)");
+ addLine(output, "at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:228)");
+ addLine(output, "at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1853)");
+ addLine(output, "");
+ addLine(output, "INSTRUMENTATION_STATUS: current=2");
+ addLine(output, "INSTRUMENTATION_STATUS_CODE: -4");
+ addLine(output, "INSTRUMENTATION_STATUS: numtests=3");
+ addLine(output, "INSTRUMENTATION_STATUS: stream=");
+ addLine(output, "com.example.helloworld.HelloWorldTest:");
+ addLine(output, "INSTRUMENTATION_STATUS: id=AndroidJUnitRunner");
+ addLine(output, "INSTRUMENTATION_STATUS: test=testPreconditions");
+ addLine(output, "INSTRUMENTATION_STATUS: class=com.example.helloworld.HelloWorldTest");
+ addLine(output, "INSTRUMENTATION_STATUS: current=3");
+ addLine(output, "INSTRUMENTATION_STATUS_CODE: 1");
+ addLine(output, "INSTRUMENTATION_STATUS: numtests=3");
+ addLine(output, "INSTRUMENTATION_STATUS: stream=.");
+ addLine(output, "INSTRUMENTATION_STATUS: id=AndroidJUnitRunner");
+ addLine(output, "INSTRUMENTATION_STATUS: test=testPreconditions");
+ addLine(output, "INSTRUMENTATION_STATUS: class=com.example.helloworld.HelloWorldTest");
+ addLine(output, "INSTRUMENTATION_STATUS: current=3");
+ addLine(output, "INSTRUMENTATION_STATUS_CODE: 0");
+ addLine(output, "INSTRUMENTATION_RESULT: stream=");
+ addLine(output, "");
+ addLine(output, "Time: 0.676");
+ addLine(output, "");
+ addLine(output, "OK (2 tests)");
+ addLine(output, "");
+ addLine(output, "");
+ addLine(output, "INSTRUMENTATION_CODE: -1");
+
+
+ mMockListener.testRunStarted(RUN_NAME, 3);
+ final TestIdentifier IGNORED_ANNOTATION = new TestIdentifier(
+ "com.example.helloworld.FailureAssumptionTest",
+ "checkIgnoreTestsArePossible");
+ mMockListener.testStarted(IGNORED_ANNOTATION);
+ mMockListener.testIgnored(IGNORED_ANNOTATION);
+ mMockListener.testEnded(IGNORED_ANNOTATION, Collections.EMPTY_MAP);
+ final TestIdentifier ASSUME_FALSE = new TestIdentifier(
+ "com.example.helloworld.FailureAssumptionTest",
+ "checkAssumptionIsSkipped");
+ mMockListener.testStarted(ASSUME_FALSE);
+ mMockListener.testAssumptionFailure(EasyMock.eq(ASSUME_FALSE),
+ EasyMock.startsWith(
+ "org.junit.AssumptionViolatedException: got: <false>, expected: is <true>"));
+ mMockListener.testEnded(ASSUME_FALSE, Collections.EMPTY_MAP);
+ final TestIdentifier HELLO_WORLD = new TestIdentifier("com.example.helloworld.HelloWorldTest",
+ "testPreconditions");
+ mMockListener.testStarted(HELLO_WORLD);
+ mMockListener.testEnded(HELLO_WORLD, Collections.EMPTY_MAP);
+ mMockListener.testRunEnded(EasyMock.eq(676L), EasyMock.anyObject(Map.class));
+
+ injectAndVerifyTestString(output.toString());
+}
+
/**
* Builds a common test result using TEST_NAME and TEST_CLASS.
*/
diff --git a/ddmlib/src/test/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java b/ddmlib/src/test/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
index eaa6f19..523e599 100644
--- a/ddmlib/src/test/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
+++ b/ddmlib/src/test/java/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
@@ -54,7 +54,7 @@
* Test the basic case building of the instrumentation runner command with no arguments.
*/
public void testRun() throws Exception {
- String expectedCmd = EasyMock.eq(String.format("am instrument -w -r %s/%s", TEST_PACKAGE,
+ String expectedCmd = EasyMock.eq(String.format("am instrument -w -r %s/%s", TEST_PACKAGE,
TEST_RUNNER));
runAndVerify(expectedCmd);
}
@@ -103,6 +103,20 @@
}
/**
+ * Test additional run options.
+ */
+ public void testRun_runOptions() throws Exception {
+ mRunner.setRunOptions("--no-window-animation");
+ String expectedCmd =
+ EasyMock.eq(
+ String.format(
+ "am instrument -w -r --no-window-animation %s/%s",
+ TEST_PACKAGE,
+ TEST_RUNNER));
+ runAndVerify(expectedCmd);
+ }
+
+ /**
* Test run when the device throws a IOException
*/
@SuppressWarnings("unchecked")
diff --git a/ddmlib/src/test/java/com/android/ddmlib/utils/DebuggerPortsTest.java b/ddmlib/src/test/java/com/android/ddmlib/utils/DebuggerPortsTest.java
new file mode 100644
index 0000000..ac54c4b
--- /dev/null
+++ b/ddmlib/src/test/java/com/android/ddmlib/utils/DebuggerPortsTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ddmlib.utils;
+
+import junit.framework.TestCase;
+
+public class DebuggerPortsTest extends TestCase {
+ public void testNextFreePort() {
+ DebuggerPorts freePorts = new DebuggerPorts(9000);
+ assertEquals(9000, freePorts.next());
+ assertEquals(9001, freePorts.next());
+ }
+
+ public void testReleasePort() {
+ DebuggerPorts freePorts = new DebuggerPorts(9000);
+ int first = freePorts.next();
+ int second = freePorts.next();
+
+ freePorts.free(first);
+ assertEquals(first, freePorts.next());
+ assertEquals(second + 1, freePorts.next());
+
+ freePorts.free(second);
+ assertEquals(second, freePorts.next());
+ }
+}
diff --git a/device_validator/dvlib/build.gradle b/device_validator/dvlib/build.gradle
index ceea5a9..4111d48 100644
--- a/device_validator/dvlib/build.gradle
+++ b/device_validator/dvlib/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
+apply plugin: 'jacoco'
apply plugin: 'sdk-java-lib'
dependencies {
diff --git a/device_validator/dvlib/src/main/java/com/android/dvlib/DeviceSchema.java b/device_validator/dvlib/src/main/java/com/android/dvlib/DeviceSchema.java
index cd89d7e..4aaa6f7 100644
--- a/device_validator/dvlib/src/main/java/com/android/dvlib/DeviceSchema.java
+++ b/device_validator/dvlib/src/main/java/com/android/dvlib/DeviceSchema.java
@@ -46,7 +46,6 @@
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
-import javax.xml.validation.Validator;
public class DeviceSchema {
diff --git a/device_validator/dvlib/src/main/resources/com/android/dvlib/devices-2.xsd b/device_validator/dvlib/src/main/resources/com/android/dvlib/devices-2.xsd
index 2589e46..2017a6a 100644
--- a/device_validator/dvlib/src/main/resources/com/android/dvlib/devices-2.xsd
+++ b/device_validator/dvlib/src/main/resources/com/android/dvlib/devices-2.xsd
@@ -515,7 +515,9 @@
<xsd:enumeration value="mdpi" />
<xsd:enumeration value="tvdpi" />
<xsd:enumeration value="hdpi" />
+ <xsd:enumeration value="280dpi" />
<xsd:enumeration value="xhdpi" />
+ <xsd:enumeration value="360dpi" />
<xsd:enumeration value="400dpi" />
<xsd:enumeration value="xxhdpi" />
<xsd:enumeration value="560dpi" />
diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices.xml b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices.xml
index 59587b8..808d6ff 100644
--- a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices.xml
+++ b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices.xml
@@ -398,5 +398,92 @@
</d:boot-props>
</d:device>
+ <d:device>
+ <d:name>Android Wear Round Chin</d:name>
+ <d:id>wear_round_chin</d:id>
+ <d:manufacturer>Google</d:manufacturer>
+ <d:meta/>
+ <d:hardware>
+ <d:screen>
+ <d:screen-size>small</d:screen-size>
+ <d:diagonal-length>1.65</d:diagonal-length>
+ <d:pixel-density>tvdpi</d:pixel-density>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:dimensions>
+ <d:x-dimension>320</d:x-dimension>
+ <d:y-dimension>290</d:y-dimension>
+ </d:dimensions>
+ <d:xdpi>239.99</d:xdpi>
+ <d:ydpi>239.99</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>
+ Bluetooth
+ Wifi
+ NFC
+ </d:networking>
+ <d:sensors>
+ Accelerometer
+ Barometer
+ Compass
+ GPS
+ Gyroscope
+ LightSensor
+ ProximitySensor
+ </d:sensors>
+ <d:mic>true</d:mic>
+ <d:keyboard>qwerty</d:keyboard>
+ <d:nav>dpad</d:nav>
+ <d:ram unit="MiB">512</d:ram>
+ <d:buttons>hard</d:buttons>
+ <d:internal-storage unit="KiB">
+ 7811891
+ </d:internal-storage>
+ <d:removable-storage unit="TiB"/>
+ <d:cpu>Qualcomm Snapdragon S4 Pro</d:cpu>
+ <d:gpu>Adreno 320</d:gpu>
+ <d:abi>
+ armeabi-v7a
+ </d:abi>
+ <d:dock/>
+ <d:power-type>battery</d:power-type>
+ <d:skin>AndroidWearRound</d:skin>
+ </d:hardware>
+ <d:software>
+ <d:api-level>20-</d:api-level>
+ <d:live-wallpaper-support>true</d:live-wallpaper-support>
+ <d:bluetooth-profiles/>
+ <d:gl-version>2.0</d:gl-version>
+ <d:gl-extensions/>
+ <d:status-bar>false</d:status-bar>
+ </d:software>
+ <d:state name="Portrait">
+ <d:description>The device in portrait orientation</d:description>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:keyboard-state>keyshidden</d:keyboard-state>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:state>
+ <d:state default="true" name="Landscape">
+ <d:description>The device in landscape orientation</d:description>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:keyboard-state>keyshidden</d:keyboard-state>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:state>
+ <d:tag-id>android-wear</d:tag-id>
+ <d:boot-props>
+ <d:boot-prop>
+ <d:prop-name>ro.emulator.circular</d:prop-name>
+ <d:prop-value>true</d:prop-value>
+ </d:boot-prop>
+ <d:boot-prop>
+ <d:prop-name>ro.emu.win_outset_bottom_px</d:prop-name>
+ <d:prop-value>30</d:prop-value>
+ </d:boot-prop>
+ </d:boot-props>
+ </d:device>
</d:devices>
diff --git a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_v2.xml b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_v2.xml
index 54a283c..d1804fb 100755
--- a/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_v2.xml
+++ b/device_validator/dvlib/src/test/resources/com/android/dvlib/devices_v2.xml
@@ -406,5 +406,92 @@
</d:boot-props>
</d:device>
+ <d:device>
+ <d:name>Android Wear Round Chin</d:name>
+ <d:id>wear_round_chin</d:id>
+ <d:manufacturer>Google</d:manufacturer>
+ <d:meta/>
+ <d:hardware>
+ <d:screen>
+ <d:screen-size>small</d:screen-size>
+ <d:diagonal-length>1.65</d:diagonal-length>
+ <d:pixel-density>tvdpi</d:pixel-density>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:dimensions>
+ <d:x-dimension>320</d:x-dimension>
+ <d:y-dimension>290</d:y-dimension>
+ </d:dimensions>
+ <d:xdpi>239.99</d:xdpi>
+ <d:ydpi>239.99</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>
+ Bluetooth
+ Wifi
+ NFC
+ </d:networking>
+ <d:sensors>
+ Accelerometer
+ Barometer
+ Compass
+ GPS
+ Gyroscope
+ LightSensor
+ ProximitySensor
+ </d:sensors>
+ <d:mic>true</d:mic>
+ <d:keyboard>qwerty</d:keyboard>
+ <d:nav>dpad</d:nav>
+ <d:ram unit="MiB">512</d:ram>
+ <d:buttons>hard</d:buttons>
+ <d:internal-storage unit="KiB">
+ 7811891
+ </d:internal-storage>
+ <d:removable-storage unit="TiB"/>
+ <d:cpu>Qualcomm Snapdragon S4 Pro</d:cpu>
+ <d:gpu>Adreno 320</d:gpu>
+ <d:abi>
+ armeabi-v7a
+ </d:abi>
+ <d:dock/>
+ <d:power-type>battery</d:power-type>
+ <d:skin>AndroidWearRound</d:skin>
+ </d:hardware>
+ <d:software>
+ <d:api-level>20-</d:api-level>
+ <d:live-wallpaper-support>true</d:live-wallpaper-support>
+ <d:bluetooth-profiles/>
+ <d:gl-version>2.0</d:gl-version>
+ <d:gl-extensions/>
+ <d:status-bar>false</d:status-bar>
+ </d:software>
+ <d:state name="Portrait">
+ <d:description>The device in portrait orientation</d:description>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:keyboard-state>keyshidden</d:keyboard-state>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:state>
+ <d:state default="true" name="Landscape">
+ <d:description>The device in landscape orientation</d:description>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:keyboard-state>keyshidden</d:keyboard-state>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:state>
+ <d:tag-id>android-wear</d:tag-id>
+ <d:boot-props>
+ <d:boot-prop>
+ <d:prop-name>ro.emulator.circular</d:prop-name>
+ <d:prop-value>true</d:prop-value>
+ </d:boot-prop>
+ <d:boot-prop>
+ <d:prop-name>ro.emu.win_outset_bottom_px</d:prop-name>
+ <d:prop-value>30</d:prop-value>
+ </d:boot-prop>
+ </d:boot-props>
+ </d:device>
</d:devices>
diff --git a/draw9patch/build.gradle b/draw9patch/build.gradle
index ec84cd1..b54a7f0 100644
--- a/draw9patch/build.gradle
+++ b/draw9patch/build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'java'
+apply plugin: 'jacoco'
apply plugin: 'sdk-java-lib'
group = 'com.android.tools'
diff --git a/draw9patch/src/main/java/com/android/draw9patch/ui/ImageEditorPanel.java b/draw9patch/src/main/java/com/android/draw9patch/ui/ImageEditorPanel.java
index d745a69..d3858ee 100644
--- a/draw9patch/src/main/java/com/android/draw9patch/ui/ImageEditorPanel.java
+++ b/draw9patch/src/main/java/com/android/draw9patch/ui/ImageEditorPanel.java
@@ -21,7 +21,6 @@
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
-import java.awt.FlowLayout;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
@@ -37,7 +36,6 @@
import java.net.URL;
import javax.swing.Box;
-import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
diff --git a/draw9patch/src/main/java/com/android/draw9patch/ui/PatchInfo.java b/draw9patch/src/main/java/com/android/draw9patch/ui/PatchInfo.java
index 1b9c73d..0d28302 100644
--- a/draw9patch/src/main/java/com/android/draw9patch/ui/PatchInfo.java
+++ b/draw9patch/src/main/java/com/android/draw9patch/ui/PatchInfo.java
@@ -135,7 +135,7 @@
}
private Pair<Integer> getPadding(List<Pair<Integer>> pairs) {
- if (pairs.size() == 0) {
+ if (pairs.isEmpty()) {
return new Pair<Integer>(0, 0);
} else if (pairs.size() == 1) {
if (pairs.get(0).first == 1) {
diff --git a/draw9patch/src/main/java/com/android/draw9patch/ui/StretchesViewer.java b/draw9patch/src/main/java/com/android/draw9patch/ui/StretchesViewer.java
index f799a18..1206dd3 100644
--- a/draw9patch/src/main/java/com/android/draw9patch/ui/StretchesViewer.java
+++ b/draw9patch/src/main/java/com/android/draw9patch/ui/StretchesViewer.java
@@ -163,7 +163,7 @@
both.remainderVertical = both.scaledHeight - remainderVertical;
horizontalPatchesSum = 0;
- if (patchInfo.horizontalPatches.size() > 0) {
+ if (!patchInfo.horizontalPatches.isEmpty()) {
int start = -1;
for (Rectangle rect : patchInfo.horizontalPatches) {
if (rect.x > start) {
@@ -182,7 +182,7 @@
}
verticalPatchesSum = 0;
- if (patchInfo.verticalPatches.size() > 0) {
+ if (!patchInfo.verticalPatches.isEmpty()) {
int start = -1;
for (Rectangle rect : patchInfo.verticalPatches) {
if (rect.y > start) {
diff --git a/jack/jack-api/README.txt b/jack/jack-api/README.txt
new file mode 100644
index 0000000..1cb0166
--- /dev/null
+++ b/jack/jack-api/README.txt
@@ -0,0 +1,5 @@
+Jack-API version 0.9
+
+This is a clone of the jack-api source code so that it can be
+build and uploaded to bintray/mavenCentral using the same release
+mechanism as the rest of the Gradle plugin libraries.
\ No newline at end of file
diff --git a/jack/jack-api/build.gradle b/jack/jack-api/build.gradle
new file mode 100644
index 0000000..d5bf9de
--- /dev/null
+++ b/jack/jack-api/build.gradle
@@ -0,0 +1,26 @@
+apply plugin: 'java'
+
+group = 'com.android.tools.jack'
+archivesBaseName = 'jack-api'
+version = '0.9.0'
+
+configurations {
+ provided
+}
+
+dependencies {
+ provided 'com.google.code.findbugs:jsr305:1.3.9'
+}
+
+compileJava.classpath += configurations.provided
+
+project.ext.pomName = 'Android Jack API'
+project.ext.pomDesc = 'API to dynamically load Jack'
+
+apply from: "$rootDir/buildSrc/base/publish.gradle"
+apply from: "$rootDir/buildSrc/base/bintray.gradle"
+apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+javadoc {
+ classpath += configurations.provided
+}
diff --git a/jack/jack-api/jack-api.iml b/jack/jack-api/jack-api.iml
new file mode 100644
index 0000000..00040c6
--- /dev/null
+++ b/jack/jack-api/jack-api.iml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/.settings" />
+ <excludeFolder url="file://$MODULE_DIR$/build" />
+ </content>
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="library" exported="" scope="TEST" name="JUnit4" level="project" />
+ <orderEntry type="library" name="jsr-305" level="project" />
+ </component>
+</module>
\ No newline at end of file
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/ConfigNotSupportedException.java b/jack/jack-api/src/main/java/com/android/jack/api/ConfigNotSupportedException.java
new file mode 100644
index 0000000..8fea150
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/ConfigNotSupportedException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Thrown when the requested Jack configuration for a given API version is not supported.
+ */
+public class ConfigNotSupportedException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public ConfigNotSupportedException() {
+ super();
+ }
+
+ public ConfigNotSupportedException(@Nonnull String message) {
+ super(message);
+ }
+
+ public ConfigNotSupportedException(@Nonnull String message, @Nonnull Throwable cause) {
+ super(message, cause);
+ }
+
+ public ConfigNotSupportedException(@Nonnull Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/JackConfig.java b/jack/jack-api/src/main/java/com/android/jack/api/JackConfig.java
new file mode 100644
index 0000000..31e56f7
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/JackConfig.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api;
+
+/**
+ * Allows to set a configuration for Jack.
+ */
+public interface JackConfig {
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/JackProvider.java b/jack/jack-api/src/main/java/com/android/jack/api/JackProvider.java
new file mode 100644
index 0000000..0a802ac
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/JackProvider.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api;
+
+import java.util.Collection;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * Provides instances of {@link JackConfig}.
+ */
+public interface JackProvider {
+ /**
+ * Creates a {@link JackConfig} instance for an interface representing a {@link JackConfig} API
+ * version.
+ * @param cls the {@link JackConfig} API interface
+ * @return the {@link JackConfig} instance
+ * @throws ConfigNotSupportedException If no implementation is found for the given interface.
+ */
+ @Nonnull
+ <T extends JackConfig> T createConfig(@Nonnull Class<T> cls) throws ConfigNotSupportedException;
+
+ /**
+ * Returns whether an interface representing a {@link JackConfig} API version is supported.
+ *
+ * @param cls the {@link JackConfig} API interface
+ * @return <code>true</true> if the config is supported
+ */
+ @Nonnull
+ <T extends JackConfig> boolean isConfigSupported(@Nonnull Class<T> cls);
+
+ /**
+ * Gives a {@link Collection} containing supported {@link JackConfig} API versions.
+ * @return the supported {@link JackConfig} API versions
+ */
+ @Nonnull
+ Collection<Class<? extends JackConfig>> getSupportedConfigs();
+
+ /**
+ * Gives the version of this Jack compiler, summarized in one string (e.g. "1.1-rc1", "2.0-a2",
+ * ...).
+ *
+ * @return the version
+ */
+ @Nonnull
+ String getCompilerVersion();
+
+ /**
+ * Gives the release name of this Jack compiler (e.g. Arzon, Brest, ...).
+ *
+ * @return the release name
+ */
+ @Nonnull
+ String getCompilerReleaseName();
+
+ /**
+ * Gives an integer value that represents the release of this Jack compiler, relative to other
+ * releases.
+ *
+ * @return the release code
+ */
+ @Nonnegative
+ int getCompilerReleaseCode();
+
+ /**
+ * Gives an integer value that represents the sub-release of this Jack compiler, relative to other
+ * sub-releases of the same release.
+ *
+ * @return the sub-release code
+ */
+ @Nonnegative
+ int getCompilerSubReleaseCode();
+
+ /**
+ * Gives the kind of sub-release of this Jack compiler.
+ *
+ * @return the sub-release kind
+ */
+ @Nonnull
+ SubReleaseKind getCompilerSubReleaseKind();
+
+ /**
+ * The kind of sub-release.
+ */
+ public enum SubReleaseKind {
+ /**
+ * A sub-release from an engineering development, not tested, not in the code base repository.
+ */
+ ENGINEERING,
+ /**
+ * A sub-release that is not feature complete, not tested.
+ */
+ PRE_ALPHA,
+ /**
+ * A sub-release that is not feature complete, tested.
+ */
+ ALPHA,
+ /**
+ * A sub-release that is feature complete, tested, but likely contains known or unknown bugs.
+ */
+ BETA,
+ /**
+ * A pre-production sub-release, tested.
+ */
+ CANDIDATE,
+ /**
+ * A production and stable sub-release.
+ */
+ RELEASE;
+ }
+
+ /**
+ * The build ID of this Jack compiler.
+ * @return the build ID, or null if not available
+ */
+ @CheckForNull
+ String getCompilerBuildId();
+
+ /**
+ * Identify the source code base of this Jack compiler.
+ * @return the source code base, or null if not available
+ */
+ @CheckForNull
+ String getCompilerSourceCodeBase();
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/Api01CompilationTask.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/Api01CompilationTask.java
new file mode 100644
index 0000000..cf6141f
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/Api01CompilationTask.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api.v01;
+
+
+/**
+ * A task allowing to run the Jack compiler once.
+ */
+public interface Api01CompilationTask {
+
+ /**
+ * Runs the Jack compiler. May be called only once.
+ * @throws CompilationException If a fatal error occurred during the compilation
+ * @throws UnrecoverableException If an error out of Jack's control occurred
+ * @throws ConfigurationException If there is an error in the configuration
+ * @throws IllegalStateException If Jack is run more than once
+ */
+ void run() throws CompilationException, UnrecoverableException, ConfigurationException,
+ IllegalStateException;
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/Api01Config.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/Api01Config.java
new file mode 100644
index 0000000..81e552a
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/Api01Config.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api.v01;
+
+import com.android.jack.api.JackConfig;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A configuration for API level 01 of the Jack compiler.
+ */
+public interface Api01Config extends JackConfig {
+
+ /**
+ * Sets an {@link OutputStream} where Jack will write errors, warnings and other information.
+ * @param reporterKind The type of reporter
+ * @param reporterStream The stream where to write
+ * @throws ConfigurationException
+ */
+ void setReporter(@Nonnull ReporterKind reporterKind, @Nonnull OutputStream reporterStream)
+ throws ConfigurationException;
+
+ /**
+ * Sets the policy to follow when there is a collision between imported types.
+ * @param typeImportCollisionPolicy the collision policy for imported types
+ * @throws ConfigurationException
+ */
+ void setTypeImportCollisionPolicy(@Nonnull TypeCollisionPolicy typeImportCollisionPolicy)
+ throws ConfigurationException;
+
+ /**
+ * Sets the policy to follow when there is a collision between imported resources.
+ * @param resourceImportCollisionPolicy the collision policy for imported resources
+ * @throws ConfigurationException
+ */
+ void setResourceImportCollisionPolicy(
+ @Nonnull ResourceCollisionPolicy resourceImportCollisionPolicy) throws ConfigurationException;
+
+ /**
+ * Sets the Java source version.
+ * @param javaSourceVersion the Java source version
+ * @throws ConfigurationException
+ */
+ void setJavaSourceVersion(@Nonnull JavaSourceVersion javaSourceVersion)
+ throws ConfigurationException;
+
+ /**
+ * Sets the file where to write the obfuscation mapping. The file may already exist and will be
+ * overwritten.
+ * @param obfuscationMappingOutputFile the obfuscation mapping output file
+ * @throws ConfigurationException
+ */
+ void setObfuscationMappingOutputFile(@Nonnull File obfuscationMappingOutputFile)
+ throws ConfigurationException;
+
+ /**
+ * Sets the classpath.
+ * @param classpath The classpath as a list
+ * @throws ConfigurationException
+ */
+ void setClasspath(@Nonnull List<File> classpath) throws ConfigurationException;
+
+ /**
+ * Sets the Jack library files that will be imported into the output.
+ * @param importedJackLibraryFiles The Jack library files to import
+ * @throws ConfigurationException
+ */
+ void setImportedJackLibraryFiles(@Nonnull List<File> importedJackLibraryFiles)
+ throws ConfigurationException;
+
+ /**
+ * Sets the directories containing files to import into the output as meta-files.
+ * @param metaDirs The directories containing the meta-files
+ * @throws ConfigurationException
+ */
+ void setMetaDirs(@Nonnull List<File> metaDirs) throws ConfigurationException;
+
+ /**
+ * Sets the directories containing files to import into the output as resources.
+ * @param resourceDirs The directories containing the resources
+ * @throws ConfigurationException
+ */
+ void setResourceDirs(@Nonnull List<File> resourceDirs) throws ConfigurationException;
+
+ /**
+ * Sets the directory that will be used to store data for incremental support. This directory must
+ * already exist.
+ * @param incrementalDir The directory used for incremental data
+ * @throws ConfigurationException
+ */
+ void setIncrementalDir(@Nonnull File incrementalDir) throws ConfigurationException;
+
+ /**
+ * Sets the directory that will be used to write dex files and resources. This directory must
+ * already exist.
+ * @param outputDexDir The output directory for dex files and resources
+ * @throws ConfigurationException
+ */
+ void setOutputDexDir(@Nonnull File outputDexDir) throws ConfigurationException;
+
+ /**
+ * Sets the file where the output Jack library will be written. The file may already exist and
+ * will be overwritten.
+ * @param outputJackFile The output Jack library file
+ * @throws ConfigurationException
+ */
+ void setOutputJackFile(@Nonnull File outputJackFile) throws ConfigurationException;
+
+ /**
+ * Sets JarJar configuration files to use for repackaging.
+ * @param jarjarConfigFiles The JarJar configuration files
+ * @throws ConfigurationException
+ */
+ void setJarJarConfigFiles(@Nonnull List<File> jarjarConfigFiles) throws ConfigurationException;
+
+ /**
+ * Sets ProGuard configuration files.
+ * @param proguardConfigFiles The ProGuard configuration files
+ * @throws ConfigurationException
+ */
+ void setProguardConfigFiles(@Nonnull List<File> proguardConfigFiles)
+ throws ConfigurationException;
+
+ /**
+ * Set how much debug info should be emitted.
+ * @param debugInfoLevel The level of debug info to emit
+ * @throws ConfigurationException
+ */
+ void setDebugInfoLevel(@Nonnull DebugInfoLevel debugInfoLevel) throws ConfigurationException;
+
+ /**
+ * Sets whether to allow splitting the output in several dex files, and which method to use.
+ * @param multiDexKind the multidex kind
+ * @throws ConfigurationException
+ */
+ void setMultiDexKind(@Nonnull MultiDexKind multiDexKind) throws ConfigurationException;
+
+ /**
+ * Sets the verbosity level.
+ * @param verbosityLevel the verbosity level
+ * @throws ConfigurationException
+ */
+ void setVerbosityLevel(@Nonnull VerbosityLevel verbosityLevel) throws ConfigurationException;
+
+ /**
+ * Sets the class names of the annotation processors to run.
+ * @param processorNames Annotation processor class names
+ * @throws ConfigurationException
+ */
+ void setProcessorNames(@Nonnull List<String> processorNames) throws ConfigurationException;
+
+ /**
+ * Sets the path where to find annotation processors.
+ * @param processorPath The processor path as a list
+ * @throws ConfigurationException
+ */
+ void setProcessorPath(@Nonnull List<File> processorPath) throws ConfigurationException;
+
+ /**
+ * Sets options for the annotation processors.
+ * @param processorOptions The processor options as a map
+ * @throws ConfigurationException
+ */
+ void setProcessorOptions(@Nonnull Map<String, String> processorOptions)
+ throws ConfigurationException;
+
+ /**
+ * Sets the Java source files entries to compile.
+ * @param sourceEntries The source entries
+ * @throws ConfigurationException
+ */
+ void setSourceEntries(@Nonnull Collection<File> sourceEntries) throws ConfigurationException;
+
+ /**
+ * Sets the value for the given property.
+ * @param key The name of the property
+ * @param value The value to set the property to
+ * @throws ConfigurationException
+ */
+ void setProperty(@Nonnull String key, @Nonnull String value) throws ConfigurationException;
+
+ /**
+ * Creates an instance of the {@link Api01CompilationTask} according to this configuration.
+ * @return The {@link Api01CompilationTask}
+ * @throws ConfigurationException
+ */
+ @Nonnull
+ Api01CompilationTask getTask() throws ConfigurationException;
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/ChainedException.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/ChainedException.java
new file mode 100644
index 0000000..1efea16
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/ChainedException.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api.v01;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * Abstract class to easily chain exceptions together.
+ *
+ * The exception can be managed like any other exception. In this case, the first one will be the
+ * only one used.
+ *
+ * Special management can use the {@link #iterator()} or the {@link #getNextException()} to browse
+ * all chained exceptions and dispatch them.
+ *
+ * See {@link ChainedExceptionBuilder} to build the chain of exceptions.
+ */
+public abstract class ChainedException extends Exception
+ implements Iterable<ChainedException> {
+ private static final long serialVersionUID = 1L;
+
+ @Nonnull
+ private String message;
+
+ @Nonnegative
+ private int count = 1;
+
+ @Nonnull
+ private ChainedException tail = this;
+
+ @CheckForNull
+ private ChainedException next = null;
+
+ public ChainedException(@Nonnull String message) {
+ super("");
+ this.message = message;
+ }
+
+ public ChainedException(@Nonnull String message, @Nonnull Throwable cause) {
+ super("", cause);
+ this.message = message;
+ }
+
+ public ChainedException(@Nonnull Throwable cause) {
+ super(cause);
+ this.message = cause.getMessage();
+ }
+
+ @Override
+ @Nonnull
+ public String getMessage() {
+ return message;
+ }
+
+ @Override
+ @Nonnull
+ public String getLocalizedMessage() {
+ return message;
+ }
+
+ public void setMessage(@Nonnull String message) {
+ this.message = message;
+ }
+
+ @Nonnull
+ protected ChainedException putAsLastExceptionOf(
+ @CheckForNull ChainedException head) {
+ if (head == null) {
+ this.tail = this;
+ this.next = null;
+ this.count = 1;
+
+ return this;
+ } else {
+ head.tail.next = this;
+ head.tail = this;
+ head.count++;
+
+ return head;
+ }
+ }
+
+ @CheckForNull
+ public ChainedException getNextException() {
+ return next;
+ }
+
+ @Nonnegative
+ public int getNextExceptionCount() {
+ return count;
+ }
+
+ @Override
+ @Nonnull
+ public Iterator<ChainedException> iterator() {
+ ArrayList<ChainedException> list = new ArrayList<ChainedException>(count);
+
+ ChainedException exception = this;
+ do {
+ list.add(exception);
+ exception = exception.next;
+ } while (exception != null);
+
+ return list.iterator();
+ }
+
+ /**
+ * Builder to construct a chain of exceptions.
+ */
+ public static class ChainedExceptionBuilder<T extends ChainedException> {
+ @CheckForNull
+ private T head = null;
+
+ @SuppressWarnings("unchecked")
+ public void appendException(@Nonnull T exceptions) {
+ for (ChainedException exception : exceptions) {
+ head = (T) exception.putAsLastExceptionOf(head);
+ }
+ }
+
+ public void throwIfNecessary() throws T {
+ if (head != null) {
+ throw head;
+ }
+ }
+
+ @Nonnull
+ public T getException() {
+ assert head != null;
+ return head;
+ }
+ }
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/CompilationException.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/CompilationException.java
new file mode 100644
index 0000000..99423a8
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/CompilationException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api.v01;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A fatal problem that caused Jack to abort the compilation. The problem should already have
+ * reported, so it is safe to ignore its message.
+ */
+public class CompilationException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public CompilationException() {
+ super();
+ }
+
+ public CompilationException(@Nonnull String message) {
+ super(message);
+ }
+
+ public CompilationException(@Nonnull String message, @Nonnull Throwable cause) {
+ super(message, cause);
+ }
+
+ public CompilationException(@Nonnull Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/ConfigurationException.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/ConfigurationException.java
new file mode 100644
index 0000000..63b008f
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/ConfigurationException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api.v01;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Thrown when something is wrong in Jack's configuration.
+ */
+public class ConfigurationException extends ChainedException {
+ private static final long serialVersionUID = 1L;
+
+ public ConfigurationException(@Nonnull String message) {
+ super(message);
+ }
+
+ public ConfigurationException(@Nonnull String message, @Nonnull Throwable cause) {
+ super(message, cause);
+ }
+
+ public ConfigurationException(@Nonnull Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/DebugInfoLevel.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/DebugInfoLevel.java
new file mode 100644
index 0000000..01db098
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/DebugInfoLevel.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api.v01;
+
+/**
+ * Various levels of debug information.
+ */
+public enum DebugInfoLevel {
+ /**
+ * No debug info.
+ */
+ NONE,
+
+ /**
+ * Line and source file debug info.
+ */
+ LINES,
+
+ /**
+ * All debug info.
+ */
+ FULL
+}
\ No newline at end of file
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/JavaSourceVersion.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/JavaSourceVersion.java
new file mode 100644
index 0000000..c0f9dec
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/JavaSourceVersion.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api.v01;
+
+/**
+ * Supported Java source version.
+ */
+public enum JavaSourceVersion {
+ JAVA_3, JAVA_4, JAVA_5, JAVA_6, JAVA_7
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/MultiDexKind.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/MultiDexKind.java
new file mode 100644
index 0000000..59bcf70
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/MultiDexKind.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api.v01;
+
+/**
+ * The method to use to split in several dex files.
+ */
+public enum MultiDexKind {
+ /**
+ * Do not split.
+ */
+ NONE,
+
+ /**
+ * Multidex split targeting L-or-more platforms.
+ */
+ NATIVE,
+
+ /**
+ * Multidex split targeting K-or-less platforms.
+ */
+ LEGACY
+}
\ No newline at end of file
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/ReporterKind.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/ReporterKind.java
new file mode 100644
index 0000000..5b54464
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/ReporterKind.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api.v01;
+
+/**
+ * Available reporters.
+ */
+public enum ReporterKind {
+ DEFAULT, SDK
+}
\ No newline at end of file
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/ResourceCollisionPolicy.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/ResourceCollisionPolicy.java
new file mode 100644
index 0000000..ad68fd9
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/ResourceCollisionPolicy.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api.v01;
+
+/**
+ * How to handle resource collisions.
+ */
+public enum ResourceCollisionPolicy {
+ /**
+ * In case of collision between resources, keep the first resource that was encountered.
+ */
+ KEEP_FIRST,
+
+ /**
+ * In case of collision between resources, fail.
+ */
+ FAIL
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/TypeCollisionPolicy.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/TypeCollisionPolicy.java
new file mode 100644
index 0000000..8e19505
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/TypeCollisionPolicy.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api.v01;
+
+/**
+ * How to handle type collisions.
+ */
+public enum TypeCollisionPolicy {
+ /**
+ * In case of collision between types, keep the first type that was encountered.
+ */
+ KEEP_FIRST,
+
+ /**
+ * In case of collision between types, fail.
+ */
+ FAIL
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/UnrecoverableException.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/UnrecoverableException.java
new file mode 100644
index 0000000..631827f7
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/UnrecoverableException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api.v01;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Thrown when a major problem occurred because of an event out of control (e.g. an external process
+ * remove a temporary file used by Jack). Handling this error should only be reporting to the user
+ * and maybe just retry exactly the same thing as the one that has thrown.
+ */
+public class UnrecoverableException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public UnrecoverableException() {
+ super();
+ }
+
+ public UnrecoverableException(@Nonnull String message) {
+ super(message);
+ }
+
+ public UnrecoverableException(@Nonnull String message, @Nonnull Throwable cause) {
+ super(message, cause);
+ }
+
+ public UnrecoverableException(@Nonnull Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/jack/jack-api/src/main/java/com/android/jack/api/v01/VerbosityLevel.java b/jack/jack-api/src/main/java/com/android/jack/api/v01/VerbosityLevel.java
new file mode 100644
index 0000000..6a88745
--- /dev/null
+++ b/jack/jack-api/src/main/java/com/android/jack/api/v01/VerbosityLevel.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.api.v01;
+
+
+/**
+ * Jack verbosity level
+ */
+public enum VerbosityLevel {
+ ERROR,
+ WARNING,
+ INFO,
+ DEBUG
+}
\ No newline at end of file
diff --git a/jack/jill-api/README.txt b/jack/jill-api/README.txt
new file mode 100644
index 0000000..f8c1fcd
--- /dev/null
+++ b/jack/jill-api/README.txt
@@ -0,0 +1,5 @@
+Jill-API version 0.9
+
+This is a clone of the jill-api source code so that it can be
+build and uploaded to bintray/mavenCentral using the same release
+mechanism as the rest of the Gradle plugin libraries.
\ No newline at end of file
diff --git a/jack/jill-api/build.gradle b/jack/jill-api/build.gradle
new file mode 100644
index 0000000..5c0edf4
--- /dev/null
+++ b/jack/jill-api/build.gradle
@@ -0,0 +1,26 @@
+apply plugin: 'java'
+
+group = 'com.android.tools.jill'
+archivesBaseName = 'jill-api'
+version = '0.9.0'
+
+configurations {
+ provided
+}
+
+dependencies {
+ provided 'com.google.code.findbugs:jsr305:1.3.9'
+}
+
+compileJava.classpath += configurations.provided
+
+project.ext.pomName = 'Android Jill API'
+project.ext.pomDesc = 'API to dynamically load Jill'
+
+apply from: "$rootDir/buildSrc/base/publish.gradle"
+apply from: "$rootDir/buildSrc/base/bintray.gradle"
+apply from: "$rootDir/buildSrc/base/javadoc.gradle"
+
+javadoc {
+ classpath += configurations.provided
+}
diff --git a/jack/jill-api/jill-api.iml b/jack/jill-api/jill-api.iml
new file mode 100644
index 0000000..00040c6
--- /dev/null
+++ b/jack/jill-api/jill-api.iml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/.settings" />
+ <excludeFolder url="file://$MODULE_DIR$/build" />
+ </content>
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="library" exported="" scope="TEST" name="JUnit4" level="project" />
+ <orderEntry type="library" name="jsr-305" level="project" />
+ </component>
+</module>
\ No newline at end of file
diff --git a/jack/jill-api/src/main/java/com/android/jill/api/ConfigNotSupportedException.java b/jack/jill-api/src/main/java/com/android/jill/api/ConfigNotSupportedException.java
new file mode 100644
index 0000000..e4848f5
--- /dev/null
+++ b/jack/jill-api/src/main/java/com/android/jill/api/ConfigNotSupportedException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jill.api;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Thrown when the requested Jill configuration for a given API version is not supported.
+ */
+public class ConfigNotSupportedException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public ConfigNotSupportedException() {
+ super();
+ }
+
+ public ConfigNotSupportedException(@Nonnull String message) {
+ super(message);
+ }
+
+ public ConfigNotSupportedException(@Nonnull String message, @Nonnull Throwable cause) {
+ super(message, cause);
+ }
+
+ public ConfigNotSupportedException(@Nonnull Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/jack/jill-api/src/main/java/com/android/jill/api/JillConfig.java b/jack/jill-api/src/main/java/com/android/jill/api/JillConfig.java
new file mode 100644
index 0000000..cde0c35
--- /dev/null
+++ b/jack/jill-api/src/main/java/com/android/jill/api/JillConfig.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jill.api;
+
+/**
+ * Allows to set a configuration for Jill.
+ */
+public interface JillConfig {
+}
diff --git a/jack/jill-api/src/main/java/com/android/jill/api/JillProvider.java b/jack/jill-api/src/main/java/com/android/jill/api/JillProvider.java
new file mode 100644
index 0000000..30fd4e3
--- /dev/null
+++ b/jack/jill-api/src/main/java/com/android/jill/api/JillProvider.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jill.api;
+
+import java.util.Collection;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * Provides instances of {@link JillConfig}.
+ */
+public interface JillProvider {
+ /**
+ * Creates a {@link JillConfig} instance for an interface representing a {@link JillConfig} API
+ * version.
+ * @param cls the {@link JillConfig} API interface
+ * @return the {@link JillConfig} instance
+ * @throws ConfigNotSupportedException If no implementation is found for the given interface.
+ */
+ @Nonnull
+ <T extends JillConfig> T createConfig(@Nonnull Class<T> cls) throws ConfigNotSupportedException;
+
+ /**
+ * Returns whether an interface representing a {@link JillConfig} API version is supported.
+ *
+ * @param cls the {@link JillConfig} API interface
+ * @return <code>true</true> if the config is supported
+ */
+ @Nonnull
+ <T extends JillConfig> boolean isConfigSupported(@Nonnull Class<T> cls);
+
+ /**
+ * Gives a {@link Collection} containing supported {@link JillConfig} API versions.
+ * @return the supported {@link JillConfig} API versions
+ */
+ @Nonnull
+ Collection<Class<? extends JillConfig>> getSupportedConfigs();
+
+ /**
+ * Gives the version of this Jill, summarized in one string (e.g. "1.1-rc1", "2.0-a2",
+ * ...).
+ *
+ * @return the version
+ */
+ @Nonnull
+ String getTranslatorVersion();
+
+ /**
+ * Gives the release name of this Jill (e.g. Arzon, Brest, ...).
+ *
+ * @return the release name
+ */
+ @Nonnull
+ String getTranslatorReleaseName();
+
+ /**
+ * Gives an integer value that represents the release of this Jill, relative to other
+ * releases.
+ *
+ * @return the release code
+ */
+ @Nonnegative
+ int getTranslatorReleaseCode();
+
+ /**
+ * Gives an integer value that represents the sub-release of this Jill, relative to other
+ * sub-releases of the same release.
+ *
+ * @return the sub-release code
+ */
+ @Nonnegative
+ int getTranslatorSubReleaseCode();
+
+ /**
+ * Gives the kind of sub-release of this Jill.
+ *
+ * @return the sub-release kind
+ */
+ @Nonnull
+ SubReleaseKind getTranslatorSubReleaseKind();
+
+ /**
+ * The kind of sub-release.
+ */
+ public enum SubReleaseKind {
+ /**
+ * A sub-release from an engineering development, not tested, not in the code base repository.
+ */
+ ENGINEERING,
+ /**
+ * A sub-release that is not feature complete, not tested.
+ */
+ PRE_ALPHA,
+ /**
+ * A sub-release that is not feature complete, tested.
+ */
+ ALPHA,
+ /**
+ * A sub-release that is feature complete, tested, but likely contains known or unknown bugs.
+ */
+ BETA,
+ /**
+ * A pre-production sub-release, tested.
+ */
+ CANDIDATE,
+ /**
+ * A production and stable sub-release.
+ */
+ RELEASE;
+ }
+
+ /**
+ * The build ID of this Jill.
+ * @return the build ID, or null if not available
+ */
+ @CheckForNull
+ String getTranslatorBuildId();
+
+ /**
+ * Identify the source code base of this Jill.
+ * @return the source code base, or null if not available
+ */
+ @CheckForNull
+ String getTranslatorSourceCodeBase();
+}
diff --git a/jack/jill-api/src/main/java/com/android/jill/api/v01/Api01Config.java b/jack/jill-api/src/main/java/com/android/jill/api/v01/Api01Config.java
new file mode 100644
index 0000000..b1923bd
--- /dev/null
+++ b/jack/jill-api/src/main/java/com/android/jill/api/v01/Api01Config.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jill.api.v01;
+
+
+import com.android.jill.api.JillConfig;
+
+import java.io.File;
+
+import javax.annotation.Nonnull;
+
+/**
+* A configuration implementation for API level 01 of Jill.
+*/
+public interface Api01Config extends JillConfig {
+
+ /**
+ * Sets verbosity mode.
+ * @param isVerbose the desired verbosity mode
+ */
+ void setVerbose(boolean isVerbose) throws ConfigurationException;
+
+ /**
+ * Sets jar file to apply the Jill translation onto. The file must exist.
+ * @param input jar file to translate
+ */
+ void setInputJavaBinaryFile(@Nonnull File input) throws ConfigurationException;
+
+ /**
+ * Sets the file where the output Jack library will be written. The file may already exist and
+ * will be overwritten.
+ * @param outputJackFile The output Jack library file
+ * @throws ConfigurationException
+ */
+ void setOutputJackFile(@Nonnull File outputJackFile) throws ConfigurationException;
+
+
+ /**
+ * Sets whether debug info should be emitted.
+ * @param debugInfo the desired mode for debug info emission
+ */
+ void setDebugInfo(boolean debugInfo) throws ConfigurationException;
+
+ /**
+ * Creates an instance of the {@link Api01TranslationTask} according to this configuration.
+ * @return The {@link Api01TranslationTask}
+ */
+ @Nonnull
+ Api01TranslationTask getTask() throws ConfigurationException;
+}
diff --git a/jack/jill-api/src/main/java/com/android/jill/api/v01/Api01TranslationTask.java b/jack/jill-api/src/main/java/com/android/jill/api/v01/Api01TranslationTask.java
new file mode 100644
index 0000000..25f70bf
--- /dev/null
+++ b/jack/jill-api/src/main/java/com/android/jill/api/v01/Api01TranslationTask.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jill.api.v01;
+
+
+/**
+ * A task allowing to run Jill once.
+ */
+public interface Api01TranslationTask {
+
+ /**
+ * Runs the translation task. May be called only once.
+ * @throws TranslationException If a fatal error occurred during the translation
+ * @throws IllegalStateException If the translation task is run more than once
+ */
+ void run() throws TranslationException, IllegalStateException;
+}
diff --git a/jack/jill-api/src/main/java/com/android/jill/api/v01/ConfigurationException.java b/jack/jill-api/src/main/java/com/android/jill/api/v01/ConfigurationException.java
new file mode 100644
index 0000000..76f0d87
--- /dev/null
+++ b/jack/jill-api/src/main/java/com/android/jill/api/v01/ConfigurationException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jill.api.v01;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Thrown when something is wrong in Jill configuration.
+ */
+public class ConfigurationException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public ConfigurationException(@Nonnull String message) {
+ super(message);
+ }
+
+ public ConfigurationException(@Nonnull String message, @Nonnull Throwable cause) {
+ super(message, cause);
+ }
+
+ public ConfigurationException(@Nonnull Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/jack/jill-api/src/main/java/com/android/jill/api/v01/TranslationException.java b/jack/jill-api/src/main/java/com/android/jill/api/v01/TranslationException.java
new file mode 100644
index 0000000..41121b3
--- /dev/null
+++ b/jack/jill-api/src/main/java/com/android/jill/api/v01/TranslationException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jill.api.v01;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A fatal problem that caused Jill to abort the translation. The problem should already have
+ * reported, so it is safe to ignore its message.
+ */
+public class TranslationException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public TranslationException() {
+ super();
+ }
+
+ public TranslationException(@Nonnull String message) {
+ super(message);
+ }
+
+ public TranslationException(@Nonnull String message, @Nonnull Throwable cause) {
+ super(message, cause);
+ }
+
+ public TranslationException(@Nonnull Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/layoutlib-api/build.gradle b/layoutlib-api/build.gradle
index 97c76ec..2ac4682 100644
--- a/layoutlib-api/build.gradle
+++ b/layoutlib-api/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
+apply plugin: 'jacoco'
apply plugin: 'sdk-java-lib'
group = 'com.android.tools.layoutlib'
diff --git a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/Bridge.java b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/Bridge.java
index 16af248..e356ea6 100644
--- a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/Bridge.java
+++ b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/Bridge.java
@@ -23,9 +23,7 @@
import java.awt.image.BufferedImage;
import java.io.File;
-import java.util.Collections;
import java.util.EnumSet;
-import java.util.List;
import java.util.Map;
/**
diff --git a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/HardwareConfig.java b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/HardwareConfig.java
index 89f1424..2e1dfd6 100644
--- a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/HardwareConfig.java
+++ b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/HardwareConfig.java
@@ -16,8 +16,10 @@
package com.android.ide.common.rendering.api;
+import com.android.annotations.Nullable;
import com.android.resources.Density;
import com.android.resources.ScreenOrientation;
+import com.android.resources.ScreenRound;
import com.android.resources.ScreenSize;
/**
@@ -35,6 +37,7 @@
private final float mYdpi;
private final ScreenOrientation mOrientation;
private final ScreenSize mScreenSize;
+ private final ScreenRound mScreenRound;
private final boolean mSoftwareButtons;
@@ -46,6 +49,7 @@
float ydpi,
ScreenSize screenSize,
ScreenOrientation orientation,
+ ScreenRound screenRoundness,
boolean softwareButtons) {
mScreenWidth = screenWidth;
mScreenHeight = screenHeight;
@@ -54,6 +58,7 @@
mYdpi = ydpi;
mScreenSize = screenSize;
mOrientation = orientation;
+ mScreenRound = screenRoundness;
mSoftwareButtons = softwareButtons;
}
@@ -88,4 +93,20 @@
public boolean hasSoftwareButtons() {
return mSoftwareButtons;
}
+
+ /**
+ * @since 15
+ */
+ @Nullable
+ public ScreenRound getScreenRoundness() {
+ return mScreenRound;
+ }
+
+ /**
+ * @since 15
+ */
+ @SuppressWarnings({"MethodMayBeStatic", "unused"})
+ public int getDensityDpi() {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/IProjectCallback.java b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/IProjectCallback.java
index ab6d0f2..24f11e0 100644
--- a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/IProjectCallback.java
+++ b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/IProjectCallback.java
@@ -57,10 +57,12 @@
* to use a different constructor or replace it with a MockView.
* <p/>
* This is done so that LayoutLib can continue to work on older versions of the IDE.
+ * Newer versions of LayoutLib should call {@link
+ * LayoutlibCallback#loadClass(String, Class[], Object[])} in such a case.
*
- * @param name The fully qualified name of the class.
+ * @param name The fully qualified name of the class.
* @param constructorSignature The signature of the class to use
- * @param constructorArgs The arguments to use on the constructor
+ * @param constructorArgs The arguments to use on the constructor
* @return A newly instantiated object.
*/
Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
diff --git a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/ItemResourceValue.java b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/ItemResourceValue.java
index f48cbce..41e6651 100644
--- a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/ItemResourceValue.java
+++ b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/ItemResourceValue.java
@@ -25,22 +25,32 @@
/**
* @see #ItemResourceValue(String, boolean, String, boolean)
*/
- public ItemResourceValue(String name, boolean isFrameworkAttr, boolean isFramework) {
- this(name, isFrameworkAttr, null, isFramework);
+ public ItemResourceValue(String name, boolean isFrameworkAttr, boolean isFrameworkStyle) {
+ this(name, isFrameworkAttr, null, isFrameworkStyle);
}
/**
+ * If the value is a reference to a framework resource or not is NOT represented with a boolean!
+ * but can be deduced with:
+ * <pre> {@code
+ * boolean isFrameworkValue = item.isFramework() ||
+ * item.getValue().startsWith(SdkConstants.ANDROID_PREFIX) ||
+ * item.getValue().startsWith(SdkConstants.ANDROID_THEME_PREFIX);
+ * } </pre>
* For {@code <item name="foo">bar</item>}, item in a style resource, the values of the
* parameters will be as follows:
*
- * @param attributeName foo
+ * @param attributeName foo
* @param isFrameworkAttr is foo in framework namespace.
- * @param value bar
- * @param isFramework if the style is a framework file or project file.
+ * @param value bar (in case of a reference, the value may include the namespace.
+ * if the namespace is absent, default namespace is assumed based on
+ * isFrameworkStyle (android namespace when isFrameworkStyle=true and app
+ * namespace when isFrameworkStyle=false))
+ * @param isFrameworkStyle if the style is a framework file or project file.
*/
public ItemResourceValue(String attributeName, boolean isFrameworkAttr, String value,
- boolean isFramework) {
- super(null, attributeName, value, isFramework);
+ boolean isFrameworkStyle) {
+ super(null, attributeName, value, isFrameworkStyle);
mIsFrameworkAttr = isFrameworkAttr;
}
diff --git a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutLog.java b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutLog.java
index 4d4d4cb..7e45604 100644
--- a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutLog.java
+++ b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutLog.java
@@ -129,6 +129,17 @@
public static final String TAG_MISSING_ASSET = "missingasset";
/**
+ * Fidelity Tag used when something went wrong in rendering text.
+ */
+ public static final String TAG_TEXT_RENDERING = "textRendering";
+
+ /**
+ * Tag for reporting messages that should not be logged in the render error panel,
+ * but silently logged to a more detailed log file.
+ */
+ public static final String TAG_INFO = "info";
+
+ /**
* Logs a warning.
* @param tag a tag describing the type of the warning
* @param message the message of the warning
diff --git a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutlibCallback.java b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutlibCallback.java
index 04d7f63..3829d82 100644
--- a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutlibCallback.java
+++ b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/LayoutlibCallback.java
@@ -32,7 +32,7 @@
* Clients should use this instead of {@link IProjectCallback} to target both old and new
* Layout Libraries.
*/
-@SuppressWarnings("deprecation")
+@SuppressWarnings({"deprecation", "MethodMayBeStatic", "unused"})
public abstract class LayoutlibCallback implements IProjectCallback,
com.android.layoutlib.api.IProjectCallback {
@@ -78,6 +78,28 @@
return null;
}
+ /**
+ * Get a ParserFactory which can be used to create XmlPullParsers.
+ * @since API 15
+ */
+ @NonNull
+ public ParserFactory getParserFactory() {
+ throw new UnsupportedOperationException("getParserFactory not supported.");
+ }
+
+ /**
+ * Find a custom class in the project.
+ * <p/>
+ * Like {@link #loadClass(String, Class[], Object[])}, but doesn't instantiate
+ * an object and just returns the class found.
+ * @param name className in binary format. (see {@link ClassLoader}.
+ * @since API 15
+ */
+ @NonNull
+ public Class<?> findClass(@NonNull String name) throws ClassNotFoundException {
+ throw new ClassNotFoundException(name + " not found.");
+ }
+
// ------ implementation of the old interface using the new interface.
@Override
diff --git a/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/ParserFactory.java b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/ParserFactory.java
new file mode 100644
index 0000000..7948c88
--- /dev/null
+++ b/layoutlib-api/src/main/java/com/android/ide/common/rendering/api/ParserFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.common.rendering.api;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Callback used by LayoutLib to create {@link XmlPullParser}s.
+ */
+public abstract class ParserFactory {
+ /**
+ * Creates a new XmlPullParser with an optional display name.
+ *
+ * @param debugName an optional name to aid with debugging.
+ * @since API 15
+ */
+ @NonNull
+ public XmlPullParser createParser(@Nullable String debugName) throws XmlPullParserException {
+ throw new UnsupportedOperationException("createNewParser not supported.");
+ }
+}
diff --git a/layoutlib-api/src/main/java/com/android/resources/Density.java b/layoutlib-api/src/main/java/com/android/resources/Density.java
index 4f56909..15c81f5 100644
--- a/layoutlib-api/src/main/java/com/android/resources/Density.java
+++ b/layoutlib-api/src/main/java/com/android/resources/Density.java
@@ -27,6 +27,7 @@
DPI_560("560dpi", "560 DPI Density", 560, 1), //$NON-NLS-1$
XXHIGH( "xxhdpi", "XX-High Density", 480, 16), //$NON-NLS-1$
DPI_400("400dpi", "400 DPI Density", 400, 1), //$NON-NLS-1$
+ DPI_360("360dpi", "360 DPI Density", 360, 23), //$NON-NLS-1$
XHIGH( "xhdpi", "X-High Density", 320, 8), //$NON-NLS-1$
DPI_280("280dpi", "280 DPI Density", 280, 22), //$NON-NLS-1$
HIGH( "hdpi", "High Density", 240, 4), //$NON-NLS-1$
@@ -140,6 +141,7 @@
switch (this) {
case TV:
case DPI_280:
+ case DPI_360:
case DPI_400:
case DPI_560:
return false;
diff --git a/layoutlib-api/src/main/java/com/android/resources/ScreenRound.java b/layoutlib-api/src/main/java/com/android/resources/ScreenRound.java
new file mode 100644
index 0000000..5c7935d
--- /dev/null
+++ b/layoutlib-api/src/main/java/com/android/resources/ScreenRound.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.resources;
+
+/**
+ * Screen Round enum.
+ * <p/>This is used in the resource folder names.
+ */
+public enum ScreenRound implements ResourceEnum {
+ NOTROUND("notround", "Not Round", "Not Round screen"), //$NON-NLS-1$
+ ROUND( "round", "Round", "Round screen"); //$NON-NLS-1$
+
+ private final String mValue;
+ private final String mShortDisplayValue;
+ private final String mLongDisplayValue;
+
+ ScreenRound(String value, String displayValue, String longDisplayValue) {
+ mValue = value;
+ mShortDisplayValue = displayValue;
+ mLongDisplayValue = longDisplayValue;
+ }
+
+ /**
+ * Returns the enum for matching the provided qualifier value.
+ * @param value The qualifier value.
+ * @return the enum for the qualifier value or null if no matching was found.
+ */
+ public static ScreenRound getEnum(String value) {
+ for (ScreenRound orient : values()) {
+ if (orient.mValue.equals(value)) {
+ return orient;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public String getResourceValue() {
+ return mValue;
+ }
+
+ @Override
+ public String getShortDisplayValue() {
+ return mShortDisplayValue;
+ }
+
+ @Override
+ public String getLongDisplayValue() {
+ return mLongDisplayValue;
+ }
+
+ public static int getIndex(ScreenRound orientation) {
+ int i = 0;
+ for (ScreenRound orient : values()) {
+ if (orient == orientation) {
+ return i;
+ }
+
+ i++;
+ }
+
+ return -1;
+ }
+
+ public static ScreenRound getByIndex(int index) {
+ ScreenRound[] values = values();
+ if (index >= 0 && index < values.length) {
+ return values[index];
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isFakeValue() {
+ return false;
+ }
+
+ @Override
+ public boolean isValidValueForDevice() {
+ return true;
+ }
+
+}
+
diff --git a/layoutlib-api/src/test/java/com/android/resources/FolderTypeRelationShipTest.java b/layoutlib-api/src/test/java/com/android/resources/FolderTypeRelationShipTest.java
index 809eae7..5d09b36 100644
--- a/layoutlib-api/src/test/java/com/android/resources/FolderTypeRelationShipTest.java
+++ b/layoutlib-api/src/test/java/com/android/resources/FolderTypeRelationShipTest.java
@@ -16,10 +16,6 @@
package com.android.resources;
-import com.android.resources.FolderTypeRelationship;
-import com.android.resources.ResourceFolderType;
-import com.android.resources.ResourceType;
-
import junit.framework.TestCase;
public class FolderTypeRelationShipTest extends TestCase {
@@ -29,7 +25,7 @@
// loop on all the enum, and make sure there's at least one folder type for it.
for (ResourceType type : ResourceType.values()) {
assertTrue(type.getDisplayName(),
- FolderTypeRelationship.getRelatedFolders(type).size() > 0);
+ !FolderTypeRelationship.getRelatedFolders(type).isEmpty());
}
}
@@ -38,7 +34,7 @@
// loop on all the enum, and make sure there's at least one res type for it.
for (ResourceFolderType type : ResourceFolderType.values()) {
assertTrue(type.getName(),
- FolderTypeRelationship.getRelatedResourceTypes(type).size() > 0);
+ !FolderTypeRelationship.getRelatedResourceTypes(type).isEmpty());
}
}
}
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/AaptExecTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/AaptExecTask.java
index 6690df8..ea14dac 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/AaptExecTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/AaptExecTask.java
@@ -77,7 +77,7 @@
* If the extension is null, this will disable compression for all files in assets/ and
* res/raw/
*/
- public final static class NoCompress {
+ public static final class NoCompress {
String mExtension;
/**
@@ -149,7 +149,7 @@
}
}
- private final static InputPathFactory sPathFactory = new InputPathFactory() {
+ private static final InputPathFactory sPathFactory = new InputPathFactory() {
@Override
public InputPath createPath(File file, Set<String> extensionsToCheck) {
@@ -206,7 +206,7 @@
}
public void setVersioncode(String versionCode) {
- if (versionCode.length() > 0) {
+ if (!versionCode.isEmpty()) {
try {
mVersionCode = Integer.decode(versionCode);
} catch (NumberFormatException e) {
@@ -245,7 +245,7 @@
* @param packageName The package ID the APK should have.
*/
public void setManifestpackage(String packageName) {
- if (packageName != null && packageName.length() != 0) {
+ if (packageName != null && !packageName.isEmpty()) {
mManifestPackage = packageName;
}
}
@@ -332,7 +332,7 @@
}
public void setresourcefilter(String filter) {
- if (filter != null && filter.length() > 0) {
+ if (filter != null && !filter.isEmpty()) {
mResourceFilter = filter;
}
}
@@ -559,7 +559,7 @@
}
// filters if needed
- if (mResourceFilter != null && mResourceFilter.length() > 0) {
+ if (mResourceFilter != null && !mResourceFilter.isEmpty()) {
task.createArg().setValue("-c");
task.createArg().setValue(mResourceFilter);
}
@@ -588,7 +588,7 @@
}
// if this is a library or there are library dependencies
- if (mNonConstantId || (libPkgProp != null && libPkgProp.length() > 0)) {
+ if (mNonConstantId || (libPkgProp != null && !libPkgProp.isEmpty())) {
if (mBinFolder == null) {
throw new BuildException(
"Missing attribute binFolder when compiling libraries or projects with libraries.");
@@ -607,13 +607,13 @@
task.createArg().setValue(Integer.toString(mVersionCode));
}
- if (mVersionName != null && mVersionName.length() > 0) {
+ if (mVersionName != null && !mVersionName.isEmpty()) {
task.createArg().setValue("--version-name");
task.createArg().setValue(mVersionName);
}
// manifest location
- if (mManifestFile != null && mManifestFile.length() > 0) {
+ if (mManifestFile != null && !mManifestFile.isEmpty()) {
task.createArg().setValue("-M");
task.createArg().setValue(mManifestFile);
}
@@ -625,7 +625,7 @@
}
// resources locations.
- if (mResources.size() > 0) {
+ if (!mResources.isEmpty()) {
for (Path pathList : mResources) {
for (String path : pathList.list()) {
// This may not exists, and aapt doesn't like it, so we check first.
@@ -681,7 +681,7 @@
}
// ignore assets flag
- if (mIgnoreAssets != null && mIgnoreAssets.length() > 0) {
+ if (mIgnoreAssets != null && !mIgnoreAssets.isEmpty()) {
task.createArg().setValue("--ignore-assets");
task.createArg().setValue(mIgnoreAssets);
}
@@ -690,7 +690,7 @@
task.createArg().setValue("--generate-dependencies");
// use the proguard file
- if (mProguardFile != null && mProguardFile.length() > 0) {
+ if (mProguardFile != null && !mProguardFile.isEmpty()) {
task.createArg().setValue("-G");
task.createArg().setValue(mProguardFile);
}
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/ApkBuilderTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/ApkBuilderTask.java
index 8997ad1..f5d8e6b 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/ApkBuilderTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/ApkBuilderTask.java
@@ -33,7 +33,7 @@
public class ApkBuilderTask extends SingleDependencyTask {
- private final static Pattern PATTERN_JAR_EXT = Pattern.compile("^.+\\.jar$",
+ private static final Pattern PATTERN_JAR_EXT = Pattern.compile("^.+\\.jar$",
Pattern.CASE_INSENSITIVE);
private String mOutFolder;
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/CheckEnvTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/CheckEnvTask.java
index 98312f9..3d40af1 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/CheckEnvTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/CheckEnvTask.java
@@ -34,7 +34,7 @@
*/
public class CheckEnvTask extends Task {
- private final static String ANT_MIN_VERSION = "1.8.0";
+ private static final String ANT_MIN_VERSION = "1.8.0";
@Override
public void execute() {
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/DependencyHelper.java b/legacy/ant-tasks/src/main/java/com/android/ant/DependencyHelper.java
index e3952c4..51a9cf7 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/DependencyHelper.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/DependencyHelper.java
@@ -58,7 +58,7 @@
* Advanced version of the {@link LibraryProcessor} that provides the library properties
* to the processor.
*/
- public static abstract class AdvancedLibraryProcessor implements LibraryProcessor {
+ public abstract static class AdvancedLibraryProcessor implements LibraryProcessor {
public abstract void processLibrary(String libRootPath, IPropertySource properties);
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/GetBuildToolsTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/GetBuildToolsTask.java
index 4e89c9e..2d2ccfe 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/GetBuildToolsTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/GetBuildToolsTask.java
@@ -83,5 +83,9 @@
}
antProject.setProperty(mName, buildToolInfo.getLocation().getAbsolutePath());
+ antProject.setProperty("aidl", buildToolInfo.getPath(BuildToolInfo.PathId.AIDL));
+ antProject.setProperty("aapt", buildToolInfo.getPath(BuildToolInfo.PathId.AAPT));
+ antProject.setProperty("zipalign", buildToolInfo.getPath(BuildToolInfo.PathId.ZIP_ALIGN));
+ antProject.setProperty("dx", buildToolInfo.getPath(BuildToolInfo.PathId.DX));
}
}
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/GetEmmaFilterTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/GetEmmaFilterTask.java
index f449f8d..10c6d9c 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/GetEmmaFilterTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/GetEmmaFilterTask.java
@@ -68,12 +68,12 @@
String libraryPackagesValue = getProject().getProperty(mLibraryPackagesRefId);
- if (libraryPackagesValue != null && libraryPackagesValue.length() > 0) {
+ if (libraryPackagesValue != null && !libraryPackagesValue.isEmpty()) {
// split the app packages.
String[] libPackages = libraryPackagesValue.split(";");
for (String libPackage : libPackages) {
- if (libPackage.length() > 0) {
+ if (!libPackage.isEmpty()) {
for (String filterClass : FILTER_CLASSES) {
sb.append(libPackage).append('.').append(filterClass).append(',');
}
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/GetLibraryPathTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/GetLibraryPathTask.java
index 813574e..d80e381 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/GetLibraryPathTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/GetLibraryPathTask.java
@@ -46,7 +46,7 @@
private boolean mVerbose = false;
private static class LeafProcessor extends AdvancedLibraryProcessor {
- private final static Pattern PH = Pattern.compile("^\\@\\{(.*)\\}$");
+ private static final Pattern PH = Pattern.compile("^\\@\\{(.*)\\}$");
private Path mPath;
private final String[] mLeafSegments;
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/GetTargetTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/GetTargetTask.java
index 1dc33b3..3a6df9a 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/GetTargetTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/GetTargetTask.java
@@ -21,7 +21,7 @@
import com.android.annotations.Nullable;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
+import com.android.sdklib.IAndroidTarget.OptionalLibrary;
import com.android.sdklib.SdkManager;
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.utils.ILogger;
@@ -40,6 +40,7 @@
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
@@ -194,17 +195,15 @@
element.setPath(androidJar);
// create PathElement for each optional library.
- IOptionalLibrary[] libraries = androidTarget.getOptionalLibraries();
- if (libraries != null) {
- HashSet<String> visitedJars = new HashSet<String>();
- for (IOptionalLibrary library : libraries) {
- String jarPath = library.getJarPath();
- if (visitedJars.contains(jarPath) == false) {
- visitedJars.add(jarPath);
+ List<OptionalLibrary> libraries = androidTarget.getAdditionalLibraries();
+ HashSet<File> visitedJars = new HashSet<File>();
+ for (OptionalLibrary library : libraries) {
+ File jarFile = library.getJar();
+ if (!visitedJars.contains(jarFile)) {
+ visitedJars.add(jarFile);
- element = bootclasspath.createPathElement();
- element.setPath(jarPath);
- }
+ element = bootclasspath.createPathElement();
+ element.setPath(jarFile.getAbsolutePath());
}
}
@@ -260,7 +259,7 @@
antProject.setProperty(mMinSdkVersionOut,
Integer.toString(androidVersion.getApiLevel()));
- } else if (value.length() > 0) {
+ } else if (!value.isEmpty()) {
// for normal platform, we'll only display warnings if the value is lower or higher
// than the target api level.
// First convert to an int.
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/GetTypeTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/GetTypeTask.java
index 143bc35..e7b6f53 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/GetTypeTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/GetTypeTask.java
@@ -89,7 +89,7 @@
":" + AndroidManifest.ATTRIBUTE_TARGET_PACKAGE,
new InputSource(new FileInputStream(manifest)));
- if (value != null && value.length() > 0) {
+ if (value != null && !value.isEmpty()) {
System.out.println("Project Type: Self-Tested Application");
antProject.setProperty(mProjectTypeOut, "test-app");
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/GetUiTargetTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/GetUiTargetTask.java
index 283a4b2..762c827 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/GetUiTargetTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/GetUiTargetTask.java
@@ -19,7 +19,7 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
+import com.android.sdklib.IAndroidTarget.OptionalLibrary;
import com.android.sdklib.SdkManager;
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.utils.ILogger;
@@ -33,6 +33,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
/**
* Task to resolve the target of the current Android uiautomator project.
@@ -114,7 +115,7 @@
// display the project info
System.out.println( "Project Target: " + androidTarget.getName());
- if (androidTarget.isPlatform() == false) {
+ if (!androidTarget.isPlatform()) {
System.out.println("Vendor: " + androidTarget.getVendor());
System.out.println("Platform Version: " + androidTarget.getVersionName());
}
@@ -141,17 +142,15 @@
element.setPath(uiAutomatorJar);
// create PathElement for each optional library.
- IOptionalLibrary[] libraries = androidTarget.getOptionalLibraries();
- if (libraries != null) {
- HashSet<String> visitedJars = new HashSet<String>();
- for (IOptionalLibrary library : libraries) {
- String jarPath = library.getJarPath();
- if (visitedJars.contains(jarPath) == false) {
- visitedJars.add(jarPath);
+ List<OptionalLibrary> libraries = androidTarget.getAdditionalLibraries();
+ HashSet<File> visitedJars = new HashSet<File>();
+ for (OptionalLibrary library : libraries) {
+ File jarPath = library.getJar();
+ if (!visitedJars.contains(jarPath)) {
+ visitedJars.add(jarPath);
- element = compileclasspath.createPathElement();
- element.setPath(jarPath);
- }
+ element = compileclasspath.createPathElement();
+ element.setPath(jarPath.getAbsolutePath());
}
}
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/RenderScriptTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/RenderScriptTask.java
index c7d8853..60b2e50 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/RenderScriptTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/RenderScriptTask.java
@@ -73,7 +73,7 @@
private int mTargetApi = 0;
private boolean mSupportMode = false;
- public enum OptLevel { O0, O1, O2, O3 };
+ public enum OptLevel { O0, O1, O2, O3 }
private OptLevel mOptLevel;
private boolean mDebug = false;
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/SignApkTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/SignApkTask.java
index 6c29d68..60cdff0 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/SignApkTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/SignApkTask.java
@@ -137,7 +137,7 @@
}
}
- private final static class NullZipFilter implements IZipEntryFilter {
+ private static final class NullZipFilter implements IZipEntryFilter {
@Override
public boolean checkEntry(String archivePath) throws ZipAbortException {
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/SingleDependencyTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/SingleDependencyTask.java
index e0dc178..08f86bf 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/SingleDependencyTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/SingleDependencyTask.java
@@ -38,7 +38,7 @@
InputPath createPath(File file, Set<String> extensionsToCheck);
}
- private final static InputPathFactory sDefaultFactory = new InputPathFactory() {
+ private static final InputPathFactory sDefaultFactory = new InputPathFactory() {
@Override
public InputPath createPath(File file, Set<String> extensionsToCheck) {
return new InputPath(file, extensionsToCheck);
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/TaskHelper.java b/legacy/ant-tasks/src/main/java/com/android/ant/TaskHelper.java
index c93b193..426ec25 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/TaskHelper.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/TaskHelper.java
@@ -55,7 +55,7 @@
String sdkOsPath = antProject.getProperty(ProjectProperties.PROPERTY_SDK);
// check if it's valid and exists
- if (sdkOsPath == null || sdkOsPath.length() == 0) {
+ if (sdkOsPath == null || sdkOsPath.isEmpty()) {
throw new BuildException("SDK Location is not set.");
}
diff --git a/legacy/ant-tasks/src/main/java/com/android/ant/XPathTask.java b/legacy/ant-tasks/src/main/java/com/android/ant/XPathTask.java
index b6de469..c3f3242 100644
--- a/legacy/ant-tasks/src/main/java/com/android/ant/XPathTask.java
+++ b/legacy/ant-tasks/src/main/java/com/android/ant/XPathTask.java
@@ -76,7 +76,7 @@
String file = mManifestFile.list()[0];
String result = xpath.evaluate(mExpression, new InputSource(new FileInputStream(file)));
- if (result.length() == 0 && mDefault != null) {
+ if (result.isEmpty() && mDefault != null) {
result = mDefault;
}
diff --git a/lint/cli/.classpath b/lint/cli/.classpath
index b1e76db..f9b6945 100644
--- a/lint/cli/.classpath
+++ b/lint/cli/.classpath
@@ -3,6 +3,6 @@
<classpathentry kind="src" path="src/main/java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" exported="true" kind="src" path="/lint-checks"/>
- <classpathentry exported="true" kind="var" path="ANDROID_SRC/prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4/ecj-4.4.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4/ecj-4.4-sources.jar"/>
+ <classpathentry exported="true" kind="var" path="ANDROID_SRC/prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4.2/ecj-4.4.2.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/m2/repository/org/eclipse/jdt/core/compiler/ecj/4.4.2/ecj-4.4.2-sources.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/lint/cli/build.gradle b/lint/cli/build.gradle
index 9399b0c..7d7fe85 100644
--- a/lint/cli/build.gradle
+++ b/lint/cli/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
+apply plugin: 'jacoco'
apply plugin: 'sdk-java-lib'
group = 'com.android.tools.lint'
@@ -16,11 +8,7 @@
dependencies {
compile project(':base:lint-checks')
- compile 'org.eclipse.jdt.core.compiler:ecj:4.4'
-
- testCompile project(':base:lint-tests')
- testCompile 'org.mockito:mockito-all:1.9.5'
- testCompile 'org.codehaus.groovy:groovy-all:2.2.1'
+ compile 'org.eclipse.jdt.core.compiler:ecj:4.4.2'
}
sourceSets {
diff --git a/lint/cli/etc/lint b/lint/cli/etc/lint
index 4a09eb9..3d2f9f4 100755
--- a/lint/cli/etc/lint
+++ b/lint/cli/etc/lint
@@ -66,7 +66,7 @@
jarpath="$frameworkdir/$jarfile"
exec "$javaCmd" \
- -Xmx512m $os_opts $java_debug \
+ -Xmx1024m $os_opts $java_debug \
-Dcom.android.tools.lint.bindir="$progdir" \
-Djava.awt.headless=true \
-classpath "$jarpath" \
diff --git a/lint/cli/etc/lint.bat b/lint/cli/etc/lint.bat
index ff0bb52..31dd760 100755
--- a/lint/cli/etc/lint.bat
+++ b/lint/cli/etc/lint.bat
@@ -54,5 +54,5 @@
set jarpath=%frameworkdir%\%jarfile%
set javaextdirs=%frameworkdir%
-call "%java_exe%" %java_debug% -Xmx512m "-Dcom.android.tools.lint.bindir=%prog_dir%" "-Dcom.android.tools.lint.workdir=%work_dir%" -Djava.awt.headless=true -classpath "%jarpath%" com.android.tools.lint.Main %*
+call "%java_exe%" %java_debug% -Xmx1024m "-Dcom.android.tools.lint.bindir=%prog_dir%" "-Dcom.android.tools.lint.workdir=%work_dir%" -Djava.awt.headless=true -classpath "%jarpath%" com.android.tools.lint.Main %*
diff --git a/lint/cli/src/main/java/com/android/tools/lint/EcjParser.java b/lint/cli/src/main/java/com/android/tools/lint/EcjParser.java
index 9103597..090e3f1 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/EcjParser.java
+++ b/lint/cli/src/main/java/com/android/tools/lint/EcjParser.java
@@ -23,6 +23,7 @@
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
import com.android.sdklib.IAndroidTarget;
import com.android.tools.lint.client.api.JavaParser;
import com.android.tools.lint.client.api.LintClient;
@@ -93,6 +94,7 @@
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.NestedTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
@@ -106,6 +108,7 @@
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import java.io.File;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
@@ -551,6 +554,11 @@
} else if (nativeNode instanceof ExplicitConstructorCall) {
return resolve(((ExplicitConstructorCall) nativeNode).binding);
} else if (nativeNode instanceof Annotation) {
+ AnnotationBinding compilerAnnotation =
+ ((Annotation) nativeNode).getCompilerAnnotation();
+ if (compilerAnnotation != null) {
+ return new EcjResolvedAnnotation(compilerAnnotation);
+ }
return resolve(((Annotation) nativeNode).resolvedType);
} else if (nativeNode instanceof AbstractMethodDeclaration) {
return resolve(((AbstractMethodDeclaration) nativeNode).binding);
@@ -558,7 +566,16 @@
if (nativeNode instanceof LocalDeclaration) {
return resolve(((LocalDeclaration) nativeNode).binding);
} else if (nativeNode instanceof FieldDeclaration) {
- return resolve(((FieldDeclaration) nativeNode).binding);
+ FieldDeclaration fieldDeclaration = (FieldDeclaration) nativeNode;
+ if (fieldDeclaration.initialization instanceof AllocationExpression) {
+ AllocationExpression allocation =
+ (AllocationExpression)fieldDeclaration.initialization;
+ if (allocation.binding != null) {
+ // Field constructor call: this is an enum constant.
+ return new EcjResolvedMethod(allocation.binding);
+ }
+ }
+ return resolve(fieldDeclaration.binding);
}
}
@@ -747,17 +764,39 @@
return new DefaultTypeDescriptor(fqn);
}
+ /** Computes the super method, if any, given a method binding */
+ private static MethodBinding findSuperMethodBinding(@NonNull MethodBinding binding) {
+ try {
+ ReferenceBinding superclass = binding.declaringClass.superclass();
+ while (superclass != null) {
+ MethodBinding[] methods = superclass.getMethods(binding.selector,
+ binding.parameters.length);
+ for (MethodBinding method : methods) {
+ if (method.areParameterErasuresEqual(binding)) {
+ return method;
+ }
+ }
+
+ superclass = superclass.superclass();
+ }
+ } catch (Exception ignore) {
+ // Work around ECJ bugs; see https://code.google.com/p/android/issues/detail?id=172268
+ }
+
+ return null;
+ }
+
@NonNull
private static Collection<ResolvedAnnotation> merge(
@Nullable Collection<ResolvedAnnotation> first,
@Nullable Collection<ResolvedAnnotation> second) {
- if (first == null) {
+ if (first == null || first.isEmpty()) {
if (second == null) {
return Collections.emptyList();
} else {
return second;
}
- } else if (second == null) {
+ } else if (second == null || second.isEmpty()) {
return first;
} else {
int size = first.size() + second.size();
@@ -972,52 +1011,81 @@
return mBinding.isConstructor();
}
+ @Override
+ @Nullable
+ public ResolvedMethod getSuperMethod() {
+ MethodBinding superBinding = findSuperMethodBinding(mBinding);
+ if (superBinding != null) {
+ return new EcjResolvedMethod(superBinding);
+ }
+
+ return null;
+ }
+
@NonNull
@Override
public Iterable<ResolvedAnnotation> getAnnotations() {
- List<ResolvedAnnotation> compiled = null;
- AnnotationBinding[] annotations = mBinding.getAnnotations();
- int count = annotations.length;
- if (count > 0) {
- compiled = Lists.newArrayListWithExpectedSize(count);
- for (AnnotationBinding annotation : annotations) {
- if (annotation != null) {
- compiled.add(new EcjResolvedAnnotation(annotation));
+ List<ResolvedAnnotation> all = Lists.newArrayListWithExpectedSize(4);
+ ExternalAnnotationRepository manager = ExternalAnnotationRepository.get(mClient);
+
+ MethodBinding binding = this.mBinding;
+ while (binding != null) {
+ AnnotationBinding[] annotations = binding.getAnnotations();
+ int count = annotations.length;
+ if (count > 0) {
+ for (AnnotationBinding annotation : annotations) {
+ if (annotation != null) {
+ all.add(new EcjResolvedAnnotation(annotation));
+ }
}
}
+
+ // Look for external annotations
+ Collection<ResolvedAnnotation> external = manager.getAnnotations(
+ new EcjResolvedMethod(binding));
+ if (external != null) {
+ all.addAll(external);
+ }
+
+ binding = findSuperMethodBinding(binding);
}
- // Look for external annotations
- ExternalAnnotationRepository manager = ExternalAnnotationRepository.get(mClient);
- Collection<ResolvedAnnotation> external = manager.getAnnotations(this);
-
- return merge(compiled, external);
+ return all;
}
@NonNull
@Override
public Iterable<ResolvedAnnotation> getParameterAnnotations(int index) {
- List<ResolvedAnnotation> compiled = null;
- AnnotationBinding[][] parameterAnnotations = mBinding.getParameterAnnotations();
- if (parameterAnnotations != null &&
- index >= 0 && index < parameterAnnotations.length) {
- AnnotationBinding[] annotations = parameterAnnotations[index];
- int count = annotations.length;
- if (count > 0) {
- compiled = Lists.newArrayListWithExpectedSize(count);
- for (AnnotationBinding annotation : annotations) {
- if (annotation != null) {
- compiled.add(new EcjResolvedAnnotation(annotation));
+ List<ResolvedAnnotation> all = Lists.newArrayListWithExpectedSize(4);
+ ExternalAnnotationRepository manager = ExternalAnnotationRepository.get(mClient);
+
+ MethodBinding binding = this.mBinding;
+ while (binding != null) {
+ AnnotationBinding[][] parameterAnnotations = binding.getParameterAnnotations();
+ if (parameterAnnotations != null &&
+ index >= 0 && index < parameterAnnotations.length) {
+ AnnotationBinding[] annotations = parameterAnnotations[index];
+ int count = annotations.length;
+ if (count > 0) {
+ for (AnnotationBinding annotation : annotations) {
+ if (annotation != null) {
+ all.add(new EcjResolvedAnnotation(annotation));
+ }
}
}
}
+
+ // Look for external annotations
+ Collection<ResolvedAnnotation> external = manager.getAnnotations(
+ new EcjResolvedMethod(binding), index);
+ if (external != null) {
+ all.addAll(external);
+ }
+
+ binding = findSuperMethodBinding(binding);
}
- // Look for external annotations
- ExternalAnnotationRepository manager = ExternalAnnotationRepository.get(mClient);
- Collection<ResolvedAnnotation> external = manager.getAnnotations(this, index);
-
- return merge(compiled, external);
+ return all;
}
@Override
@@ -1030,6 +1098,17 @@
return mBinding.toString();
}
+ @Override
+ public boolean isInPackage(@NonNull String pkgName, boolean includeSubPackages) {
+ PackageBinding pkg = mBinding.declaringClass.getPackage();
+ if (pkg != null) {
+ return includeSubPackages ?
+ startsWithCompound(pkgName, pkg.compoundName) :
+ equalsCompound(pkgName, pkg.compoundName);
+ }
+ return false;
+ }
+
@SuppressWarnings("RedundantIfStatement")
@Override
public boolean equals(Object o) {
@@ -1225,26 +1304,94 @@
@NonNull
@Override
public Iterable<ResolvedAnnotation> getAnnotations() {
- List<ResolvedAnnotation> compiled = null;
+ List<ResolvedAnnotation> all = Lists.newArrayListWithExpectedSize(2);
+ ExternalAnnotationRepository manager = ExternalAnnotationRepository.get(mClient);
+
if (mBinding instanceof ReferenceBinding) {
ReferenceBinding cls = (ReferenceBinding) mBinding;
- AnnotationBinding[] annotations = cls.getAnnotations();
- int count = annotations.length;
- if (count > 0) {
- compiled = Lists.newArrayListWithExpectedSize(count);
- for (AnnotationBinding annotation : annotations) {
- if (annotation != null) {
- compiled.add(new EcjResolvedAnnotation(annotation));
+ while (cls != null) {
+ AnnotationBinding[] annotations = cls.getAnnotations();
+ int count = annotations.length;
+ if (count > 0) {
+ all = Lists.newArrayListWithExpectedSize(count);
+ for (AnnotationBinding annotation : annotations) {
+ if (annotation != null) {
+ all.add(new EcjResolvedAnnotation(annotation));
+ }
}
}
+
+ // Look for external annotations
+ Collection<ResolvedAnnotation> external = manager.getAnnotations(
+ new EcjResolvedClass(cls));
+ if (external != null) {
+ all.addAll(external);
+ }
+
+ cls = cls.superclass();
+ }
+ } else {
+ Collection<ResolvedAnnotation> external = manager.getAnnotations(this);
+ if (external != null) {
+ all.addAll(external);
+ }
+ }
+
+ return all;
+ }
+
+ @NonNull
+ @Override
+ public Iterable<ResolvedField> getFields(boolean includeInherited) {
+ if (mBinding instanceof ReferenceBinding) {
+ ReferenceBinding cls = (ReferenceBinding) mBinding;
+ if (includeInherited) {
+ List<ResolvedField> result = null;
+ while (cls != null) {
+ FieldBinding[] fields = cls.fields();
+ if (fields != null) {
+ int count = fields.length;
+ if (count > 0) {
+ if (result == null) {
+ result = Lists.newArrayListWithExpectedSize(count);
+ }
+ for (FieldBinding field : fields) {
+ // See if this field looks like it's masked
+ boolean masked = false;
+ for (ResolvedField f : result) {
+ FieldBinding mb = ((EcjResolvedField) f).mBinding;
+ if (Arrays.equals(mb.readableName(),
+ field.readableName())) {
+ masked = true;
+ break;
+ }
+ }
+ if (masked) {
+ continue;
+ }
+
+ result.add(new EcjResolvedField(field));
+ }
+ }
+ }
+ cls = cls.superclass();
+ }
+
+ return result != null ? result : Collections.<ResolvedField>emptyList();
+ } else {
+ FieldBinding[] fields = cls.fields();
+ if (fields != null) {
+ int count = fields.length;
+ List<ResolvedField> result = Lists.newArrayListWithExpectedSize(count);
+ for (FieldBinding field : fields) {
+ result.add(new EcjResolvedField(field));
+ }
+ return result;
+ }
}
}
- // Look for external annotations
- ExternalAnnotationRepository manager = ExternalAnnotationRepository.get(mClient);
- Collection<ResolvedAnnotation> external = manager.getAnnotations(this);
-
- return merge(compiled, external);
+ return Collections.emptyList();
}
@Override
@@ -1272,6 +1419,12 @@
return null;
}
+ @Nullable
+ @Override
+ public ResolvedPackage getPackage() {
+ return new EcjResolvedPackage(mBinding.getPackage());
+ }
+
@Override
public int getModifiers() {
if (mBinding instanceof ReferenceBinding) {
@@ -1288,6 +1441,17 @@
return getName();
}
+ @Override
+ public boolean isInPackage(@NonNull String pkgName, boolean includeSubPackages) {
+ PackageBinding pkg = mBinding.getPackage();
+ if (pkg != null) {
+ return includeSubPackages ?
+ startsWithCompound(pkgName, pkg.compoundName) :
+ equalsCompound(pkgName, pkg.compoundName);
+ }
+ return false;
+ }
+
@SuppressWarnings("RedundantIfStatement")
@Override
public boolean equals(Object o) {
@@ -1313,6 +1477,67 @@
}
}
+ // "package-info" as a char
+ private static final char[] PACKAGE_INFO_CHARS = new char[] {
+ 'p', 'a', 'c', 'k', 'a', 'g', 'e', '-', 'i', 'n', 'f', 'o'
+ };
+
+ private class EcjResolvedPackage extends ResolvedPackage {
+ private final PackageBinding mBinding;
+
+ public EcjResolvedPackage(PackageBinding binding) {
+ mBinding = binding;
+ }
+
+ @NonNull
+ @Override
+ public String getName() {
+ return new String(mBinding.readableName());
+ }
+
+ @Override
+ public String getSignature() {
+ return getName();
+ }
+
+ @NonNull
+ @Override
+ public Iterable<ResolvedAnnotation> getAnnotations() {
+ List<ResolvedAnnotation> all = Lists.newArrayListWithExpectedSize(2);
+
+ AnnotationBinding[] annotations = mBinding.getAnnotations();
+ int count = annotations.length;
+ if (count == 0) {
+ Binding pkgInfo = mBinding.getTypeOrPackage(PACKAGE_INFO_CHARS);
+ if (pkgInfo != null) {
+ annotations = pkgInfo.getAnnotations();
+ }
+ count = annotations.length;
+ }
+ if (count > 0) {
+ for (AnnotationBinding annotation : annotations) {
+ if (annotation != null) {
+ all.add(new EcjResolvedAnnotation(annotation));
+ }
+ }
+ }
+
+ // Merge external annotations
+ ExternalAnnotationRepository manager = ExternalAnnotationRepository.get(mClient);
+ Collection<ResolvedAnnotation> external = manager.getAnnotations(this);
+ if (external != null) {
+ all.addAll(external);
+ }
+
+ return all;
+ }
+
+ @Override
+ public int getModifiers() {
+ return 0;
+ }
+ }
+
private class EcjResolvedField extends ResolvedField {
private FieldBinding mBinding;
@@ -1383,6 +1608,17 @@
return mBinding.toString();
}
+ @Override
+ public boolean isInPackage(@NonNull String pkgName, boolean includeSubPackages) {
+ PackageBinding pkg = mBinding.declaringClass.getPackage();
+ if (pkg != null) {
+ return includeSubPackages ?
+ startsWithCompound(pkgName, pkg.compoundName) :
+ equalsCompound(pkgName, pkg.compoundName);
+ }
+ return false;
+ }
+
@SuppressWarnings("RedundantIfStatement")
@Override
public boolean equals(Object o) {
@@ -1740,8 +1976,11 @@
}
@Nullable
- private static Object getConstantValue(@Nullable Object value) {
+ private Object getConstantValue(@Nullable Object value) {
if (value instanceof Constant) {
+ if (value == Constant.NotAConstant) {
+ return null;
+ }
if (value instanceof StringConstant) {
return ((StringConstant) value).stringValue();
} else if (value instanceof IntConstant) {
@@ -1786,6 +2025,8 @@
return list.toArray();
}
+ } else if (value instanceof AnnotationBinding) {
+ return new EcjResolvedAnnotation((AnnotationBinding) value);
}
return value;
@@ -1805,4 +2046,79 @@
return true;
}
+
+ /**
+ * Does the given compound name match the given string?
+ * <p>
+ * TODO: Check if ECJ already has this as a utility somewhere
+ */
+ @VisibleForTesting
+ static boolean startsWithCompound(@NonNull String name, @NonNull char[][] compoundName) {
+ int length = name.length();
+ if (length == 0) {
+ return false;
+ }
+ int index = 0;
+ for (int i = 0, n = compoundName.length; i < n; i++) {
+ char[] o = compoundName[i];
+ for (int j = 0, m = o.length; j < m; j++) {
+ if (index == length) {
+ return false; // Don't allow prefix in a compound name
+ }
+ if (name.charAt(index) != o[j]) {
+ return false;
+ }
+ index++;
+ }
+ if (i < n - 1) {
+ if (index == length) {
+ return true;
+ }
+ if (name.charAt(index) != '.') {
+ return false;
+ }
+ index++;
+ if (index == length) {
+ return true;
+ }
+ }
+ }
+
+ return index == length;
+ }
+
+ @VisibleForTesting
+ static boolean equalsCompound(@NonNull String name, @NonNull char[][] compoundName) {
+ int length = name.length();
+ if (length == 0) {
+ return false;
+ }
+ int index = 0;
+ for (int i = 0, n = compoundName.length; i < n; i++) {
+ char[] o = compoundName[i];
+ for (int j = 0, m = o.length; j < m; j++) {
+ if (index == length) {
+ return false; // Don't allow prefix in a compound name
+ }
+ if (name.charAt(index) != o[j]) {
+ return false;
+ }
+ index++;
+ }
+ if (i < n - 1) {
+ if (index == length) {
+ return false;
+ }
+ if (name.charAt(index) != '.') {
+ return false;
+ }
+ index++;
+ if (index == length) {
+ return false;
+ }
+ }
+ }
+
+ return index == length;
+ }
}
\ No newline at end of file
diff --git a/lint/cli/src/main/java/com/android/tools/lint/ExternalAnnotationRepository.java b/lint/cli/src/main/java/com/android/tools/lint/ExternalAnnotationRepository.java
index 5154222..0cf2eff 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/ExternalAnnotationRepository.java
+++ b/lint/cli/src/main/java/com/android/tools/lint/ExternalAnnotationRepository.java
@@ -21,6 +21,7 @@
import static com.android.SdkConstants.FN_ANNOTATIONS_ZIP;
import static com.android.SdkConstants.VALUE_FALSE;
import static com.android.SdkConstants.VALUE_TRUE;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -35,6 +36,7 @@
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
import com.android.tools.lint.client.api.JavaParser.ResolvedField;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
+import com.android.tools.lint.client.api.JavaParser.ResolvedPackage;
import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.detector.api.LintUtils;
@@ -337,6 +339,29 @@
return null;
}
+ @Nullable
+ public ResolvedAnnotation getAnnotation(@NonNull ResolvedPackage pkg, @NonNull String type) {
+ for (AnnotationsDatabase database : mDatabases) {
+ ResolvedAnnotation annotation = database.getAnnotation(pkg, type);
+ if (annotation != null) {
+ return annotation;
+ }
+ }
+
+ return null;
+ }
+ @Nullable
+ public Collection<ResolvedAnnotation> getAnnotations(@NonNull ResolvedPackage pkg) {
+ for (AnnotationsDatabase database : mDatabases) {
+ Collection<ResolvedAnnotation> annotations = database.getAnnotations(pkg);
+ if (annotations != null) {
+ return annotations;
+ }
+ }
+
+ return null;
+ }
+
// ---- Reading from storage ----
private static final Pattern XML_SIGNATURE = Pattern.compile(
@@ -371,7 +396,8 @@
* */
static class AnnotationsDatabase {
AnnotationsDatabase(@NonNull File file) throws IOException {
- if (file.getPath().endsWith(DOT_JAR)) {
+ String path = file.getPath();
+ if (path.endsWith(DOT_JAR) || path.endsWith(FN_ANNOTATIONS_ZIP)) {
initializeFromJar(file);
} else {
assert file.isDirectory() : file;
@@ -486,6 +512,35 @@
}
@Nullable
+ public ResolvedAnnotation getAnnotation(@NonNull ResolvedPackage pkg, @NonNull String type) {
+ ClassInfo c = findPackage(pkg);
+
+ if (c == null) {
+ return null;
+ }
+
+ if (c.annotations != null) {
+ for (ResolvedAnnotation annotation : c.annotations) {
+ if (type.equals(annotation.getSignature())) {
+ return annotation;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @Nullable
+ public List<ResolvedAnnotation> getAnnotations(@NonNull ResolvedPackage pkg) {
+ ClassInfo c = findPackage(pkg);
+ if (c == null) {
+ return null;
+ }
+
+ return c.annotations;
+ }
+
+ @Nullable
public ResolvedAnnotation getAnnotation(@NonNull ResolvedField field, @NonNull String type) {
FieldInfo f = findField(field);
@@ -616,6 +671,10 @@
return mClassMap.get(cls.getName());
}
+ private ClassInfo findPackage(@NonNull ResolvedPackage pkg) {
+ return mClassMap.get(pkg.getName() +".package-info");
+ }
+
@Nullable
private MethodInfo findMethod(@NonNull ResolvedMethod method) {
ClassInfo c = findClass(method.getContainingClass());
@@ -901,20 +960,6 @@
public List<Value> getValues() {
return mValues == null ? Collections.<Value>emptyList() : mValues;
}
-
- @Nullable
- @Override
- public Object getValue(@NonNull String name) {
- if (mValues != null) {
- for (Value value : mValues) {
- if (name.equals(value.name)) {
- return value.value;
- }
- }
- }
-
- return null;
- }
}
private Map<String, ResolvedExternalAnnotation> mMarkerAnnotations = Maps.newHashMapWithExpectedSize(30);
@@ -933,7 +978,11 @@
annotation = new ResolvedExternalAnnotation(name);
List<Element> valueElements = getChildren(annotationElement);
- if (valueElements.isEmpty()) {
+ if (valueElements.isEmpty()
+ // Permission annotations are sometimes used as marker annotations (on
+ // parameters) but that shouldn't let us conclude that any future
+ // permission annotations are
+ && !name.startsWith(PERMISSION_ANNOTATION)) {
mMarkerAnnotations.put(name, annotation);
return annotation;
}
@@ -951,20 +1000,31 @@
value = false;
} else if (valueString.startsWith("\"") && valueString.endsWith("\"") &&
valueString.length() >= 2) {
- value = valueString.substring(1, valueString.length() - 2);
+ value = valueString.substring(1, valueString.length() - 1);
} else if (valueString.startsWith("{") && valueString.endsWith("}")) {
// Array of values
- String listString = valueString.substring(1, valueString.length() - 2);
- // We can't actually know; this could be for example
- // {java.util.zip.ZipEntry.STORED, java.util.zip.ZipEntry.DEFLATED}
- // and we don't know the types of these
- // For now, initialize the fields to the string names
+ String listString = valueString.substring(1, valueString.length() - 1);
+ // We don't know the types, but we'll assume that they're either
+ // all strings (the most common array type in our annotations), or
+ // field references. We can't know the types of the fields; it's
+ // not part of the annotation metadata. We'll place them in an Object[]
+ // for now.
+ boolean allStrings = true;
Splitter splitter = Splitter.on(',').omitEmptyStrings().trimResults();
List<Object> result = Lists.newArrayList();
for (String reference : splitter.split(listString)) {
- result.add(new ResolvedExternalField(reference));
+ if (reference.startsWith("\"")) {
+ result.add(reference.substring(1, reference.length() - 1));
+ } else {
+ result.add(new ResolvedExternalField(reference));
+ allStrings = false;
+ }
}
- value = result.toArray();
+ if (allStrings) {
+ value = result.toArray(new String[result.size()]);
+ } else {
+ value = result.toArray();
+ }
// We don't know the actual type of these fields; we'll assume they're
// a special form of
diff --git a/lint/cli/src/main/java/com/android/tools/lint/HtmlReporter.java b/lint/cli/src/main/java/com/android/tools/lint/HtmlReporter.java
index bd9bc62..c71b5a0 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/HtmlReporter.java
+++ b/lint/cli/src/main/java/com/android/tools/lint/HtmlReporter.java
@@ -472,7 +472,7 @@
// See if any projects disable this warning
for (Project project : projects) {
- if (!project.getConfiguration().isEnabled(issue)) {
+ if (!project.getConfiguration(null).isEnabled(issue)) {
map.put(issue, "Project lint.xml file");
break;
}
diff --git a/lint/cli/src/main/java/com/android/tools/lint/LintCliClient.java b/lint/cli/src/main/java/com/android/tools/lint/LintCliClient.java
index ad11229..8bcbedf 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/LintCliClient.java
+++ b/lint/cli/src/main/java/com/android/tools/lint/LintCliClient.java
@@ -182,8 +182,9 @@
return new LintCliXmlParser();
}
+ @NonNull
@Override
- public Configuration getConfiguration(@NonNull Project project) {
+ public Configuration getConfiguration(@NonNull Project project, @Nullable LintDriver driver) {
return new CliConfiguration(getConfiguration(), project, mFlags.isFatalOnly());
}
@@ -545,7 +546,7 @@
if (!isSuppressed(IssueRegistry.LINT_ERROR)) {
report(new Context(mDriver, project, project, project.getDir()),
IssueRegistry.LINT_ERROR,
- project.getConfiguration().getSeverity(IssueRegistry.LINT_ERROR),
+ project.getConfiguration(mDriver).getSeverity(IssueRegistry.LINT_ERROR),
location, message, TextFormat.RAW);
}
} else {
diff --git a/lint/cli/src/main/java/com/android/tools/lint/LintCliXmlParser.java b/lint/cli/src/main/java/com/android/tools/lint/LintCliXmlParser.java
index 7165d9b..61d3a37 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/LintCliXmlParser.java
+++ b/lint/cli/src/main/java/com/android/tools/lint/LintCliXmlParser.java
@@ -20,6 +20,7 @@
import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.IssueRegistry;
import com.android.tools.lint.client.api.XmlParser;
+import com.android.tools.lint.detector.api.DefaultPosition;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.Location.Handle;
import com.android.tools.lint.detector.api.Position;
@@ -43,14 +44,6 @@
* It also catches and reports parser errors as lint errors.
*/
public class LintCliXmlParser extends XmlParser {
- private final PositionXmlParser mParser = new PositionXmlParser() {
- @NonNull
- @Override
- protected OffsetPosition createPosition(int line, int column, int offset) {
- return new OffsetPosition(line, column, offset);
- }
- };
-
@Override
public Document parseXml(@NonNull XmlContext context) {
String xml = null;
@@ -58,7 +51,7 @@
// Do we need to provide an input stream for encoding?
xml = context.getContents();
if (xml != null) {
- return mParser.parse(xml);
+ return PositionXmlParser.parse(xml);
}
} catch (UnsupportedEncodingException e) {
context.report(
@@ -66,7 +59,7 @@
// is valid
IssueRegistry.PARSER_ERROR, Location.create(context.file),
e.getCause() != null ? e.getCause().getLocalizedMessage() :
- e.getLocalizedMessage()
+ e.getLocalizedMessage()
);
} catch (SAXException e) {
Location location = Location.create(context.file);
@@ -96,24 +89,14 @@
@NonNull
@Override
public Location getLocation(@NonNull XmlContext context, @NonNull Node node) {
- OffsetPosition pos = (OffsetPosition) mParser.getPosition(node, -1, -1);
- if (pos != null) {
- return Location.create(context.file, pos, (OffsetPosition) pos.getEnd());
- }
-
- return Location.create(context.file);
+ return Location.create(context.file, PositionXmlParser.getPosition(node));
}
@NonNull
@Override
public Location getLocation(@NonNull XmlContext context, @NonNull Node node,
int start, int end) {
- OffsetPosition pos = (OffsetPosition) mParser.getPosition(node, start, end);
- if (pos != null) {
- return Location.create(context.file, pos, (OffsetPosition) pos.getEnd());
- }
-
- return Location.create(context.file);
+ return Location.create(context.file, PositionXmlParser.getPosition(node, start, end));
}
@Override
@@ -130,8 +113,8 @@
int startOffset = start.getOffset() + delta;
int startColumn = start.getColumn() + delta;
return Location.create(location.getFile(),
- new OffsetPosition(start.getLine(), startColumn, startOffset),
- new OffsetPosition(end.getLine(), startColumn + length, startOffset + length));
+ new DefaultPosition(start.getLine(), startColumn, startOffset),
+ new DefaultPosition(end.getLine(), startColumn + length, startOffset + length));
}
@Override
@@ -149,8 +132,8 @@
int startOffset = start.getOffset() + delta;
int startColumn = start.getColumn() + delta;
return Location.create(location.getFile(),
- new OffsetPosition(start.getLine(), startColumn, startOffset),
- new OffsetPosition(end.getLine(), startColumn + length, startOffset + length));
+ new DefaultPosition(start.getLine(), startColumn, startOffset),
+ new DefaultPosition(end.getLine(), startColumn + length, startOffset + length));
}
@NonNull
@@ -159,92 +142,14 @@
return new LocationHandle(context.file, node);
}
- private static class OffsetPosition extends com.android.tools.lint.detector.api.Position
- implements PositionXmlParser.Position {
- /** The line number (0-based where the first line is line 0) */
- private final int mLine;
-
- /**
- * The column number (where the first character on the line is 0), or -1 if
- * unknown
- */
- private final int mColumn;
-
- /** The character offset */
- private final int mOffset;
-
- /**
- * Linked position: for a begin offset this will point to the end
- * offset, and for an end offset this will be null
- */
- private PositionXmlParser.Position mEnd;
-
- /**
- * Creates a new {@link OffsetPosition}
- *
- * @param line the 0-based line number, or -1 if unknown
- * @param column the 0-based column number, or -1 if unknown
- * @param offset the offset, or -1 if unknown
- */
- public OffsetPosition(int line, int column, int offset) {
- mLine = line;
- mColumn = column;
- mOffset = offset;
- }
-
- @Override
- public int getLine() {
- return mLine;
- }
-
- @Override
- public int getOffset() {
- return mOffset;
- }
-
- @Override
- public int getColumn() {
- return mColumn;
- }
-
- @Override
- public PositionXmlParser.Position getEnd() {
- return mEnd;
- }
-
- @Override
- public void setEnd(@NonNull PositionXmlParser.Position end) {
- mEnd = end;
- }
-
- @Override
- public String toString() {
- return "OffsetPosition [line=" + mLine + ", column=" + mColumn + ", offset="
- + mOffset + ", end=" + mEnd + ']';
- }
- }
-
@Override
public int getNodeStartOffset(@NonNull XmlContext context, @NonNull Node node) {
- OffsetPosition pos = (OffsetPosition) mParser.getPosition(node, -1, -1);
- if (pos != null) {
- return pos.getOffset();
- }
-
- return -1;
+ return PositionXmlParser.getPosition(node).getStartOffset();
}
@Override
public int getNodeEndOffset(@NonNull XmlContext context, @NonNull Node node) {
- OffsetPosition pos = (OffsetPosition) mParser.getPosition(node, -1, -1);
- if (pos != null) {
- PositionXmlParser.Position end = pos.getEnd();
- if (end != null) {
- return end.getOffset();
- }
- }
-
- return -1;
+ return PositionXmlParser.getPosition(node).getEndOffset();
}
/* Handle for creating DOM positions cheaply and returning full fledged locations later */
@@ -261,12 +166,7 @@
@NonNull
@Override
public Location resolve() {
- OffsetPosition pos = (OffsetPosition) mParser.getPosition(mNode);
- if (pos != null) {
- return Location.create(mFile, pos, (OffsetPosition) pos.getEnd());
- }
-
- return Location.create(mFile);
+ return Location.create(mFile, PositionXmlParser.getPosition(mNode));
}
@Override
diff --git a/lint/cli/src/main/java/com/android/tools/lint/Main.java b/lint/cli/src/main/java/com/android/tools/lint/Main.java
index af3846a..126d02f 100644
--- a/lint/cli/src/main/java/com/android/tools/lint/Main.java
+++ b/lint/cli/src/main/java/com/android/tools/lint/Main.java
@@ -32,6 +32,7 @@
import com.android.tools.lint.checks.BuiltinIssueRegistry;
import com.android.tools.lint.client.api.Configuration;
import com.android.tools.lint.client.api.IssueRegistry;
+import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Issue;
@@ -145,19 +146,22 @@
Location location = Location.create(project.getDir());
Context context = new Context(mDriver, project, project, project.getDir());
if (context.isEnabled(IssueRegistry.LINT_ERROR) &&
- !getConfiguration(project).isIgnored(context, IssueRegistry.LINT_ERROR,
- location, message)) {
+ !getConfiguration(project, null).isIgnored(context,
+ IssueRegistry.LINT_ERROR, location, message)) {
report(context,
- IssueRegistry.LINT_ERROR,
- project.getConfiguration().getSeverity(IssueRegistry.LINT_ERROR),
- location, message, TextFormat.RAW);
+ IssueRegistry.LINT_ERROR,
+ project.getConfiguration(null).getSeverity(
+ IssueRegistry.LINT_ERROR), location, message,
+ TextFormat.RAW);
}
}
return project;
}
+ @NonNull
@Override
- public Configuration getConfiguration(@NonNull final Project project) {
+ public Configuration getConfiguration(@NonNull final Project project,
+ @Nullable LintDriver driver) {
if (project.isGradleProject()) {
// Don't report any issues when analyzing a Gradle project from the
// non-Gradle runner; they are likely to be false, and will hide the real
@@ -184,7 +188,7 @@
}
};
}
- return super.getConfiguration(project);
+ return super.getConfiguration(project, driver);
}
};
diff --git a/lint/cli/src/test/java/com/android/tools/lint/EcjParserTest.java b/lint/cli/src/test/java/com/android/tools/lint/EcjParserTest.java
deleted file mode 100644
index cb97e27..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/EcjParserTest.java
+++ /dev/null
@@ -1,772 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint;
-
-import static com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import static com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import static com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import static com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import static com.android.tools.lint.client.api.JavaParser.ResolvedVariable;
-
-import com.android.annotations.NonNull;
-import com.android.tools.lint.checks.AbstractCheckTest;
-import com.android.tools.lint.checks.SdCardDetector;
-import com.android.tools.lint.client.api.JavaParser;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.LintUtilsTest;
-import com.android.tools.lint.detector.api.Project;
-
-import junit.framework.Assert;
-
-import java.io.File;
-
-import lombok.ast.AnnotationElement;
-import lombok.ast.BinaryExpression;
-import lombok.ast.Block;
-import lombok.ast.DescribedNode;
-import lombok.ast.ExpressionStatement;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.Identifier;
-import lombok.ast.KeywordModifier;
-import lombok.ast.MethodInvocation;
-import lombok.ast.Modifiers;
-import lombok.ast.Node;
-import lombok.ast.NormalTypeBody;
-import lombok.ast.Select;
-import lombok.ast.TypeReferencePart;
-import lombok.ast.VariableDeclaration;
-import lombok.ast.VariableDefinition;
-import lombok.ast.VariableReference;
-import lombok.ast.printer.SourceFormatter;
-import lombok.ast.printer.SourcePrinter;
-import lombok.ast.printer.TextFormatter;
-
-public class EcjParserTest extends AbstractCheckTest {
- public void testTryCatchHang() throws Exception {
- // Ensure that we're really using this parser
- JavaParser javaParser = createClient().getJavaParser(null);
- assertNotNull(javaParser);
- assertTrue(javaParser.getClass().getName(), javaParser instanceof EcjParser);
-
- // See https://code.google.com/p/projectlombok/issues/detail?id=573#c6
- // With lombok.ast 0.2.1 and the parboiled-based Java parser this test will hang forever.
- assertEquals(
- "No warnings.",
-
- lintProject("src/test/pkg/TryCatchHang.java.txt=>src/test/pkg/TryCatchHang.java"));
- }
-
- public void testKitKatLanguageFeatures() throws Exception {
- String testClass = "" +
- "package test.pkg;\n" +
- "\n" +
- "import java.io.BufferedReader;\n" +
- "import java.io.FileReader;\n" +
- "import java.io.IOException;\n" +
- "import java.lang.reflect.InvocationTargetException;\n" +
- "import java.util.List;\n" +
- "import java.util.Map;\n" +
- "import java.util.TreeMap;\n" +
- "\n" +
- "public class Java7LanguageFeatureTest {\n" +
- " public void testDiamondOperator() {\n" +
- " Map<String, List<Integer>> map = new TreeMap<>();\n" +
- " }\n" +
- "\n" +
- " public int testStringSwitches(String value) {\n" +
- " final String first = \"first\";\n" +
- " final String second = \"second\";\n" +
- "\n" +
- " switch (value) {\n" +
- " case first:\n" +
- " return 41;\n" +
- " case second:\n" +
- " return 42;\n" +
- " default:\n" +
- " return 0;\n" +
- " }\n" +
- " }\n" +
- "\n" +
- " public String testTryWithResources(String path) throws IOException {\n" +
- " try (BufferedReader br = new BufferedReader(new FileReader(path))) {\n" +
- " return br.readLine();\n" +
- " }\n" +
- " }\n" +
- "\n" +
- " public void testNumericLiterals() {\n" +
- " int thousand = 1_000;\n" +
- " int million = 1_000_000;\n" +
- " int binary = 0B01010101;\n" +
- " }\n" +
- "\n" +
- " public void testMultiCatch() {\n" +
- "\n" +
- " try {\n" +
- " Class.forName(\"java.lang.Integer\").getMethod(\"toString\").invoke(null);\n" +
- " } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {\n" +
- " e.printStackTrace();\n" +
- " } catch (ClassNotFoundException e) {\n" +
- " // TODO: Logging here\n" +
- " }\n" +
- " }\n" +
- "}\n";
-
- Node unit = LintUtilsTest.getCompilationUnit(testClass);
- assertNotNull(unit);
-
- // Now print the AST back and make sure that it contains at least the essence of the AST
- TextFormatter formatter = new TextFormatter();
- unit.accept(new SourcePrinter(formatter));
- String actual = formatter.finish();
- assertEquals(""
- + "package test.pkg;\n"
- + "\n"
- + "import java.io.BufferedReader;\n"
- + "import java.io.FileReader;\n"
- + "import java.io.IOException;\n"
- + "import java.lang.reflect.InvocationTargetException;\n"
- + "import java.util.List;\n"
- + "import java.util.Map;\n"
- + "import java.util.TreeMap;\n"
- + "\n"
- + "public class Java7LanguageFeatureTest {\n"
- + " public void testDiamondOperator() {\n"
- + " Map<String, List<Integer>> map = new TreeMap();\n" // missing types on rhs
- + " }\n"
- + " \n"
- + " public int testStringSwitches(String value) {\n"
- + " final String first = \"first\";\n"
- + " final String second = \"second\";\n"
- + " switch (value) {\n"
- + " case first:\n"
- + " return 41;\n"
- + " case second:\n"
- + " return 42;\n"
- + " default:\n"
- + " return 0;\n"
- + " }\n"
- + " }\n"
- + " \n"
- + " public String testTryWithResources(String path) throws IOException {\n"
- + " try {\n" // Note how the initialization clause is gone here
- + " return br.readLine();\n"
- + " }\n"
- + " }\n"
- + " \n"
- + " public void testNumericLiterals() {\n"
- + " int thousand = 1_000;\n"
- + " int million = 1_000_000;\n"
- + " int binary = 0B01010101;\n"
- + " }\n"
- + " \n"
- + " public void testMultiCatch() {\n"
- + " try {\n"
- + " Class.forName(\"java.lang.Integer\").getMethod(\"toString\").invoke(null);\n"
- + " } catch (IllegalAccessException e) {\n" // Note: missing other union types
- + " e.printStackTrace();\n"
- + " } catch (ClassNotFoundException e) {\n"
- + " }\n"
- + " }\n"
- + "}",
- actual);
- }
-
- public void testResolution() throws Exception {
- String source =
- "package test.pkg;\n" +
- "\n" +
- "import java.io.File;\n" +
- "\n" +
- "public class TypeResolutionTest {\n" +
- " public static class Inner extends File {\n" +
- " public float myField = 5f;\n" +
- " public int[] myInts;\n" +
- "\n" +
- " public Inner(File dir, String name) {\n" +
- " super(dir, name);\n" +
- " }\n" +
- "\n" +
- " public void call(int arg1, double arg2) {\n" +
- " boolean x = super.canRead();\n" +
- " System.out.println(x);\n" +
- " }\n" +
- " }\n" +
- "\n" +
- " @SuppressWarnings(\"all\")\n" +
- " public static class Other {\n" +
- " private void client(int z) {\n" +
- " int x = z;\n" +
- " int y = x + 5;\n" +
- " Inner inner = new Inner(null, null);\n" +
- " inner.myField = 6;\n" +
- " System.out.println(inner.myInts);\n" +
- " }\n" +
- " }\n" +
- "}\n";
-
- Node unit = LintUtilsTest.getCompilationUnit(source,
- new File("src/test/pkg/TypeResolutionTest.java"));
-
- // Visit all nodes and assert nativeNode != null unless I expect it!
- unit.accept(new ForwardingAstVisitor() {
- @SuppressWarnings("Contract")
- @Override
- public boolean visitNode(Node node) {
- if (node.getNativeNode() == null && requiresNativeNode(node)) {
- fail("Expected native node on node of type " +
- node.getClass().getSimpleName());
- }
- return super.visitNode(node);
- }
-
- private boolean requiresNativeNode(Node node) {
- if (node instanceof TypeReferencePart &&
- node.getParent().getNativeNode() != null) {
- return false;
- }
-
- if (node instanceof Identifier
- || node instanceof NormalTypeBody
- || node instanceof Block
- || node instanceof VariableDeclaration
- || node instanceof VariableDefinition
- || node instanceof AnnotationElement
- || node instanceof BinaryExpression
- || node instanceof Modifiers
- || node instanceof KeywordModifier) {
- return false;
- }
-
- if (node instanceof VariableReference) {
- VariableReference reference = (VariableReference)node;
- if (reference.getParent() instanceof Select) {
- return false;
- }
- } else if (node instanceof MethodInvocation) {
- Node parent = node.getParent();
- if (parent instanceof ExpressionStatement &&
- parent.getNativeNode() != null) {
- return false;
- }
- }
-
- return true;
- }
- });
-
- JavaParser parser = new EcjParser(new LintCliClient(), null);
- AstPrettyPrinter astPrettyPrinter = new AstPrettyPrinter(parser);
- unit.accept(new SourcePrinter(astPrettyPrinter));
- String actual = astPrettyPrinter.finish();
- assertEquals(
- "[CompilationUnit]\n" +
- " [PackageDeclaration]\n" +
- " [Identifier test]\n" +
- " PROPERTY: name = test\n" +
- " [Identifier pkg]\n" +
- " PROPERTY: name = pkg\n" +
- " [ImportDeclaration]\n" +
- " PROPERTY: static = false\n" +
- " PROPERTY: star = false\n" +
- " [Identifier java]\n" +
- " PROPERTY: name = java\n" +
- " [Identifier io]\n" +
- " PROPERTY: name = io\n" +
- " [Identifier File]\n" +
- " PROPERTY: name = File\n" +
- " [ClassDeclaration TypeResolutionTest], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
- " [Modifiers], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
- " [KeywordModifier public]\n" +
- " PROPERTY: modifier = public\n" +
- " typeName: [Identifier TypeResolutionTest], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
- " PROPERTY: name = TypeResolutionTest\n" +
- " [NormalTypeBody], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
- " [ClassDeclaration Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
- " [Modifiers], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
- " [KeywordModifier public]\n" +
- " PROPERTY: modifier = public\n" +
- " [KeywordModifier static]\n" +
- " PROPERTY: modifier = static\n" +
- " typeName: [Identifier Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
- " PROPERTY: name = Inner\n" +
- " extends: [TypeReference File], type: java.io.File, resolved class: java.io.File \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: java.io.File, resolved class: java.io.File \n" +
- " [Identifier File]\n" +
- " PROPERTY: name = File\n" +
- " [NormalTypeBody], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
- " [VariableDeclaration], type: float, resolved class: float \n" +
- " [VariableDefinition]\n" +
- " PROPERTY: varargs = false\n" +
- " [Modifiers]\n" +
- " [KeywordModifier public]\n" +
- " PROPERTY: modifier = public\n" +
- " type: [TypeReference float], type: float, resolved class: float \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: float, resolved class: float \n" +
- " [Identifier float]\n" +
- " PROPERTY: name = float\n" +
- " [VariableDefinitionEntry], resolved field: myField test.pkg.TypeResolutionTest.Inner\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " varName: [Identifier myField], resolved field: myField test.pkg.TypeResolutionTest.Inner\n" +
- " PROPERTY: name = myField\n" +
- " [FloatingPointLiteral 5.0], type: float\n" +
- " PROPERTY: value = 5f\n" +
- " [VariableDeclaration], type: int[], resolved class: int[] \n" +
- " [VariableDefinition]\n" +
- " PROPERTY: varargs = false\n" +
- " [Modifiers]\n" +
- " [KeywordModifier public]\n" +
- " PROPERTY: modifier = public\n" +
- " type: [TypeReference int[]], type: int[], resolved class: int[] \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 1\n" +
- " [TypeReferencePart], type: int[], resolved class: int[] \n" +
- " [Identifier int]\n" +
- " PROPERTY: name = int\n" +
- " [VariableDefinitionEntry], resolved field: myInts test.pkg.TypeResolutionTest.Inner\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " varName: [Identifier myInts], resolved field: myInts test.pkg.TypeResolutionTest.Inner\n" +
- " PROPERTY: name = myInts\n" +
- " [ConstructorDeclaration], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
- " [Modifiers], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
- " [KeywordModifier public]\n" +
- " PROPERTY: modifier = public\n" +
- " typeName: [Identifier Inner], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
- " PROPERTY: name = Inner\n" +
- " parameter: [VariableDefinition], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
- " PROPERTY: varargs = false\n" +
- " [Modifiers]\n" +
- " type: [TypeReference File], type: java.io.File, resolved class: java.io.File \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: java.io.File, resolved class: java.io.File \n" +
- " [Identifier File]\n" +
- " PROPERTY: name = File\n" +
- " [VariableDefinitionEntry], resolved variable: dir java.io.File\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " varName: [Identifier dir], resolved variable: dir java.io.File\n" +
- " PROPERTY: name = dir\n" +
- " parameter: [VariableDefinition], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
- " PROPERTY: varargs = false\n" +
- " [Modifiers]\n" +
- " type: [TypeReference String], type: java.lang.String, resolved class: java.lang.String \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: java.lang.String, resolved class: java.lang.String \n" +
- " [Identifier String]\n" +
- " PROPERTY: name = String\n" +
- " [VariableDefinitionEntry], resolved variable: name java.lang.String\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " varName: [Identifier name], resolved variable: name java.lang.String\n" +
- " PROPERTY: name = name\n" +
- " [Block], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
- " [SuperConstructorInvocation], resolved method: java.io.File java.io.File\n" +
- " [VariableReference], type: java.io.File, resolved variable: dir java.io.File\n" +
- " [Identifier dir], type: java.io.File, resolved variable: dir java.io.File\n" +
- " PROPERTY: name = dir\n" +
- " [VariableReference], type: java.lang.String, resolved variable: name java.lang.String\n" +
- " [Identifier name], type: java.lang.String, resolved variable: name java.lang.String\n" +
- " PROPERTY: name = name\n" +
- " [MethodDeclaration call], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
- " [Modifiers], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
- " [KeywordModifier public]\n" +
- " PROPERTY: modifier = public\n" +
- " returnType: [TypeReference void], type: void, resolved class: void \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: void, resolved class: void \n" +
- " [Identifier void]\n" +
- " PROPERTY: name = void\n" +
- " methodName: [Identifier call], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
- " PROPERTY: name = call\n" +
- " parameter: [VariableDefinition], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
- " PROPERTY: varargs = false\n" +
- " [Modifiers]\n" +
- " type: [TypeReference int], type: int, resolved class: int \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: int, resolved class: int \n" +
- " [Identifier int]\n" +
- " PROPERTY: name = int\n" +
- " [VariableDefinitionEntry], resolved variable: arg1 int\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " varName: [Identifier arg1], resolved variable: arg1 int\n" +
- " PROPERTY: name = arg1\n" +
- " parameter: [VariableDefinition], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
- " PROPERTY: varargs = false\n" +
- " [Modifiers]\n" +
- " type: [TypeReference double], type: double, resolved class: double \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: double, resolved class: double \n" +
- " [Identifier double]\n" +
- " PROPERTY: name = double\n" +
- " [VariableDefinitionEntry], resolved variable: arg2 double\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " varName: [Identifier arg2], resolved variable: arg2 double\n" +
- " PROPERTY: name = arg2\n" +
- " [Block], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
- " [VariableDeclaration], type: boolean, resolved class: boolean \n" +
- " [VariableDefinition]\n" +
- " PROPERTY: varargs = false\n" +
- " [Modifiers]\n" +
- " type: [TypeReference boolean], type: boolean, resolved class: boolean \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: boolean, resolved class: boolean \n" +
- " [Identifier boolean]\n" +
- " PROPERTY: name = boolean\n" +
- " [VariableDefinitionEntry], resolved variable: x boolean\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " varName: [Identifier x], resolved variable: x boolean\n" +
- " PROPERTY: name = x\n" +
- " [MethodInvocation canRead], type: boolean, resolved method: canRead java.io.File\n" +
- " operand: [Super], type: java.io.File\n" +
- " methodName: [Identifier canRead], type: boolean, resolved method: canRead java.io.File\n" +
- " PROPERTY: name = canRead\n" +
- " [ExpressionStatement], type: void, resolved method: println java.io.PrintStream\n" +
- " [MethodInvocation println], type: void, resolved method: println java.io.PrintStream\n" +
- " operand: [Select], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
- " operand: [VariableReference], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
- " [Identifier System]\n" +
- " PROPERTY: name = System\n" +
- " selected: [Identifier out], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
- " PROPERTY: name = out\n" +
- " methodName: [Identifier println]\n" +
- " PROPERTY: name = println\n" +
- " [VariableReference], type: boolean, resolved variable: x boolean\n" +
- " [Identifier x], type: boolean, resolved variable: x boolean\n" +
- " PROPERTY: name = x\n" +
- " [ClassDeclaration Other], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
- " [Modifiers], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
- " [Annotation SuppressWarnings], type: java.lang.SuppressWarnings, resolved class: java.lang.SuppressWarnings \n" +
- " [TypeReference SuppressWarnings], type: java.lang.SuppressWarnings, resolved class: java.lang.SuppressWarnings \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: java.lang.SuppressWarnings, resolved class: java.lang.SuppressWarnings \n" +
- " [Identifier SuppressWarnings]\n" +
- " PROPERTY: name = SuppressWarnings\n" +
- " [AnnotationElement null], type: java.lang.SuppressWarnings, resolved class: java.lang.SuppressWarnings \n" +
- " [StringLiteral all], type: java.lang.String\n" +
- " PROPERTY: value = \"all\"\n" +
- " [KeywordModifier public]\n" +
- " PROPERTY: modifier = public\n" +
- " [KeywordModifier static]\n" +
- " PROPERTY: modifier = static\n" +
- " typeName: [Identifier Other], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
- " PROPERTY: name = Other\n" +
- " [NormalTypeBody], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
- " [MethodDeclaration client], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
- " [Modifiers], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
- " [KeywordModifier private]\n" +
- " PROPERTY: modifier = private\n" +
- " returnType: [TypeReference void], type: void, resolved class: void \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: void, resolved class: void \n" +
- " [Identifier void]\n" +
- " PROPERTY: name = void\n" +
- " methodName: [Identifier client], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
- " PROPERTY: name = client\n" +
- " parameter: [VariableDefinition], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
- " PROPERTY: varargs = false\n" +
- " [Modifiers]\n" +
- " type: [TypeReference int], type: int, resolved class: int \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: int, resolved class: int \n" +
- " [Identifier int]\n" +
- " PROPERTY: name = int\n" +
- " [VariableDefinitionEntry], resolved variable: z int\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " varName: [Identifier z], resolved variable: z int\n" +
- " PROPERTY: name = z\n" +
- " [Block], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
- " [VariableDeclaration], type: int, resolved class: int \n" +
- " [VariableDefinition]\n" +
- " PROPERTY: varargs = false\n" +
- " [Modifiers]\n" +
- " type: [TypeReference int], type: int, resolved class: int \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: int, resolved class: int \n" +
- " [Identifier int]\n" +
- " PROPERTY: name = int\n" +
- " [VariableDefinitionEntry], resolved variable: x int\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " varName: [Identifier x], resolved variable: x int\n" +
- " PROPERTY: name = x\n" +
- " [VariableReference], type: int, resolved variable: z int\n" +
- " [Identifier z], type: int, resolved variable: z int\n" +
- " PROPERTY: name = z\n" +
- " [VariableDeclaration], type: int, resolved class: int \n" +
- " [VariableDefinition]\n" +
- " PROPERTY: varargs = false\n" +
- " [Modifiers]\n" +
- " type: [TypeReference int], type: int, resolved class: int \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: int, resolved class: int \n" +
- " [Identifier int]\n" +
- " PROPERTY: name = int\n" +
- " [VariableDefinitionEntry], resolved variable: y int\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " varName: [Identifier y], resolved variable: y int\n" +
- " PROPERTY: name = y\n" +
- " [BinaryExpression +], type: int\n" +
- " PROPERTY: operator = +\n" +
- " left: [VariableReference], type: int, resolved variable: x int\n" +
- " [Identifier x], type: int, resolved variable: x int\n" +
- " PROPERTY: name = x\n" +
- " right: [IntegralLiteral 5], type: int\n" +
- " PROPERTY: value = 5\n" +
- " [VariableDeclaration], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
- " [VariableDefinition]\n" +
- " PROPERTY: varargs = false\n" +
- " [Modifiers]\n" +
- " type: [TypeReference Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
- " [Identifier Inner]\n" +
- " PROPERTY: name = Inner\n" +
- " [VariableDefinitionEntry], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " varName: [Identifier inner], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
- " PROPERTY: name = inner\n" +
- " [ConstructorInvocation Inner], type: test.pkg.TypeResolutionTest.Inner, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
- " type: [TypeReference Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
- " PROPERTY: WildcardKind = NONE\n" +
- " PROPERTY: arrayDimensions = 0\n" +
- " [TypeReferencePart], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
- " [Identifier Inner]\n" +
- " PROPERTY: name = Inner\n" +
- " [NullLiteral], type: null\n" +
- " [NullLiteral], type: null\n" +
- " [ExpressionStatement], type: float\n" +
- " [BinaryExpression =], type: float\n" +
- " PROPERTY: operator = =\n" +
- " left: [Select], type: float, resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
- " operand: [VariableReference], type: float, resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
- " [Identifier inner]\n" +
- " PROPERTY: name = inner\n" +
- " selected: [Identifier myField], type: float, resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
- " PROPERTY: name = myField\n" +
- " right: [IntegralLiteral 6], type: int\n" +
- " PROPERTY: value = 6\n" +
- " [ExpressionStatement], type: void, resolved method: println java.io.PrintStream\n" +
- " [MethodInvocation println], type: void, resolved method: println java.io.PrintStream\n" +
- " operand: [Select], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
- " operand: [VariableReference], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
- " [Identifier System]\n" +
- " PROPERTY: name = System\n" +
- " selected: [Identifier out], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
- " PROPERTY: name = out\n" +
- " methodName: [Identifier println]\n" +
- " PROPERTY: name = println\n" +
- " [Select], type: int[], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
- " operand: [VariableReference], type: int[], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
- " [Identifier inner]\n" +
- " PROPERTY: name = inner\n" +
- " selected: [Identifier myInts], type: int[], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
- " PROPERTY: name = myInts\n",
- actual);
- }
-
- @Override
- protected Detector getDetector() {
- return new SdCardDetector();
- }
-
- @Override
- protected TestLintClient createClient() {
- return new TestLintClient() {
- @NonNull
- @Override
- protected ClassPathInfo getClassPath(@NonNull Project project) {
- ClassPathInfo classPath = super.getClassPath(project);
- // Insert fake classpath entries (non existent directories) to
- // make sure the parser handles that gracefully. See issue 87740.
- classPath.getLibraries().add(new File("nonexistent path"));
- return classPath;
- }
- };
- }
-
- public static class AstPrettyPrinter implements SourceFormatter {
-
- private final StringBuilder mOutput = new StringBuilder(1000);
-
- private final JavaParser mResolver;
-
- private int mIndent;
-
- private String mName;
-
- public AstPrettyPrinter(JavaParser resolver) {
- mResolver = resolver;
- }
-
- private void add(String in, Object... args) {
- for (int i = 0; i < mIndent; i++) {
- mOutput.append(" ");
- }
- if (mName != null) {
- mOutput.append(mName).append(": ");
- mName = null;
- }
- if (args.length == 0) {
- mOutput.append(in);
- } else {
- mOutput.append(String.format(in, args));
- }
- }
-
- @Override
- public void buildInline(Node node) {
- buildNode(node);
- }
-
- @Override
- public void buildBlock(Node node) {
- buildNode(node);
- }
-
- private void buildNode(Node node) {
- if (node == null) {
- mIndent++;
- return;
- }
- String name = node.getClass().getSimpleName();
- String description = "";
- if (node instanceof DescribedNode) {
- description = " " + ((DescribedNode) node).getDescription();
- }
-
- String typeDescription = "";
- String resolutionDescription = "";
- JavaParser.TypeDescriptor t = mResolver.getType(null, node);
- if (t != null) {
- typeDescription = ", type: " + t.getName();
- }
- ResolvedNode resolved = mResolver.resolve(null, node);
- if (resolved != null) {
- String c = "unknown";
- String extra = "";
- if (resolved instanceof ResolvedClass) {
- c = "class";
- } else if (resolved instanceof ResolvedMethod) {
- c = "method";
- ResolvedMethod method = (ResolvedMethod) resolved;
- extra = method.getContainingClass().getName();
- } else if (resolved instanceof ResolvedField) {
- c = "field";
- ResolvedField field = (ResolvedField) resolved;
- extra = field.getContainingClass().getName();
- } else if (resolved instanceof ResolvedVariable) {
- c = "variable";
- ResolvedVariable variable = (ResolvedVariable) resolved;
- extra = variable.getType().getName();
- }
- resolutionDescription = String.format(", resolved %1$s: %2$s %3$s",
- c, resolved.getName(), extra);
- }
-
- add("[%1$s%2$s]%3$s%4$s\n", name, description, typeDescription, resolutionDescription);
-
- mIndent++;
- }
-
- @Override
- public void fail(String fail) {
- Assert.fail(fail);
- }
-
- @Override
- public void property(String name, Object value) {
- add("PROPERTY: %s = %s\n", name, value);
- }
-
- @Override
- public void keyword(String text) {
- }
-
- @Override
- public void operator(String text) {
- }
-
- @Override
- public void verticalSpace() {
- }
-
- @Override
- public void space() {
- }
-
- @Override
- public void append(String text) {
- }
-
- @Override
- public void startSuppressBlock() {
- }
-
- @Override
- public void endSuppressBlock() {
- }
-
- @Override
- public void startSuppressIndent() {
- }
-
- @Override
- public void endSuppressIndent() {
- }
-
- @Override
- public void closeInline() {
- mIndent--;
- }
-
- @Override
- public void closeBlock() {
- mIndent--;
- }
-
- @Override
- public void addError(int start, int end, String message) {
- fail(message);
- }
-
- @Override
- public String finish() {
- return mOutput.toString();
- }
-
- @Override
- public void setTimeTaken(long taken) {
- }
-
- @Override
- public void nameNextElement(String name) {
- mName = name;
- }
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/ExternalAnnotationRepositoryTest.java b/lint/cli/src/test/java/com/android/tools/lint/ExternalAnnotationRepositoryTest.java
deleted file mode 100644
index 71a0392..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/ExternalAnnotationRepositoryTest.java
+++ /dev/null
@@ -1,512 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint;
-
-import static com.android.tools.lint.ExternalAnnotationRepository.FN_ANNOTATIONS_XML;
-import static com.google.common.base.Charsets.UTF_8;
-import static java.io.File.separatorChar;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.testutils.SdkTestCase;
-import com.android.tools.lint.client.api.JavaParser.DefaultTypeDescriptor;
-import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
-import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
-import com.android.tools.lint.client.api.JavaParser.ResolvedField;
-import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
-import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
-import com.android.tools.lint.detector.api.JavaContext;
-import com.android.tools.lint.detector.api.LintUtilsTest;
-import com.google.common.base.Splitter;
-import com.google.common.io.Files;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import lombok.ast.ClassDeclaration;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.MethodDeclaration;
-import lombok.ast.Node;
-
-public class ExternalAnnotationRepositoryTest extends SdkTestCase {
-
- @Nullable
- private ExternalAnnotationRepository getSdkAnnotations() {
- File annotations = findSrcRelativeDir("tools/adt/idea/android/annotations");
- if (annotations != null) {
- List<File> files = Collections.singletonList(annotations);
- ExternalAnnotationRepository manager = ExternalAnnotationRepository.create(null, files);
- assertNotNull(manager);
- return manager;
- } else {
- // Can't find it when running from Gradle; ignore for now
- //fail("Could not find annotations database");
- }
-
- return null;
- }
-
- @Nullable
- private ExternalAnnotationRepository getExternalAnnotations(@NonNull String pkg,
- @NonNull String contents) throws IOException {
- File dir = Files.createTempDir();
- try {
- File pkgDir = new File(dir, pkg.replace('.', separatorChar));
- boolean mkdirs = pkgDir.mkdirs();
- assertTrue(mkdirs);
- Files.write(contents, new File(pkgDir, FN_ANNOTATIONS_XML), UTF_8);
-
- List<File> files = Collections.singletonList(dir);
- ExternalAnnotationRepository manager = ExternalAnnotationRepository.create(null, files);
- assertNotNull(manager);
- return manager;
-
- } finally {
- deleteFile(dir);
- }
- }
-
- public void testFields() throws Exception {
- ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
- + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- + "<root>\n"
- + " <item name=\"android.graphics.Color\">\n"
- + " <annotation name=\"android.support.annotation.Annotation1\" />\n"
- + " </item>\n"
- + " <item name=\"android.graphics.Color BLUE\">\n"
- + " <annotation name=\"android.support.annotation.Annotation3\" />\n"
- + " </item>\n"
- + " <item name=\"android.graphics.Color TRANSPARENT\">\n"
- + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
- + " </item>\n"
- + "</root>\n");
- assertNotNull(manager);
- ResolvedClass cls = createClass("android.graphics.Color");
- assertNotNull(manager.getAnnotation(cls, "android.support.annotation.Annotation1"));
- ResolvedField blueField = createField("android.graphics.Color", "BLUE");
- ResolvedField transparentField = createField("android.graphics.Color", "TRANSPARENT");
- assertNotNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation3"));
- assertNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation4"));
- assertNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation5"));
-
- assertNotNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation5"));
- assertNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation3"));
- assertNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation4"));
- }
-
- public void testMethods1() throws Exception {
- ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
- + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- + "<root>\n"
- + " <item name=\"android.graphics.Color int HSVToColor(float[]) 0\">\n"
- + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
- + " </item>\n"
- + " <item name=\"android.graphics.Color int HSVToColor(int, float[]) 1\">\n"
- + " <annotation name=\"android.support.annotation.Annotation7\">\n"
- + " <val name=\"value\" val=\"3\" />\n"
- + " </annotation>\n"
- + " </item>\n"
- + " <item name=\"android.graphics.Color int alpha(int)\">\n"
- + " <annotation name=\"android.support.annotation.Annotation3\" />\n"
- + " </item>\n"
- + " <item name=\"android.graphics.Color int argb(int, int, int, int)\">\n"
- + " <annotation name=\"android.support.annotation.Annotation3\" />\n"
- + " </item>\n"
- + " <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode) 4\">\n"
- + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
- + " </item>\n"
- + " <item name=\"android.graphics.ArrayAdapter ArrayAdapter(android.content.Context, int, int, java.util.List<T>) 3\">\n"
- + " <annotation name=\"android.support.annotation.Annotation4\" />\n"
- + " </item>"
- + "</root>\n");
- assertNotNull(manager);
- ResolvedMethod method1 = createMethod("android.graphics.Color", "int", "HSVToColor",
- "float[]");
- assertNotNull(manager.getAnnotation(method1, 0, "android.support.annotation.Annotation5"));
-
- // Generic types
- ResolvedMethod method2 = createConstructor("android.graphics.ArrayAdapter", "ArrayAdapter",
- "android.content.Context, int, int, java.util.List<T>");
- assertNotNull(manager.getAnnotation(method2, 3, "android.support.annotation.Annotation4"));
-
- // Raw types
- method2 = createConstructor("android.graphics.ArrayAdapter", "ArrayAdapter",
- "android.content.Context, int, int, java.util.List");
- assertNotNull(manager.getAnnotation(method2, 3, "android.support.annotation.Annotation4"));
- }
-
- public void testMethods2() throws Exception {
- ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
- + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- + "<root>\n"
- + " <item name=\"test.pkg.Test java.lang.Object myMethod()\">\n"
- + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
- + " <annotation name=\"android.support.annotation.Annotation6\">\n"
- + " <val name=\"suggest\" val=\""#other(String,String)"\" />\n"
- + " </annotation>\n"
- + " </item>\n"
- + " <item name=\"test.pkg.Test java.lang.Object myMethod(int) 0\">\n"
- + " <annotation name=\"android.support.annotation.Annotation7\">\n"
- + " <val name=\"value\" val=\"3\" />\n"
- + " </annotation>\n"
- + " </item>\n"
- + " <item name=\"test.pkg.Test java.lang.Object myMethod(int[]) 0\">\n"
- + " <annotation name=\"android.support.annotation.Annotation3\" />\n"
- + " </item>\n"
- + " <item name=\"test.pkg.Test java.lang.Object myMethod(int,java.lang.Object) 1\">\n"
- + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
- + " </item>\n"
- + " <item name=\"test.pkg.Test java.lang.Object myMethod(android.content.Context, int, int, java.util.List<T>) 0\">\n"
- + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
- + " </item>\n"
- + " <item name=\"test.pkg.Test java.lang.Object myMethod(android.content.Context, int, int, java.util.List<T>) 1\">\n"
- + " <annotation name=\"android.support.annotation.Annotation3\" />\n"
- + " </item>\n"
- + " <item name=\"test.pkg.Test java.lang.Object myMethod(java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.String>>,int) 0\">\n"
- + " <annotation name=\"android.support.annotation.Annotation4\" />\n"
- + " </item>\n"
- + "</root>\n");
- assertNotNull(manager);
- ResolvedMethod method;
- method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "");
- assertNotNull(manager.getAnnotation(method, "android.support.annotation.Annotation5"));
- assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
- assertNotNull(manager.getAnnotation(method, "android.support.annotation.Annotation6"));
-
- method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "int");
- assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
- assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation7"));
-
- method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "int[]");
- assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
- assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation3"));
- assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
- assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation3"));
-
- method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
- "int,java.lang.Object");
- assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation5"));
-
- method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
- "android.content.Context, int, int, java.util.List<T>");
- assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
- assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation5"));
- assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
- assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation3"));
- assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation5"));
- assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation3"));
- assertNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation5"));
-
- method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
- "android.content.Context, int, int, java.util.List");
- assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation5"));
- assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation3"));
-
- method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
- Arrays.asList("java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.String>>",
- "int"), false);
- assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
- method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
- "java.util.Map,int");
- assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
- }
-
- // test intdef!
-
-
- public void testAnnotationAttributes() throws Exception {
- ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
- + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- + "<root>\n"
- + " <item name=\"android.graphics.Color int HSVToColor(float[]) 0\">\n"
- + " <annotation name=\"android.support.annotation.Annotation7\">\n"
- + " <val name=\"value\" val=\"3\" />\n"
- + " </annotation>\n"
- + " </item>\n"
- + " <item name=\"android.graphics.Color int HSVToColor(int, float[]) 1\">\n"
- + " <annotation name=\"android.support.annotation.Annotation7\">\n"
- + " <val name=\"value\" val=\"3\" />\n"
- + " </annotation>\n"
- + " </item>\n"
- + " <item name=\"android.graphics.Canvas void drawLines(float[], android.graphics.Paint) 0\">\n"
- + " <annotation name=\"android.support.annotation.Annotation7\">\n"
- + " <val name=\"min\" val=\"4\" />\n"
- + " <val name=\"multiple\" val=\"2\" />\n"
- + " </annotation>\n"
- + " <annotation name=\"android.support.annotation.Annotation4\" />\n"
- + " </item>\n"
- + " <item name=\"android.graphics.Canvas int saveLayer(android.graphics.RectF, android.graphics.Paint, int) 2\">\n"
- + " <annotation name=\"android.support.annotation.Annotation8\">\n"
- + " <val name=\"value\" val=\"{android.graphics.Canvas.MATRIX_SAVE_FLAG, android.graphics.Canvas.CLIP_SAVE_FLAG, android.graphics.Canvas.HAS_ALPHA_LAYER_SAVE_FLAG, android.graphics.Canvas.FULL_COLOR_LAYER_SAVE_FLAG, android.graphics.Canvas.CLIP_TO_LAYER_SAVE_FLAG, android.graphics.Canvas.ALL_SAVE_FLAG}\" />\n"
- + " <val name=\"flag\" val=\"true\" />\n"
- + " </annotation>\n"
- + " </item>\n"
- + "</root>\n");
- assertNotNull(manager);
- ResolvedMethod method;
- ResolvedAnnotation annotation;
-
- // Size 1
- method = createMethod("android.graphics.Color", "int", "HSVToColor", "int, float[]");
- assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation7"));
- assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation7"));
- annotation = manager.getAnnotation(method, 1, "android.support.annotation.Annotation7");
- assertNotNull(annotation);
- assertEquals(3L, annotation.getValue());
- assertEquals(3L, annotation.getValue("value"));
- //noinspection ConstantConditions
- assertEquals(3, ((Number)annotation.getValue("value")).intValue());
- assertNotNull(annotation);
-
- // Size 2
- method = createMethod("android.graphics.Canvas", "void", "drawLines", "float[], android.graphics.Paint");
- assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
- annotation = manager.getAnnotation(method, 0, "android.support.annotation.Annotation7");
- assertNotNull(annotation);
- assertEquals(4L, annotation.getValue("min"));
- assertEquals(2L, annotation.getValue("multiple"));
- assertNotNull(annotation);
-
- // Intdef
- method = createMethod("android.graphics.Canvas", "int", "saveLayer",
- "android.graphics.RectF, android.graphics.Paint, int");
- annotation = manager.getAnnotation(method, 2, "android.support.annotation.Annotation8");
- assertNotNull(annotation);
- assertEquals(true, annotation.getValue("flag"));
- Object[] values = (Object[]) annotation.getValue("value");
- assertNotNull(values);
- assertEquals(6, values.length);
- assertTrue(values[0] instanceof ResolvedField);
- assertFalse(values[0].equals(createField("android.graphics.Canvas", "WRONG_NAME")));
- assertEquals(values[0], createField("android.graphics.Canvas", "MATRIX_SAVE_FLAG"));
- assertEquals(values[1], createField("android.graphics.Canvas", "CLIP_SAVE_FLAG"));
- assertEquals(values[2], createField("android.graphics.Canvas", "HAS_ALPHA_LAYER_SAVE_FLAG"));
- assertEquals(values[3], createField("android.graphics.Canvas", "FULL_COLOR_LAYER_SAVE_FLAG"));
- assertEquals(values[4], createField("android.graphics.Canvas", "CLIP_TO_LAYER_SAVE_FLAG"));
- assertEquals(values[5], createField("android.graphics.Canvas", "ALL_SAVE_FLA"));
-
- ResolvedField field = (ResolvedField)values[0];
- assertEquals("android.graphics.Canvas.MATRIX_SAVE_FLAG", field.getSignature());
- assertEquals("android.graphics.Canvas", field.getContainingClassName());
- assertEquals("MATRIX_SAVE_FLAG", field.getName());
- }
-
- public void testConstructors() throws Exception {
- ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
- + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- + "<root>\n"
- + " <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode) 4\">\n"
- + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
- + " </item>\n"
- + "</root>\n");
- assertNotNull(manager);
- ResolvedMethod method = createConstructor("android.graphics.RadialGradient",
- "RadialGradient",
- "float, float, float, int[], float[], android.graphics.Shader.TileMode");
- assertNull(manager.getAnnotation(method, 4, "android.support.annotation.Annotation4"));
- assertNotNull(manager.getAnnotation(method, 4, "android.support.annotation.Annotation5"));
- }
-
- public void testVarArgs() throws Exception {
- ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
- + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- + "<root>\n"
- + " <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int...) 3\">\n"
- + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
- + " </item>\n"
- + " <item name=\"android.graphics.Bitmap android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]) 2\">\n"
- + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
- + " </item>\n"
- + "</root>\n");
- assertNotNull(manager);
- // Match "..." in external annotation with ... in code lookup
- ResolvedMethod method = createConstructor("android.graphics.RadialGradient",
- "RadialGradient",
- "float, float, float, int...");
- assertNotNull(manager.getAnnotation(method, 3, "android.support.annotation.Annotation5"));
- // Match "..." in external annotation with [] in code lookup
- method = createConstructor("android.graphics.RadialGradient",
- "RadialGradient",
- "float, float, float, int[]");
- assertNotNull(manager.getAnnotation(method, 3, "android.support.annotation.Annotation5"));
-
- // Match "[]" in external annotation with [] in code lookup
- method = createMethod("android.graphics.Bitmap",
- "android.graphics.Bitmap",
- "extractAlpha",
- "android.graphics.Paint, int[]");
- assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.Annotation5"));
-
- // Match "[]" in external annotation with ... in code lookup
- method = createMethod("android.graphics.Bitmap",
- "android.graphics.Bitmap",
- "extractAlpha",
- "android.graphics.Paint, int...");
- assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.Annotation5"));
- }
-
- public void testMatchWithEcj() throws Exception {
- try {
- ExternalAnnotationRepository manager = getExternalAnnotations("test.pkg", ""
- + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- + "<root>\n"
- + " <item name=\"test.pkg.Test\">\n"
- + " <annotation name=\"android.support.annotation.Annotation1\" />\n"
- + " </item>\n"
- + " <item name=\"test.pkg.Test.Inner\">\n"
- + " <annotation name=\"android.support.annotation.Annotation2\" />\n"
- + " </item>\n"
- + " <item name=\"test.pkg.Test void foo(int, int[], int...)\">\n"
- + " <annotation name=\"android.support.annotation.Annotation6\" />\n"
- + " </item>\n"
- + " <item name=\"test.pkg.Test void foo(int, int[], int...) 0\">\n"
- + " <annotation name=\"android.support.annotation.Annotation3\" />\n"
- + " </item>\n"
- + " <item name=\"test.pkg.Test void foo(int, int[], int...) 1\">\n"
- + " <annotation name=\"android.support.annotation.Annotation3\" />\n"
- + " <annotation name=\"android.support.annotation.Annotation4\" />\n"
- + " </item>\n"
- + " <item name=\"test.pkg.Test void foo(int, int[], int...) 2\">\n"
- + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
- + " </item>\n"
- + "</root>\n");
- assertNotNull(manager);
- ExternalAnnotationRepository.set(manager);
-
- String source =
- "package test.pkg;\n" +
- "\n" +
- "public class Test {\n" +
- " public void foo(int a, int[] b, int c...) {\n" +
- " }\n" +
- " public static class Inner {\n" +
- " }\n" +
- "}\n";
-
- final JavaContext context = LintUtilsTest.parse(source,
- new File("src/test/pkg/Test.java"));
- assertNotNull(context);
- Node unit = context.getCompilationUnit();
- assertNotNull(unit);
- unit.accept(new ForwardingAstVisitor() {
- @Override
- public boolean visitClassDeclaration(ClassDeclaration node) {
- ResolvedNode resolved = context.resolve(node);
- assertNotNull(resolved);
- assertTrue(resolved.getClass().getName(), resolved instanceof ResolvedClass);
- ResolvedClass cls = (ResolvedClass) resolved;
- if (cls.getName().endsWith(".Inner")) {
- assertNull(cls.getAnnotation("android.support.annotation.Annotation1"));
- assertNotNull(cls.getAnnotation("android.support.annotation.Annotation2"));
- } else {
- assertNotNull(cls.getAnnotation("android.support.annotation.Annotation1"));
- assertNull(cls.getAnnotation("android.support.annotation.Annotation2"));
- }
-
- return super.visitClassDeclaration(node);
- }
-
- @Override
- public boolean visitMethodDeclaration(MethodDeclaration node) {
- ResolvedNode resolved = context.resolve(node);
- assertNotNull(resolved);
- assertTrue(resolved.getClass().getName(), resolved instanceof ResolvedMethod);
- ResolvedMethod method = (ResolvedMethod) resolved;
- assertNull(method.getAnnotation("android.support.annotation.Annotation5"));
- assertNotNull(method.getAnnotation("android.support.annotation.Annotation6"));
- assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation3", 0));
- assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation4", 1));
- assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation3", 1));
- assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation5", 2));
-
- return super.visitMethodDeclaration(node);
- }
- });
- } finally {
- ExternalAnnotationRepository.set(null);
- }
- }
-
- public void testSdkAnnotations() throws Exception {
- ExternalAnnotationRepository manager = getSdkAnnotations();
- if (manager == null) {
- // Can't find it when running from Gradle; ignore for now
- return;
- }
- ResolvedMethod method = createMethod("android.view.LayoutInflater", "android.view.View",
- "createView", "java.lang.String, java.lang.String, android.util.AttributeSet");
- assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.NonNull"));
- }
-
- private static ResolvedClass createClass(String name) {
- ResolvedClass mock = mock(ResolvedClass.class);
- when(mock.getName()).thenReturn(name);
- when(mock.getSignature()).thenReturn(name);
- return mock;
- }
-
- private static ResolvedMethod createConstructor(String containingClass, String name,
- String parameters) {
- return createMethod(containingClass, null, name, parameters, true);
- }
-
- private static ResolvedMethod createMethod(String containingClass, String returnType,
- String name, String parameters) {
- return createMethod(containingClass, returnType, name, parameters, false);
- }
-
- private static ResolvedMethod createMethod(String containingClass, String returnType,
- String name, String parameters, boolean isConstructor) {
- return createMethod(containingClass, returnType, name,
- Splitter.on(',').trimResults().split(parameters), isConstructor);
- }
-
- private static ResolvedMethod createMethod(String containingClass, String returnType,
- String name, Iterable<String> parameters, boolean isConstructor) {
- ResolvedMethod mock = mock(ResolvedMethod.class);
- when(mock.isConstructor()).thenReturn(isConstructor);
- when(mock.getName()).thenReturn(name);
- if (!isConstructor) {
- DefaultTypeDescriptor typeDescriptor = new DefaultTypeDescriptor(returnType);
- when(mock.getReturnType()).thenReturn(typeDescriptor);
- }
- ResolvedClass cls = createClass(containingClass);
- when(mock.getContainingClass()).thenReturn(cls);
- int index = 0;
- for (String argument : parameters) {
- TypeDescriptor typeDescriptor = new DefaultTypeDescriptor(argument);
- when(mock.getArgumentType(index)).thenReturn(typeDescriptor);
- index++;
- }
- when(mock.getArgumentCount()).thenReturn(index);
- return mock;
- }
-
- private static ResolvedField createField(String containingClass, String name) {
- ResolvedField mock = mock(ResolvedField.class);
- when(mock.getName()).thenReturn(name);
- ResolvedClass cls = createClass(containingClass);
- when(mock.getContainingClass()).thenReturn(cls);
- return mock;
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/XmlReporterTest.java b/lint/cli/src/test/java/com/android/tools/lint/XmlReporterTest.java
deleted file mode 100644
index 68c72f8..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/XmlReporterTest.java
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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;
-
-import com.android.tools.lint.checks.AbstractCheckTest;
-import com.android.tools.lint.checks.HardcodedValuesDetector;
-import com.android.tools.lint.checks.ManifestDetector;
-import com.android.tools.lint.checks.TypographyDetector;
-import com.android.tools.lint.detector.api.DefaultPosition;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Location;
-import com.android.tools.lint.detector.api.Project;
-import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.TextFormat;
-import com.android.utils.PositionXmlParser;
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-@SuppressWarnings("javadoc")
-public class XmlReporterTest extends AbstractCheckTest {
- public void test() throws Exception {
- File file = new File(getTargetDir(), "report");
- try {
- LintCliClient client = new LintCliClient() {
- @Override
- String getRevision() {
- return "unittest"; // Hardcode version to keep unit test output stable
- }
- };
- //noinspection ResultOfMethodCallIgnored
- file.getParentFile().mkdirs();
- XmlReporter reporter = new XmlReporter(client, file);
- Project project = Project.create(client, new File("/foo/bar/Foo"),
- new File("/foo/bar/Foo"));
-
- Warning warning1 = new Warning(ManifestDetector.USES_SDK,
- "<uses-sdk> tag should specify a target API level (the highest verified " +
- "version; when running on later versions, compatibility behaviors may " +
- "be enabled) with android:targetSdkVersion=\"?\"",
- Severity.WARNING, project);
- warning1.line = 6;
- warning1.file = new File("/foo/bar/Foo/AndroidManifest.xml");
- warning1.errorLine = " <uses-sdk android:minSdkVersion=\"8\" />\n ^\n";
- warning1.path = "AndroidManifest.xml";
- warning1.location = Location.create(warning1.file,
- new DefaultPosition(6, 4, 198), new DefaultPosition(6, 42, 236));
-
- Warning warning2 = new Warning(HardcodedValuesDetector.ISSUE,
- "[I18N] Hardcoded string \"Fooo\", should use @string resource",
- Severity.WARNING, project);
- warning2.line = 11;
- warning2.file = new File("/foo/bar/Foo/res/layout/main.xml");
- warning2.errorLine = " android:text=\"Fooo\" />\n" +
- " ~~~~~~~~~~~~~~~~~~~\n";
- warning2.path = "res/layout/main.xml";
- warning2.location = Location.create(warning2.file,
- new DefaultPosition(11, 8, 377), new DefaultPosition(11, 27, 396));
-
- List<Warning> warnings = new ArrayList<Warning>();
- warnings.add(warning1);
- warnings.add(warning2);
-
- reporter.write(0, 2, warnings);
-
- String report = Files.toString(file, Charsets.UTF_8);
- assertEquals(
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
- "<issues format=\"4\" by=\"lint unittest\">\n" +
- "\n" +
- " <issue\n" +
- " id=\"UsesMinSdkAttributes\"\n" +
- " severity=\"Warning\"\n" +
- " message=\"<uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion="?"\"\n" +
- " category=\"Correctness\"\n" +
- " priority=\"9\"\n" +
- " summary=\"Minimum SDK and target SDK attributes not defined\"\n" +
- " explanation=\"The manifest should contain a `<uses-sdk>` element which defines the minimum API Level required for the application to run, as well as the target version (the highest API level you have tested the version for.)\"\n" +
- " url=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
- " urls=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
- " errorLine1=\" <uses-sdk android:minSdkVersion="8" />\"\n" +
- " errorLine2=\" ^\">\n" +
- " <location\n" +
- " file=\"AndroidManifest.xml\"\n" +
- " line=\"7\"\n" +
- " column=\"5\"/>\n" +
- " </issue>\n" +
- "\n" +
- " <issue\n" +
- " id=\"HardcodedText\"\n" +
- " severity=\"Warning\"\n" +
- " message=\"[I18N] Hardcoded string "Fooo", should use @string resource\"\n" +
- " category=\"Internationalization\"\n" +
- " priority=\"5\"\n" +
- " summary=\"Hardcoded text\"\n" +
- " explanation=\"Hardcoding text attributes directly in layout files is bad for several reasons:\n" +
- "\n" +
- "* When creating configuration variations (for example for landscape or portrait)you have to repeat the actual text (and keep it up to date when making changes)\n" +
- "\n" +
- "* The application cannot be translated to other languages by just adding new translations for existing string resources.\n" +
- "\n" +
- "In Android Studio and Eclipse there are quickfixes to automatically extract this hardcoded string into a resource lookup.\"\n" +
- " errorLine1=\" android:text="Fooo" />\"\n" +
- " errorLine2=\" ~~~~~~~~~~~~~~~~~~~\">\n" +
- " <location\n" +
- " file=\"res/layout/main.xml\"\n" +
- " line=\"12\"\n" +
- " column=\"9\"/>\n" +
- " </issue>\n" +
- "\n" +
- "</issues>\n",
- report);
-
- // Make sure the XML is valid
- Document document = new PositionXmlParser().parse(report);
- assertNotNull(document);
- assertEquals(2, document.getElementsByTagName("issue").getLength());
- } finally {
- //noinspection ResultOfMethodCallIgnored
- file.delete();
- }
- }
-
- public void testFullPaths() throws Exception {
- File file = new File(getTargetDir(), "report");
- try {
- LintCliClient client = new LintCliClient() {
- @Override
- String getRevision() {
- return "unittest"; // Hardcode version to keep unit test output stable
- }
- };
- client.mFlags.setFullPath(true);
-
- //noinspection ResultOfMethodCallIgnored
- file.getParentFile().mkdirs();
- XmlReporter reporter = new XmlReporter(client, file);
- Project project = Project.create(client, new File("/foo/bar/Foo"),
- new File("/foo/bar/Foo"));
-
- Warning warning1 = new Warning(ManifestDetector.USES_SDK,
- "<uses-sdk> tag should specify a target API level (the highest verified " +
- "version; when running on later versions, compatibility behaviors may " +
- "be enabled) with android:targetSdkVersion=\"?\"",
- Severity.WARNING, project);
- warning1.line = 6;
- warning1.file = new File("/foo/bar/../Foo/AndroidManifest.xml");
- warning1.errorLine = " <uses-sdk android:minSdkVersion=\"8\" />\n ^\n";
- warning1.path = "AndroidManifest.xml";
- warning1.location = Location.create(warning1.file,
- new DefaultPosition(6, 4, 198), new DefaultPosition(6, 42, 236));
-
- Warning warning2 = new Warning(HardcodedValuesDetector.ISSUE,
- "[I18N] Hardcoded string \"Fooo\", should use @string resource",
- Severity.WARNING, project);
- warning2.line = 11;
- warning2.file = new File("/foo/bar/Foo/res/layout/main.xml");
- warning2.errorLine = " android:text=\"Fooo\" />\n" +
- " ~~~~~~~~~~~~~~~~~~~\n";
- warning2.path = "res/layout/main.xml";
- warning2.location = Location.create(warning2.file,
- new DefaultPosition(11, 8, 377), new DefaultPosition(11, 27, 396));
-
- List<Warning> warnings = new ArrayList<Warning>();
- warnings.add(warning1);
- warnings.add(warning2);
-
- reporter.write(0, 2, warnings);
-
- String report = Files.toString(file, Charsets.UTF_8);
- assertEquals(
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
- "<issues format=\"4\" by=\"lint unittest\">\n" +
- "\n" +
- " <issue\n" +
- " id=\"UsesMinSdkAttributes\"\n" +
- " severity=\"Warning\"\n" +
- " message=\"<uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion="?"\"\n" +
- " category=\"Correctness\"\n" +
- " priority=\"9\"\n" +
- " summary=\"Minimum SDK and target SDK attributes not defined\"\n" +
- " explanation=\"The manifest should contain a `<uses-sdk>` element which defines the minimum API Level required for the application to run, as well as the target version (the highest API level you have tested the version for.)\"\n" +
- " url=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
- " urls=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
- " errorLine1=\" <uses-sdk android:minSdkVersion="8" />\"\n" +
- " errorLine2=\" ^\">\n" +
- " <location\n" +
- " file=\"/foo/Foo/AndroidManifest.xml\"\n" +
- " line=\"7\"\n" +
- " column=\"5\"/>\n" +
- " </issue>\n" +
- "\n" +
- " <issue\n" +
- " id=\"HardcodedText\"\n" +
- " severity=\"Warning\"\n" +
- " message=\"[I18N] Hardcoded string "Fooo", should use @string resource\"\n" +
- " category=\"Internationalization\"\n" +
- " priority=\"5\"\n" +
- " summary=\"Hardcoded text\"\n" +
- " explanation=\"Hardcoding text attributes directly in layout files is bad for several reasons:\n" +
- "\n" +
- "* When creating configuration variations (for example for landscape or portrait)you have to repeat the actual text (and keep it up to date when making changes)\n" +
- "\n" +
- "* The application cannot be translated to other languages by just adding new translations for existing string resources.\n" +
- "\n" +
- "In Android Studio and Eclipse there are quickfixes to automatically extract this hardcoded string into a resource lookup.\"\n" +
- " errorLine1=\" android:text="Fooo" />\"\n" +
- " errorLine2=\" ~~~~~~~~~~~~~~~~~~~\">\n" +
- " <location\n" +
- " file=\"/foo/bar/Foo/res/layout/main.xml\"\n" +
- " line=\"12\"\n" +
- " column=\"9\"/>\n" +
- " </issue>\n" +
- "\n" +
- "</issues>\n",
- report);
-
- // Make sure the XML is valid
- Document document = new PositionXmlParser().parse(report);
- assertNotNull(document);
- assertEquals(2, document.getElementsByTagName("issue").getLength());
- } finally {
- //noinspection ResultOfMethodCallIgnored
- file.delete();
- }
- }
-
- public void testNonPrintableChars() throws Exception {
- // See https://code.google.com/p/android/issues/detail?id=56205
- File file = new File(getTargetDir(), "report");
- try {
- LintCliClient client = new LintCliClient() {
- @Override
- String getRevision() {
- return "unittest"; // Hardcode version to keep unit test output stable
- }
- };
- //noinspection ResultOfMethodCallIgnored
- file.getParentFile().mkdirs();
- XmlReporter reporter = new XmlReporter(client, file);
- Project project = Project.create(client, new File("/foo/bar/Foo"),
- new File("/foo/bar/Foo"));
-
- Warning warning1 = new Warning(TypographyDetector.FRACTIONS,
- String.format("Use fraction character %1$c (%2$s) instead of %3$s ?",
- '\u00BC', "¼", "1/4"), Severity.WARNING, project);
- warning1.line = 592;
- warning1.file = new File("/foo/bar/Foo/AndroidManifest.xml");
- warning1.errorLine =
- " <string name=\"user_registration_name3_3\">Register 3/3</string>\n" +
- " ^";
- warning1.path = "res/values-en/common_strings.xml";
- warning1.location = Location.create(warning1.file,
- new DefaultPosition(592, 46, -1), null);
-
- List<Warning> warnings = new ArrayList<Warning>();
- warnings.add(warning1);
-
- reporter.write(0, 2, warnings);
-
- String report = Files.toString(file, Charsets.UTF_8);
- assertEquals(""
- + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- + "<issues format=\"4\" by=\"lint unittest\">\n"
- + "\n"
- + " <issue\n"
- + " id=\"TypographyFractions\"\n"
- + " severity=\"Warning\"\n"
- + " message=\"Use fraction character ¼ (&#188;) instead of 1/4 ?\"\n"
- + " category=\"Usability:Typography\"\n"
- + " priority=\"5\"\n"
- + " summary=\"Fraction string can be replaced with fraction character\"\n"
- + " explanation=\"You can replace certain strings, such as 1/2, and 1/4, with dedicated characters for these, such as ½ (&#189;) and ¼ (&#188;). This can help make the text more readable.\"\n"
- + " url=\"http://en.wikipedia.org/wiki/Number_Forms\"\n"
- + " urls=\"http://en.wikipedia.org/wiki/Number_Forms\">\n"
- + " <location\n"
- + " file=\"AndroidManifest.xml\"\n"
- + " line=\"593\"\n"
- + " column=\"47\"/>\n"
- + " </issue>\n"
- + "\n"
- + "</issues>\n",
- report);
-
- // Make sure the XML is valid
- Document document = new PositionXmlParser().parse(report);
- assertNotNull(document);
- assertEquals(1, document.getElementsByTagName("issue").getLength());
- String explanation = ((Element)document.getElementsByTagName("issue").item(0)).
- getAttribute("explanation");
- assertEquals(TypographyDetector.FRACTIONS.getExplanation(TextFormat.RAW),
- explanation);
- } finally {
- //noinspection ResultOfMethodCallIgnored
- file.delete();
- }
- }
-
- @Override
- protected Detector getDetector() {
- fail("Not used in this test");
- return null;
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java
deleted file mode 100644
index 11c0e1d..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint.checks;
-
-import com.android.tools.lint.checks.infrastructure.LintDetectorTest;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-
-public abstract class AbstractCheckTest extends LintDetectorTest {
- @Override
- protected InputStream getTestResource(String relativePath, boolean expectExists) {
- String path = "data" + File.separator + relativePath; //$NON-NLS-1$
- InputStream stream = AbstractCheckTest.class.getResourceAsStream(path);
- if (stream == null) {
- File root = getRootDir();
- assertNotNull(root);
- String pkg = AbstractCheckTest.class.getName();
- pkg = pkg.substring(0, pkg.lastIndexOf('.'));
- File f = new File(root,
- "tools/base/lint/cli/src/test/java/".replace('/', File.separatorChar)
- + pkg.replace('.', File.separatorChar)
- + File.separatorChar + path);
- if (f.exists()) {
- try {
- return new BufferedInputStream(new FileInputStream(f));
- } catch (FileNotFoundException e) {
- stream = null;
- if (expectExists) {
- fail("Could not find file " + relativePath);
- }
- }
- }
- }
- if (!expectExists && stream == null) {
- return null;
- }
- return stream;
- }
-
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java
deleted file mode 100644
index 7da1444..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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;
-import com.android.tools.lint.detector.api.Issue;
-
-import java.util.List;
-
-@SuppressWarnings("javadoc")
-public class AnnotationDetectorTest extends AbstractCheckTest {
- public void test() throws Exception {
- assertEquals(
- "src/test/pkg/WrongAnnotation.java:9: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
- " public static void foobar(View view, @SuppressLint(\"NewApi\") int foo) { // Invalid: class-file check\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "src/test/pkg/WrongAnnotation.java:10: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
- " @SuppressLint(\"NewApi\") // Invalid\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "src/test/pkg/WrongAnnotation.java:12: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
- " @SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid: class-file based check on local variable\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "src/test/pkg/WrongAnnotation.java:14: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
- " @android.annotation.SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid (FQN)\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "src/test/pkg/WrongAnnotation.java:28: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
- " @SuppressLint(\"NewApi\")\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "5 errors, 0 warnings\n",
-
- lintProject(
- "src/test/pkg/WrongAnnotation.java.txt=>src/test/pkg/WrongAnnotation.java"
- ));
- }
-
- @Override
- protected Detector getDetector() {
- return new AnnotationDetector();
- }
-
- @Override
- protected List<Issue> getIssues() {
- List<Issue> issues = super.getIssues();
-
- // Need these issues on to be found by the registry as well to look up scope
- // in id references (these ids are referenced in the unit test java file below)
- issues.add(ApiDetector.UNSUPPORTED);
- issues.add(SdCardDetector.ISSUE);
-
- return issues;
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ButtonDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/ButtonDetectorTest.java
deleted file mode 100644
index dbd1093..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/ButtonDetectorTest.java
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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;
-import com.android.tools.lint.detector.api.Issue;
-
-@SuppressWarnings("javadoc")
-public class ButtonDetectorTest extends AbstractCheckTest {
- private static Issue sTestIssue;
-
- @Override
- protected boolean isEnabled(Issue issue) {
- return super.isEnabled(issue) && sTestIssue == null || issue == sTestIssue;
- }
-
- @Override
- protected Detector getDetector() {
- return new ButtonDetector();
- }
-
- public void testButtonOrder() throws Exception {
- sTestIssue = ButtonDetector.ORDER;
- assertEquals(
- "res/layout/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "0 errors, 7 warnings\n" +
- "",
-
- lintProject(
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "res/layout/buttonbar.xml",
- "res/values/buttonbar-values.xml"));
- }
-
- public void testButtonOrder2() throws Exception {
- // If the layout is in v14, it had better have the right order
- sTestIssue = ButtonDetector.ORDER;
- assertEquals(
- "res/layout-v14/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-v14/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-v14/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-v14/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-v14/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-v14/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-v14/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "0 errors, 7 warnings\n" +
- "",
-
- lintProject(
- "minsdk5targetsdk14.xml=>AndroidManifest.xml",
- "res/layout/buttonbar.xml=>res/layout-v14/buttonbar.xml",
- "res/values/buttonbar-values.xml"));
- }
-
- public void testButtonOrder3() throws Exception {
- // Similar to test 3, but also complain if the -v version is *higher* than 14
- sTestIssue = ButtonDetector.ORDER;
- assertEquals(
- "res/layout-v16/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-v16/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-v16/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-v16/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-v16/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-v16/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-v16/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "0 errors, 7 warnings\n" +
- "",
-
- lintProject(
- "minsdk5targetsdk14.xml=>AndroidManifest.xml",
- "res/layout/buttonbar.xml=>res/layout-v16/buttonbar.xml",
- "res/values/buttonbar-values.xml"));
- }
-
- public void testButtonOrder4() throws Exception {
- // Targeting 14 but using a layout that also needs to work for older platforms:
- sTestIssue = ButtonDetector.ORDER;
- assertEquals(
- "res/layout/buttonbar.xml:12: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:44: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:92: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:124: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:140: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:156: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:177: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "0 errors, 7 warnings\n" +
- "",
-
- lintProject(
- "minsdk5targetsdk14.xml=>AndroidManifest.xml",
- "res/layout/buttonbar.xml",
- "res/values/buttonbar-values.xml"));
- }
-
- public void testButtonOrder5() throws Exception {
- // If the layout is in a non-ICS folder and has the wrong button order,
- // but there is a v14 version of the layout, don't complain about the non-v14 version
- sTestIssue = ButtonDetector.ORDER;
- assertEquals(
- "No warnings.",
-
- lintProject(
- "minsdk5targetsdk14.xml=>AndroidManifest.xml",
- "res/layout/buttonbar.xml",
- "res/layout/layout1.xml=>res/layout-v14/buttonbar.xml",
- "res/values/buttonbar-values.xml"));
- }
-
- public void testSuppressed() throws Exception {
- sTestIssue = ButtonDetector.ORDER;
- assertEquals(
- "No warnings.",
-
- lintProject(
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "res/layout/buttonbar_suppressed.xml",
- "res/values/buttonbar-values.xml"));
- }
-
- public void testButtonOrderRelativeLayout() throws Exception {
- sTestIssue = ButtonDetector.ORDER;
- assertEquals(
- "No warnings.",
-
- lintProject("res/layout/buttonbar2.xml", "res/values/buttonbar-values.xml"));
- }
-
- public void testButtonOrderRelativeLayout2() throws Exception {
- sTestIssue = ButtonDetector.ORDER;
- assertEquals(
- "res/layout/buttonbar3.xml:27: Warning: Cancel button should be on the left [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "0 errors, 1 warnings\n" +
- "",
-
- lintProject(
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "res/layout/buttonbar3.xml",
- "res/values/buttonbar-values.xml"));
- }
-
- public void testButtonOrderRelativeLayout3() throws Exception {
- sTestIssue = ButtonDetector.ORDER;
- assertEquals(
- "No warnings.",
-
- lintProject("res/layout/buttonbar4.xml", "res/values/buttonbar-values.xml"));
- }
-
-
- public void testCase() throws Exception {
- sTestIssue = ButtonDetector.CASE;
- assertEquals(
- "res/values/buttonbar-values.xml:9: Warning: The standard Android way to capitalize Ok is \"OK\" (tip: use @android:string/ok instead) [ButtonCase]\n" +
- " <string name=\"resume2\"> Ok </string>\n" +
- " ^\n" +
- "res/values/buttonbar-values.xml:10: Warning: The standard Android way to capitalize CANCEL is \"Cancel\" (tip: use @android:string/cancel instead) [ButtonCase]\n" +
- " <string name=\"giveup2\">\"CANCEL\"</string>\n" +
- " ^\n" +
- "0 errors, 2 warnings\n" +
- "",
-
- lintProject("res/layout/buttonbar.xml", "res/values/buttonbar-values.xml"));
- }
-
- public void testBack() throws Exception {
- sTestIssue = ButtonDetector.BACK_BUTTON;
- assertEquals(
- "res/layout/buttonbar.xml:183: Warning: Back buttons are not standard on Android; see design guide's navigation section [BackButton]\n" +
- " <Button\n" +
- " ^\n" +
- "0 errors, 1 warnings\n" +
- "",
-
- lintProject("res/layout/buttonbar.xml", "res/values/buttonbar-values.xml"));
- }
-
- public void testOldApp() throws Exception {
- // Target SDK < 14 - no warnings on button order
- sTestIssue = ButtonDetector.ORDER;
- assertEquals(
- "No warnings.",
-
- lintProject(
- "minsdk5targetsdk9.xml=>AndroidManifest.xml",
- "res/layout/buttonbar.xml",
- "res/values/buttonbar-values.xml"));
- }
-
- public void testEnglishLocales() throws Exception {
- sTestIssue = ButtonDetector.ORDER;
- assertEquals(
- "res/layout-en-rGB/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-en-rGB/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-en-rGB/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-en-rGB/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-en-rGB/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-en-rGB/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-en-rGB/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "0 errors, 7 warnings\n" +
- "",
-
- lintProject(
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "res/layout/buttonbar.xml=>res/layout-en-rGB/buttonbar.xml",
- "res/values/buttonbar-values.xml=>res/values-en-rGB/buttonbar-values.xml"));
- }
-
- public void testOtherLocales() throws Exception {
- sTestIssue = ButtonDetector.ORDER;
- assertEquals(
- // Hardcoded values only
- "res/layout-de/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout-de/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
- " <Button\n" +
- " ^\n" +
- "0 errors, 2 warnings\n" +
- "",
-
- lintProject(
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "res/layout/buttonbar.xml=>res/layout-de/buttonbar.xml",
- "res/values/buttonbar-values.xml=>res/values-de/buttonbar-values.xml"));
- }
-
- public void testOtherLocales2() throws Exception {
- sTestIssue = ButtonDetector.CASE;
- assertEquals(
- "No warnings.",
-
- lintProject("res/layout/buttonbar.xml=>res/layout-de/buttonbar.xml",
- "res/values/buttonbar-values.xml=>res/values-de/buttonbar-values.xml"));
- }
-
- public void testButtonStyle() throws Exception {
- sTestIssue = ButtonDetector.STYLE;
- assertEquals(
- "res/layout/buttonbar.xml:12: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:17: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:28: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:33: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:44: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:49: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:60: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:65: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:76: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:81: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:92: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:97: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:108: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:113: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:124: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:129: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:140: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:145: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:156: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/buttonbar.xml:161: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
- " <Button\n" +
- " ^\n" +
- "0 errors, 20 warnings\n",
-
- lintProject(
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "res/layout/buttonbar.xml",
- "res/layout/buttonbar2.xml",
- "res/layout/buttonbar3.xml",
- "res/values/buttonbar-values.xml"));
- }
-
- public void testButtonStyleOldMinSdk() throws Exception {
- sTestIssue = ButtonDetector.STYLE;
- assertEquals(
- "No warnings.",
-
- lintProject(
- "apicheck/minsdk4.xml=>AndroidManifest.xml",
- "res/layout/buttonbar.xml",
- "res/layout/buttonbar2.xml",
- "res/layout/buttonbar3.xml",
- "res/values/buttonbar-values.xml"));
- }
-
- public void testYesNo() throws Exception {
- sTestIssue = ButtonDetector.CASE;
- assertEquals(""
- + "res/layout/yesno.xml:10: Warning: @android:string/yes actually returns \"OK\", not \"Yes\"; use @android:string/ok instead or create a local string resource for Yes [ButtonCase]\n"
- + " android:text=\"@android:string/yes\" />\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "res/layout/yesno.xml:15: Warning: @android:string/no actually returns \"Cancel\", not \"No\"; use @android:string/cancel instead or create a local string resource for No [ButtonCase]\n"
- + " android:text=\"@android:string/no\" />\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 2 warnings\n",
-
- lintProject(
- "apicheck/minsdk4.xml=>AndroidManifest.xml",
- "res/layout/yesno.xml"));
- }
-
- public void testIssue101279() throws Exception {
- // Regression test for https://code.google.com/p/android/issues/detail?id=101279
- sTestIssue = ButtonDetector.STYLE;
- assertEquals(
- "No warnings.",
-
- lintProject(
- "apicheck/minsdk4.xml=>AndroidManifest.xml",
- "res/layout/buttonbar5.xml"));
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/CallSuperDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/CallSuperDetectorTest.java
deleted file mode 100644
index e0c988d..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/CallSuperDetectorTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.tools.lint.checks;
-
-import com.android.tools.lint.detector.api.Detector;
-
-public class CallSuperDetectorTest extends AbstractCheckTest {
- @Override
- protected Detector getDetector() {
- return new CallSuperDetector();
- }
-
- public void testCallSuper() throws Exception {
- assertEquals(""
- + "src/test/pkg/CallSuperTest.java:11: Warning: Overriding method should call super.test1 [MissingSuperCall]\n"
- + " protected void test1() { // ERROR\n"
- + " ~~~~~~~\n"
- + "src/test/pkg/CallSuperTest.java:14: Warning: Overriding method should call super.test2 [MissingSuperCall]\n"
- + " protected void test2() { // ERROR\n"
- + " ~~~~~~~\n"
- + "src/test/pkg/CallSuperTest.java:17: Warning: Overriding method should call super.test3 [MissingSuperCall]\n"
- + " protected void test3() { // ERROR\n"
- + " ~~~~~~~\n"
- + "src/test/pkg/CallSuperTest.java:20: Warning: Overriding method should call super.test4 [MissingSuperCall]\n"
- + " protected void test4(int arg) { // ERROR\n"
- + " ~~~~~~~~~~~~~~\n"
- + "src/test/pkg/CallSuperTest.java:26: Warning: Overriding method should call super.test5 [MissingSuperCall]\n"
- + " protected void test5(int arg1, boolean arg2, Map<List<String>,?> arg3, // ERROR\n"
- + " ^\n"
- + "src/test/pkg/CallSuperTest.java:30: Warning: Overriding method should call super.test5 [MissingSuperCall]\n"
- + " protected void test5() { // ERROR\n"
- + " ~~~~~~~\n"
- + "0 errors, 6 warnings\n",
-
- lintProject("src/test/pkg/CallSuperTest.java.txt=>src/test/pkg/CallSuperTest.java",
- "src/android/support/annotation/CallSuper.java.txt=>src/android/support/annotation/CallSuper.java"));
- }
-
- public void testDetachFromWindow() throws Exception {
- assertEquals(""
- + "src/test/pkg/DetachedFromWindow.java:7: Warning: Overriding method should call super.onDetachedFromWindow [MissingSuperCall]\n"
- + " protected void onDetachedFromWindow() {\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/DetachedFromWindow.java:26: Warning: Overriding method should call super.onDetachedFromWindow [MissingSuperCall]\n"
- + " protected void onDetachedFromWindow() {\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 2 warnings\n",
-
- lintProject("src/test/pkg/DetachedFromWindow.java.txt=>" +
- "src/test/pkg/DetachedFromWindow.java"));
- }
-
- public void testWatchFaceVisibility() throws Exception {
- assertEquals(""
- + "src/test/pkg/WatchFaceTest.java:9: Warning: Overriding method should call super.onVisibilityChanged [MissingSuperCall]\n"
- + " public void onVisibilityChanged(boolean visible) { // ERROR: Missing super call\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 1 warnings\n",
-
- lintProject(
- "src/test/pkg/WatchFaceTest.java.txt=>src/test/pkg/WatchFaceTest.java",
- "stubs/WatchFaceService.java.txt=>src/android/support/wearable/watchface/WatchFaceService.java",
- "stubs/CanvasWatchFaceService.java.txt=>src/android/support/wearable/watchface/CanvasWatchFaceService.java"
- ));
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/DetectMissingPrefixTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/DetectMissingPrefixTest.java
deleted file mode 100644
index e09e85c..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/DetectMissingPrefixTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint.checks;
-
-import com.android.tools.lint.detector.api.Detector;
-
-@SuppressWarnings("javadoc")
-public class DetectMissingPrefixTest extends AbstractCheckTest {
- @Override
- protected Detector getDetector() {
- return new DetectMissingPrefix();
- }
-
- public void test() throws Exception {
- assertEquals(
- "res/layout/namespace.xml:2: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
- "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" xmlns:other=\"http://foo.bar\" android:id=\"@+id/newlinear\" android:orientation=\"vertical\" android:layout_width=\"match_parent\" android:layout_height=\"match_parent\" orientation=\"true\">\n" +
- " ~~~~~~~~~~~~~~~~~~\n" +
- "res/layout/namespace.xml:3: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
- " <Button style=\"@style/setupWizardOuterFrame\" android.text=\"Button\" android:id=\"@+id/button1\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\"></Button>\n" +
- " ~~~~~~~~~~~~~~~~~~~~~\n" +
- "res/layout/namespace.xml:5: Error: Unexpected namespace prefix \"other\" found for tag LinearLayout [MissingPrefix]\n" +
- " <LinearLayout other:orientation=\"horizontal\"/>\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "3 errors, 0 warnings\n",
-
- lintFiles("res/layout/namespace.xml"));
- }
-
- public void testCustomNamespace() throws Exception {
- assertEquals(
- "res/layout/namespace2.xml:9: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
- " orientation=\"true\">\n" +
- " ~~~~~~~~~~~~~~~~~~\n" +
- "1 errors, 0 warnings\n",
-
- lintFiles("res/layout/namespace2.xml"));
- }
-
- public void testManifest() throws Exception {
- assertEquals(
- "AndroidManifest.xml:4: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
- " versionCode=\"1\"\n" +
- " ~~~~~~~~~~~~~~~\n" +
- "AndroidManifest.xml:11: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
- " android.label=\"@string/app_name\" >\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "AndroidManifest.xml:18: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
- " <category name=\"android.intent.category.LAUNCHER\" />\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "3 errors, 0 warnings\n",
-
- lintFiles("missingprefix.xml=>AndroidManifest.xml"));
- }
-
- public void testLayoutAttributes() throws Exception {
- assertEquals(
- "No warnings.",
-
- lintFiles("res/layout/namespace3.xml"));
- }
-
- public void testLayoutAttributes2() throws Exception {
- assertEquals(
- "No warnings.",
-
- lintFiles("res/layout/namespace4.xml"));
- }
-
- public void testUnusedNamespace() throws Exception {
- assertEquals(
- "No warnings.",
-
- lintProject("res/layout/message_edit_detail.xml"));
- }
-
- public void testMissingLayoutAttribute() throws Exception {
- assertEquals(
- "res/layout/rtl.xml:7: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
- " layout_gravity=\"left\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~\n" +
- "res/layout/rtl.xml:8: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
- " layout_alignParentLeft=\"true\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "res/layout/rtl.xml:9: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
- " editable=\"false\"\n" +
- " ~~~~~~~~~~~~~~~~\n" +
- "3 errors, 0 warnings\n",
-
- lintProject(
- "overdraw/project.properties=>project.properties",
- "rtl/minsdk5targetsdk17.xml=>AndroidManifest.xml",
- "rtl/rtl_noprefix.xml=>res/layout/rtl.xml"
- ));
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/GradleDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/GradleDetectorTest.java
deleted file mode 100644
index b883a7b..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/GradleDetectorTest.java
+++ /dev/null
@@ -1,882 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint.checks;
-
-import static com.android.SdkConstants.GRADLE_PLUGIN_MINIMUM_VERSION;
-import static com.android.SdkConstants.GRADLE_PLUGIN_RECOMMENDED_VERSION;
-import static com.android.tools.lint.checks.GradleDetector.ACCIDENTAL_OCTAL;
-import static com.android.tools.lint.checks.GradleDetector.COMPATIBILITY;
-import static com.android.tools.lint.checks.GradleDetector.DEPENDENCY;
-import static com.android.tools.lint.checks.GradleDetector.DEPRECATED;
-import static com.android.tools.lint.checks.GradleDetector.GRADLE_GETTER;
-import static com.android.tools.lint.checks.GradleDetector.GRADLE_PLUGIN_COMPATIBILITY;
-import static com.android.tools.lint.checks.GradleDetector.PATH;
-import static com.android.tools.lint.checks.GradleDetector.PLUS;
-import static com.android.tools.lint.checks.GradleDetector.REMOTE_VERSION;
-import static com.android.tools.lint.checks.GradleDetector.STRING_INTEGER;
-import static com.android.tools.lint.checks.GradleDetector.getNamedDependency;
-import static com.android.tools.lint.checks.GradleDetector.getNewValue;
-import static com.android.tools.lint.checks.GradleDetector.getOldValue;
-import static com.android.tools.lint.detector.api.TextFormat.TEXT;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.builder.model.AndroidArtifact;
-import com.android.builder.model.AndroidLibrary;
-import com.android.builder.model.Dependencies;
-import com.android.builder.model.MavenCoordinates;
-import com.android.builder.model.Variant;
-import com.android.tools.lint.client.api.LintClient;
-import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.DefaultPosition;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Implementation;
-import com.android.tools.lint.detector.api.Issue;
-import com.android.tools.lint.detector.api.Location;
-import com.android.tools.lint.detector.api.Project;
-import com.android.tools.lint.detector.api.Scope;
-import com.android.tools.lint.detector.api.Severity;
-import com.android.utils.Pair;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.io.Files;
-
-import org.codehaus.groovy.ast.ASTNode;
-import org.codehaus.groovy.ast.CodeVisitorSupport;
-import org.codehaus.groovy.ast.GroovyCodeVisitor;
-import org.codehaus.groovy.ast.builder.AstBuilder;
-import org.codehaus.groovy.ast.expr.ArgumentListExpression;
-import org.codehaus.groovy.ast.expr.ClosureExpression;
-import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.ast.expr.MapEntryExpression;
-import org.codehaus.groovy.ast.expr.MethodCallExpression;
-import org.codehaus.groovy.ast.expr.NamedArgumentListExpression;
-import org.codehaus.groovy.ast.expr.TupleExpression;
-import org.codehaus.groovy.ast.stmt.BlockStatement;
-import org.codehaus.groovy.ast.stmt.ExpressionStatement;
-import org.codehaus.groovy.ast.stmt.ReturnStatement;
-import org.codehaus.groovy.ast.stmt.Statement;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * <b>NOTE</b>: Most GradleDetector unit tests are in the Studio plugin, as tests
- * for IntellijGradleDetector
- */
-public class GradleDetectorTest extends AbstractCheckTest {
-
- private File mSdkDir;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
-
- if (mSdkDir != null) {
- deleteFile(mSdkDir);
- mSdkDir = null;
- }
- }
-
- /** Creates a mock SDK installation structure, containing a fixed set of dependencies */
- private File getMockSupportLibraryInstallation() {
- if (mSdkDir == null) {
- // Make fake SDK "installation" such that we can predict the set
- // of Maven repositories discovered by this test
- mSdkDir = Files.createTempDir();
-
- String[] paths = new String[]{
- // Android repository
- "extras/android/m2repository/com/android/support/appcompat-v7/18.0.0/appcompat-v7-18.0.0.aar",
- "extras/android/m2repository/com/android/support/appcompat-v7/19.0.0/appcompat-v7-19.0.0.aar",
- "extras/android/m2repository/com/android/support/appcompat-v7/19.0.1/appcompat-v7-19.0.1.aar",
- "extras/android/m2repository/com/android/support/appcompat-v7/19.1.0/appcompat-v7-19.1.0.aar",
- "extras/android/m2repository/com/android/support/appcompat-v7/20.0.0/appcompat-v7-20.0.0.aar",
- "extras/android/m2repository/com/android/support/appcompat-v7/21.0.0/appcompat-v7-21.0.0.aar",
- "extras/android/m2repository/com/android/support/appcompat-v7/21.0.2/appcompat-v7-21.0.2.aar",
- "extras/android/m2repository/com/android/support/cardview-v7/21.0.0/cardview-v7-21.0.0.aar",
- "extras/android/m2repository/com/android/support/cardview-v7/21.0.2/cardview-v7-21.0.2.aar",
- "extras/android/m2repository/com/android/support/support-v13/20.0.0/support-v13-20.0.0.aar",
- "extras/android/m2repository/com/android/support/support-v13/21.0.0/support-v13-21.0.0.aar",
- "extras/android/m2repository/com/android/support/support-v13/21.0.2/support-v13-21.0.2.aar",
- "extras/android/m2repository/com/android/support/support-v4/20.0.0/support-v4-20.0.0.aar",
- "extras/android/m2repository/com/android/support/support-v4/21.0.0/support-v4-21.0.0.aar",
- "extras/android/m2repository/com/android/support/support-v4/21.0.2/support-v4-21.0.2.aar",
-
- // Google repository
- "extras/google/m2repository/com/google/android/gms/play-services/3.1.36/play-services-3.1.36.aar",
- "extras/google/m2repository/com/google/android/gms/play-services/3.1.59/play-services-3.1.59.aar",
- "extras/google/m2repository/com/google/android/gms/play-services/3.2.25/play-services-3.2.25.aar",
- "extras/google/m2repository/com/google/android/gms/play-services/3.2.65/play-services-3.2.65.aar",
- "extras/google/m2repository/com/google/android/gms/play-services/4.0.30/play-services-4.0.30.aar",
- "extras/google/m2repository/com/google/android/gms/play-services/4.1.32/play-services-4.1.32.aar",
- "extras/google/m2repository/com/google/android/gms/play-services/4.2.42/play-services-4.2.42.aar",
- "extras/google/m2repository/com/google/android/gms/play-services/4.3.23/play-services-4.3.23.aar",
- "extras/google/m2repository/com/google/android/gms/play-services/4.4.52/play-services-4.4.52.aar",
- "extras/google/m2repository/com/google/android/gms/play-services/5.0.89/play-services-5.0.89.aar",
- "extras/google/m2repository/com/google/android/gms/play-services/6.1.11/play-services-6.1.11.aar",
- "extras/google/m2repository/com/google/android/gms/play-services/6.1.71/play-services-6.1.71.aar",
- "extras/google/m2repository/com/google/android/gms/play-services-wearable/5.0.77/play-services-wearable-5.0.77.aar",
- "extras/google/m2repository/com/google/android/gms/play-services-wearable/6.1.11/play-services-wearable-6.1.11.aar",
- "extras/google/m2repository/com/google/android/gms/play-services-wearable/6.1.71/play-services-wearable-6.1.71.aar",
- "extras/google/m2repository/com/google/android/support/wearable/1.0.0/wearable-1.0.0.aar"
- };
-
- for (String path : paths) {
- File file = new File(mSdkDir, path.replace('/', File.separatorChar));
- File parent = file.getParentFile();
- if (!parent.exists()) {
- boolean ok = parent.mkdirs();
- assertTrue(ok);
- }
- try {
- boolean created = file.createNewFile();
- assertTrue(created);
- } catch (IOException e) {
- fail(e.toString());
- }
- }
- }
-
- return mSdkDir;
- }
-
- public void testGetOldValue() {
- assertEquals("11.0.2", getOldValue(DEPENDENCY,
- "A newer version of com.google.guava:guava than 11.0.2 is available: 17.0.0",
- TEXT));
- assertNull(getOldValue(DEPENDENCY, "Bogus", TEXT));
- assertNull(getOldValue(DEPENDENCY, "bogus", TEXT));
- // targetSdkVersion 20, compileSdkVersion 19: Should replace targetVersion 20 with 19
- assertEquals("20", getOldValue(DEPENDENCY,
- "The targetSdkVersion (20) should not be higher than the compileSdkVersion (19)",
- TEXT));
- assertEquals("'19'", getOldValue(STRING_INTEGER,
- "Use an integer rather than a string here (replace '19' with just 19)", TEXT));
- assertEquals("android", getOldValue(DEPRECATED,
- "'android' is deprecated; use 'com.android.application' instead", TEXT));
- assertEquals("android-library", getOldValue(DEPRECATED,
- "'android-library' is deprecated; use 'com.android.library' instead", TEXT));
- assertEquals("packageName", getOldValue(DEPRECATED,
- "Deprecated: Replace 'packageName' with 'applicationId'", TEXT));
- assertEquals("packageNameSuffix", getOldValue(DEPRECATED,
- "Deprecated: Replace 'packageNameSuffix' with 'applicationIdSuffix'", TEXT));
- assertEquals("18.0.0", getOldValue(DEPENDENCY,
- "Old buildToolsVersion 18.0.0; recommended version is 19.1 or later", TEXT));
- }
-
- public void testGetNewValue() {
- assertEquals("17.0.0", getNewValue(DEPENDENCY,
- "A newer version of com.google.guava:guava than 11.0.2 is available: 17.0.0",
- TEXT));
- assertNull(getNewValue(DEPENDENCY,
- "A newer version of com.google.guava:guava than 11.0.2 is available", TEXT));
- assertNull(getNewValue(DEPENDENCY, "bogus", TEXT));
- // targetSdkVersion 20, compileSdkVersion 19: Should replace targetVersion 20 with 19
- assertEquals("19", getNewValue(DEPENDENCY,
- "The targetSdkVersion (20) should not be higher than the compileSdkVersion (19)",
- TEXT));
- assertEquals("19", getNewValue(STRING_INTEGER,
- "Use an integer rather than a string here (replace '19' with just 19)", TEXT));
- assertEquals("com.android.application", getNewValue(DEPRECATED,
- "'android' is deprecated; use 'com.android.application' instead", TEXT));
- assertEquals("com.android.library", getNewValue(DEPRECATED,
- "'android-library' is deprecated; use 'com.android.library' instead", TEXT));
- assertEquals("applicationId", getNewValue(DEPRECATED,
- "Deprecated: Replace 'packageName' with 'applicationId'", TEXT));
- assertEquals("applicationIdSuffix", getNewValue(DEPRECATED,
- "Deprecated: Replace 'packageNameSuffix' with 'applicationIdSuffix'", TEXT));
- assertEquals("19.1", getNewValue(DEPENDENCY,
- "Old buildToolsVersion 18.0.0; recommended version is 19.1 or later", TEXT));
- }
-
- public void test() throws Exception {
- mEnabled = Sets.newHashSet(COMPATIBILITY, DEPRECATED, DEPENDENCY, PLUS);
- assertEquals(""
- + "build.gradle:25: Error: This support library should not use a lower version (13) than the targetSdkVersion (17) [GradleCompatible]\n"
- + " compile 'com.android.support:appcompat-v7:13.0.0'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:1: Warning: 'android' is deprecated; use 'com.android.application' instead [GradleDeprecated]\n"
- + "apply plugin: 'android'\n"
- + "~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:5: Warning: Old buildToolsVersion 19.0.0; recommended version is 19.1 or later [GradleDependency]\n"
- + " buildToolsVersion \"19.0.0\"\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:24: Warning: A newer version of com.google.guava:guava than 11.0.2 is available: 18.0 [GradleDependency]\n"
- + " freeCompile 'com.google.guava:guava:11.0.2'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:25: Warning: A newer version of com.android.support:appcompat-v7 than 13.0.0 is available: 21.0.2 [GradleDependency]\n"
- + " compile 'com.android.support:appcompat-v7:13.0.0'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:23: Warning: Avoid using + in version numbers; can lead to unpredictable and unrepeatable builds (com.android.support:appcompat-v7:+) [GradleDynamicVersion]\n"
- + " compile 'com.android.support:appcompat-v7:+'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "1 errors, 5 warnings\n",
-
- lintProject("gradle/Dependencies.gradle=>build.gradle"));
- }
-
- public void testCompatibility() throws Exception {
- mEnabled = Collections.singleton(COMPATIBILITY);
- assertEquals(""
- + "build.gradle:16: Error: This support library should not use a lower version (18) than the targetSdkVersion (19) [GradleCompatible]\n"
- + " compile 'com.android.support:support-v4:18.0.0'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "1 errors, 0 warnings\n",
-
- lintProject("gradle/Compatibility.gradle=>build.gradle"));
- }
-
- public void testIncompatiblePlugin() throws Exception {
- mEnabled = Collections.singleton(GRADLE_PLUGIN_COMPATIBILITY);
- assertEquals(""
- + "build.gradle:6: Error: You must use a newer version of the Android Gradle plugin. The minimum supported version is " + GRADLE_PLUGIN_MINIMUM_VERSION + " and the recommended version is " + GRADLE_PLUGIN_RECOMMENDED_VERSION + " [AndroidGradlePluginVersion]\n"
- + " classpath 'com.android.tools.build:gradle:0.1.0'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "1 errors, 0 warnings\n",
-
- lintProject("gradle/IncompatiblePlugin.gradle=>build.gradle"));
- }
-
- public void testSetter() throws Exception {
- mEnabled = Collections.singleton(GRADLE_GETTER);
- assertEquals(""
- + "build.gradle:18: Error: Bad method name: pick a unique method name which does not conflict with the implicit getters for the defaultConfig properties. For example, try using the prefix compute- instead of get-. [GradleGetter]\n"
- + " versionCode getVersionCode\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:19: Error: Bad method name: pick a unique method name which does not conflict with the implicit getters for the defaultConfig properties. For example, try using the prefix compute- instead of get-. [GradleGetter]\n"
- + " versionName getVersionName\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "2 errors, 0 warnings\n",
-
- lintProject("gradle/Setter.gradle=>build.gradle"));
- }
-
- public void testDependencies() throws Exception {
- mEnabled = Collections.singleton(DEPENDENCY);
- assertEquals(""
- + "build.gradle:5: Warning: Old buildToolsVersion 19.0.0; recommended version is 19.1 or later [GradleDependency]\n"
- + " buildToolsVersion \"19.0.0\"\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:24: Warning: A newer version of com.google.guava:guava than 11.0.2 is available: 18.0 [GradleDependency]\n"
- + " freeCompile 'com.google.guava:guava:11.0.2'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:25: Warning: A newer version of com.android.support:appcompat-v7 than 13.0.0 is available: 21.0.2 [GradleDependency]\n"
- + " compile 'com.android.support:appcompat-v7:13.0.0'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 3 warnings\n",
-
- lintProject("gradle/Dependencies.gradle=>build.gradle"));
- }
-
- public void testLongHandDependencies() throws Exception {
- mEnabled = Collections.singleton(DEPENDENCY);
- assertEquals(""
- + "build.gradle:9: Warning: A newer version of com.android.support:support-v4 than 19.0 is available: 21.0.2 [GradleDependency]\n"
- + " compile group: 'com.android.support', name: 'support-v4', version: '19.0'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 1 warnings\n",
-
- lintProject("gradle/DependenciesProps.gradle=>build.gradle"));
- }
-
- public void testDependenciesMinSdkVersion() throws Exception {
- mEnabled = Collections.singleton(DEPENDENCY);
- assertEquals(""
- + "build.gradle:13: Warning: Using the appcompat library when minSdkVersion >= 14 and compileSdkVersion < 21 is not necessary [GradleDependency]\n"
- + " compile 'com.android.support:appcompat-v7:+'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 1 warnings\n",
-
- lintProject("gradle/Dependencies14.gradle=>build.gradle"));
- }
-
- public void testDependenciesMinSdkVersionLollipop() throws Exception {
- mEnabled = Collections.singleton(DEPENDENCY);
- assertEquals("No warnings.",
- lintProject("gradle/Dependencies14_21.gradle=>build.gradle"));
- }
-
- public void testDependenciesNoMicroVersion() throws Exception {
- // Regression test for https://code.google.com/p/android/issues/detail?id=77594
- mEnabled = Collections.singleton(DEPENDENCY);
- assertEquals(""
- + "build.gradle:13: Warning: A newer version of com.google.code.gson:gson than 2.2 is available: 2.3 [GradleDependency]\n"
- + " compile 'com.google.code.gson:gson:2.2'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 1 warnings\n",
-
- lintProject("gradle/DependenciesGson.gradle=>build.gradle"));
- }
-
- public void testPaths() throws Exception {
- mEnabled = Collections.singleton(PATH);
- assertEquals(""
- + "build.gradle:4: Warning: Do not use Windows file separators in .gradle files; use / instead [GradlePath]\n"
- + " compile files('my\\\\libs\\\\http.jar')\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:5: Warning: Avoid using absolute paths in .gradle files [GradlePath]\n"
- + " compile files('/libs/android-support-v4.jar')\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 2 warnings\n",
-
- lintProject("gradle/Paths.gradle=>build.gradle"));
- }
-
- public void testIdSuffix() throws Exception {
- mEnabled = Collections.singleton(PATH);
- assertEquals(""
- + "build.gradle:6: Warning: Package suffix should probably start with a \".\" [GradlePath]\n"
- + " applicationIdSuffix \"debug\"\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 1 warnings\n",
-
- lintProject("gradle/IdSuffix.gradle=>build.gradle"));
- }
-
- public void testPackage() throws Exception {
- mEnabled = Collections.singleton(DEPRECATED);
- assertEquals(""
- + "build.gradle:5: Warning: Deprecated: Replace 'packageName' with 'applicationId' [GradleDeprecated]\n"
- + " packageName 'my.pkg'\n"
- + " ~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:9: Warning: Deprecated: Replace 'packageNameSuffix' with 'applicationIdSuffix' [GradleDeprecated]\n"
- + " packageNameSuffix \".debug\"\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 2 warnings\n",
-
- lintProject("gradle/Package.gradle=>build.gradle"));
- }
-
- public void testPlus() throws Exception {
- mEnabled = Collections.singleton(PLUS);
- assertEquals(""
- + "build.gradle:9: Warning: Avoid using + in version numbers; can lead to unpredictable and unrepeatable builds (com.android.support:appcompat-v7:+) [GradleDynamicVersion]\n"
- + " compile 'com.android.support:appcompat-v7:+'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:10: Warning: Avoid using + in version numbers; can lead to unpredictable and unrepeatable builds (com.android.support:support-v4:21.0.+) [GradleDynamicVersion]\n"
- + " compile group: 'com.android.support', name: 'support-v4', version: '21.0.+'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 2 warnings\n",
-
- lintProject("gradle/Plus.gradle=>build.gradle"));
- }
-
- public void testStringInt() throws Exception {
- mEnabled = Collections.singleton(STRING_INTEGER);
- assertEquals(""
- + "build.gradle:4: Error: Use an integer rather than a string here (replace '19' with just 19) [StringShouldBeInt]\n"
- + " compileSdkVersion '19'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:7: Error: Use an integer rather than a string here (replace '8' with just 8) [StringShouldBeInt]\n"
- + " minSdkVersion '8'\n"
- + " ~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:8: Error: Use an integer rather than a string here (replace '16' with just 16) [StringShouldBeInt]\n"
- + " targetSdkVersion '16'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~\n"
- + "3 errors, 0 warnings\n",
-
- lintProject("gradle/StringInt.gradle=>build.gradle"));
- }
-
- public void testSuppressLine2() throws Exception {
- mEnabled = null;
- assertEquals("No warnings.",
-
- lintProject("gradle/SuppressLine2.gradle=>build.gradle"));
- }
-
- public void testDeprecatedPluginId() throws Exception {
- mEnabled = Sets.newHashSet(DEPRECATED);
- assertEquals(""
- + "build.gradle:4: Warning: 'android' is deprecated; use 'com.android.application' instead [GradleDeprecated]\n"
- + "apply plugin: 'android'\n"
- + "~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:5: Warning: 'android-library' is deprecated; use 'com.android.library' instead [GradleDeprecated]\n"
- + "apply plugin: 'android-library'\n"
- + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 2 warnings\n",
-
- lintProject("gradle/DeprecatedPluginId.gradle=>build.gradle"));
- }
-
- public void testIgnoresGStringsInDependencies() throws Exception {
- mEnabled = null;
- assertEquals("No warnings.",
-
- lintProject("gradle/IgnoresGStringsInDependencies.gradle=>build.gradle"));
- }
-
- public void testAccidentalOctal() throws Exception {
- mEnabled = Collections.singleton(ACCIDENTAL_OCTAL);
- assertEquals(""
- + "build.gradle:13: Error: The leading 0 turns this number into octal which is probably not what was intended (interpreted as 8) [AccidentalOctal]\n"
- + " versionCode 010\n"
- + " ~~~~~~~~~~~~~~~\n"
- + "build.gradle:16: Error: The leading 0 turns this number into octal which is probably not what was intended (and it is not a valid octal number) [AccidentalOctal]\n"
- + " versionCode 01 // line suffix comments are not handled correctly\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "2 errors, 0 warnings\n",
-
- lintProject("gradle/AccidentalOctal.gradle=>build.gradle"));
- }
-
- public void testBadPlayServicesVersion() throws Exception {
- mEnabled = Collections.singleton(COMPATIBILITY);
- assertEquals(""
- + "build.gradle:5: Error: Version 5.2.08 should not be used; the app can not be published with this version. Use version 6.1.71 instead. [GradleCompatible]\n"
- + " compile 'com.google.android.gms:play-services:5.2.08'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "1 errors, 0 warnings\n",
-
- lintProject("gradle/PlayServices.gradle=>build.gradle"));
- }
-
- public void testRemoteVersions() throws Exception {
- mEnabled = Collections.singleton(REMOTE_VERSION);
- try {
- HashMap<String, String> data = Maps.newHashMap();
- GradleDetector.ourMockData = data;
- data.put("http://search.maven.org/solrsearch/select?q=g:%22joda-time%22+AND+a:%22joda-time%22&core=gav&rows=1&wt=json",
- "{\"responseHeader\":{\"status\":0,\"QTime\":1,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"joda-time\\\" AND a:\\\"joda-time\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"rows\":\"1\",\"version\":\"2.2\"}},\"response\":{\"numFound\":17,\"start\":0,\"docs\":[{\"id\":\"joda-time:joda-time:2.3\",\"g\":\"joda-time\",\"a\":\"joda-time\",\"v\":\"2.3\",\"p\":\"jar\",\"timestamp\":1376674285000,\"tags\":[\"replace\",\"time\",\"library\",\"date\",\"handling\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\".pom\"]}]}}");
- data.put("http://search.maven.org/solrsearch/select?q=g:%22com.squareup.dagger%22+AND+a:%22dagger%22&core=gav&rows=1&wt=json",
- "{\"responseHeader\":{\"status\":0,\"QTime\":1,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"com.squareup.dagger\\\" AND a:\\\"dagger\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"rows\":\"1\",\"version\":\"2.2\"}},\"response\":{\"numFound\":5,\"start\":0,\"docs\":[{\"id\":\"com.squareup.dagger:dagger:1.2.1\",\"g\":\"com.squareup.dagger\",\"a\":\"dagger\",\"v\":\"1.2.1\",\"p\":\"jar\",\"timestamp\":1392614597000,\"tags\":[\"dependency\",\"android\",\"injector\",\"java\",\"fast\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\"-tests.jar\",\".jar\",\".pom\"]}]}}");
-
- assertEquals(""
- + "build.gradle:9: Warning: A newer version of joda-time:joda-time than 2.1 is available: 2.3 [NewerVersionAvailable]\n"
- + " compile 'joda-time:joda-time:2.1'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:10: Warning: A newer version of com.squareup.dagger:dagger than 1.2.0 is available: 1.2.1 [NewerVersionAvailable]\n"
- + " compile 'com.squareup.dagger:dagger:1.2.0'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 2 warnings\n",
-
- lintProject("gradle/RemoteVersions.gradle=>build.gradle"));
- } finally {
- GradleDetector.ourMockData = null;
- }
- }
-
- public void testRemoteVersionsWithPreviews() throws Exception {
- // If the most recent version is a rc version, query for all versions
- mEnabled = Collections.singleton(REMOTE_VERSION);
- try {
- HashMap<String, String> data = Maps.newHashMap();
- GradleDetector.ourMockData = data;
- data.put("http://search.maven.org/solrsearch/select?q=g:%22com.google.guava%22+AND+a:%22guava%22&core=gav&rows=1&wt=json",
- "{\"responseHeader\":{\"status\":0,\"QTime\":0,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"com.google.guava\\\" AND a:\\\"guava\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"rows\":\"1\",\"version\":\"2.2\"}},\"response\":{\"numFound\":38,\"start\":0,\"docs\":[{\"id\":\"com.google.guava:guava:18.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"18.0-rc1\",\"p\":\"bundle\",\"timestamp\":1407266204000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]}]}}");
- data.put("http://search.maven.org/solrsearch/select?q=g:%22com.google.guava%22+AND+a:%22guava%22&core=gav&wt=json",
- "{\"responseHeader\":{\"status\":0,\"QTime\":1,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"com.google.guava\\\" AND a:\\\"guava\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"version\":\"2.2\"}},\"response\":{\"numFound\":38,\"start\":0,\"docs\":[{\"id\":\"com.google.guava:guava:18.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"18.0-rc1\",\"p\":\"bundle\",\"timestamp\":1407266204000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:17.0\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"17.0\",\"p\":\"bundle\",\"timestamp\":1398199666000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:17.0-rc2\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"17.0-rc2\",\"p\":\"bundle\",\"timestamp\":1397162341000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:17.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"17.0-rc1\",\"p\":\"bundle\",\"timestamp\":1396985408000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:16.0.1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"16.0.1\",\"p\":\"bundle\",\"timestamp\":1391467528000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:16.0\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"16.0\",\"p\":\"bundle\",\"timestamp\":1389995088000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:16.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"16.0-rc1\",\"p\":\"bundle\",\"timestamp\":1387495574000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:15.0\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"15.0\",\"p\":\"bundle\",\"timestamp\":1378497169000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"inject\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"that\",\"more\",\"utility\",\"guava\",\"dependencies\",\"javax\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\",\"-cdi1.0.jar\"]},{\"id\":\"com.google.guava:guava:15.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"15.0-rc1\",\"p\":\"bundle\",\"timestamp\":1377542588000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"inject\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"that\",\"more\",\"utility\",\"guava\",\"dependencies\",\"javax\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:14.0.1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"14.0.1\",\"p\":\"bundle\",\"timestamp\":1363305439000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"inject\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"that\",\"more\",\"utility\",\"guava\",\"dependencies\",\"javax\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]}]}}");
-
- assertEquals(""
- + "build.gradle:9: Warning: A newer version of com.google.guava:guava than 11.0.2 is available: 17.0 [NewerVersionAvailable]\n"
- + " compile 'com.google.guava:guava:11.0.2'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "build.gradle:10: Warning: A newer version of com.google.guava:guava than 16.0-rc1 is available: 18.0.0-rc1 [NewerVersionAvailable]\n"
- + " compile 'com.google.guava:guava:16.0-rc1'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 2 warnings\n",
-
- lintProject("gradle/RemoteVersions2.gradle=>build.gradle"));
- } finally {
- GradleDetector.ourMockData = null;
- }
- }
-
- public void testPreviewVersions() throws Exception {
- mEnabled = Collections.singleton(DEPENDENCY);
- // This test only works when SdkConstants.GRADLE_PLUGIN_RECOMMENDED_VERSION contains
- // a preview string:
- if (!GRADLE_PLUGIN_RECOMMENDED_VERSION.startsWith("1.0.0-rc")) {
- return;
- }
- assertEquals(""
- + "build.gradle:6: Warning: A newer version of com.android.tools.build:gradle than 1.0.0-rc0 is available: " + GRADLE_PLUGIN_RECOMMENDED_VERSION + " [GradleDependency]\n"
- + " classpath 'com.android.tools.build:gradle:1.0.0-rc0'\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 1 warnings\n",
-
- lintProject("gradle/PreviewDependencies.gradle=>build.gradle"));
- }
-
-
- public void testDependenciesInVariables() throws Exception {
- mEnabled = Collections.singleton(DEPENDENCY);
- assertEquals(""
- + "build.gradle:10: Warning: A newer version of com.google.android.gms:play-services-wearable than 5.0.77 is available: 6.1.71 [GradleDependency]\n"
- + " compile \"com.google.android.gms:play-services-wearable:${GPS_VERSION}\"\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 1 warnings\n",
-
- lintProject("gradle/DependenciesVariable.gradle=>build.gradle"));
- }
-
- @Override
- protected void checkReportedError(@NonNull Context context, @NonNull Issue issue,
- @NonNull Severity severity, @Nullable Location location, @NonNull String message) {
- if (issue == DEPENDENCY && message.startsWith("Using the appcompat library when ")) {
- // No data embedded in this specific message
- return;
- }
-
- // Issues we're supporting getOldFrom
- if (issue == DEPENDENCY
- || issue == STRING_INTEGER
- || issue == DEPRECATED
- || issue == PLUS) {
- assertNotNull("Could not extract message tokens from " + message,
- GradleDetector.getOldValue(issue, message, TEXT));
- }
-
- if (issue == DEPENDENCY
- || issue == STRING_INTEGER
- || issue == DEPRECATED) {
- assertNotNull("Could not extract message tokens from " + message,
- GradleDetector.getNewValue(issue, message, TEXT));
- }
-
- if (issue == COMPATIBILITY) {
- if (message.startsWith("Version ")) {
- assertNotNull("Could not extract message tokens from " + message,
- GradleDetector.getNewValue(issue, message, TEXT));
- }
- }
- }
-
- public void testGetNamedDependency() {
- assertEquals("com.android.support:support-v4:21.0.+", getNamedDependency(
- "group: 'com.android.support', name: 'support-v4', version: '21.0.+'"
- ));
- assertEquals("com.android.support:support-v4:21.0.+", getNamedDependency(
- "name:'support-v4', group: \"com.android.support\", version: '21.0.+'"
- ));
- assertEquals("junit:junit:4.+", getNamedDependency(
- "group: 'junit', name: 'junit', version: '4.+'"
- ));
- assertEquals("com.android.support:support-v4:19.0.+", getNamedDependency(
- "group: 'com.android.support', name: 'support-v4', version: '19.0.+'"
- ));
- assertEquals("com.google.guava:guava:11.0.1", getNamedDependency(
- "group: 'com.google.guava', name: 'guava', version: '11.0.1', transitive: false"
- ));
- assertEquals("com.google.api-client:google-api-client:1.6.0-beta", getNamedDependency(
- "group: 'com.google.api-client', name: 'google-api-client', version: '1.6.0-beta', transitive: false"
- ));
- assertEquals("org.robolectric:robolectric:2.3-SNAPSHOT", getNamedDependency(
- "group: 'org.robolectric', name: 'robolectric', version: '2.3-SNAPSHOT'"
- ));
- }
-
- // -------------------------------------------------------------------------------------------
- // Test infrastructure below here
- // -------------------------------------------------------------------------------------------
-
- static final Implementation IMPLEMENTATION = new Implementation(
- GroovyGradleDetector.class,
- Scope.GRADLE_SCOPE);
- static {
- for (Issue issue : new BuiltinIssueRegistry().getIssues()) {
- if (issue.getImplementation().getDetectorClass() == GradleDetector.class) {
- issue.setImplementation(IMPLEMENTATION);
- }
- }
- }
-
- @Override
- protected Detector getDetector() {
- return new GroovyGradleDetector();
- }
-
- private Set<Issue> mEnabled;
-
- @Override
- protected TestConfiguration getConfiguration(LintClient client, Project project) {
- return new TestConfiguration(client, project, null) {
- @Override
- public boolean isEnabled(@NonNull Issue issue) {
- return super.isEnabled(issue) && (mEnabled == null || mEnabled.contains(issue));
- }
- };
- }
-
- @Override
- protected TestLintClient createClient() {
- return new TestLintClient() {
- @Nullable
- @Override
- public File getSdkHome() {
- return getMockSupportLibraryInstallation();
- }
-
- @NonNull
- @Override
- protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
- if (!"testDependenciesInVariables".equals(getName())) {
- return super.createProject(dir, referenceDir);
- }
-
- return new Project(this, dir, referenceDir) {
- @Override
- public boolean isGradleProject() {
- return true;
- }
-
- @Nullable
- @Override
- public Variant getCurrentVariant() {
- /*
- Simulate variant which has an AndroidLibrary with
- resolved coordinates
-
- com.google.android.gms:play-services-wearable:5.0.77"
- */
- MavenCoordinates coordinates = mock(MavenCoordinates.class);
- when(coordinates.getGroupId()).thenReturn("com.google.android.gms");
- when(coordinates.getArtifactId()).thenReturn("play-services-wearable");
- when(coordinates.getVersion()).thenReturn("5.0.77");
-
- AndroidLibrary library = mock(AndroidLibrary.class);
- when(library.getResolvedCoordinates()).thenReturn(coordinates);
- List<AndroidLibrary> libraries = Collections.singletonList(library);
-
- Dependencies dependencies = mock(Dependencies.class);
- when(dependencies.getLibraries()).thenReturn(libraries);
-
- AndroidArtifact artifact = mock(AndroidArtifact.class);
- when(artifact.getDependencies()).thenReturn(dependencies);
-
- Variant variant = mock(Variant.class);
- when(variant.getMainArtifact()).thenReturn(artifact);
- return variant;
- }
- };
- }
- };
- }
-
- // Copy of com.android.build.gradle.tasks.GroovyGradleDetector (with "static" added as
- // a modifier, and the unused field IMPLEMENTATION removed, and with fail(t.toString())
- // inserted into visitBuildScript's catch handler.
- //
- // THIS CODE DUPLICATION IS NOT AN IDEAL SITUATION! But, it's preferable to a lack of
- // tests.
- //
- // A more proper fix would be to extract the groovy detector into a library shared by
- // the testing framework and the gradle plugin.
-
- public static class GroovyGradleDetector extends GradleDetector {
- @Override
- public void visitBuildScript(@NonNull final Context context, Map<String, Object> sharedData) {
- try {
- visitQuietly(context, sharedData);
- } catch (Throwable t) {
- // ignore
- // Parsing the build script can involve class loading that we sometimes can't
- // handle. This happens for example when running lint in build-system/tests/api/.
- // This is a lint limitation rather than a user error, so don't complain
- // about these. Consider reporting a Issue#LINT_ERROR.
- fail(t.toString());
- }
- }
-
- private void visitQuietly(@NonNull final Context context,
- @SuppressWarnings("UnusedParameters") Map<String, Object> sharedData) {
- String source = context.getContents();
- if (source == null) {
- return;
- }
-
- List<ASTNode> astNodes = new AstBuilder().buildFromString(source);
- GroovyCodeVisitor visitor = new CodeVisitorSupport() {
- private List<MethodCallExpression> mMethodCallStack = Lists.newArrayList();
- @Override
- public void visitMethodCallExpression(MethodCallExpression expression) {
- mMethodCallStack.add(expression);
- super.visitMethodCallExpression(expression);
- Expression arguments = expression.getArguments();
- String parent = expression.getMethodAsString();
- String parentParent = getParentParent();
- if (arguments instanceof ArgumentListExpression) {
- ArgumentListExpression ale = (ArgumentListExpression)arguments;
- List<Expression> expressions = ale.getExpressions();
- if (expressions.size() == 1 &&
- expressions.get(0) instanceof ClosureExpression) {
- if (isInterestingBlock(parent, parentParent)) {
- ClosureExpression closureExpression =
- (ClosureExpression)expressions.get(0);
- Statement block = closureExpression.getCode();
- if (block instanceof BlockStatement) {
- BlockStatement bs = (BlockStatement)block;
- for (Statement statement : bs.getStatements()) {
- if (statement instanceof ExpressionStatement) {
- ExpressionStatement e = (ExpressionStatement)statement;
- if (e.getExpression() instanceof MethodCallExpression) {
- checkDslProperty(parent,
- (MethodCallExpression)e.getExpression(),
- parentParent);
- }
- } else if (statement instanceof ReturnStatement) {
- // Single item in block
- ReturnStatement e = (ReturnStatement)statement;
- if (e.getExpression() instanceof MethodCallExpression) {
- checkDslProperty(parent,
- (MethodCallExpression)e.getExpression(),
- parentParent);
- }
- }
- }
- }
- }
- }
- } else if (arguments instanceof TupleExpression) {
- if (isInterestingStatement(parent, parentParent)) {
- TupleExpression te = (TupleExpression) arguments;
- Map<String, String> namedArguments = Maps.newHashMap();
- List<String> unnamedArguments = Lists.newArrayList();
- for (Expression subExpr : te.getExpressions()) {
- if (subExpr instanceof NamedArgumentListExpression) {
- NamedArgumentListExpression nale = (NamedArgumentListExpression) subExpr;
- for (MapEntryExpression mae : nale.getMapEntryExpressions()) {
- namedArguments.put(mae.getKeyExpression().getText(),
- mae.getValueExpression().getText());
- }
- }
- }
- checkMethodCall(context, parent, parentParent, namedArguments, unnamedArguments, expression);
- }
- }
- assert !mMethodCallStack.isEmpty();
- assert mMethodCallStack.get(mMethodCallStack.size() - 1) == expression;
- mMethodCallStack.remove(mMethodCallStack.size() - 1);
- }
-
- private String getParentParent() {
- for (int i = mMethodCallStack.size() - 2; i >= 0; i--) {
- MethodCallExpression expression = mMethodCallStack.get(i);
- Expression arguments = expression.getArguments();
- if (arguments instanceof ArgumentListExpression) {
- ArgumentListExpression ale = (ArgumentListExpression)arguments;
- List<Expression> expressions = ale.getExpressions();
- if (expressions.size() == 1 &&
- expressions.get(0) instanceof ClosureExpression) {
- return expression.getMethodAsString();
- }
- }
- }
-
- return null;
- }
-
- private void checkDslProperty(String parent, MethodCallExpression c,
- String parentParent) {
- String property = c.getMethodAsString();
- if (isInterestingProperty(property, parent, getParentParent())) {
- String value = getText(c.getArguments());
- checkDslPropertyAssignment(context, property, value, parent, parentParent, c, c);
- }
- }
-
- private String getText(ASTNode node) {
- String source = context.getContents();
- Pair<Integer, Integer> offsets = getOffsets(node, context);
- return source.substring(offsets.getFirst(), offsets.getSecond());
- }
- };
-
- for (ASTNode node : astNodes) {
- node.visit(visitor);
- }
- }
-
- @NonNull
- private static Pair<Integer, Integer> getOffsets(ASTNode node, Context context) {
- if (node.getLastLineNumber() == -1 && node instanceof TupleExpression) {
- // Workaround: TupleExpressions yield bogus offsets, so use its
- // children instead
- TupleExpression exp = (TupleExpression) node;
- List<Expression> expressions = exp.getExpressions();
- if (!expressions.isEmpty()) {
- return Pair.of(
- getOffsets(expressions.get(0), context).getFirst(),
- getOffsets(expressions.get(expressions.size() - 1), context).getSecond());
- }
- }
- String source = context.getContents();
- assert source != null; // because we successfully parsed
- int start = 0;
- int end = source.length();
- int line = 1;
- int startLine = node.getLineNumber();
- int startColumn = node.getColumnNumber();
- int endLine = node.getLastLineNumber();
- int endColumn = node.getLastColumnNumber();
- int column = 1;
- for (int index = 0, len = end; index < len; index++) {
- if (line == startLine && column == startColumn) {
- start = index;
- }
- if (line == endLine && column == endColumn) {
- end = index;
- break;
- }
-
- char c = source.charAt(index);
- if (c == '\n') {
- line++;
- column = 1;
- } else {
- column++;
- }
- }
-
- return Pair.of(start, end);
- }
-
- @Override
- protected int getStartOffset(@NonNull Context context, @NonNull Object cookie) {
- ASTNode node = (ASTNode) cookie;
- Pair<Integer, Integer> offsets = getOffsets(node, context);
- return offsets.getFirst();
- }
-
- @Override
- protected Location createLocation(@NonNull Context context, @NonNull Object cookie) {
- ASTNode node = (ASTNode) cookie;
- Pair<Integer, Integer> offsets = getOffsets(node, context);
- int fromLine = node.getLineNumber() - 1;
- int fromColumn = node.getColumnNumber() - 1;
- int toLine = node.getLastLineNumber() - 1;
- int toColumn = node.getLastColumnNumber() - 1;
- return Location.create(context.file,
- new DefaultPosition(fromLine, fromColumn, offsets.getFirst()),
- new DefaultPosition(toLine, toColumn, offsets.getSecond()));
- }
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java
deleted file mode 100644
index 631f246..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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("javadoc")
-public class HandlerDetectorTest extends AbstractCheckTest {
- @Override
- protected Detector getDetector() {
- return new HandlerDetector();
- }
-
- public void testRegistered() throws Exception {
- assertEquals(
- "src/test/pkg/HandlerTest.java:12: Warning: This Handler class should be static or leaks might occur (test.pkg.HandlerTest.Inner) [HandlerLeak]\n" +
- " public class Inner extends Handler { // ERROR\n" +
- " ~~~~~\n" +
- "src/test/pkg/HandlerTest.java:18: Warning: This Handler class should be static or leaks might occur (new android.os.Handler(){}) [HandlerLeak]\n" +
- " Handler anonymous = new Handler() { // ERROR\n" +
- " ^\n" +
- "0 errors, 2 warnings\n",
-
- lintProject(
- "bytecode/HandlerTest.java.txt=>src/test/pkg/HandlerTest.java",
- "bytecode/HandlerTest.class.data=>bin/classes/test/pkg/HandlerTest.class",
- "bytecode/HandlerTest$Inner.class.data=>bin/classes/test/pkg/HandlerTest$Inner.class",
- "bytecode/HandlerTest$StaticInner.class.data=>bin/classes/test/pkg/HandlerTest$StaticInner.class",
- "bytecode/HandlerTest$WithArbitraryLooper.class.data=>bin/classes/test/pkg/HandlerTest$WithArbitraryLooper.class",
- "bytecode/HandlerTest$1.class.data=>bin/classes/test/pkg/HandlerTest$1.class",
- "bytecode/HandlerTest$2.class.data=>bin/classes/test/pkg/HandlerTest$2.class"));
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/IconDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/IconDetectorTest.java
deleted file mode 100644
index e9d2e29..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/IconDetectorTest.java
+++ /dev/null
@@ -1,812 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint.checks;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.build.FilterData;
-import com.android.build.OutputFile;
-import com.android.builder.model.AndroidArtifact;
-import com.android.builder.model.AndroidArtifactOutput;
-import com.android.builder.model.AndroidProject;
-import com.android.builder.model.ProductFlavor;
-import com.android.builder.model.ProductFlavorContainer;
-import com.android.builder.model.Variant;
-import com.android.tools.lint.client.api.LintClient;
-import com.android.tools.lint.client.api.LintDriver;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Issue;
-import com.android.tools.lint.detector.api.Project;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
-import org.mockito.stubbing.OngoingStubbing;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-@SuppressWarnings("javadoc")
-public class IconDetectorTest extends AbstractCheckTest {
- @Override
- protected Detector getDetector() {
- return new IconDetector();
- }
-
- private Set<Issue> mEnabled = new HashSet<Issue>();
- private boolean mAbbreviate;
-
- private static final Set<Issue> ALL = new HashSet<Issue>();
- static {
- ALL.add(IconDetector.DUPLICATES_CONFIGURATIONS);
- ALL.add(IconDetector.DUPLICATES_NAMES);
- ALL.add(IconDetector.GIF_USAGE);
- ALL.add(IconDetector.ICON_DENSITIES);
- ALL.add(IconDetector.ICON_DIP_SIZE);
- ALL.add(IconDetector.ICON_EXTENSION);
- ALL.add(IconDetector.ICON_LOCATION);
- ALL.add(IconDetector.ICON_MISSING_FOLDER);
- ALL.add(IconDetector.ICON_NODPI);
- ALL.add(IconDetector.ICON_COLORS);
- ALL.add(IconDetector.ICON_XML_AND_PNG);
- ALL.add(IconDetector.ICON_LAUNCHER_SHAPE);
- ALL.add(IconDetector.ICON_MIX_9PNG);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mAbbreviate = true;
- }
-
- @Override
- protected void configureDriver(LintDriver driver) {
- driver.setAbbreviating(mAbbreviate);
- }
-
- @Override
- protected TestConfiguration getConfiguration(LintClient client, Project project) {
- return new TestConfiguration(client, project, null) {
- @Override
- public boolean isEnabled(@NonNull Issue issue) {
- return super.isEnabled(issue) && mEnabled.contains(issue);
- }
- };
- }
-
- public void test() throws Exception {
- mEnabled = ALL;
- assertEquals(
- "res/drawable-mdpi/sample_icon.gif: Warning: Using the .gif format for bitmaps is discouraged [GifUsage]\n" +
- "res/drawable/ic_launcher.png: Warning: The ic_launcher.png icon has identical contents in the following configuration folders: drawable-mdpi, drawable [IconDuplicatesConfig]\n" +
- " res/drawable-mdpi/ic_launcher.png: <No location-specific message\n" +
- "res/drawable/ic_launcher.png: Warning: Found bitmap drawable res/drawable/ic_launcher.png in densityless folder [IconLocation]\n" +
- "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: sample_icon.gif (found in drawable-mdpi) [IconDensities]\n" +
- "res: Warning: Missing density variation folders in res: drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
- "0 errors, 5 warnings\n" +
- "",
-
- lintProject(
- // Use minSDK4 to ensure that we get warnings about missing drawables
- "apicheck/minsdk4.xml=>AndroidManifest.xml",
- "res/drawable/ic_launcher.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher.png",
- "res/drawable-mdpi/sample_icon.gif",
- // Make a dummy file named .svn to make sure it doesn't get seen as
- // an icon name
- "res/drawable-mdpi/sample_icon.gif=>res/drawable-hdpi/.svn",
- "res/drawable-hdpi/ic_launcher.png"));
- }
-
- public void testMixed() throws Exception {
- mEnabled = Collections.singleton(IconDetector.ICON_XML_AND_PNG);
- assertEquals(
- "res/drawable/background.xml: Warning: The following images appear both as density independent .xml files and as bitmap files: res/drawable-mdpi/background.png, res/drawable/background.xml [IconXmlAndPng]\n" +
- " res/drawable-mdpi/background.png: <No location-specific message\n" +
- "0 errors, 1 warnings\n",
-
- lintProject(
- "apicheck/minsdk4.xml=>AndroidManifest.xml",
- "apicheck/minsdk4.xml=>res/drawable/background.xml",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/background.png"));
- }
-
- public void testApi1() throws Exception {
- mEnabled = ALL;
- assertEquals(
- "No warnings.",
-
- lintProject(
- // manifest file which specifies uses sdk = 2
- "apicheck/minsdk2.xml=>AndroidManifest.xml",
- "res/drawable/ic_launcher.png"));
- }
-
- public void test2() throws Exception {
- mEnabled = ALL;
- assertEquals(
- "res/drawable-hdpi/other.9.png: Warning: The following unrelated icon files have identical contents: appwidget_bg.9.png, other.9.png [IconDuplicates]\n" +
- " res/drawable-hdpi/appwidget_bg.9.png: <No location-specific message\n" +
- "res/drawable-hdpi/unrelated.png: Warning: The following unrelated icon files have identical contents: ic_launcher.png, unrelated.png [IconDuplicates]\n" +
- " res/drawable-hdpi/ic_launcher.png: <No location-specific message\n" +
- "res: Warning: Missing density variation folders in res: drawable-mdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
- "0 errors, 3 warnings\n",
-
- lintProject(
- "res/drawable-hdpi/unrelated.png",
- "res/drawable-hdpi/appwidget_bg.9.png",
- "res/drawable-hdpi/appwidget_bg_focus.9.png",
- "res/drawable-hdpi/other.9.png",
- "res/drawable-hdpi/ic_launcher.png"
- ));
- }
-
- public void testNoDpi() throws Exception {
- mEnabled = ALL;
- assertEquals(
- "res/drawable-mdpi/frame.png: Warning: The following images appear in both -nodpi and in a density folder: frame.png [IconNoDpi]\n" +
- "res/drawable-xlarge-nodpi-v11/frame.png: Warning: The frame.png icon has identical contents in the following configuration folders: drawable-mdpi, drawable-nodpi, drawable-xlarge-nodpi-v11 [IconDuplicatesConfig]\n" +
- " res/drawable-nodpi/frame.png: <No location-specific message\n" +
- " res/drawable-mdpi/frame.png: <No location-specific message\n" +
- "res: Warning: Missing density variation folders in res: drawable-hdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
- "0 errors, 3 warnings\n" +
- "",
-
- lintProject(
- "res/drawable-mdpi/frame.png",
- "res/drawable-nodpi/frame.png",
- "res/drawable-xlarge-nodpi-v11/frame.png"));
- }
-
- public void testNoDpi2() throws Exception {
- mEnabled = ALL;
- // Having additional icon names in the no-dpi folder should not cause any complaints
- assertEquals(
- "res/drawable-xxxhdpi/frame.png: Warning: The image frame.png varies significantly in its density-independent (dip) size across the various density versions: drawable-ldpi/frame.png: 629x387 dp (472x290 px), drawable-mdpi/frame.png: 472x290 dp (472x290 px), drawable-hdpi/frame.png: 315x193 dp (472x290 px), drawable-xhdpi/frame.png: 236x145 dp (472x290 px), drawable-xxhdpi/frame.png: 157x97 dp (472x290 px), drawable-xxxhdpi/frame.png: 118x73 dp (472x290 px) [IconDipSize]\n" +
- " res/drawable-xxhdpi/frame.png: <No location-specific message\n" +
- " res/drawable-xhdpi/frame.png: <No location-specific message\n" +
- " res/drawable-hdpi/frame.png: <No location-specific message\n" +
- " res/drawable-mdpi/frame.png: <No location-specific message\n" +
- " res/drawable-ldpi/frame.png: <No location-specific message\n" +
- "res/drawable-xxxhdpi/frame.png: Warning: The following unrelated icon files have identical contents: frame.png, frame.png, frame.png, file1.png, file2.png, frame.png, frame.png, frame.png [IconDuplicates]\n" +
- " res/drawable-xxhdpi/frame.png: <No location-specific message\n" +
- " res/drawable-xhdpi/frame.png: <No location-specific message\n" +
- " res/drawable-nodpi/file2.png: <No location-specific message\n" +
- " res/drawable-nodpi/file1.png: <No location-specific message\n" +
- " res/drawable-mdpi/frame.png: <No location-specific message\n" +
- " res/drawable-ldpi/frame.png: <No location-specific message\n" +
- " res/drawable-hdpi/frame.png: <No location-specific message\n" +
- "0 errors, 2 warnings\n" +
- "",
-
- lintProject(
- "res/drawable-mdpi/frame.png=>res/drawable-mdpi/frame.png",
- "res/drawable-mdpi/frame.png=>res/drawable-hdpi/frame.png",
- "res/drawable-mdpi/frame.png=>res/drawable-ldpi/frame.png",
- "res/drawable-mdpi/frame.png=>res/drawable-xhdpi/frame.png",
- "res/drawable-mdpi/frame.png=>res/drawable-xxhdpi/frame.png",
- "res/drawable-mdpi/frame.png=>res/drawable-xxxhdpi/frame.png",
- "res/drawable-mdpi/frame.png=>res/drawable-nodpi/file1.png",
- "res/drawable-mdpi/frame.png=>res/drawable-nodpi/file2.png"));
- }
-
- public void testNoDpiMix() throws Exception {
- mEnabled = ALL;
- assertEquals(
- "res/drawable-mdpi/frame.xml: Warning: The following images appear in both -nodpi and in a density folder: frame.png, frame.xml [IconNoDpi]\n" +
- " res/drawable-mdpi/frame.png: <No location-specific message\n" +
- "res/drawable-nodpi/frame.xml: Warning: The following images appear both as density independent .xml files and as bitmap files: res/drawable-mdpi/frame.png, res/drawable-nodpi/frame.xml [IconXmlAndPng]\n" +
- " res/drawable-mdpi/frame.png: <No location-specific message\n" +
- "res: Warning: Missing density variation folders in res: drawable-hdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
- "0 errors, 3 warnings\n",
-
- lintProject(
- "res/drawable-mdpi/frame.png",
- "res/drawable/states.xml=>res/drawable-nodpi/frame.xml"));
- }
-
-
- public void testMixedFormat() throws Exception {
- mEnabled = ALL;
- // Test having a mixture of .xml and .png resources for the same name
- // Make sure we don't get:
- // drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: f.png (found in drawable-mdpi)
- // drawable-xhdpi: Warning: Missing the following drawables in drawable-xhdpi: f.png (found in drawable-mdpi)
- assertEquals(
- "res/drawable-xxxhdpi/f.xml: Warning: The following images appear both as density independent .xml files and as bitmap files: res/drawable-hdpi/f.xml, res/drawable-mdpi/f.png [IconXmlAndPng]\n" +
- " res/drawable-xxhdpi/f.xml: <No location-specific message\n" +
- " res/drawable-xhdpi/f.xml: <No location-specific message\n" +
- " res/drawable-mdpi/f.png: <No location-specific message\n" +
- " res/drawable-hdpi/f.xml: <No location-specific message\n" +
- "0 errors, 1 warnings\n",
-
- lintProject(
- "res/drawable-mdpi/frame.png=>res/drawable-mdpi/f.png",
- "res/drawable/states.xml=>res/drawable-hdpi/f.xml",
- "res/drawable/states.xml=>res/drawable-xhdpi/f.xml",
- "res/drawable/states.xml=>res/drawable-xxhdpi/f.xml",
- "res/drawable/states.xml=>res/drawable-xxxhdpi/f.xml"));
- }
-
- public void testMisleadingFileName() throws Exception {
- mEnabled = Collections.singleton(IconDetector.ICON_EXTENSION);
- assertEquals(
- "res/drawable-mdpi/frame.gif: Warning: Misleading file extension; named .gif but the file format is png [IconExtension]\n" +
- "res/drawable-mdpi/frame.jpg: Warning: Misleading file extension; named .jpg but the file format is png [IconExtension]\n" +
- "res/drawable-mdpi/myjpg.png: Warning: Misleading file extension; named .png but the file format is JPEG [IconExtension]\n" +
- "res/drawable-mdpi/sample_icon.jpeg: Warning: Misleading file extension; named .jpeg but the file format is gif [IconExtension]\n" +
- "res/drawable-mdpi/sample_icon.jpg: Warning: Misleading file extension; named .jpg but the file format is gif [IconExtension]\n" +
- "res/drawable-mdpi/sample_icon.png: Warning: Misleading file extension; named .png but the file format is gif [IconExtension]\n" +
- "0 errors, 6 warnings\n",
-
- lintProject(
- "res/drawable-mdpi/sample_icon.jpg=>res/drawable-mdpi/myjpg.jpg", // VALID
- "res/drawable-mdpi/sample_icon.jpg=>res/drawable-mdpi/myjpg.jpeg", // VALID
- "res/drawable-mdpi/frame.png=>res/drawable-mdpi/frame.gif",
- "res/drawable-mdpi/frame.png=>res/drawable-mdpi/frame.jpg",
- "res/drawable-mdpi/sample_icon.jpg=>res/drawable-mdpi/myjpg.png",
- "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/sample_icon.jpg",
- "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/sample_icon.jpeg",
- "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/sample_icon.png"));
- }
-
- public void testColors() throws Exception {
- mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
- assertEquals(
- "res/drawable-mdpi/ic_menu_my_action.png: Warning: Action Bar icons should use a single gray color (#333333 for light themes (with 60%/30% opacity for enabled/disabled), and #FFFFFF with opacity 80%/30% for dark themes [IconColors]\n" +
- "res/drawable-mdpi-v11/ic_stat_my_notification.png: Warning: Notification icons must be entirely white [IconColors]\n" +
- "res/drawable-mdpi-v9/ic_stat_my_notification2.png: Warning: Notification icons must be entirely white [IconColors]\n" +
- "0 errors, 3 warnings\n",
-
- lintProject(
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_menu_my_action.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi-v11/ic_stat_my_notification.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi-v9/ic_stat_my_notification2.png",
- "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
- }
-
- public void testNotActionBarIcons() throws Exception {
- mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
- assertEquals(
- "No warnings.",
-
- // No Java code designates the menu as an action bar menu
- lintProject(
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "res/menu/menu.xml",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon2.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png", // Not action bar
- "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
- }
-
- public void testActionBarIcons() throws Exception {
- mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
- assertEquals(
- "res/drawable-mdpi/icon1.png: Warning: Action Bar icons should use a single gray color (#333333 for light themes (with 60%/30% opacity for enabled/disabled), and #FFFFFF with opacity 80%/30% for dark themes [IconColors]\n" +
- "res/drawable-mdpi/icon2.png: Warning: Action Bar icons should use a single gray color (#333333 for light themes (with 60%/30% opacity for enabled/disabled), and #FFFFFF with opacity 80%/30% for dark themes [IconColors]\n" +
- "0 errors, 2 warnings\n",
-
- lintProject(
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "res/menu/menu.xml",
- "src/test/pkg/ActionBarTest.java.txt=>src/test/pkg/ActionBarTest.java",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon2.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png", // Not action bar
- "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
- }
-
- public void testOkActionBarIcons() throws Exception {
- mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
- assertEquals(
- "No warnings.",
-
- lintProject(
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "res/menu/menu.xml",
- "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon1.png",
- "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon2.png"));
- }
-
- public void testNotificationIcons() throws Exception {
- mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
- assertEquals(
- "res/drawable-mdpi/icon1.png: Warning: Notification icons must be entirely white [IconColors]\n" +
- "res/drawable-mdpi/icon2.png: Warning: Notification icons must be entirely white [IconColors]\n" +
- "res/drawable-mdpi/icon3.png: Warning: Notification icons must be entirely white [IconColors]\n" +
- "res/drawable-mdpi/icon4.png: Warning: Notification icons must be entirely white [IconColors]\n" +
- "res/drawable-mdpi/icon5.png: Warning: Notification icons must be entirely white [IconColors]\n" +
- "0 errors, 5 warnings\n",
-
- lintProject(
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "src/test/pkg/NotificationTest.java.txt=>src/test/pkg/NotificationTest.java",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon2.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon4.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon5.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon6.png", // not a notification
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon7.png", // ditto
- "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
- }
-
- public void testOkNotificationIcons() throws Exception {
- mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
- assertEquals(
- "No warnings.",
-
- lintProject(
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "src/test/pkg/NotificationTest.java.txt=>src/test/pkg/NotificationTest.java",
- "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon1.png",
- "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon2.png",
- "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon3.png",
- "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon4.png",
- "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon5.png"));
- }
-
- public void testExpectedSize() throws Exception {
- mEnabled = Collections.singleton(IconDetector.ICON_EXPECTED_SIZE);
- assertEquals(
- "res/drawable-mdpi/ic_launcher.png: Warning: Incorrect icon size for drawable-mdpi/ic_launcher.png: expected 48x48, but was 24x24 [IconExpectedSize]\n" +
- "res/drawable-mdpi/icon1.png: Warning: Incorrect icon size for drawable-mdpi/icon1.png: expected 32x32, but was 48x48 [IconExpectedSize]\n" +
- "res/drawable-mdpi/icon3.png: Warning: Incorrect icon size for drawable-mdpi/icon3.png: expected 24x24, but was 48x48 [IconExpectedSize]\n" +
- "0 errors, 3 warnings\n",
-
- lintProject(
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "src/test/pkg/NotificationTest.java.txt=>src/test/pkg/NotificationTest.java",
- "res/menu/menu.xml",
- "src/test/pkg/ActionBarTest.java.txt=>src/test/pkg/ActionBarTest.java",
-
- // 3 wrong-sized icons:
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png",
- "res/drawable-mdpi/stat_notify_alarm.png=>res/drawable-mdpi/ic_launcher.png",
-
- // OK sizes
- "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon2.png",
- "res/drawable-mdpi/stat_notify_alarm.png=>res/drawable-mdpi/icon4.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png"
- ));
- }
-
- public void testAbbreviate() throws Exception {
- mEnabled = Collections.singleton(IconDetector.ICON_DENSITIES);
- assertEquals(
- "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: " +
- "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png, " +
- "ic_launcher3.png... (6 more) [IconDensities]\n" +
- "res/drawable-xhdpi: Warning: Missing the following drawables in drawable-xhdpi: " +
- "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png, " +
- "ic_launcher3.png... (6 more) [IconDensities]\n" +
- "0 errors, 2 warnings\n",
-
- lintProject(
- // Use minSDK4 to ensure that we get warnings about missing drawables
- "apicheck/minsdk4.xml=>AndroidManifest.xml",
- "res/drawable/ic_launcher.png=>res/drawable-hdpi/ic_launcher1.png",
- "res/drawable/ic_launcher.png=>res/drawable-xhdpi/ic_launcher1.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher1.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher3.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher4.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher5.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher6.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher7.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher8.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher9.webp",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher10.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher11.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher12.png"
- ));
- }
-
- public void testShowAll() throws Exception {
- mEnabled = Collections.singleton(IconDetector.ICON_DENSITIES);
- mAbbreviate = false;
- assertEquals(
- "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: " +
- "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png, " +
- "ic_launcher3.png, ic_launcher4.png, ic_launcher5.png, ic_launcher6.png, " +
- "ic_launcher7.png, ic_launcher8.png, ic_launcher9.png [IconDensities]\n" +
- "res/drawable-xhdpi: Warning: Missing the following drawables in drawable-xhdpi: " +
- "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png," +
- " ic_launcher3.png, ic_launcher4.png, ic_launcher5.png, ic_launcher6.png, " +
- "ic_launcher7.png, ic_launcher8.png, ic_launcher9.png [IconDensities]\n" +
- "0 errors, 2 warnings\n",
-
- lintProject(
- // Use minSDK4 to ensure that we get warnings about missing drawables
- "apicheck/minsdk4.xml=>AndroidManifest.xml",
- "res/drawable/ic_launcher.png=>res/drawable-hdpi/ic_launcher1.png",
- "res/drawable/ic_launcher.png=>res/drawable-xhdpi/ic_launcher1.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher1.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher3.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher4.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher5.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher6.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher7.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher8.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher9.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher10.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher11.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher12.png"
- ));
- }
-
- public void testIgnoreMissingFolders() throws Exception {
- mEnabled = Collections.singleton(IconDetector.ICON_DENSITIES);
- assertEquals(
- "No warnings.",
-
- lintProject(
- // Use minSDK4 to ensure that we get warnings about missing drawables
- "apicheck/minsdk4.xml=>AndroidManifest.xml",
- "ignoremissing.xml=>lint.xml",
- "res/drawable/ic_launcher.png=>res/drawable-hdpi/ic_launcher1.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher1.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png"
- ));
- }
-
- public void testSquareLauncher() throws Exception {
- mEnabled = Collections.singleton(IconDetector.ICON_LAUNCHER_SHAPE);
- assertEquals(
- "res/drawable-hdpi/ic_launcher_filled.png: Warning: Launcher icons should not fill every pixel of their square region; see the design guide for details [IconLauncherShape]\n" +
- "0 errors, 1 warnings\n",
-
- lintProject(
- "apicheck/minsdk4.xml=>AndroidManifest.xml",
- "res/drawable-hdpi/filled.png=>res/drawable-hdpi/ic_launcher_filled.png",
- "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/ic_launcher_2.gif"
- ));
- }
-
- public void testMixNinePatch() throws Exception {
- // https://code.google.com/p/android/issues/detail?id=43075
- mEnabled = Collections.singleton(IconDetector.ICON_MIX_9PNG);
- assertEquals(""
- + "res/drawable-mdpi/ic_launcher_filled.png: Warning: The files ic_launcher_filled.png and ic_launcher_filled.9.png clash; both will map to @drawable/ic_launcher_filled [IconMixedNinePatch]\n"
- + " res/drawable-hdpi/ic_launcher_filled.png: <No location-specific message\n"
- + " res/drawable-hdpi/ic_launcher_filled.9.png: <No location-specific message\n"
- + "0 errors, 1 warnings\n",
-
- lintProject(
- "apicheck/minsdk4.xml=>AndroidManifest.xml",
- "res/drawable-hdpi/filled.png=>res/drawable-mdpi/ic_launcher_filled.png",
- "res/drawable-hdpi/filled.png=>res/drawable-hdpi/ic_launcher_filled.png",
- "res/drawable-hdpi/filled.png=>res/drawable-hdpi/ic_launcher_filled.9.png",
- "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/ic_launcher_2.gif"
- ));
- }
-
- public void test67486() throws Exception {
- // Regression test for https://code.google.com/p/android/issues/detail?id=67486
- mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
- assertEquals("No warnings.",
-
- lintProject(
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "res/drawable-xhdpi/ic_stat_notify.png=>res/drawable-xhdpi/ic_stat_notify.png"
- ));
- }
-
- public void testDuplicatesWithDpNames() throws Exception {
- // Regression test for https://code.google.com/p/android/issues/detail?id=74584
- mEnabled = Collections.singleton(IconDetector.DUPLICATES_NAMES);
- assertEquals("No warnings.",
-
- lintProject(
- "res/drawable-hdpi/unrelated.png=>res/drawable-mdpi/foo_72dp.png",
- "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_36dp.png"
- ));
- }
-
- public void testClaimedSize() throws Exception {
- // Check that icons which declare a dp size actually correspond to that dp size
- mEnabled = Collections.singleton(IconDetector.ICON_DIP_SIZE);
- assertEquals(""
- + "res/drawable-xhdpi/foo_30dp.png: Warning: Suspicious file name foo_30dp.png: The implied 30 dp size does not match the actual dp size (pixel size 72\u00d772 in a drawable-xhdpi folder computes to 36\u00d736 dp) [IconDipSize]\n"
- + "res/drawable-mdpi/foo_80dp.png: Warning: Suspicious file name foo_80dp.png: The implied 80 dp size does not match the actual dp size (pixel size 72\u00d772 in a drawable-mdpi folder computes to 72\u00d772 dp) [IconDipSize]\n"
- + "0 errors, 2 warnings\n",
-
- lintProject(
- "res/drawable-hdpi/unrelated.png=>res/drawable-mdpi/foo_72dp.png", // ok
- "res/drawable-hdpi/unrelated.png=>res/drawable-mdpi/foo_80dp.png", // wrong
- "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_36dp.png", // ok
- "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_35dp.png", // ~ok
- "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_30dp.png" // wrong
- ));
- }
-
- public void testResConfigs1() throws Exception {
- // resConfigs in the Gradle model sets up the specific set of resource configs
- // that are included in the packaging: we use this to limit the set of required
- // densities
- mEnabled = Sets.newHashSet(IconDetector.ICON_DENSITIES, IconDetector.ICON_MISSING_FOLDER);
- assertEquals(""
- + "res: Warning: Missing density variation folders in res: drawable-hdpi [IconMissingDensityFolder]\n"
- + "0 errors, 1 warnings\n",
-
- lintProject(
- "res/drawable-mdpi/frame.png",
- "res/drawable-nodpi/frame.png",
- "res/drawable-xlarge-nodpi-v11/frame.png"));
- }
-
- public void testResConfigs2() throws Exception {
- mEnabled = Sets.newHashSet(IconDetector.ICON_DENSITIES, IconDetector.ICON_MISSING_FOLDER);
- assertEquals(""
- + "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: sample_icon.gif (found in drawable-mdpi) [IconDensities]\n"
- + "0 errors, 1 warnings\n",
-
- lintProject(
- // Use minSDK4 to ensure that we get warnings about missing drawables
- "apicheck/minsdk4.xml=>AndroidManifest.xml",
- "res/drawable/ic_launcher.png",
- "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher.png",
- "res/drawable/ic_launcher.png=>res/drawable-xhdpi/ic_launcher.png",
- "res/drawable-mdpi/sample_icon.gif",
- "res/drawable-hdpi/ic_launcher.png"));
- }
-
- public void testSplits1() throws Exception {
- // splits in the Gradle model sets up the specific set of resource configs
- // that are included in the packaging: we use this to limit the set of required
- // densities
- mEnabled = Sets.newHashSet(IconDetector.ICON_DENSITIES, IconDetector.ICON_MISSING_FOLDER);
- assertEquals(""
- + "res: Warning: Missing density variation folders in res: drawable-hdpi [IconMissingDensityFolder]\n"
- + "0 errors, 1 warnings\n",
-
- lintProject(
- "res/drawable-mdpi/frame.png",
- "res/drawable-nodpi/frame.png",
- "res/drawable-xlarge-nodpi-v11/frame.png"));
- }
-
- @Override
- protected TestLintClient createClient() {
- String testName = getName();
- if (testName.startsWith("testResConfigs")) {
- return createClientForTestResConfigs();
- } else if (testName.startsWith("testSplits")) {
- return createClientForTestSplits();
- } else {
- return super.createClient();
- }
- }
-
- private TestLintClient createClientForTestResConfigs() {
-
- // Set up a mock project model for the resource configuration test(s)
- // where we provide a subset of densities to be included
-
- return new TestLintClient() {
- @NonNull
- @Override
- protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
- return new Project(this, dir, referenceDir) {
- @Override
- public boolean isGradleProject() {
- return true;
- }
-
- @Nullable
- @Override
- public AndroidProject getGradleProjectModel() {
- /*
- Simulate variant freeBetaDebug in this setup:
- defaultConfig {
- ...
- resConfigs "mdpi"
- }
- flavorDimensions "pricing", "releaseType"
- productFlavors {
- beta {
- flavorDimension "releaseType"
- resConfig "en"
- resConfigs "nodpi", "hdpi"
- }
- normal { flavorDimension "releaseType" }
- free { flavorDimension "pricing" }
- paid { flavorDimension "pricing" }
- }
- */
- ProductFlavor flavorFree = mock(ProductFlavor.class);
- when(flavorFree.getName()).thenReturn("free");
- when(flavorFree.getResourceConfigurations())
- .thenReturn(Collections.<String>emptyList());
-
- ProductFlavor flavorNormal = mock(ProductFlavor.class);
- when(flavorNormal.getName()).thenReturn("normal");
- when(flavorNormal.getResourceConfigurations())
- .thenReturn(Collections.<String>emptyList());
-
- ProductFlavor flavorPaid = mock(ProductFlavor.class);
- when(flavorPaid.getName()).thenReturn("paid");
- when(flavorPaid.getResourceConfigurations())
- .thenReturn(Collections.<String>emptyList());
-
- ProductFlavor flavorBeta = mock(ProductFlavor.class);
- when(flavorBeta.getName()).thenReturn("beta");
- List<String> resConfigs = Arrays.asList("hdpi", "en", "nodpi");
- when(flavorBeta.getResourceConfigurations()).thenReturn(resConfigs);
-
- ProductFlavor defaultFlavor = mock(ProductFlavor.class);
- when(defaultFlavor.getName()).thenReturn("main");
- when(defaultFlavor.getResourceConfigurations()).thenReturn(
- Collections.singleton("mdpi"));
-
- ProductFlavorContainer containerBeta =
- mock(ProductFlavorContainer.class);
- when(containerBeta.getProductFlavor()).thenReturn(flavorBeta);
-
- ProductFlavorContainer containerFree =
- mock(ProductFlavorContainer.class);
- when(containerFree.getProductFlavor()).thenReturn(flavorFree);
-
- ProductFlavorContainer containerPaid =
- mock(ProductFlavorContainer.class);
- when(containerPaid.getProductFlavor()).thenReturn(flavorPaid);
-
- ProductFlavorContainer containerNormal =
- mock(ProductFlavorContainer.class);
- when(containerNormal.getProductFlavor()).thenReturn(flavorNormal);
-
- ProductFlavorContainer defaultContainer =
- mock(ProductFlavorContainer.class);
- when(defaultContainer.getProductFlavor()).thenReturn(defaultFlavor);
-
- List<ProductFlavorContainer> containers = Arrays.asList(
- containerPaid, containerFree, containerNormal, containerBeta
- );
-
- AndroidProject project = mock(AndroidProject.class);
- when(project.getProductFlavors()).thenReturn(containers);
- when(project.getDefaultConfig()).thenReturn(defaultContainer);
- return project;
- }
-
- @Nullable
- @Override
- public Variant getCurrentVariant() {
- List<String> productFlavorNames = Arrays.asList("free", "beta");
- Variant mock = mock(Variant.class);
- when(mock.getProductFlavors()).thenReturn(productFlavorNames);
- return mock;
- }
- };
- }
- };
- }
-
- private TestLintClient createClientForTestSplits() {
-
- // Set up a mock project model for the resource configuration test(s)
- // where we provide a subset of densities to be included
-
- return new TestLintClient() {
- @NonNull
- @Override
- protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
- return new Project(this, dir, referenceDir) {
- @Override
- public boolean isGradleProject() {
- return true;
- }
-
- @Nullable
- @Override
- public AndroidProject getGradleProjectModel() {
- /*
- Simulate variant debug in this setup:
- splits {
- density {
- enable true
- reset()
- include "mdpi", "hdpi"
- }
- }
- */
-
- ProductFlavor defaultFlavor = mock(ProductFlavor.class);
- when(defaultFlavor.getName()).thenReturn("main");
- when(defaultFlavor.getResourceConfigurations()).thenReturn(
- Collections.<String>emptyList());
-
- ProductFlavorContainer defaultContainer =
- mock(ProductFlavorContainer.class);
- when(defaultContainer.getProductFlavor()).thenReturn(defaultFlavor);
-
- AndroidProject project = mock(AndroidProject.class);
- when(project.getProductFlavors()).thenReturn(
- Collections.<ProductFlavorContainer>emptyList());
- when(project.getDefaultConfig()).thenReturn(defaultContainer);
- return project;
- }
-
- @Nullable
- @Override
- public Variant getCurrentVariant() {
- Collection<AndroidArtifactOutput> outputs = Lists.newArrayList();
-
- outputs.add(createAndroidArtifactOutput("", ""));
- outputs.add(createAndroidArtifactOutput("DENSITY", "mdpi"));
- outputs.add(createAndroidArtifactOutput("DENSITY", "hdpi"));
-
- AndroidArtifact mainArtifact = mock(AndroidArtifact.class);
- when(mainArtifact.getOutputs()).thenReturn(outputs);
-
- List<String> productFlavorNames = Collections.emptyList();
- Variant mock = mock(Variant.class);
- when(mock.getProductFlavors()).thenReturn(productFlavorNames);
- when(mock.getMainArtifact()).thenReturn(mainArtifact);
- return mock;
- }
-
- private AndroidArtifactOutput createAndroidArtifactOutput(
- @NonNull String filterType,
- @NonNull String identifier) {
- AndroidArtifactOutput artifactOutput = mock(
- AndroidArtifactOutput.class);
-
- OutputFile outputFile = mock(OutputFile.class);
- if (filterType.isEmpty()) {
- when(outputFile.getFilterTypes())
- .thenReturn(Collections.<String>emptyList());
- when(outputFile.getFilters())
- .thenReturn(Collections.<FilterData>emptyList());
- } else {
- when(outputFile.getFilterTypes())
- .thenReturn(Collections.singletonList(filterType));
- List<FilterData> filters = Lists.newArrayList();
- FilterData filter = mock(FilterData.class);
- when(filter.getFilterType()).thenReturn(filterType);
- when(filter.getIdentifier()).thenReturn(identifier);
- filters.add(filter);
- when(outputFile.getFilters()).thenReturn(filters);
- }
-
- // Work around wildcard capture
- //when(artifactOutput.getOutputs()).thenReturn(outputFiles);
- List<OutputFile> outputFiles = Collections.singletonList(outputFile);
- OngoingStubbing<? extends Collection<? extends OutputFile>> when = when(
- artifactOutput.getOutputs());
- //noinspection unchecked
- ((OngoingStubbing<Collection<? extends OutputFile>>) when)
- .thenReturn(outputFiles);
-
- return artifactOutput;
- }
- };
- }
- };
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestDetectorTest.java
deleted file mode 100644
index 766546e..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestDetectorTest.java
+++ /dev/null
@@ -1,778 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint.checks;
-
-import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.builder.model.AndroidProject;
-import com.android.builder.model.ApiVersion;
-import com.android.builder.model.BuildType;
-import com.android.builder.model.BuildTypeContainer;
-import com.android.builder.model.ProductFlavor;
-import com.android.builder.model.ProductFlavorContainer;
-import com.android.builder.model.SourceProvider;
-import com.android.builder.model.SourceProviderContainer;
-import com.android.builder.model.Variant;
-import com.android.tools.lint.client.api.LintClient;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Issue;
-import com.android.tools.lint.detector.api.Project;
-import com.google.common.collect.Lists;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-@SuppressWarnings("javadoc")
-public class ManifestDetectorTest extends AbstractCheckTest {
- @Override
- protected Detector getDetector() {
- return new ManifestDetector();
- }
-
- private Set<Issue> mEnabled = new HashSet<Issue>();
-
- @Override
- protected TestConfiguration getConfiguration(LintClient client, Project project) {
- return new TestConfiguration(client, project, null) {
- @Override
- public boolean isEnabled(@NonNull Issue issue) {
- return super.isEnabled(issue) && mEnabled.contains(issue);
- }
- };
- }
-
- public void testOrderOk() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.ORDER);
- assertEquals(
- "No warnings.",
- lintProject(
- "AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testBrokenOrder() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.ORDER);
- assertEquals(
- "AndroidManifest.xml:16: Warning: <uses-sdk> tag appears after <application> tag [ManifestOrder]\n" +
- " <uses-sdk android:minSdkVersion=\"Froyo\" />\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "0 errors, 1 warnings\n" +
- "",
-
- lintProject(
- "broken-manifest.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testMissingUsesSdk() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.USES_SDK);
- assertEquals(
- "AndroidManifest.xml: Warning: Manifest should specify a minimum API level with <uses-sdk android:minSdkVersion=\"?\" />; if it really supports all versions of Android set it to 1. [UsesMinSdkAttributes]\n" +
- "0 errors, 1 warnings\n",
- lintProject(
- "missingusessdk.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testMissingUsesSdkInGradle() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.SET_VERSION);
- assertEquals(""
- + "No warnings.",
- lintProject("missingusessdk.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>build.gradle")); // dummy; only name counts
- }
-
- public void testMissingMinSdk() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.USES_SDK);
- assertEquals(
- "AndroidManifest.xml:7: Warning: <uses-sdk> tag should specify a minimum API level with android:minSdkVersion=\"?\" [UsesMinSdkAttributes]\n" +
- " <uses-sdk android:targetSdkVersion=\"10\" />\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "0 errors, 1 warnings\n" +
- "",
- lintProject(
- "missingmin.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testMissingTargetSdk() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.USES_SDK);
- assertEquals(
- "AndroidManifest.xml:7: Warning: <uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion=\"?\" [UsesMinSdkAttributes]\n" +
- " <uses-sdk android:minSdkVersion=\"10\" />\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "0 errors, 1 warnings\n",
- lintProject(
- "missingtarget.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testOldTargetSdk() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.TARGET_NEWER);
- assertEquals(
- "AndroidManifest.xml:7: Warning: Not targeting the latest versions of Android; compatibility modes apply. Consider testing and updating this version. Consult the android.os.Build.VERSION_CODES javadoc for details. [OldTargetApi]\n" +
- " <uses-sdk android:minSdkVersion=\"10\" android:targetSdkVersion=\"14\" />\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "0 errors, 1 warnings\n",
- lintProject(
- "oldtarget.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testMultipleSdk() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.MULTIPLE_USES_SDK);
- assertEquals(
- "AndroidManifest.xml:8: Error: There should only be a single <uses-sdk> element in the manifest: merge these together [MultipleUsesSdk]\n" +
- " <uses-sdk android:targetSdkVersion=\"14\" />\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- " AndroidManifest.xml:7: Also appears here\n" +
- " AndroidManifest.xml:9: Also appears here\n" +
- "1 errors, 0 warnings\n",
-
- lintProject(
- "multiplesdk.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testWrongLocation() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.WRONG_PARENT);
- assertEquals(
- "AndroidManifest.xml:8: Error: The <uses-sdk> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
- " <uses-sdk android:minSdkVersion=\"Froyo\" />\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "AndroidManifest.xml:9: Error: The <uses-permission> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
- " <uses-permission />\n" +
- " ~~~~~~~~~~~~~~~~~~~\n" +
- "AndroidManifest.xml:10: Error: The <permission> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
- " <permission />\n" +
- " ~~~~~~~~~~~~~~\n" +
- "AndroidManifest.xml:11: Error: The <permission-tree> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
- " <permission-tree />\n" +
- " ~~~~~~~~~~~~~~~~~~~\n" +
- "AndroidManifest.xml:12: Error: The <permission-group> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
- " <permission-group />\n" +
- " ~~~~~~~~~~~~~~~~~~~~\n" +
- "AndroidManifest.xml:14: Error: The <uses-sdk> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
- " <uses-sdk />\n" +
- " ~~~~~~~~~~~~\n" +
- "AndroidManifest.xml:15: Error: The <uses-configuration> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
- " <uses-configuration />\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~\n" +
- "AndroidManifest.xml:16: Error: The <uses-feature> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
- " <uses-feature />\n" +
- " ~~~~~~~~~~~~~~~~\n" +
- "AndroidManifest.xml:17: Error: The <supports-screens> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
- " <supports-screens />\n" +
- " ~~~~~~~~~~~~~~~~~~~~\n" +
- "AndroidManifest.xml:18: Error: The <compatible-screens> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
- " <compatible-screens />\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~\n" +
- "AndroidManifest.xml:19: Error: The <supports-gl-texture> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
- " <supports-gl-texture />\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "AndroidManifest.xml:24: Error: The <uses-library> element must be a direct child of the <application> element [WrongManifestParent]\n" +
- " <uses-library />\n" +
- " ~~~~~~~~~~~~~~~~\n" +
- "AndroidManifest.xml:25: Error: The <activity> element must be a direct child of the <application> element [WrongManifestParent]\n" +
- " <activity android:name=\".HelloWorld\"\n" +
- " ^\n" +
- "13 errors, 0 warnings\n" +
- "",
-
- lintProject("broken-manifest2.xml=>AndroidManifest.xml"));
- }
-
- public void testDuplicateActivity() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_ACTIVITY);
- assertEquals(
- "AndroidManifest.xml:16: Error: Duplicate registration for activity com.example.helloworld.HelloWorld [DuplicateActivity]\n" +
- " <activity android:name=\"com.example.helloworld.HelloWorld\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "1 errors, 0 warnings\n" +
- "",
-
- lintProject(
- "duplicate-manifest.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testDuplicateActivityAcrossSourceSets() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_ACTIVITY);
- File master = getProjectDir("MasterProject",
- // Master project
- "AndroidManifest.xml=>AndroidManifest.xml",
- "multiproject/main-merge.properties=>project.properties",
- "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
- );
- File library = getProjectDir("LibraryProject",
- // Library project
- "AndroidManifest.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>project.properties",
- "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
- "multiproject/strings.xml=>res/values/strings.xml"
- );
- assertEquals("No warnings.",
- checkLint(Arrays.asList(master, library)));
- }
-
- public void testIgnoreDuplicateActivity() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_ACTIVITY);
- assertEquals(
- "No warnings.",
-
- lintProject(
- "duplicate-manifest-ignore.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testAllowBackup() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
- assertEquals(
- "AndroidManifest.xml:9: Warning: Should explicitly set android:allowBackup to " +
- "true or false (it's true by default, and that can have some security " +
- "implications for the application's data) [AllowBackup]\n" +
- " <application\n" +
- " ^\n" +
- "0 errors, 1 warnings\n",
- lintProject(
- "AndroidManifest.xml",
- "apicheck/minsdk14.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testAllowBackupOk() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
- assertEquals(
- "No warnings.",
- lintProject(
- "allowbackup.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testAllowBackupOk2() throws Exception {
- // Requires build api >= 4
- mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
- assertEquals(
- "No warnings.",
- lintProject(
- "apicheck/minsdk1.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testAllowBackupOk3() throws Exception {
- // Not flagged in library projects
- mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
- assertEquals(
- "No warnings.",
- lintProject(
- "AndroidManifest.xml",
- "multiproject/library.properties=>project.properties",
- "res/values/strings.xml"));
- }
-
- public void testAllowIgnore() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
- assertEquals(
- "No warnings.",
- lintProject(
- "allowbackup_ignore.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testDuplicatePermissions() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.UNIQUE_PERMISSION);
- assertEquals(
- "AndroidManifest.xml:12: Error: Permission name SEND_SMS is not unique (appears in both foo.permission.SEND_SMS and bar.permission.SEND_SMS) [UniquePermission]\n" +
- " <permission android:name=\"bar.permission.SEND_SMS\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- " AndroidManifest.xml:9: Previous permission here\n" +
- "1 errors, 0 warnings\n",
-
- lintProject(
- "duplicate_permissions1.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testDuplicatePermissionsMultiProject() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.UNIQUE_PERMISSION);
-
- File master = getProjectDir("MasterProject",
- // Master project
- "duplicate_permissions2.xml=>AndroidManifest.xml",
- "multiproject/main-merge.properties=>project.properties",
- "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
- );
- File library = getProjectDir("LibraryProject",
- // Library project
- "duplicate_permissions3.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>project.properties",
- "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
- "multiproject/strings.xml=>res/values/strings.xml"
- );
- assertEquals(
- "LibraryProject/AndroidManifest.xml:9: Error: Permission name SEND_SMS is not unique (appears in both foo.permission.SEND_SMS and bar.permission.SEND_SMS) [UniquePermission]\n" +
- " <permission android:name=\"bar.permission.SEND_SMS\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "1 errors, 0 warnings\n",
-
- checkLint(Arrays.asList(master, library)));
- }
-
- public void testMissingVersion() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.SET_VERSION);
- assertEquals(""
- + "AndroidManifest.xml:2: Warning: Should set android:versionCode to specify the application version [MissingVersion]\n"
- + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
- + "^\n"
- + "AndroidManifest.xml:2: Warning: Should set android:versionName to specify the application version [MissingVersion]\n"
- + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
- + "^\n"
- + "0 errors, 2 warnings\n",
- lintProject("no_version.xml=>AndroidManifest.xml"));
- }
-
- public void testVersionNotMissingInGradleProjects() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.SET_VERSION);
- assertEquals(""
- + "No warnings.",
- lintProject("no_version.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>build.gradle")); // dummy; only name counts
- }
-
- public void testIllegalReference() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.ILLEGAL_REFERENCE);
- assertEquals(""
- + "AndroidManifest.xml:4: Warning: The android:versionCode cannot be a resource url, it must be a literal integer [IllegalResourceRef]\n"
- + " android:versionCode=\"@dimen/versionCode\"\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "AndroidManifest.xml:7: Warning: The android:minSdkVersion cannot be a resource url, it must be a literal integer (or string if a preview codename) [IllegalResourceRef]\n"
- + " <uses-sdk android:minSdkVersion=\"@dimen/minSdkVersion\" android:targetSdkVersion=\"@dimen/targetSdkVersion\" />\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "AndroidManifest.xml:7: Warning: The android:targetSdkVersion cannot be a resource url, it must be a literal integer (or string if a preview codename) [IllegalResourceRef]\n"
- + " <uses-sdk android:minSdkVersion=\"@dimen/minSdkVersion\" android:targetSdkVersion=\"@dimen/targetSdkVersion\" />\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 3 warnings\n",
-
- lintProject("illegal_version.xml=>AndroidManifest.xml"));
- }
-
- public void testDuplicateUsesFeature() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_USES_FEATURE);
- assertEquals(
- "AndroidManifest.xml:11: Warning: Duplicate declaration of uses-feature android.hardware.camera [DuplicateUsesFeature]\n" +
- " <uses-feature android:name=\"android.hardware.camera\"/>\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "0 errors, 1 warnings\n",
- lintProject(
- "duplicate_uses_feature.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testDuplicateUsesFeatureOk() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_USES_FEATURE);
- assertEquals(
- "No warnings.",
- lintProject(
- "duplicate_uses_feature_ok.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testMissingApplicationIcon() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.APPLICATION_ICON);
- assertEquals(
- "AndroidManifest.xml:9: Warning: Should explicitly set android:icon, there is no default [MissingApplicationIcon]\n" +
- " <application\n" +
- " ^\n" +
- "0 errors, 1 warnings\n",
- lintProject(
- "missing_application_icon.xml=>AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testMissingApplicationIconInLibrary() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.APPLICATION_ICON);
- assertEquals(
- "No warnings.",
- lintProject(
- "missing_application_icon.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>project.properties",
- "res/values/strings.xml"));
- }
-
- public void testMissingApplicationIconOk() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.APPLICATION_ICON);
- assertEquals(
- "No warnings.",
- lintProject(
- "AndroidManifest.xml",
- "res/values/strings.xml"));
- }
-
- public void testDeviceAdmin() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.DEVICE_ADMIN);
- assertEquals(""
- + "AndroidManifest.xml:31: Warning: You must have an intent filter for action android.app.action.DEVICE_ADMIN_ENABLED [DeviceAdmin]\n"
- + " <meta-data android:name=\"android.app.device_admin\"\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "AndroidManifest.xml:44: Warning: You must have an intent filter for action android.app.action.DEVICE_ADMIN_ENABLED [DeviceAdmin]\n"
- + " <meta-data android:name=\"android.app.device_admin\"\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "AndroidManifest.xml:56: Warning: You must have an intent filter for action android.app.action.DEVICE_ADMIN_ENABLED [DeviceAdmin]\n"
- + " <meta-data android:name=\"android.app.device_admin\"\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 3 warnings\n",
- lintProject("deviceadmin.xml=>AndroidManifest.xml"));
- }
-
- public void testMockLocations() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.MOCK_LOCATION);
- assertEquals(""
- + "AndroidManifest.xml:9: Error: Mock locations should only be requested in a test or debug-specific manifest file (typically src/debug/AndroidManifest.xml) [MockLocation]\n"
- + " <uses-permission android:name=\"android.permission.ACCESS_MOCK_LOCATION\" /> \n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "1 errors, 0 warnings\n",
- lintProject(
- "mock_location.xml=>AndroidManifest.xml",
- "mock_location.xml=>debug/AndroidManifest.xml",
- "mock_location.xml=>test/AndroidManifest.xml",
- "multiproject/library.properties=>build.gradle")); // dummy; only name counts
- // TODO: When we have an instantiatable gradle model, test with real model and verify
- // that a manifest file in a debug build type does not get flagged.
- }
-
- public void testMockLocationsOk() throws Exception {
- // Not a Gradle project
- mEnabled = Collections.singleton(ManifestDetector.MOCK_LOCATION);
- assertEquals(""
- + "No warnings.",
- lintProject(
- "mock_location.xml=>AndroidManifest.xml"));
- }
-
- public void testGradleOverrides() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.GRADLE_OVERRIDES);
- assertEquals(""
- + "AndroidManifest.xml:4: Warning: This versionCode value (1) is not used; it is always overridden by the value specified in the Gradle build script (2) [GradleOverrides]\n"
- + " android:versionCode=\"1\"\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "AndroidManifest.xml:5: Warning: This versionName value (1.0) is not used; it is always overridden by the value specified in the Gradle build script (MyName) [GradleOverrides]\n"
- + " android:versionName=\"1.0\" >\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "AndroidManifest.xml:7: Warning: This minSdkVersion value (14) is not used; it is always overridden by the value specified in the Gradle build script (5) [GradleOverrides]\n"
- + " <uses-sdk android:minSdkVersion=\"14\" android:targetSdkVersion=\"17\" />\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "AndroidManifest.xml:7: Warning: This targetSdkVersion value (17) is not used; it is always overridden by the value specified in the Gradle build script (16) [GradleOverrides]\n"
- + " <uses-sdk android:minSdkVersion=\"14\" android:targetSdkVersion=\"17\" />\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 4 warnings\n",
- lintProject(
- "gradle_override.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>build.gradle")); // dummy; only name counts
- }
-
- public void testGradleOverridesOk() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.GRADLE_OVERRIDES);
- // (See custom logic in #createClient which returns -1/null for the merged flavor
- // from this test, and not from testGradleOverrides)
- assertEquals(""
- + "No warnings.",
- lintProject(
- "gradle_override.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>build.gradle")); // dummy; only name counts
- }
-
- public void testManifestPackagePlaceholder() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.GRADLE_OVERRIDES);
- assertEquals(""
- + "AndroidManifest.xml:3: Warning: Cannot use placeholder for the package in the manifest; set applicationId in build.gradle instead [GradleOverrides]\n"
- + " package=\"${packageName}\" >\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 1 warnings\n",
-
- lintProject(
- "gradle_override_placeholder.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>build.gradle")); // dummy; only name counts
- }
-
- public void testMipMap() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.MIPMAP);
- assertEquals("No warnings.",
-
- lintProject(
- "mipmap.xml=>AndroidManifest.xml"));
- }
-
- public void testMipMapWithDensityFiltering() throws Exception {
- mEnabled = Collections.singleton(ManifestDetector.MIPMAP);
- assertEquals(""
- + "AndroidManifest.xml:9: Warning: Should use @mipmap instead of @drawable for launcher icons [MipmapIcons]\n"
- + " android:icon=\"@drawable/ic_launcher\"\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "AndroidManifest.xml:14: Warning: Should use @mipmap instead of @drawable for launcher icons [MipmapIcons]\n"
- + " android:icon=\"@drawable/activity1\"\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 2 warnings\n",
-
- lintProject(
- "mipmap.xml=>AndroidManifest.xml"));
- }
-
- // Custom project which locates all manifest files in the project rather than just
- // being hardcoded to the root level
-
- @Override
- protected TestLintClient createClient() {
- if ("testMipMapWithDensityFiltering".equals(getName())) {
- // Set up a mock project model for the resource configuration test(s)
- // where we provide a subset of densities to be included
- return new TestLintClient() {
- @NonNull
- @Override
- protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
- return new Project(this, dir, referenceDir) {
- @Override
- public boolean isGradleProject() {
- return true;
- }
-
- @Nullable
- @Override
- public AndroidProject getGradleProjectModel() {
- /*
- Simulate variant freeBetaDebug in this setup:
- defaultConfig {
- ...
- resConfigs "cs"
- }
- flavorDimensions "pricing", "releaseType"
- productFlavors {
- beta {
- flavorDimension "releaseType"
- resConfig "en", "de"
- resConfigs "nodpi", "hdpi"
- }
- normal { flavorDimension "releaseType" }
- free { flavorDimension "pricing" }
- paid { flavorDimension "pricing" }
- }
- */
- ProductFlavor flavorFree = mock(ProductFlavor.class);
- when(flavorFree.getName()).thenReturn("free");
- when(flavorFree.getResourceConfigurations())
- .thenReturn(Collections.<String>emptyList());
-
- ProductFlavor flavorNormal = mock(ProductFlavor.class);
- when(flavorNormal.getName()).thenReturn("normal");
- when(flavorNormal.getResourceConfigurations())
- .thenReturn(Collections.<String>emptyList());
-
- ProductFlavor flavorPaid = mock(ProductFlavor.class);
- when(flavorPaid.getName()).thenReturn("paid");
- when(flavorPaid.getResourceConfigurations())
- .thenReturn(Collections.<String>emptyList());
-
- ProductFlavor flavorBeta = mock(ProductFlavor.class);
- when(flavorBeta.getName()).thenReturn("beta");
- List<String> resConfigs = Arrays.asList("hdpi", "en", "de", "nodpi");
- when(flavorBeta.getResourceConfigurations()).thenReturn(resConfigs);
-
- ProductFlavor defaultFlavor = mock(ProductFlavor.class);
- when(defaultFlavor.getName()).thenReturn("main");
- when(defaultFlavor.getResourceConfigurations()).thenReturn(
- Collections.singleton("cs"));
-
- ProductFlavorContainer containerBeta =
- mock(ProductFlavorContainer.class);
- when(containerBeta.getProductFlavor()).thenReturn(flavorBeta);
-
- ProductFlavorContainer containerFree =
- mock(ProductFlavorContainer.class);
- when(containerFree.getProductFlavor()).thenReturn(flavorFree);
-
- ProductFlavorContainer containerPaid =
- mock(ProductFlavorContainer.class);
- when(containerPaid.getProductFlavor()).thenReturn(flavorPaid);
-
- ProductFlavorContainer containerNormal =
- mock(ProductFlavorContainer.class);
- when(containerNormal.getProductFlavor()).thenReturn(flavorNormal);
-
- ProductFlavorContainer defaultContainer =
- mock(ProductFlavorContainer.class);
- when(defaultContainer.getProductFlavor()).thenReturn(defaultFlavor);
-
- List<ProductFlavorContainer> containers = Arrays.asList(
- containerPaid, containerFree, containerNormal, containerBeta
- );
-
- AndroidProject project = mock(AndroidProject.class);
- when(project.getProductFlavors()).thenReturn(containers);
- when(project.getDefaultConfig()).thenReturn(defaultContainer);
- return project;
- }
-
- @Nullable
- @Override
- public Variant getCurrentVariant() {
- List<String> productFlavorNames = Arrays.asList("free", "beta");
- Variant mock = mock(Variant.class);
- when(mock.getProductFlavors()).thenReturn(productFlavorNames);
- return mock;
- }
- };
- }
- };
- }
- if (mEnabled.contains(ManifestDetector.MOCK_LOCATION)) {
- return new TestLintClient() {
- @NonNull
- @Override
- protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
- return new Project(this, dir, referenceDir) {
- @NonNull
- @Override
- public List<File> getManifestFiles() {
- if (mManifestFiles == null) {
- mManifestFiles = Lists.newArrayList();
- addManifestFiles(mDir);
- }
-
- return mManifestFiles;
- }
-
- private void addManifestFiles(File dir) {
- if (dir.getName().equals(ANDROID_MANIFEST_XML)) {
- mManifestFiles.add(dir);
- } else if (dir.isDirectory()) {
- File[] files = dir.listFiles();
- if (files != null) {
- for (File file : files) {
- addManifestFiles(file);
- }
- }
- }
- }
-
- @NonNull SourceProvider createSourceProvider(File manifest) {
- SourceProvider provider = mock(SourceProvider.class);
- when(provider.getManifestFile()).thenReturn(manifest);
- return provider;
- }
-
- @Nullable
- @Override
- public AndroidProject getGradleProjectModel() {
- if (!isGradleProject()) {
- return null;
- }
-
- File main = new File(mDir, ANDROID_MANIFEST_XML);
- File debug = new File(mDir, "debug" + File.separator + ANDROID_MANIFEST_XML);
- File test = new File(mDir, "test" + File.separator + ANDROID_MANIFEST_XML);
-
- SourceProvider defaultSourceProvider = createSourceProvider(main);
- SourceProvider debugSourceProvider = createSourceProvider(debug);
- SourceProvider testSourceProvider = createSourceProvider(test);
-
- ProductFlavorContainer defaultConfig = mock(ProductFlavorContainer.class);
- when(defaultConfig.getSourceProvider()).thenReturn(defaultSourceProvider);
-
- BuildType buildType = mock(BuildType.class);
- when(buildType.isDebuggable()).thenReturn(true);
-
- BuildTypeContainer buildTypeContainer = mock(BuildTypeContainer.class);
- when(buildTypeContainer.getBuildType()).thenReturn(buildType);
- when(buildTypeContainer.getSourceProvider()).thenReturn(debugSourceProvider);
- List<BuildTypeContainer> buildTypes = Lists.newArrayList(buildTypeContainer);
-
- SourceProviderContainer extraProvider = mock(SourceProviderContainer.class);
- when(extraProvider.getArtifactName()).thenReturn(AndroidProject.ARTIFACT_ANDROID_TEST);
- when(extraProvider.getSourceProvider()).thenReturn(testSourceProvider);
- List<SourceProviderContainer> extraProviders = Lists.newArrayList(extraProvider);
-
- ProductFlavorContainer productFlavorContainer = mock(ProductFlavorContainer.class);
- when(productFlavorContainer.getExtraSourceProviders()).thenReturn(extraProviders);
- List<ProductFlavorContainer> productFlavors = Lists.newArrayList(productFlavorContainer);
-
- AndroidProject project = mock(AndroidProject.class);
- when(project.getDefaultConfig()).thenReturn(defaultConfig);
- when(project.getBuildTypes()).thenReturn(buildTypes);
- when(project.getProductFlavors()).thenReturn(productFlavors);
- return project;
- }
- };
- }
- };
- } else if (mEnabled.contains(ManifestDetector.GRADLE_OVERRIDES)) {
- return new TestLintClient() {
- @NonNull
- @Override
- protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
- return new Project(this, dir, referenceDir) {
- @Override
- public boolean isGradleProject() {
- return true;
- }
-
- @Nullable
- @Override
- public Variant getCurrentVariant() {
- ProductFlavor flavor = mock(ProductFlavor.class);
- if (getName().equals("ManifestDetectorTest_testGradleOverridesOk") ||
- getName().equals(
- "ManifestDetectorTest_testManifestPackagePlaceholder")) {
- when(flavor.getMinSdkVersion()).thenReturn(null);
- when(flavor.getTargetSdkVersion()).thenReturn(null);
- when(flavor.getVersionCode()).thenReturn(null);
- when(flavor.getVersionName()).thenReturn(null);
- } else {
- assertEquals(getName(), "ManifestDetectorTest_testGradleOverrides");
-
- ApiVersion apiMock = mock(ApiVersion.class);
- when(apiMock.getApiLevel()).thenReturn(5);
- when(apiMock.getApiString()).thenReturn("5");
- when(flavor.getMinSdkVersion()).thenReturn(apiMock);
-
- apiMock = mock(ApiVersion.class);
- when(apiMock.getApiLevel()).thenReturn(16);
- when(apiMock.getApiString()).thenReturn("16");
- when(flavor.getTargetSdkVersion()).thenReturn(apiMock);
-
- when(flavor.getVersionCode()).thenReturn(2);
- when(flavor.getVersionName()).thenReturn("MyName");
- }
-
- Variant mock = mock(Variant.class);
- when(mock.getMergedFlavor()).thenReturn(flavor);
- return mock;
- }
- };
- }
- };
-
- }
- return super.createClient();
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/NamespaceDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/NamespaceDetectorTest.java
deleted file mode 100644
index bf183da..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/NamespaceDetectorTest.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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("javadoc")
-public class NamespaceDetectorTest extends AbstractCheckTest {
- @Override
- protected Detector getDetector() {
- return new NamespaceDetector();
- }
-
- public void testCustom() throws Exception {
- assertEquals(
- "res/layout/customview.xml:5: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" +
- " xmlns:foo=\"http://schemas.android.com/apk/res/foo\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "1 errors, 0 warnings\n",
-
- lintProject(
- "multiproject/library-manifest.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>project.properties",
- "res/layout/customview.xml"
- ));
- }
-
- public void testGradle() throws Exception {
- assertEquals(""
- + "res/layout/customview.xml:5: Error: In Gradle projects, always use http://schemas.android.com/apk/res-auto for custom attributes [ResAuto]\n"
- + " xmlns:foo=\"http://schemas.android.com/apk/res/foo\"\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "1 errors, 0 warnings\n",
-
- lintProject(
- "multiproject/library.properties=>build.gradle", // dummy; only name counts
- "res/layout/customview.xml"
- ));
- }
-
- public void testCustomOk() throws Exception {
- assertEquals(
- "No warnings.",
-
- lintProject(
- "multiproject/library-manifest.xml=>AndroidManifest.xml",
-
- // Use a standard project properties instead: no warning since it's
- // not a library project:
- //"multiproject/library.properties=>project.properties",
-
- "res/layout/customview.xml"
- ));
- }
-
- public void testCustomOk2() throws Exception {
- assertEquals(
- "No warnings.",
-
- lintProject(
- "multiproject/library-manifest.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>project.properties",
- // This project already uses the res-auto package
- "res/layout/customview2.xml"
- ));
- }
-
- public void testTypo() throws Exception {
- assertEquals(
- "res/layout/wrong_namespace.xml:2: Error: Unexpected namespace URI bound to the \"android\" prefix, was http://schemas.android.com/apk/res/andriod, expected http://schemas.android.com/apk/res/android [NamespaceTypo]\n" +
- "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/andriod\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "1 errors, 0 warnings\n",
-
- lintProject("res/layout/wrong_namespace.xml"));
- }
-
- public void testTypo2() throws Exception {
- assertEquals(
- "res/layout/wrong_namespace2.xml:2: Error: URI is case sensitive: was \"http://schemas.android.com/apk/res/Android\", expected \"http://schemas.android.com/apk/res/android\" [NamespaceTypo]\n" +
- "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/Android\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "1 errors, 0 warnings\n",
-
- lintProject("res/layout/wrong_namespace2.xml"));
- }
-
- public void testTypo3() throws Exception {
- assertEquals(
- "res/layout/wrong_namespace3.xml:2: Error: Unexpected namespace URI bound to the \"android\" prefix, was http://schemas.android.com/apk/res/androi, expected http://schemas.android.com/apk/res/android [NamespaceTypo]\n" +
- "<LinearLayout xmlns:a=\"http://schemas.android.com/apk/res/androi\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "1 errors, 0 warnings\n",
-
- lintProject("res/layout/wrong_namespace3.xml"));
- }
-
- public void testTypo4() throws Exception {
- assertEquals(
- "res/layout/wrong_namespace5.xml:2: Error: Suspicious namespace: should start with http:// [NamespaceTypo]\n" +
- " xmlns:noturi=\"tp://schems.android.com/apk/res/com.my.package\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "res/layout/wrong_namespace5.xml:3: Error: Possible typo in URL: was \"http://schems.android.com/apk/res/com.my.package\", should probably be \"http://schemas.android.com/apk/res/com.my.package\" [NamespaceTypo]\n" +
- " xmlns:typo1=\"http://schems.android.com/apk/res/com.my.package\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "res/layout/wrong_namespace5.xml:4: Error: Possible typo in URL: was \"http://schems.android.comm/apk/res/com.my.package\", should probably be \"http://schemas.android.com/apk/res/com.my.package\" [NamespaceTypo]\n" +
- " xmlns:typo2=\"http://schems.android.comm/apk/res/com.my.package\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "3 errors, 0 warnings\n",
-
- lintProject("res/layout/wrong_namespace5.xml"));
- }
-
- public void testTypoOk() throws Exception {
- assertEquals(
- "No warnings.",
-
- lintProject("res/layout/wrong_namespace4.xml"));
- }
-
- public void testUnused() throws Exception {
- assertEquals(
- "res/layout/unused_namespace.xml:3: Warning: Unused namespace unused1 [UnusedNamespace]\n" +
- " xmlns:unused1=\"http://schemas.android.com/apk/res/unused1\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "res/layout/unused_namespace.xml:4: Warning: Unused namespace unused2 [UnusedNamespace]\n" +
- " xmlns:unused2=\"http://schemas.android.com/apk/res/unused1\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "0 errors, 2 warnings\n" +
- "",
-
- lintProject("res/layout/unused_namespace.xml"));
- }
-
- public void testUnusedOk() throws Exception {
- assertEquals(
- "No warnings.",
-
- lintProject("res/layout/layout1.xml"));
- }
-
- public void testLayoutAttributesOk() throws Exception {
- assertEquals(
- "No warnings.",
-
- lintFiles("res/layout/namespace3.xml"));
- }
-
- public void testLayoutAttributesOk2() throws Exception {
- assertEquals(
- "No warnings.",
-
- lintFiles("res/layout/namespace4.xml"));
- }
-
- public void testLayoutAttributes() throws Exception {
- assertEquals(
- "res/layout/namespace3.xml:2: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" +
- " xmlns:app=\"http://schemas.android.com/apk/res/com.example.apicalltest\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "1 errors, 0 warnings\n",
-
- lintFiles("res/layout/namespace3.xml",
- "multiproject/library-manifest.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>project.properties"));
- }
-
- public void testLayoutAttributes2() throws Exception {
- assertEquals(
- "res/layout/namespace4.xml:3: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" +
- " xmlns:app=\"http://schemas.android.com/apk/res/com.example.apicalltest\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "1 errors, 0 warnings\n",
-
- lintFiles("res/layout/namespace4.xml",
- "multiproject/library-manifest.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>project.properties"));
- }
-
- public void testWrongResAutoUrl() throws Exception {
- assertEquals(
- "res/layout/namespace5.xml:3: Error: Suspicious namespace: Did you mean http://schemas.android.com/apk/res-auto? [ResAuto]\n" +
- " xmlns:app=\"http://schemas.android.com/apk/auto-res/\"\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "1 errors, 0 warnings\n",
-
- lintFiles("res/layout/namespace5.xml",
- "multiproject/library-manifest.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>project.properties"));
- }
-
- public void testWrongResUrl() throws Exception {
- assertEquals(""
- + "AndroidManifest.xml:2: Error: Suspicious namespace: should start with http:// [NamespaceTypo]\n"
- + "<manifest xmlns:android=\"https://schemas.android.com/apk/res/android\"\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "1 errors, 0 warnings\n",
-
- lintFiles("https_namespace.xml=>AndroidManifest.xml"));
- }
-}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/PrivateResourceDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/PrivateResourceDetectorTest.java
deleted file mode 100644
index 45df3bf..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/PrivateResourceDetectorTest.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.FN_PUBLIC_TXT;
-import static com.android.SdkConstants.FN_RESOURCE_TEXT;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.builder.model.AndroidArtifact;
-import com.android.builder.model.AndroidLibrary;
-import com.android.builder.model.AndroidProject;
-import com.android.builder.model.Dependencies;
-import com.android.builder.model.Variant;
-import com.android.builder.model.Version;
-import com.android.testutils.TestUtils;
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Project;
-import com.google.common.base.Charsets;
-import com.google.common.io.Files;
-
-import org.mockito.stubbing.OngoingStubbing;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
-
-@SuppressWarnings("javadoc")
-public class PrivateResourceDetectorTest extends AbstractCheckTest {
- @Override
- protected Detector getDetector() {
- return new PrivateResourceDetector();
- }
-
- public void testPrivateInXml() throws Exception {
- assertEquals(""
- + "res/layout/private.xml:11: Warning: The resource @string/my_private_string is marked as private in the library [PrivateResource]\n"
- + " android:text=\"@string/my_private_string\" />\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 1 warnings\n",
- lintProject(xml("res/layout/private.xml", ""
- + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
- + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
- + " android:id=\"@+id/newlinear\"\n"
- + " android:orientation=\"vertical\"\n"
- + " android:layout_width=\"match_parent\"\n"
- + " android:layout_height=\"match_parent\">\n"
- + "\n"
- + " <TextView\n"
- + " android:layout_width=\"wrap_content\"\n"
- + " android:layout_height=\"wrap_content\"\n"
- + " android:text=\"@string/my_private_string\" />\n"
- + "\n"
- + " <TextView\n"
- + " android:layout_width=\"wrap_content\"\n"
- + " android:layout_height=\"wrap_content\"\n"
- + " android:text=\"@string/my_public_string\" />\n"
- + "</LinearLayout>\n")));
- }
-
- public void testPrivateInJava() throws Exception {
- assertEquals(""
- + ""
- + "src/test/pkg/Private.java:3: Warning: The resource @string/my_private_string is marked as private in the library [PrivateResource]\n"
- + " int x = R.string.my_private_string; // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 1 warnings\n",
- lintProject(java("src/test/pkg/Private.java", ""
- + "public class PrivateResourceDetectorTest {\n"
- + " void test() {\n"
- + " int x = R.string.my_private_string; // ERROR\n"
- + " int y = R.string.my_public_string; // OK\n"
- + " int y = android.R.string.my_private_string; // OK (not in project namespace)\n"
- + " }\n"
- + "}\n")));
- }
-
- public void testOverride() throws Exception {
- assertEquals(""
- + "res/layout/my_private_layout.xml: Warning: Overriding @layout/my_private_layout which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
- + "res/values/strings.xml:5: Warning: Overriding @string/my_private_string which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
- + " <string name=\"my_private_string\">String 1</string>\n"
- + " ~~~~~~~~~~~~~~~~~\n"
- + "res/values/strings.xml:9: Warning: Overriding @string/my_private_string which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
- + " <item type=\"string\" name=\"my_private_string\">String 1</item>\n"
- + " ~~~~~~~~~~~~~~~~~\n"
- + "res/values/strings.xml:12: Warning: Overriding @string/my_private_string which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
- + " <string tools:override=\"false\" name=\"my_private_string\">String 2</string>\n"
- + " ~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 4 warnings\n",
- lintProject(xml("res/values/strings.xml", ""
- + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
- + "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n"
- + "\n"
- + " <string name=\"app_name\">LibraryProject</string>\n"
- + " <string name=\"my_private_string\">String 1</string>\n"
- + " <string name=\"my_public_string\">String 2</string>\n"
- + " <string name=\"string3\"> @my_private_string </string>\n"
- + " <string name=\"string4\"> @my_public_string </string>\n"
- + " <item type=\"string\" name=\"my_private_string\">String 1</item>\n"
- + " <dimen name=\"my_private_string\">String 1</dimen>\n" // unrelated
- + " <string tools:ignore=\"PrivateResource\" name=\"my_private_string\">String 2</string>\n"
- + " <string tools:override=\"false\" name=\"my_private_string\">String 2</string>\n"
- + " <string tools:override=\"true\" name=\"my_private_string\">String 2</string>\n"
- + "\n"
- + "</resources>\n"),
- xml("res/layout/my_private_layout.xml", "<LinearLayout/>"),
- xml("res/layout/my_public_layout.xml", "<LinearLayout/>")));
- }
-
- @Override
- protected TestLintClient createClient() {
- return new TestLintClient() {
- @NonNull
- @Override
- protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
- return new Project(this, dir, referenceDir) {
- @Override
- public boolean isGradleProject() {
- return true;
- }
-
- @Nullable
- @Override
- public AndroidProject getGradleProjectModel() {
- return createMockProject(Version.ANDROID_GRADLE_PLUGIN_VERSION,
- Version.BUILDER_MODEL_API_VERSION);
- }
-
- @Nullable
- @Override
- public Variant getCurrentVariant() {
- try {
- AndroidLibrary library = createMockLibrary(
- ""
- + "int string my_private_string 0x7f040000\n"
- + "int string my_public_string 0x7f040001\n"
- + "int layout my_private_layout 0x7f040002\n",
- ""
- + ""
- + "string my_public_string\n",
- Collections.<AndroidLibrary>emptyList()
- );
- AndroidArtifact artifact = createMockArtifact(
- Collections.singletonList(library));
-
- Variant variant = mock(Variant.class);
- when(variant.getMainArtifact()).thenReturn(artifact);
- return variant;
- } catch (Exception e) {
- fail(e.toString());
- return null;
- }
- }
- };
- }
- };
- }
-
- public static AndroidProject createMockProject(String modelVersion, int apiVersion) {
- AndroidProject project = mock(AndroidProject.class);
- when(project.getApiVersion()).thenReturn(apiVersion);
- when(project.getModelVersion()).thenReturn(modelVersion);
-
- return project;
- }
-
- public static AndroidArtifact createMockArtifact(List<AndroidLibrary> libraries) {
- Dependencies dependencies = mock(Dependencies.class);
- when(dependencies.getLibraries()).thenReturn(libraries);
-
- AndroidArtifact artifact = mock(AndroidArtifact.class);
- when(artifact.getDependencies()).thenReturn(dependencies);
-
- return artifact;
- }
-
- public static AndroidLibrary createMockLibrary(String allResources, String publicResources,
- List<AndroidLibrary> dependencies)
- throws IOException {
- final File tempDir = TestUtils.createTempDirDeletedOnExit();
-
- Files.write(allResources, new File(tempDir, FN_RESOURCE_TEXT), Charsets.UTF_8);
- File publicTxtFile = new File(tempDir, FN_PUBLIC_TXT);
- if (publicResources != null) {
- Files.write(publicResources, publicTxtFile, Charsets.UTF_8);
- }
- AndroidLibrary library = mock(AndroidLibrary.class);
- when(library.getPublicResources()).thenReturn(publicTxtFile);
-
- // Work around wildcard capture
- //when(mock.getLibraryDependencies()).thenReturn(dependencies);
- List libraryDependencies = library.getLibraryDependencies();
- OngoingStubbing<List> setter = when(libraryDependencies);
- setter.thenReturn(dependencies);
- return library;
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/RequiredAttributeDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/RequiredAttributeDetectorTest.java
deleted file mode 100644
index 9b60532..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/RequiredAttributeDetectorTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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("javadoc")
-public class RequiredAttributeDetectorTest extends AbstractCheckTest {
- @Override
- protected Detector getDetector() {
- return new RequiredAttributeDetector();
- }
-
- public void test() throws Exception {
- // Simple: Only consider missing attributes in the layout xml file
- // (though skip warnings on <merge> tags and under <GridLayout>
- assertEquals(
- "res/layout/size.xml:13: Error: The required layout_height attribute is missing [RequiredSize]\n" +
- " <RadioButton\n" +
- " ^\n" +
- "res/layout/size.xml:18: Error: The required layout_width attribute is missing [RequiredSize]\n" +
- " <EditText\n" +
- " ^\n" +
- "res/layout/size.xml:23: Error: The required layout_width and layout_height attributes are missing [RequiredSize]\n" +
- " <EditText\n" +
- " ^\n" +
- "3 errors, 0 warnings\n",
-
- lintProject("res/layout/size.xml"));
- }
-
- public void test2() throws Exception {
- // Consider styles (specifying sizes) and includes (providing sizes for the root tags)
- assertEquals(
- "res/layout/size2.xml:9: Error: The required layout_width and layout_height attributes are missing [RequiredSize]\n" +
- " <Button\n" +
- " ^\n" +
- "res/layout/size2.xml:18: Error: The required layout_height attribute is missing [RequiredSize]\n" +
- " <Button\n" +
- " ^\n" +
- "2 errors, 0 warnings\n",
-
- lintProject(
- "res/layout/size2.xml",
- "res/layout/sizeincluded.xml",
- "res/values/sizestyles.xml"
- ));
- }
-
- public void testInflaters() throws Exception {
- // Consider java inflation
- assertEquals(
- "res/layout/size5.xml:2: Error: The required layout_width and layout_height attributes are missing [RequiredSize]\n" +
- "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
- "^\n" +
- "1 errors, 0 warnings\n",
-
- lintProject(
- "src/test/pkg/InflaterTest.java.txt=>src/test/pkg/InflaterTest.java",
- "res/layout/sizeincluded.xml=>res/layout/size1.xml",
- "res/layout/sizeincluded.xml=>res/layout/size2.xml",
- "res/layout/sizeincluded.xml=>res/layout/size3.xml",
- "res/layout/sizeincluded.xml=>res/layout/size4.xml",
- "res/layout/sizeincluded.xml=>res/layout/size5.xml",
- "res/layout/sizeincluded.xml=>res/layout/size6.xml",
- "res/layout/sizeincluded.xml=>res/layout/size7.xml"
- ));
- }
-
- public void testRequestFocus() throws Exception {
- // See http://code.google.com/p/android/issues/detail?id=38700
- assertEquals(
- "No warnings.",
-
- lintProject(
- "res/layout/edit_type.xml"
- ));
- }
-
- public void testFrameworkStyles() throws Exception {
- // See http://code.google.com/p/android/issues/detail?id=38958
- assertEquals(
- "No warnings.",
-
- lintProject(
- "res/layout/listseparator.xml"
- ));
- }
-
- public void testThemeStyles() throws Exception {
- // Check that we don't complain about cases where the size is defined in a theme
- assertEquals(
- "No warnings.",
-
- lintProject(
- "res/layout/size.xml",
- "res/values/themes.xml"
- ));
- }
-
- public void testThemeStyles2() throws Exception {
- // Check that we don't complain about cases where the size is defined in a theme
- assertEquals(
- "No warnings.",
-
- lintProject(
- "res/layout/size.xml",
- "res/values/themes2.xml"
- ));
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java
deleted file mode 100644
index 39f9007..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint.checks;
-
-import com.android.tools.lint.ExternalAnnotationRepository;
-import com.android.tools.lint.detector.api.Detector;
-
-public class SupportAnnotationDetectorTest extends AbstractCheckTest {
- private static final boolean SDK_ANNOTATIONS_AVAILABLE =
- new SupportAnnotationDetectorTest().createClient().findResource(
- ExternalAnnotationRepository.SDK_ANNOTATIONS_PATH) != null;
-
- @Override
- protected Detector getDetector() {
- return new SupportAnnotationDetector();
- }
-
- public void testRange() throws Exception {
- assertEquals(""
- + "src/test/pkg/RangeTest.java:32: Error: Expected length 5 (was 4) [Range]\n"
- + " printExact(\"1234\"); // ERROR\n"
- + " ~~~~~~\n"
- + "src/test/pkg/RangeTest.java:34: Error: Expected length 5 (was 6) [Range]\n"
- + " printExact(\"123456\"); // ERROR\n"
- + " ~~~~~~~~\n"
- + "src/test/pkg/RangeTest.java:36: Error: Expected length ≥ 5 (was 4) [Range]\n"
- + " printMin(\"1234\"); // ERROR\n"
- + " ~~~~~~\n"
- + "src/test/pkg/RangeTest.java:43: Error: Expected length ≤ 8 (was 9) [Range]\n"
- + " printMax(\"123456789\"); // ERROR\n"
- + " ~~~~~~~~~~~\n"
- + "src/test/pkg/RangeTest.java:45: Error: Expected length ≥ 4 (was 3) [Range]\n"
- + " printRange(\"123\"); // ERROR\n"
- + " ~~~~~\n"
- + "src/test/pkg/RangeTest.java:49: Error: Expected length ≤ 6 (was 7) [Range]\n"
- + " printRange(\"1234567\"); // ERROR\n"
- + " ~~~~~~~~~\n"
- + "src/test/pkg/RangeTest.java:53: Error: Expected size 5 (was 4) [Range]\n"
- + " printExact(new int[]{1, 2, 3, 4}); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/RangeTest.java:55: Error: Expected size 5 (was 6) [Range]\n"
- + " printExact(new int[]{1, 2, 3, 4, 5, 6}); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/RangeTest.java:57: Error: Expected size ≥ 5 (was 4) [Range]\n"
- + " printMin(new int[]{1, 2, 3, 4}); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/RangeTest.java:65: Error: Expected size ≤ 8 (was 9) [Range]\n"
- + " printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/RangeTest.java:67: Error: Expected size ≥ 4 (was 3) [Range]\n"
- + " printRange(new int[] {1,2,3}); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/RangeTest.java:71: Error: Expected size ≤ 6 (was 7) [Range]\n"
- + " printRange(new int[] {1,2,3,4,5,6,7}); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/RangeTest.java:74: Error: Expected size to be a multiple of 3 (was 4 and should be either 3 or 6) [Range]\n"
- + " printMultiple(new int[] {1,2,3,4}); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/RangeTest.java:75: Error: Expected size to be a multiple of 3 (was 5 and should be either 3 or 6) [Range]\n"
- + " printMultiple(new int[] {1,2,3,4,5}); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/RangeTest.java:77: Error: Expected size to be a multiple of 3 (was 7 and should be either 6 or 9) [Range]\n"
- + " printMultiple(new int[] {1,2,3,4,5,6,7}); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/RangeTest.java:80: Error: Expected size ≥ 4 (was 3) [Range]\n"
- + " printMinMultiple(new int[]{1, 2, 3}); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/RangeTest.java:84: Error: Value must be ≥ 4 (was 3) [Range]\n"
- + " printAtLeast(3); // ERROR\n"
- + " ~\n"
- + "src/test/pkg/RangeTest.java:91: Error: Value must be ≤ 7 (was 8) [Range]\n"
- + " printAtMost(8); // ERROR\n"
- + " ~\n"
- + "src/test/pkg/RangeTest.java:93: Error: Value must be ≥ 4 (was 3) [Range]\n"
- + " printBetween(3); // ERROR\n"
- + " ~\n"
- + "src/test/pkg/RangeTest.java:98: Error: Value must be ≤ 7 (was 8) [Range]\n"
- + " printBetween(8); // ERROR\n"
- + " ~\n"
- + "src/test/pkg/RangeTest.java:102: Error: Value must be ≥ 2.5 (was 2.49) [Range]\n"
- + " printAtLeastInclusive(2.49f); // ERROR\n"
- + " ~~~~~\n"
- + "src/test/pkg/RangeTest.java:106: Error: Value must be > 2.5 (was 2.49) [Range]\n"
- + " printAtLeastExclusive(2.49f); // ERROR\n"
- + " ~~~~~\n"
- + "src/test/pkg/RangeTest.java:107: Error: Value must be > 2.5 (was 2.5) [Range]\n"
- + " printAtLeastExclusive(2.5f); // ERROR\n"
- + " ~~~~\n"
- + "src/test/pkg/RangeTest.java:113: Error: Value must be ≤ 7.0 (was 7.1) [Range]\n"
- + " printAtMostInclusive(7.1f); // ERROR\n"
- + " ~~~~\n"
- + "src/test/pkg/RangeTest.java:117: Error: Value must be < 7.0 (was 7.0) [Range]\n"
- + " printAtMostExclusive(7.0f); // ERROR\n"
- + " ~~~~\n"
- + "src/test/pkg/RangeTest.java:118: Error: Value must be < 7.0 (was 7.1) [Range]\n"
- + " printAtMostExclusive(7.1f); // ERROR\n"
- + " ~~~~\n"
- + "src/test/pkg/RangeTest.java:120: Error: Value must be ≥ 2.5 (was 2.4) [Range]\n"
- + " printBetweenFromInclusiveToInclusive(2.4f); // ERROR\n"
- + " ~~~~\n"
- + "src/test/pkg/RangeTest.java:124: Error: Value must be ≤ 5.0 (was 5.1) [Range]\n"
- + " printBetweenFromInclusiveToInclusive(5.1f); // ERROR\n"
- + " ~~~~\n"
- + "src/test/pkg/RangeTest.java:126: Error: Value must be > 2.5 (was 2.4) [Range]\n"
- + " printBetweenFromExclusiveToInclusive(2.4f); // ERROR\n"
- + " ~~~~\n"
- + "src/test/pkg/RangeTest.java:127: Error: Value must be > 2.5 (was 2.5) [Range]\n"
- + " printBetweenFromExclusiveToInclusive(2.5f); // ERROR\n"
- + " ~~~~\n"
- + "src/test/pkg/RangeTest.java:129: Error: Value must be ≤ 5.0 (was 5.1) [Range]\n"
- + " printBetweenFromExclusiveToInclusive(5.1f); // ERROR\n"
- + " ~~~~\n"
- + "src/test/pkg/RangeTest.java:131: Error: Value must be ≥ 2.5 (was 2.4) [Range]\n"
- + " printBetweenFromInclusiveToExclusive(2.4f); // ERROR\n"
- + " ~~~~\n"
- + "src/test/pkg/RangeTest.java:135: Error: Value must be < 5.0 (was 5.0) [Range]\n"
- + " printBetweenFromInclusiveToExclusive(5.0f); // ERROR\n"
- + " ~~~~\n"
- + "src/test/pkg/RangeTest.java:137: Error: Value must be > 2.5 (was 2.4) [Range]\n"
- + " printBetweenFromExclusiveToExclusive(2.4f); // ERROR\n"
- + " ~~~~\n"
- + "src/test/pkg/RangeTest.java:138: Error: Value must be > 2.5 (was 2.5) [Range]\n"
- + " printBetweenFromExclusiveToExclusive(2.5f); // ERROR\n"
- + " ~~~~\n"
- + "src/test/pkg/RangeTest.java:141: Error: Value must be < 5.0 (was 5.0) [Range]\n"
- + " printBetweenFromExclusiveToExclusive(5.0f); // ERROR\n"
- + " ~~~~\n"
- + "36 errors, 0 warnings\n",
-
- lintProject("src/test/pkg/RangeTest.java.txt=>src/test/pkg/RangeTest.java",
- "src/android/support/annotation/Size.java.txt=>src/android/support/annotation/Size.java",
- "src/android/support/annotation/IntRange.java.txt=>src/android/support/annotation/IntRange.java",
- "src/android/support/annotation/FloatRange.java.txt=>src/android/support/annotation/FloatRange.java"
- ));
- }
-
- public void testTypeDef() throws Exception {
- assertEquals(""
- + "src/test/pkg/IntDefTest.java:31: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
- + " setStyle(0, 0); // ERROR\n"
- + " ~\n"
- + "src/test/pkg/IntDefTest.java:32: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
- + " setStyle(-1, 0); // ERROR\n"
- + " ~~\n"
- + "src/test/pkg/IntDefTest.java:33: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
- + " setStyle(UNRELATED, 0); // ERROR\n"
- + " ~~~~~~~~~\n"
- + "src/test/pkg/IntDefTest.java:34: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
- + " setStyle(IntDefTest.UNRELATED, 0); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/IntDefTest.java:35: Error: Flag not allowed here [WrongConstant]\n"
- + " setStyle(IntDefTest.STYLE_NORMAL|STYLE_NO_FRAME, 0); // ERROR: Not a flag\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/IntDefTest.java:36: Error: Flag not allowed here [WrongConstant]\n"
- + " setStyle(~STYLE_NO_FRAME, 0); // ERROR: Not a flag\n"
- + " ~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/IntDefTest.java:55: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
- + " setFlags(\"\", UNRELATED); // ERROR\n"
- + " ~~~~~~~~~\n"
- + "src/test/pkg/IntDefTest.java:56: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
- + " setFlags(\"\", UNRELATED|STYLE_NO_TITLE); // ERROR\n"
- + " ~~~~~~~~~\n"
- + "src/test/pkg/IntDefTest.java:57: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
- + " setFlags(\"\", STYLE_NORMAL|STYLE_NO_TITLE|UNRELATED); // ERROR\n"
- + " ~~~~~~~~~\n"
- + "src/test/pkg/IntDefTest.java:58: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
- + " setFlags(\"\", 1); // ERROR\n"
- + " ~\n"
- + "src/test/pkg/IntDefTest.java:59: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
- + " setFlags(\"\", arg < 0 ? STYLE_NORMAL : UNRELATED); // ERROR\n"
- + " ~~~~~~~~~\n"
- + "src/test/pkg/IntDefTest.java:60: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
- + " setFlags(\"\", arg < 0 ? UNRELATED : STYLE_NORMAL); // ERROR\n"
- + " ~~~~~~~~~\n"
- + "src/test/pkg/IntDefTest.java:79: Error: Must be one of: IntDefTest.TYPE_1, IntDefTest.TYPE_2 [WrongConstant]\n"
- + " setTitle(\"\", UNRELATED_TYPE); // ERROR\n"
- + " ~~~~~~~~~~~~~~\n"
- + "src/test/pkg/IntDefTest.java:80: Error: Must be one of: IntDefTest.TYPE_1, IntDefTest.TYPE_2 [WrongConstant]\n"
- + " setTitle(\"\", \"type2\"); // ERROR\n"
- + " ~~~~~~~\n"
- + "src/test/pkg/IntDefTest.java:87: Error: Must be one of: IntDefTest.TYPE_1, IntDefTest.TYPE_2 [WrongConstant]\n"
- + " setTitle(\"\", type); // ERROR\n"
- + " ~~~~\n"
- + "src/test/pkg/IntDefTest.java:92: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
- + " setFlags(\"\", flag); // ERROR\n"
- + " ~~~~\n"
- + (SDK_ANNOTATIONS_AVAILABLE ?
- "src/test/pkg/IntDefTest.java:99: Error: Must be one of: View.LAYOUT_DIRECTION_LTR, View.LAYOUT_DIRECTION_RTL, View.LAYOUT_DIRECTION_INHERIT, View.LAYOUT_DIRECTION_LOCAL [WrongConstant]\n"
- + " view.setLayoutDirection(View.TEXT_DIRECTION_LTR); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/IntDefTest.java:100: Error: Must be one of: View.LAYOUT_DIRECTION_LTR, View.LAYOUT_DIRECTION_RTL, View.LAYOUT_DIRECTION_INHERIT, View.LAYOUT_DIRECTION_LOCAL [WrongConstant]\n"
- + " view.setLayoutDirection(0); // ERROR\n"
- + " ~\n"
- + "src/test/pkg/IntDefTest.java:101: Error: Flag not allowed here [WrongConstant]\n"
- + " view.setLayoutDirection(View.LAYOUT_DIRECTION_LTR|View.LAYOUT_DIRECTION_RTL); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/IntDefTest.java:102: Error: Must be one of: Context.POWER_SERVICE, Context.WINDOW_SERVICE, Context.LAYOUT_INFLATER_SERVICE, Context.ACCOUNT_SERVICE, Context.ACTIVITY_SERVICE, Context.ALARM_SERVICE, Context.NOTIFICATION_SERVICE, Context.ACCESSIBILITY_SERVICE, Context.CAPTIONING_SERVICE, Context.KEYGUARD_SERVICE, Context.LOCATION_SERVICE, Context.SEARCH_SERVICE, Context.SENSOR_SERVICE, Context.STORAGE_SERVICE, Context.WALLPAPER_SERVICE, Context.VIBRATOR_SERVICE, Context.CONNECTIVITY_SERVICE, Context.WIFI_SERVICE, Context.WIFI_P2P_SERVICE, Context.NSD_SERVICE, Context.AUDIO_SERVICE, Context.MEDIA_ROUTER_SERVICE, Context.TELEPHONY_SERVICE, Context.TELECOM_SERVICE, Context.CLIPBOARD_SERVICE, Context.INPUT_METHOD_SERVICE, Context.TEXT_SERVICES_MANAGER_SERVICE, Context.APPWIDGET_SERVICE, Context.DROPBOX_SERVICE, Context.DEVICE_POLICY_SERVICE, Context.UI_MODE_SERVICE, Context.DOWNLOAD_SERVICE, Context.NFC_SERVICE, Context.BLUETOOTH_SERVICE, Context.USB_SERVICE, Context.LAUNCHER_APPS_SERVICE, Context.INPUT_SERVICE, Context.DISPLAY_SERVICE, Context.USER_SERVICE, Context.RESTRICTIONS_SERVICE, Context.APP_OPS_SERVICE, Context.CAMERA_SERVICE, Context.PRINT_SERVICE, Context.CONSUMER_IR_SERVICE, Context.TV_INPUT_SERVICE, Context.MEDIA_SESSION_SERVICE, Context.BATTERY_SERVICE, Context.JOB_SCHEDULER_SERVICE, Context.MEDIA_PROJECTION_SERVIC [WrongConstant]\n"
- + " context.getSystemService(TYPE_1); // ERROR\n"
- + " ~~~~~~\n"
- + "20 errors, 0 warnings\n" :
- "16 errors, 0 warnings\n"),
-
- lintProject("src/test/pkg/IntDefTest.java.txt=>src/test/pkg/IntDefTest.java",
- "src/android/support/annotation/IntDef.java.txt=>src/android/support/annotation/IntDef.java",
- "src/android/support/annotation/StringDef.java.txt=>src/android/support/annotation/StringDef.java"
- ));
- }
-
- public void testColorInt() throws Exception {
- // Needs updated annotations!
- assertEquals((SDK_ANNOTATIONS_AVAILABLE ? ""
- + "src/test/pkg/WrongColor.java:9: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.blue) [ResourceAsColor]\n"
- + " paint2.setColor(R.color.blue);\n"
- + " ~~~~~~~~~~~~\n"
- + "src/test/pkg/WrongColor.java:11: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.red) [ResourceAsColor]\n"
- + " textView.setTextColor(R.color.red);\n"
- + " ~~~~~~~~~~~\n"
- + "src/test/pkg/WrongColor.java:12: Error: Should pass resolved color instead of resource id here: getResources().getColor(android.R.color.black) [ResourceAsColor]\n"
- + " textView.setTextColor(android.R.color.black);\n"
- + " ~~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/WrongColor.java:13: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.blue) [ResourceAsColor]\n"
- + " textView.setTextColor(foo > 0 ? R.color.green : R.color.blue);\n"
- + " ~~~~~~~~~~~~\n"
- + "src/test/pkg/WrongColor.java:13: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.green) [ResourceAsColor]\n"
- + " textView.setTextColor(foo > 0 ? R.color.green : R.color.blue);\n"
- + " ~~~~~~~~~~~~~\n" : "")
- + "src/test/pkg/WrongColor.java:21: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.blue) [ResourceAsColor]\n"
- + " foo2(R.color.blue);\n"
- + " ~~~~~~~~~~~~\n"
- + "src/test/pkg/WrongColor.java:20: Error: Expected resource of type color [ResourceType]\n"
- + " foo1(0xffff0000);\n"
- + " ~~~~~~~~~~\n"
- + (SDK_ANNOTATIONS_AVAILABLE ? "7 errors, 0 warnings\n" : "2 errors, 0 warnings\n"),
-
- lintProject(
- "src/test/pkg/WrongColor.java.txt=>src/test/pkg/WrongColor.java",
- "src/android/support/annotation/ColorInt.java.txt=>src/android/support/annotation/ColorInt.java"
- ));
- }
-
- public void testResourceType() throws Exception {
- assertEquals((SDK_ANNOTATIONS_AVAILABLE ? ""
- + "src/p1/p2/Flow.java:13: Error: Expected resource of type drawable [ResourceType]\n"
- + " resources.getDrawable(10); // ERROR\n"
- + " ~~\n"
- + "src/p1/p2/Flow.java:18: Error: Expected resource of type drawable [ResourceType]\n"
- + " resources.getDrawable(R.string.my_string); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~\n" : "")
- + "src/p1/p2/Flow.java:22: Error: Expected resource of type drawable [ResourceType]\n"
- + " myMethod(R.string.my_string); // ERROR\n"
- + " ~~~~~~~~~~~~~~~~~~\n"
- + "src/p1/p2/Flow.java:32: Error: Expected resource identifier (R.type.name) [ResourceType]\n"
- + " myAnyResMethod(50); // ERROR\n"
- + " ~~\n"
- + "src/p1/p2/Flow.java:68: Error: Expected resource of type drawable [ResourceType]\n"
- + " myMethod(z); // ERROR\n"
- + " ~\n"
- + (SDK_ANNOTATIONS_AVAILABLE ? "5 errors, 0 warnings\n" : "3 errors, 0 warnings\n"),
-
- lintProject("src/p1/p2/Flow.java.txt=>src/p1/p2/Flow.java",
- "src/android/support/annotation/DrawableRes.java.txt=>src/android/support/annotation/DrawableRes.java"));
- }
-
- public void testColorAsDrawable() throws Exception {
- assertEquals(
- "No warnings.",
-
- lintProject("src/p1/p2/ColorAsDrawable.java.txt=>src/p1/p2/ColorAsDrawable.java"));
- }
-
- public void testCheckResult() throws Exception {
- if (!SDK_ANNOTATIONS_AVAILABLE) {
- // Currently only tests @CheckResult on SDK annotations
- return;
- }
- assertEquals(""
- + "src/test/pkg/CheckPermissions.java:22: Warning: The result of extractAlpha is not used [CheckResult]\n"
- + " bitmap.extractAlpha(); // WARNING\n"
- + " ~~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/CheckPermissions.java:10: Warning: The result of checkCallingOrSelfPermission is not used; did you mean to call #enforceCallingOrSelfPermission(String,String? [UseCheckPermission]\n"
- + " context.checkCallingOrSelfPermission(Manifest.permission.INTERNET); // WRONG\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "src/test/pkg/CheckPermissions.java:11: Warning: The result of checkPermission is not used; did you mean to call #enforcePermission(String,int,int,String? [UseCheckPermission]\n"
- + " context.checkPermission(Manifest.permission.INTERNET, 1, 1);\n"
- + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
- + "0 errors, 3 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/UnusedResourceDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/UnusedResourceDetectorTest.java
deleted file mode 100644
index 2e2bcd4..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/UnusedResourceDetectorTest.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint.checks;
-
-import com.android.tools.lint.detector.api.Detector;
-import com.android.tools.lint.detector.api.Issue;
-
-import java.io.File;
-import java.util.Arrays;
-
-@SuppressWarnings("javadoc")
-public class UnusedResourceDetectorTest extends AbstractCheckTest {
- private boolean mEnableIds = false;
-
- @Override
- protected Detector getDetector() {
- return new UnusedResourceDetector();
- }
-
- @Override
- protected boolean isEnabled(Issue issue) {
- if (issue == UnusedResourceDetector.ISSUE_IDS) {
- return mEnableIds;
- } else {
- return true;
- }
- }
-
- public void testUnused() throws Exception {
- mEnableIds = false;
- assertEquals(
- "res/layout/accessibility.xml: Warning: The resource R.layout.accessibility appears to be unused [UnusedResources]\n" +
- "res/layout/main.xml: Warning: The resource R.layout.main appears to be unused [UnusedResources]\n" +
- "res/layout/other.xml: Warning: The resource R.layout.other appears to be unused [UnusedResources]\n" +
- "res/values/strings2.xml:3: Warning: The resource R.string.hello appears to be unused [UnusedResources]\n" +
- " <string name=\"hello\">Hello</string>\n" +
- " ~~~~~~~~~~~~\n" +
- "0 errors, 4 warnings\n" +
- "",
-
- lintProject(
- "res/values/strings2.xml",
- "res/layout/layout1.xml=>res/layout/main.xml",
- "res/layout/layout1.xml=>res/layout/other.xml",
-
- // Rename .txt files to .java
- "src/my/pkg/Test.java.txt=>src/my/pkg/Test.java",
- "gen/my/pkg/R.java.txt=>gen/my/pkg/R.java",
- "AndroidManifest.xml",
- "res/layout/accessibility.xml"));
- }
-
- public void testUnusedIds() throws Exception {
- mEnableIds = true;
-
- assertEquals(
- "res/layout/accessibility.xml: Warning: The resource R.layout.accessibility appears to be unused [UnusedResources]\n" +
- "Warning: The resource R.layout.main appears to be unused [UnusedResources]\n" +
- "Warning: The resource R.layout.other appears to be unused [UnusedResources]\n" +
- "Warning: The resource R.string.hello appears to be unused [UnusedResources]\n" +
- "res/layout/accessibility.xml:2: Warning: The resource R.id.newlinear appears to be unused [UnusedIds]\n" +
- "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" android:id=\"@+id/newlinear\" android:orientation=\"vertical\" android:layout_width=\"match_parent\" android:layout_height=\"match_parent\">\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "res/layout/accessibility.xml:3: Warning: The resource R.id.button1 appears to be unused [UnusedIds]\n" +
- " <Button android:text=\"Button\" android:id=\"@+id/button1\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\"></Button>\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "res/layout/accessibility.xml:4: Warning: The resource R.id.android_logo appears to be unused [UnusedIds]\n" +
- " <ImageView android:id=\"@+id/android_logo\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:src=\"@drawable/android_button\" android:focusable=\"false\" android:clickable=\"false\" android:layout_weight=\"1.0\" />\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "res/layout/accessibility.xml:5: Warning: The resource R.id.android_logo2 appears to be unused [UnusedIds]\n" +
- " <ImageButton android:importantForAccessibility=\"yes\" android:id=\"@+id/android_logo2\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:src=\"@drawable/android_button\" android:focusable=\"false\" android:clickable=\"false\" android:layout_weight=\"1.0\" />\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "Warning: The resource R.id.imageView1 appears to be unused [UnusedIds]\n" +
- "Warning: The resource R.id.include1 appears to be unused [UnusedIds]\n" +
- "Warning: The resource R.id.linearLayout2 appears to be unused [UnusedIds]\n" +
- "0 errors, 11 warnings\n",
-
- lintProject(
- // Rename .txt files to .java
- "src/my/pkg/Test.java.txt=>src/my/pkg/Test.java",
- "gen/my/pkg/R.java.txt=>gen/my/pkg/R.java",
- "AndroidManifest.xml",
- "res/layout/accessibility.xml"));
- }
-
- public void testArrayReference() throws Exception {
- assertEquals(
- "res/values/arrayusage.xml:3: Warning: The resource R.array.my_array appears to be unused [UnusedResources]\n" +
- "<string-array name=\"my_array\">\n" +
- " ~~~~~~~~~~~~~~~\n" +
- "0 errors, 1 warnings\n" +
- "",
-
- lintProject(
- "AndroidManifest.xml",
- "res/values/arrayusage.xml"));
- }
-
- public void testAttrs() throws Exception {
- assertEquals(
- "res/layout/customattrlayout.xml: Warning: The resource R.layout.customattrlayout appears to be unused [UnusedResources]\n" +
- "0 errors, 1 warnings\n" +
- "",
-
- lintProject(
- "res/values/customattr.xml",
- "res/layout/customattrlayout.xml",
- "unusedR.java.txt=>gen/my/pkg/R.java",
- "AndroidManifest.xml"));
- }
-
- public void testMultiProjectIgnoreLibraries() throws Exception {
- assertEquals(
- "No warnings.",
-
- lintProject(
- // Master project
- "multiproject/main-manifest.xml=>AndroidManifest.xml",
- "multiproject/main.properties=>project.properties",
- "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java",
-
- // Library project
- "multiproject/library-manifest.xml=>../LibraryProject/AndroidManifest.xml",
- "multiproject/library.properties=>../LibraryProject/project.properties",
- "multiproject/LibraryCode.java.txt=>../LibraryProject/src/foo/library/LibraryCode.java",
- "multiproject/strings.xml=>../LibraryProject/res/values/strings.xml"
- ));
- }
-
- public void testMultiProject() throws Exception {
- File master = getProjectDir("MasterProject",
- // Master project
- "multiproject/main-manifest.xml=>AndroidManifest.xml",
- "multiproject/main.properties=>project.properties",
- "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
- );
- File library = getProjectDir("LibraryProject",
- // Library project
- "multiproject/library-manifest.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>project.properties",
- "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
- "multiproject/strings.xml=>res/values/strings.xml"
- );
- assertEquals(
- // string1 is defined and used in the library project
- // string2 is defined in the library project and used in the master project
- // string3 is defined in the library project and not used anywhere
- "LibraryProject/res/values/strings.xml:7: Warning: The resource R.string.string3 appears to be unused [UnusedResources]\n" +
- " <string name=\"string3\">String 3</string>\n" +
- " ~~~~~~~~~~~~~~\n" +
- "0 errors, 1 warnings\n",
-
- checkLint(Arrays.asList(master, library)).replace("/TESTROOT/", ""));
- }
-
- public void testFqcnReference() throws Exception {
- assertEquals(
- "No warnings.",
-
- lintProject(
- "res/layout/layout1.xml=>res/layout/main.xml",
- "src/test/pkg/UnusedReference.java.txt=>src/test/pkg/UnusedReference.java",
- "AndroidManifest.xml"));
- }
-
- public void testIgnoreXmlDrawable() throws Exception {
- assertEquals(
- "No warnings.",
-
- lintProject(
- "res/drawable/ic_menu_help.xml",
- "gen/my/pkg/R2.java.txt=>gen/my/pkg/R.java"
- ));
- }
-
- public void testPlurals() throws Exception {
- assertEquals(
- "res/values/plurals.xml:3: Warning: The resource R.plurals.my_plural appears to be unused [UnusedResources]\n" +
- " <plurals name=\"my_plural\">\n" +
- " ~~~~~~~~~~~~~~~~\n" +
- "0 errors, 1 warnings\n" +
- "",
-
- lintProject(
- "res/values/strings4.xml",
- "res/values/plurals.xml",
- "AndroidManifest.xml"));
- }
-
- public void testNoMerging() throws Exception {
- // http://code.google.com/p/android/issues/detail?id=36952
-
- File master = getProjectDir("MasterProject",
- // Master project
- "multiproject/main-manifest.xml=>AndroidManifest.xml",
- "multiproject/main.properties=>project.properties",
- "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
- );
- File library = getProjectDir("LibraryProject",
- // Library project
- "multiproject/library-manifest.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>project.properties",
- "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
- "multiproject/strings.xml=>res/values/strings.xml"
- );
- assertEquals(
- // The strings are all referenced in the library project's manifest file
- // which in this project is merged in
- "LibraryProject/res/values/strings.xml:7: Warning: The resource R.string.string3 appears to be unused [UnusedResources]\n" +
- " <string name=\"string3\">String 3</string>\n" +
- " ~~~~~~~~~~~~~~\n" +
- "0 errors, 1 warnings\n",
-
- checkLint(Arrays.asList(master, library)).replace("/TESTROOT/", ""));
- }
-
- public void testLibraryMerging() throws Exception {
- // http://code.google.com/p/android/issues/detail?id=36952
- File master = getProjectDir("MasterProject",
- // Master project
- "multiproject/main-manifest.xml=>AndroidManifest.xml",
- "multiproject/main-merge.properties=>project.properties",
- "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
- );
- File library = getProjectDir("LibraryProject",
- // Library project
- "multiproject/library-manifest.xml=>AndroidManifest.xml",
- "multiproject/library.properties=>project.properties",
- "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
- "multiproject/strings.xml=>res/values/strings.xml"
- );
- assertEquals(
- // The strings are all referenced in the library project's manifest file
- // which in this project is merged in
- "No warnings.",
-
- checkLint(Arrays.asList(master, library)));
- }
-
- public void testCornerCase() throws Exception {
- // See http://code.google.com/p/projectlombok/issues/detail?id=415
- mEnableIds = true;
- assertEquals(
- "No warnings.",
-
- lintProject(
- "src/test/pkg/Foo.java.txt=>src/test/pkg/Foo.java",
- "AndroidManifest.xml"));
- }
-
- public void testAnalytics() throws Exception {
- // See http://code.google.com/p/android/issues/detail?id=42565
- mEnableIds = false;
- assertEquals(
- "No warnings.",
-
- lintProject(
- "res/values/analytics.xml"
- ));
- }
-
- public void testIntegers() throws Exception {
- // See https://code.google.com/p/android/issues/detail?id=53995
- mEnableIds = true;
- assertEquals(
- "No warnings.",
-
- lintProject(
- "res/values/integers.xml",
- "res/anim/slide_in_out.xml"
- ));
- }
-
- public void testIntegerArrays() throws Exception {
- // See http://code.google.com/p/android/issues/detail?id=59761
- mEnableIds = false;
- assertEquals(
- "No warnings.",
- lintProject("res/values/integer_arrays.xml=>res/values/integer_arrays.xml"));
- }
-
- public void testUnitTestReferences() throws Exception {
- // Make sure that we pick up references in unit tests as well
- // Regression test for
- // https://code.google.com/p/android/issues/detail?id=79066
- mEnableIds = false;
- assertEquals("No warnings.",
-
- lintProject(
- copy("res/values/strings2.xml"),
- copy("res/layout/layout1.xml", "res/layout/main.xml"),
- copy("res/layout/layout1.xml", "res/layout/other.xml"),
-
- copy("src/my/pkg/Test.java.txt", "src/my/pkg/Test.java"),
- copy("gen/my/pkg/R.java.txt", "gen/my/pkg/R.java"),
- copy("AndroidManifest.xml"),
- copy("res/layout/accessibility.xml"),
-
- // Add unit test source which references resources which would otherwise
- // be marked as unused
- java("test/my/pkg/MyTest.java", ""
- + "package my.pkg;\n"
- + "class MyTest {\n"
- + " public void test() {\n"
- + " System.out.println(R.layout.accessibility);\n"
- + " System.out.println(R.layout.main);\n"
- + " System.out.println(R.layout.other);\n"
- + " System.out.println(R.string.hello);\n"
- + " }\n"
- + "}\n")
- ));
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Compatibility.gradle b/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Compatibility.gradle
deleted file mode 100644
index 7281b50..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Compatibility.gradle
+++ /dev/null
@@ -1,21 +0,0 @@
-apply plugin: 'com.android.application'
-
-android {
- compileSdkVersion 19
- buildToolsVersion "19.0.0"
-
- defaultConfig {
- minSdkVersion 7
- targetSdkVersion 19
- versionCode 1
- versionName "1.0"
- }
-}
-
-dependencies {
- compile 'com.android.support:support-v4:18.0.0'
-
- // Suppressed:
- //noinspection GradleCompatible
- compile 'com.android.support:support-v4:18.0.0'
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntRange.java.txt b/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntRange.java.txt
deleted file mode 100644
index 06d1c6b..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntRange.java.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-package android.support.annotation;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.CONSTRUCTOR;
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
-@Retention(CLASS)
-@Target({CONSTRUCTOR,METHOD,PARAMETER,FIELD,LOCAL_VARIABLE})
-public @interface IntRange {
- long from() default Long.MIN_VALUE;
- long to() default Long.MAX_VALUE;
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/Flow.java.txt b/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/Flow.java.txt
deleted file mode 100644
index dbd5a47..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/Flow.java.txt
+++ /dev/null
@@ -1,125 +0,0 @@
-import android.content.res.Resources;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.StringRes;
-import android.support.annotation.StyleRes;
-
-import java.util.Random;
-
-@SuppressWarnings("UnusedDeclaration")
-public class Flow {
- public void testLiterals(Resources resources) {
- resources.getDrawable(0); // OK
- resources.getDrawable(-1); // OK
- resources.getDrawable(10); // ERROR
- }
-
- public void testConstants(Resources resources) {
- resources.getDrawable(R.drawable.my_drawable); // OK
- resources.getDrawable(R.string.my_string); // ERROR
- }
-
- public void testLocalAnnotation() {
- myMethod(R.string.my_string); // ERROR
- }
-
- private void myMethod(@DrawableRes int arg) {
- resources.getDrawable(R.string.my_string); // ERROR
- }
-
- private void testAnyRes() {
- myAnyResMethod(R.drawable.my_drawable); // OK
- myAnyResMethod(R.string.my_string); // OK
- myAnyResMethod(50); // ERROR
- }
-
- private void myAnyResMethod(@android.support.annotation.AnyRes int arg) {
- }
-
- public void testFields(String fileExt, Resources resources) {
- int mimeIconId = MimeTypes.styleAndDrawable;
- resources.getDrawable(mimeIconId); // OK
-
- int s1 = MimeTypes.style;
- resources.getDrawable(s1); // ERROR
- int s2 = MimeTypes.styleAndDrawable;
- resources.getDrawable(s2); // OK
- int w3 = MimeTypes.drawable;
- resources.getDrawable(w3); // OK
-
- // Direct reference
- resources.getDrawable(MimeTypes.style); // ERROR
- resources.getDrawable(MimeTypes.styleAndDrawable); // OK
- resources.getDrawable(MimeTypes.drawable); // OK
- }
-
- public void testCalls(String fileExt, Resources resources) {
- int mimeIconId = MimeTypes.getIconForExt(fileExt);
- resources.getDrawable(mimeIconId); // OK
- resources.getDrawable(MimeTypes.getInferredString()); // OK (wrong but can't infer type)
- resources.getDrawable(MimeTypes.getInferredDrawable()); // OK
- resources.getDrawable(MimeTypes.getAnnotatedString()); // Error
- resources.getDrawable(MimeTypes.getAnnotatedDrawable()); // OK
- resources.getDrawable(MimeTypes.getUnknownType()); // OK (unknown/uncertain)
- }
-
- public void testFlow() {
- int x = R.string.my_string;
- int z = x;
- myMethod(z); // ERROR
-
- int w = MY_RESOURCE;
- myMethod(w); // ERROR
- }
-
- private static final int MY_RESOURCE = R.string.my_string;
-
- private static class MimeTypes {
- @android.support.annotation.StyleRes
- @android.support.annotation.DrawableRes
- public static int styleAndDrawable;
-
- @android.support.annotation.StyleRes
- public static int style;
-
- @android.support.annotation.DrawableRes
- public static int drawable;
-
- @android.support.annotation.DrawableRes
- public static int getIconForExt(String ext) {
- return R.drawable.my_drawable;
- }
-
- public static int getInferredString() {
- // Implied string - can we handle this?
- return R.string.my_string;
- }
-
- public static int getInferredDrawable() {
- // Implied drawable - can we handle this?
- return R.drawable.my_drawable;
- }
-
- @android.support.annotation.StringRes
- public static int getAnnotatedString() {
- return R.string.my_string;
- }
-
- @android.support.annotation.DrawableRes
- public static int getAnnotatedDrawable() {
- return R.drawable.my_drawable;
- }
-
- public static int getUnknownType() {
- return new Random(1000).nextInt();
- }
- }
-
- public static final class R {
- public static final class drawable {
- public static final int my_drawable =0x7f020057;
- }
- public static final class string {
- public static final int my_string =0x7f0a000e;
- }
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/RangeTest.java.txt b/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/RangeTest.java.txt
deleted file mode 100644
index bc148c7..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/RangeTest.java.txt
+++ /dev/null
@@ -1,143 +0,0 @@
-package test.pkg;
-
-import android.support.annotation.FloatRange;
-import android.support.annotation.IntRange;
-import android.support.annotation.Size;
-
-@SuppressWarnings("UnusedDeclaration")
-public class RangeTest {
- public void printExact(@Size(5) String arg) { System.out.println(arg); }
- public void printMin(@Size(min=5) String arg) { }
- public void printMax(@Size(max=8) String arg) { }
- public void printRange(@Size(min=4,max=6) String arg) { }
- public void printExact(@Size(5) int[] arg) { }
- public void printMin(@Size(min=5) int[] arg) { }
- public void printMax(@Size(max=8) int[] arg) { }
- public void printRange(@Size(min=4,max=6) int[] arg) { }
- public void printMultiple(@Size(multiple=3) int[] arg) { }
- public void printMinMultiple(@Size(min=4,multiple=3) int[] arg) { }
- public void printAtLeast(@IntRange(from=4) int arg) { }
- public void printAtMost(@IntRange(to=7) int arg) { }
- public void printBetween(@IntRange(from=4,to=7) int arg) { }
- public void printAtLeastInclusive(@FloatRange(from=2.5) float arg) { }
- public void printAtLeastExclusive(@FloatRange(from=2.5,fromInclusive=false) float arg) { }
- public void printAtMostInclusive(@FloatRange(to=7) double arg) { }
- public void printAtMostExclusive(@FloatRange(to=7,toInclusive=false) double arg) { }
- public void printBetweenFromInclusiveToInclusive(@FloatRange(from=2.5,to=5.0) float arg) { }
- public void printBetweenFromExclusiveToInclusive(@FloatRange(from=2.5,to=5.0,fromInclusive=false) float arg) { }
- public void printBetweenFromInclusiveToExclusive(@FloatRange(from=2.5,to=5.0,toInclusive=false) float arg) { }
- public void printBetweenFromExclusiveToExclusive(@FloatRange(from=2.5,to=5.0,fromInclusive=false,toInclusive=false) float arg) { }
-
- public void testLength() {
- printExact("1234"); // ERROR
- printExact("12345"); // OK
- printExact("123456"); // ERROR
-
- printMin("1234"); // ERROR
- printMin("12345"); // OK
- printMin("123456"); // OK
-
- printMax("123456"); // OK
- printMax("1234567"); // OK
- printMax("12345678"); // OK
- printMax("123456789"); // ERROR
-
- printRange("123"); // ERROR
- printRange("1234"); // OK
- printRange("12345"); // OK
- printRange("123456"); // OK
- printRange("1234567"); // ERROR
- }
-
- public void testSize() {
- printExact(new int[]{1, 2, 3, 4}); // ERROR
- printExact(new int[]{1, 2, 3, 4, 5}); // OK
- printExact(new int[]{1, 2, 3, 4, 5, 6}); // ERROR
-
- printMin(new int[]{1, 2, 3, 4}); // ERROR
- printMin(new int[]{1, 2, 3, 4, 5}); // OK
- printMin(new int[]{1, 2, 3, 4, 5, 6}); // OK
-
- printMax(new int[]{1, 2, 3, 4, 5, 6}); // OK
- printMax(new int[]{1, 2, 3, 4, 5, 6, 7}); // OK
- printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8}); // OK
- printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8}); // OK
- printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}); // ERROR
-
- printRange(new int[] {1,2,3}); // ERROR
- printRange(new int[] {1,2,3,4}); // OK
- printRange(new int[] {1,2,3,4,5}); // OK
- printRange(new int[] {1,2,3,4,5,6}); // OK
- printRange(new int[] {1,2,3,4,5,6,7}); // ERROR
-
- printMultiple(new int[] {1,2,3}); // OK
- printMultiple(new int[] {1,2,3,4}); // ERROR
- printMultiple(new int[] {1,2,3,4,5}); // ERROR
- printMultiple(new int[] {1,2,3,4,5,6}); // OK
- printMultiple(new int[] {1,2,3,4,5,6,7}); // ERROR
-
- printMinMultiple(new int[] {1,2,3,4,5,6}); // OK
- printMinMultiple(new int[]{1, 2, 3}); // ERROR
- }
-
- public void testIntRange() {
- printAtLeast(3); // ERROR
- printAtLeast(4); // OK
- printAtLeast(5); // OK
-
- printAtMost(5); // OK
- printAtMost(6); // OK
- printAtMost(7); // OK
- printAtMost(8); // ERROR
-
- printBetween(3); // ERROR
- printBetween(4); // OK
- printBetween(5); // OK
- printBetween(6); // OK
- printBetween(7); // OK
- printBetween(8); // ERROR
- }
-
- public void testFloatRange() {
- printAtLeastInclusive(2.49f); // ERROR
- printAtLeastInclusive(2.5f); // OK
- printAtLeastInclusive(2.6f); // OK
-
- printAtLeastExclusive(2.49f); // ERROR
- printAtLeastExclusive(2.5f); // ERROR
- printAtLeastExclusive(2.501f); // OK
-
- printAtMostInclusive(6.8f); // OK
- printAtMostInclusive(6.9f); // OK
- printAtMostInclusive(7.0f); // OK
- printAtMostInclusive(7.1f); // ERROR
-
- printAtMostExclusive(6.9f); // OK
- printAtMostExclusive(6.99f); // OK
- printAtMostExclusive(7.0f); // ERROR
- printAtMostExclusive(7.1f); // ERROR
-
- printBetweenFromInclusiveToInclusive(2.4f); // ERROR
- printBetweenFromInclusiveToInclusive(2.5f); // OK
- printBetweenFromInclusiveToInclusive(3f); // OK
- printBetweenFromInclusiveToInclusive(5.0f); // OK
- printBetweenFromInclusiveToInclusive(5.1f); // ERROR
-
- printBetweenFromExclusiveToInclusive(2.4f); // ERROR
- printBetweenFromExclusiveToInclusive(2.5f); // ERROR
- printBetweenFromExclusiveToInclusive(5.0f); // OK
- printBetweenFromExclusiveToInclusive(5.1f); // ERROR
-
- printBetweenFromInclusiveToExclusive(2.4f); // ERROR
- printBetweenFromInclusiveToExclusive(2.5f); // OK
- printBetweenFromInclusiveToExclusive(3f); // OK
- printBetweenFromInclusiveToExclusive(4.99f); // OK
- printBetweenFromInclusiveToExclusive(5.0f); // ERROR
-
- printBetweenFromExclusiveToExclusive(2.4f); // ERROR
- printBetweenFromExclusiveToExclusive(2.5f); // ERROR
- printBetweenFromExclusiveToExclusive(2.51f); // OK
- printBetweenFromExclusiveToExclusive(4.99f); // OK
- printBetweenFromExclusiveToExclusive(5.0f); // ERROR
- }
-}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.java b/lint/cli/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.java
deleted file mode 100644
index 312f31f..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.java
+++ /dev/null
@@ -1,520 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint.detector.api;
-
-import static com.android.tools.lint.detector.api.LintUtils.computeResourceName;
-import static com.android.tools.lint.detector.api.LintUtils.convertVersion;
-import static com.android.tools.lint.detector.api.LintUtils.escapePropertyValue;
-import static com.android.tools.lint.detector.api.LintUtils.findSubstring;
-import static com.android.tools.lint.detector.api.LintUtils.getFormattedParameters;
-import static com.android.tools.lint.detector.api.LintUtils.getLocaleAndRegion;
-import static com.android.tools.lint.detector.api.LintUtils.isImported;
-import static com.android.tools.lint.detector.api.LintUtils.splitPath;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.builder.model.AndroidProject;
-import com.android.builder.model.ApiVersion;
-import com.android.sdklib.AndroidVersion;
-import com.android.sdklib.IAndroidTarget;
-import com.android.tools.lint.EcjParser;
-import com.android.tools.lint.LintCliClient;
-import com.android.tools.lint.checks.BuiltinIssueRegistry;
-import com.android.tools.lint.client.api.JavaParser;
-import com.android.tools.lint.client.api.LintDriver;
-import com.google.common.collect.Iterables;
-
-import junit.framework.TestCase;
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.OutputStreamWriter;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Locale;
-
-import lombok.ast.Node;
-
-@SuppressWarnings("javadoc")
-public class LintUtilsTest extends TestCase {
- public void testPrintList() throws Exception {
- assertEquals("foo, bar, baz",
- LintUtils.formatList(Arrays.asList("foo", "bar", "baz"), 3));
- assertEquals("foo, bar, baz",
- LintUtils.formatList(Arrays.asList("foo", "bar", "baz"), 5));
-
- assertEquals("foo, bar, baz... (3 more)",
- LintUtils.formatList(
- Arrays.asList("foo", "bar", "baz", "4", "5", "6"), 3));
- assertEquals("foo... (5 more)",
- LintUtils.formatList(
- Arrays.asList("foo", "bar", "baz", "4", "5", "6"), 1));
- assertEquals("foo, bar, baz",
- LintUtils.formatList(Arrays.asList("foo", "bar", "baz"), 0));
- }
-
- public void testEndsWith() throws Exception {
- assertTrue(LintUtils.endsWith("Foo", ""));
- assertTrue(LintUtils.endsWith("Foo", "o"));
- assertTrue(LintUtils.endsWith("Foo", "oo"));
- assertTrue(LintUtils.endsWith("Foo", "Foo"));
- assertTrue(LintUtils.endsWith("Foo", "FOO"));
- assertTrue(LintUtils.endsWith("Foo", "fOO"));
-
- assertFalse(LintUtils.endsWith("Foo", "f"));
- }
-
- public void testStartsWith() throws Exception {
- assertTrue(LintUtils.startsWith("FooBar", "Bar", 3));
- assertTrue(LintUtils.startsWith("FooBar", "BAR", 3));
- assertTrue(LintUtils.startsWith("FooBar", "Foo", 0));
- assertFalse(LintUtils.startsWith("FooBar", "Foo", 2));
- }
-
- public void testIsXmlFile() throws Exception {
- assertTrue(LintUtils.isXmlFile(new File("foo.xml")));
- assertTrue(LintUtils.isXmlFile(new File("foo.Xml")));
- assertTrue(LintUtils.isXmlFile(new File("foo.XML")));
-
- assertFalse(LintUtils.isXmlFile(new File("foo.png")));
- assertFalse(LintUtils.isXmlFile(new File("xml")));
- assertFalse(LintUtils.isXmlFile(new File("xml.png")));
- }
-
- public void testGetBasename() throws Exception {
- assertEquals("foo", LintUtils.getBaseName("foo.png"));
- assertEquals("foo", LintUtils.getBaseName("foo.9.png"));
- assertEquals(".foo", LintUtils.getBaseName(".foo"));
- }
-
- public void testEditDistance() {
- assertEquals(0, LintUtils.editDistance("kitten", "kitten"));
-
- // editing kitten to sitting has edit distance 3:
- // replace k with s
- // replace e with i
- // append g
- assertEquals(3, LintUtils.editDistance("kitten", "sitting"));
-
- assertEquals(3, LintUtils.editDistance("saturday", "sunday"));
- assertEquals(1, LintUtils.editDistance("button", "bitton"));
- assertEquals(6, LintUtils.editDistance("radiobutton", "bitton"));
- }
-
- public void testSplitPath() throws Exception {
- assertTrue(Arrays.equals(new String[] { "/foo", "/bar", "/baz" },
- Iterables.toArray(splitPath("/foo:/bar:/baz"), String.class)));
-
- assertTrue(Arrays.equals(new String[] { "/foo", "/bar" },
- Iterables.toArray(splitPath("/foo;/bar"), String.class)));
-
- assertTrue(Arrays.equals(new String[] { "/foo", "/bar:baz" },
- Iterables.toArray(splitPath("/foo;/bar:baz"), String.class)));
-
- assertTrue(Arrays.equals(new String[] { "\\foo\\bar", "\\bar\\foo" },
- Iterables.toArray(splitPath("\\foo\\bar;\\bar\\foo"), String.class)));
-
- assertTrue(Arrays.equals(new String[] { "${sdk.dir}\\foo\\bar", "\\bar\\foo" },
- Iterables.toArray(splitPath("${sdk.dir}\\foo\\bar;\\bar\\foo"),
- String.class)));
-
- assertTrue(Arrays.equals(new String[] { "${sdk.dir}/foo/bar", "/bar/foo" },
- Iterables.toArray(splitPath("${sdk.dir}/foo/bar:/bar/foo"),
- String.class)));
-
- assertTrue(Arrays.equals(new String[] { "C:\\foo", "/bar" },
- Iterables.toArray(splitPath("C:\\foo:/bar"), String.class)));
- }
-
- public void testCommonParen1() {
- assertEquals(new File("/a"), (LintUtils.getCommonParent(
- new File("/a/b/c/d/e"), new File("/a/c"))));
- assertEquals(new File("/a"), (LintUtils.getCommonParent(
- new File("/a/c"), new File("/a/b/c/d/e"))));
-
- assertEquals(new File("/"), LintUtils.getCommonParent(
- new File("/foo/bar"), new File("/bar/baz")));
- assertEquals(new File("/"), LintUtils.getCommonParent(
- new File("/foo/bar"), new File("/")));
- assertNull(LintUtils.getCommonParent(
- new File("C:\\Program Files"), new File("F:\\")));
- assertNull(LintUtils.getCommonParent(
- new File("C:/Program Files"), new File("F:/")));
-
- assertEquals(new File("/foo/bar/baz"), LintUtils.getCommonParent(
- new File("/foo/bar/baz"), new File("/foo/bar/baz")));
- assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
- new File("/foo/bar/baz"), new File("/foo/bar")));
- assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
- new File("/foo/bar/baz"), new File("/foo/bar/foo")));
- assertEquals(new File("/foo"), LintUtils.getCommonParent(
- new File("/foo/bar"), new File("/foo/baz")));
- assertEquals(new File("/foo"), LintUtils.getCommonParent(
- new File("/foo/bar"), new File("/foo/baz")));
- assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
- new File("/foo/bar"), new File("/foo/bar/baz")));
- }
-
- public void testCommonParent2() {
- assertEquals(new File("/"), LintUtils.getCommonParent(
- Arrays.asList(new File("/foo/bar"), new File("/bar/baz"))));
- assertEquals(new File("/"), LintUtils.getCommonParent(
- Arrays.asList(new File("/foo/bar"), new File("/"))));
- assertNull(LintUtils.getCommonParent(
- Arrays.asList(new File("C:\\Program Files"), new File("F:\\"))));
- assertNull(LintUtils.getCommonParent(
- Arrays.asList(new File("C:/Program Files"), new File("F:/"))));
-
- assertEquals(new File("/foo"), LintUtils.getCommonParent(
- Arrays.asList(new File("/foo/bar"), new File("/foo/baz"))));
- assertEquals(new File("/foo"), LintUtils.getCommonParent(
- Arrays.asList(new File("/foo/bar"), new File("/foo/baz"),
- new File("/foo/baz/f"))));
- assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
- Arrays.asList(new File("/foo/bar"), new File("/foo/bar/baz"),
- new File("/foo/bar/foo2/foo3"))));
- }
-
- public void testStripIdPrefix() throws Exception {
- assertEquals("foo", LintUtils.stripIdPrefix("@+id/foo"));
- assertEquals("foo", LintUtils.stripIdPrefix("@id/foo"));
- assertEquals("foo", LintUtils.stripIdPrefix("foo"));
- }
-
- public void testIdReferencesMatch() throws Exception {
- assertTrue(LintUtils.idReferencesMatch("@+id/foo", "@+id/foo"));
- assertTrue(LintUtils.idReferencesMatch("@id/foo", "@id/foo"));
- assertTrue(LintUtils.idReferencesMatch("@id/foo", "@+id/foo"));
- assertTrue(LintUtils.idReferencesMatch("@+id/foo", "@id/foo"));
-
- assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@+id/bar"));
- assertFalse(LintUtils.idReferencesMatch("@id/foo", "@+id/bar"));
- assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@id/bar"));
- assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@+id/bar"));
-
- assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@+id/foo1"));
- assertFalse(LintUtils.idReferencesMatch("@id/foo", "@id/foo1"));
- assertFalse(LintUtils.idReferencesMatch("@id/foo", "@+id/foo1"));
- assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@id/foo1"));
-
- assertFalse(LintUtils.idReferencesMatch("@+id/foo1", "@+id/foo"));
- assertFalse(LintUtils.idReferencesMatch("@id/foo1", "@id/foo"));
- assertFalse(LintUtils.idReferencesMatch("@id/foo1", "@+id/foo"));
- assertFalse(LintUtils.idReferencesMatch("@+id/foo1", "@id/foo"));
- }
-
- private static void checkEncoding(String encoding, boolean writeBom, String lineEnding)
- throws Exception {
- @SuppressWarnings("StringBufferReplaceableByString")
- StringBuilder sb = new StringBuilder();
-
- // Norwegian extra vowel characters such as "latin small letter a with ring above"
- String value = "\u00e6\u00d8\u00e5";
- String expected = "First line." + lineEnding + "Second line." + lineEnding
- + "Third line." + lineEnding + value + lineEnding;
- sb.append(expected);
- File file = File.createTempFile("getEncodingTest" + encoding + writeBom, ".txt");
- file.deleteOnExit();
- BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(file));
- OutputStreamWriter writer = new OutputStreamWriter(stream, encoding);
-
- if (writeBom) {
- String normalized = encoding.toLowerCase(Locale.US).replace("-", "_");
- if (normalized.equals("utf_8")) {
- stream.write(0xef);
- stream.write(0xbb);
- stream.write(0xbf);
- } else if (normalized.equals("utf_16")) {
- stream.write(0xfe);
- stream.write(0xff);
- } else if (normalized.equals("utf_16le")) {
- stream.write(0xff);
- stream.write(0xfe);
- } else if (normalized.equals("utf_32")) {
- stream.write(0x0);
- stream.write(0x0);
- stream.write(0xfe);
- stream.write(0xff);
- } else if (normalized.equals("utf_32le")) {
- stream.write(0xff);
- stream.write(0xfe);
- stream.write(0x0);
- stream.write(0x0);
- } else {
- fail("Can't write BOM for encoding " + encoding);
- }
- }
- writer.write(sb.toString());
- writer.close();
-
- String s = LintUtils.getEncodedString(new LintCliClient(), file);
- assertEquals(expected, s);
- }
-
- public void testGetEncodedString() throws Exception {
- checkEncoding("utf-8", false /*bom*/, "\n");
- checkEncoding("UTF-8", false /*bom*/, "\n");
- checkEncoding("UTF_16", false /*bom*/, "\n");
- checkEncoding("UTF-16", false /*bom*/, "\n");
- checkEncoding("UTF_16LE", false /*bom*/, "\n");
-
- // Try BOM's
- checkEncoding("utf-8", true /*bom*/, "\n");
- checkEncoding("UTF-8", true /*bom*/, "\n");
- checkEncoding("UTF_16", true /*bom*/, "\n");
- checkEncoding("UTF-16", true /*bom*/, "\n");
- checkEncoding("UTF_16LE", true /*bom*/, "\n");
- checkEncoding("UTF_32", true /*bom*/, "\n");
- checkEncoding("UTF_32LE", true /*bom*/, "\n");
-
- // Make sure this works for \r and \r\n as well
- checkEncoding("UTF-16", false /*bom*/, "\r");
- checkEncoding("UTF_16LE", false /*bom*/, "\r");
- checkEncoding("UTF-16", false /*bom*/, "\r\n");
- checkEncoding("UTF_16LE", false /*bom*/, "\r\n");
- checkEncoding("UTF-16", true /*bom*/, "\r");
- checkEncoding("UTF_16LE", true /*bom*/, "\r");
- checkEncoding("UTF_32", true /*bom*/, "\r");
- checkEncoding("UTF_32LE", true /*bom*/, "\r");
- checkEncoding("UTF-16", true /*bom*/, "\r\n");
- checkEncoding("UTF_16LE", true /*bom*/, "\r\n");
- checkEncoding("UTF_32", true /*bom*/, "\r\n");
- checkEncoding("UTF_32LE", true /*bom*/, "\r\n");
- }
-
- public void testGetLocaleAndRegion() throws Exception {
- assertNull(getLocaleAndRegion(""));
- assertNull(getLocaleAndRegion("values"));
- assertNull(getLocaleAndRegion("values-xlarge-port"));
- assertEquals("en", getLocaleAndRegion("values-en"));
- assertEquals("pt-rPT", getLocaleAndRegion("values-pt-rPT-nokeys"));
- assertEquals("b+pt+PT", getLocaleAndRegion("values-b+pt+PT-nokeys"));
- assertEquals("zh-rCN", getLocaleAndRegion("values-zh-rCN-keyshidden"));
- assertEquals("ms", getLocaleAndRegion("values-ms-keyshidden"));
- }
-
- public void testIsImported() throws Exception {
- assertFalse(isImported(getCompilationUnit(
- "package foo.bar;\n" +
- "class Foo {\n" +
- "}\n"),
- "android.app.Activity"));
-
- assertTrue(isImported(getCompilationUnit(
- "package foo.bar;\n" +
- "import foo.bar.*;\n" +
- "import android.app.Activity;\n" +
- "import foo.bar.Baz;\n" +
- "class Foo {\n" +
- "}\n"),
- "android.app.Activity"));
-
- assertTrue(isImported(getCompilationUnit(
- "package foo.bar;\n" +
- "import android.app.Activity;\n" +
- "class Foo {\n" +
- "}\n"),
- "android.app.Activity"));
-
- assertTrue(isImported(getCompilationUnit(
- "package foo.bar;\n" +
- "import android.app.*;\n" +
- "class Foo {\n" +
- "}\n"),
- "android.app.Activity"));
-
- assertFalse(isImported(getCompilationUnit(
- "package foo.bar;\n" +
- "import android.app.*;\n" +
- "import foo.bar.Activity;\n" +
- "class Foo {\n" +
- "}\n"),
- "android.app.Activity"));
- }
-
- public void testComputeResourceName() {
- assertEquals("", computeResourceName("", ""));
- assertEquals("foo", computeResourceName("", "foo"));
- assertEquals("foo", computeResourceName("foo", ""));
- assertEquals("prefix_name", computeResourceName("prefix_", "name"));
- assertEquals("prefixName", computeResourceName("prefix", "name"));
- }
-
- public static Node getCompilationUnit(String javaSource) {
- return getCompilationUnit(javaSource, new File("test"));
- }
-
-
- public static Node getCompilationUnit(final String javaSource, final File relativePath) {
- JavaContext context = parse(javaSource, relativePath);
- return context.getCompilationUnit();
- }
-
- public static JavaContext parse(final String javaSource, final File relativePath) {
- File dir = new File("projectDir");
- final File fullPath = new File(dir, relativePath.getPath());
- LintCliClient client = new LintCliClient() {
- @NonNull
- @Override
- public String readFile(@NonNull File file) {
- if (file.getPath().equals(fullPath.getPath())) {
- return javaSource;
- }
- return super.readFile(file);
- }
-
- @Nullable
- @Override
- public IAndroidTarget getCompileTarget(@NonNull Project project) {
- IAndroidTarget[] targets = getTargets();
- for (int i = targets.length - 1; i >= 0; i--) {
- IAndroidTarget target = targets[i];
- if (target.isPlatform()) {
- return target;
- }
- }
-
- return super.getCompileTarget(project);
- }
- };
- Project project = client.getProject(dir, dir);
-
- LintDriver driver = new LintDriver(new BuiltinIssueRegistry(),
- new LintCliClient());
- driver.setScope(Scope.JAVA_FILE_SCOPE);
- TestContext context = new TestContext(driver, client, project, javaSource, fullPath);
- JavaParser parser = new EcjParser(client, project);
- parser.prepareJavaParse(Collections.<JavaContext>singletonList(context));
- Node compilationUnit = parser.parseJava(context);
- assertNotNull(javaSource, compilationUnit);
- context.setCompilationUnit(compilationUnit);
- return context;
- }
-
- public void testConvertVersion() {
- assertEquals(new AndroidVersion(5, null), convertVersion(new DefaultApiVersion(5, null),
- null));
- assertEquals(new AndroidVersion(19, null), convertVersion(new DefaultApiVersion(19, null),
- null));
- //noinspection SpellCheckingInspection
- assertEquals(new AndroidVersion(18, "KITKAT"), // a preview platform API level is not final
- convertVersion(new DefaultApiVersion(0, "KITKAT"),
- null));
- }
-
- public void testIsModelOlderThan() throws Exception {
- AndroidProject project = mock(AndroidProject.class);
- when(project.getModelVersion()).thenReturn("0.10.4");
-
- assertTrue(LintUtils.isModelOlderThan(project, 0, 10, 5));
- assertTrue(LintUtils.isModelOlderThan(project, 0, 11, 0));
- assertTrue(LintUtils.isModelOlderThan(project, 0, 11, 4));
- assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 0));
-
- project = mock(AndroidProject.class);
- when(project.getModelVersion()).thenReturn("0.11.0");
-
- assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 0));
- assertFalse(LintUtils.isModelOlderThan(project, 0, 11, 0));
- assertFalse(LintUtils.isModelOlderThan(project, 0, 10, 4));
-
- project = mock(AndroidProject.class);
- when(project.getModelVersion()).thenReturn("0.11.5");
-
- assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 0));
- assertFalse(LintUtils.isModelOlderThan(project, 0, 11, 0));
-
- project = mock(AndroidProject.class);
- when(project.getModelVersion()).thenReturn("1.0.0");
-
- assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 1));
- assertFalse(LintUtils.isModelOlderThan(project, 1, 0, 0));
- assertFalse(LintUtils.isModelOlderThan(project, 0, 11, 0));
- }
-
- private static final class DefaultApiVersion implements ApiVersion {
- private final int mApiLevel;
- private final String mCodename;
-
- public DefaultApiVersion(int apiLevel, @Nullable String codename) {
- mApiLevel = apiLevel;
- mCodename = codename;
- }
-
- @Override
- public int getApiLevel() {
- return mApiLevel;
- }
-
- @Nullable
- @Override
- public String getCodename() {
- return mCodename;
- }
-
- @NonNull
- @Override
- public String getApiString() {
- fail("Not needed in this test");
- return "<invalid>";
- }
- }
-
- public void testFindSubstring() {
- assertEquals("foo", findSubstring("foo", null, null));
- assertEquals("foo", findSubstring("foo ", null, " "));
- assertEquals("foo", findSubstring(" foo", " ", null));
- assertEquals("foo", findSubstring("[foo]", "[", "]"));
- }
-
- public void testGetFormattedParameters() {
- assertEquals(Arrays.asList("foo","bar"),
- getFormattedParameters("Prefix %1$s Divider %2$s Suffix",
- "Prefix foo Divider bar Suffix"));
- }
-
- public void testEscapePropertyValue() throws Exception {
- assertEquals("foo", escapePropertyValue("foo"));
- assertEquals("\\ foo ", escapePropertyValue(" foo "));
- assertEquals("c\\:/foo/bar", escapePropertyValue("c:/foo/bar"));
- assertEquals("\\!\\#\\:\\\\a\\\\b\\\\c", escapePropertyValue("!#:\\a\\b\\c"));
- assertEquals(
- "foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo\\#foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo",
- escapePropertyValue("foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo#foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo"));
- }
-
- private static class TestContext extends JavaContext {
- private final String mJavaSource;
- public TestContext(LintDriver driver, LintCliClient client, Project project,
- String javaSource, File file) {
- //noinspection ConstantConditions
- super(driver, project,
- null, file, client.getJavaParser(null));
-
- mJavaSource = javaSource;
- }
-
- @Override
- @Nullable
- public String getContents() {
- return mJavaSource;
- }
- }
-}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/TextFormatTest.java b/lint/cli/src/test/java/com/android/tools/lint/detector/api/TextFormatTest.java
deleted file mode 100644
index a4eaf5b..0000000
--- a/lint/cli/src/test/java/com/android/tools/lint/detector/api/TextFormatTest.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.lint.detector.api;
-
-import static com.android.SdkConstants.AUTO_URI;
-import static com.android.tools.lint.detector.api.TextFormat.HTML;
-import static com.android.tools.lint.detector.api.TextFormat.RAW;
-import static com.android.tools.lint.detector.api.TextFormat.TEXT;
-
-import junit.framework.TestCase;
-
-public class TextFormatTest extends TestCase {
- private static String convertMarkup(String raw, TextFormat to) {
- return RAW.convertTo(raw, to);
- }
-
- public void testConvertMarkup() throws Exception {
- assertEquals("", convertMarkup("", HTML));
-
- // Normal escapes
- assertEquals("foo bar", convertMarkup("foo bar", HTML));
- assertEquals("foo<br/>\nbar", convertMarkup("foo\nbar", HTML));
- assertEquals("foo<br/>\nbar", convertMarkup("foo\nbar", HTML));
- assertEquals("<&>'\"", convertMarkup("<&>'\"", HTML));
-
- // HTML Formatting
- assertEquals("<code>@TargetApi(11)</code>, ", convertMarkup("`@TargetApi(11)`, ",
- HTML));
- assertEquals("with <code>getArguments()</code>.",
- convertMarkup("with `getArguments()`.", HTML));
- assertEquals("(<code>dip</code>)", convertMarkup("(`dip`)", HTML));
- assertEquals(" <code>0dp</code> ", convertMarkup(" `0dp` ", HTML));
- assertEquals(
- "resources under <code>$ANDROID_SK/platforms/android-$VERSION/data/res/.</code>",
- convertMarkup(
- "resources under `$ANDROID_SK/platforms/android-$VERSION/data/res/.`",
- HTML));
- assertEquals("wrong format. Instead of <code>-keepclasseswithmembernames</code> use ",
- convertMarkup("wrong format. Instead of `-keepclasseswithmembernames` use ",
- HTML));
- assertEquals("<code>exported=false</code>)", convertMarkup("`exported=false`)",
- HTML));
- assertEquals("by setting <code>inputType=\"text\"</code>.",
- convertMarkup("by setting `inputType=\"text\"`.", HTML));
- assertEquals("* <code>View(Context context)</code><br/>\n",
- convertMarkup("* `View(Context context)`\n", HTML));
- assertEquals("The <code>@+id/</code> syntax", convertMarkup("The `@+id/` syntax",
- HTML));
- assertEquals("", convertMarkup("", HTML));
- assertEquals("", convertMarkup("", HTML));
- assertEquals("This is <b>bold</b>", convertMarkup("This is *bold*", HTML));
- assertEquals("Visit <a href=\"http://google.com\">http://google.com</a>.",
- convertMarkup("Visit http://google.com.", HTML));
- assertEquals("This is <code>monospace</code>!", convertMarkup("This is `monospace`!",
- HTML));
- assertEquals(
- "See <a href=\"http://developer.android.com/reference/android/view/" +
- "WindowManager.LayoutParams.html#FLAG_KEEP_SCREEN_ON\">http://developer." +
- "android.com/reference/android/view/WindowManager.LayoutParams.html#" +
- "FLAG_KEEP_SCREEN_ON</a>.",
- convertMarkup(
- "See http://developer.android.com/reference/android/view/WindowManager.Layout" +
- "Params.html#FLAG_KEEP_SCREEN_ON.", HTML));
-
- // Text formatting
- assertEquals("@TargetApi(11), ", convertMarkup("`@TargetApi(11)`, ", TEXT));
- assertEquals("with getArguments().", convertMarkup("with `getArguments()`.", TEXT));
- assertEquals("bold", convertMarkup("*bold*", TEXT));
- assertEquals("Visit http://google.com.", convertMarkup("Visit http://google.com.",
- TEXT));
-
- // Corners (match at the beginning and end)
- assertEquals("<b>bold</b>", convertMarkup("*bold*", HTML));
- assertEquals("<code>monospace</code>!", convertMarkup("`monospace`!", HTML));
-
- // Not formatting
- assertEquals("a*b", convertMarkup("a*b", HTML));
- assertEquals("a* b*", convertMarkup("a* b*", HTML));
- assertEquals("*a *b", convertMarkup("*a *b", HTML));
- assertEquals("Prefix is http:// ", convertMarkup("Prefix is http:// ", HTML));
- assertEquals("", convertMarkup("", HTML));
- assertEquals("", convertMarkup("", HTML));
- assertEquals("", convertMarkup("", HTML));
- assertEquals("", convertMarkup("", HTML));
- assertEquals("This is * not * bold", convertMarkup("This is * not * bold", HTML));
- assertEquals("* List item 1<br/>\n* List Item 2",
- convertMarkup("* List item 1\n* List Item 2", HTML));
- assertEquals("myhttp://foo.bar", convertMarkup("myhttp://foo.bar", HTML));
- }
-
- public void testConvertMarkup2() throws Exception {
- // http at the end:
- // Explanation from ManifestDetector#TARGET_NEWER
- String explanation =
- "When your application runs on a version of Android that is more recent than your " +
- "targetSdkVersion specifies that it has been tested with, various compatibility " +
- "modes kick in. This ensures that your application continues to work, but it may " +
- "look out of place. For example, if the targetSdkVersion is less than 14, your " +
- "app may get an option button in the UI.\n" +
- "\n" +
- "To fix this issue, set the targetSdkVersion to the highest available value. Then " +
- "test your app to make sure everything works correctly. You may want to consult " +
- "the compatibility notes to see what changes apply to each version you are adding " +
- "support for: " +
- "http://developer.android.com/reference/android/os/Build.VERSION_CODES.html";
-
- assertEquals(
- "When your application runs on a version of Android that is more recent than your " +
- "targetSdkVersion specifies that it has been tested with, various compatibility " +
- "modes kick in. This ensures that your application continues to work, but it may " +
- "look out of place. For example, if the targetSdkVersion is less than 14, your " +
- "app may get an option button in the UI.<br/>\n" +
- "<br/>\n" +
- "To fix this issue, set the targetSdkVersion to the highest available value. Then " +
- "test your app to make sure everything works correctly. You may want to consult " +
- "the compatibility notes to see what changes apply to each version you are adding " +
- "support for: " +
- "<a href=\"http://developer.android.com/reference/android/os/Build.VERSION_CODES." +
- "html\">http://developer.android.com/reference/android/os/Build.VERSION_CODES.html" +
- "</a>",
- convertMarkup(explanation, HTML));
- }
-
- public void testConvertMarkup3() throws Exception {
- // embedded http markup test
- // Explanation from NamespaceDetector#CUSTOMVIEW
- String explanation =
- "When using a custom view with custom attributes in a library project, the layout " +
- "must use the special namespace " + AUTO_URI + " instead of a URI which includes " +
- "the library project's own package. This will be used to automatically adjust the " +
- "namespace of the attributes when the library resources are merged into the " +
- "application project.";
- assertEquals(
- "When using a custom view with custom attributes in a library project, the layout " +
- "must use the special namespace " +
- "<a href=\"http://schemas.android.com/apk/res-auto\">" +
- "http://schemas.android.com/apk/res-auto</a> " +
- "instead of a URI which includes the library project's own package. " +
- "This will be used to automatically adjust the namespace of the attributes when " +
- "the library resources are merged into the application project.",
- convertMarkup(explanation, HTML));
- }
-
- public void testConvertMarkup4() throws Exception {
- // monospace test
- String explanation =
- "The manifest should contain a `<uses-sdk>` element which defines the " +
- "minimum minimum API Level required for the application to run, " +
- "as well as the target version (the highest API level you have tested " +
- "the version for.)";
-
- assertEquals(
- "The manifest should contain a <code><uses-sdk></code> element which defines the " +
- "minimum minimum API Level required for the application to run, " +
- "as well as the target version (the highest API level you have tested " +
- "the version for.)",
- convertMarkup(explanation, HTML));
- }
-
- public void testConvertMarkup5() throws Exception {
- // monospace and bold test
- // From ManifestDetector#MULTIPLE_USES_SDK
- String explanation =
- "The `<uses-sdk>` element should appear just once; the tools will *not* merge the " +
- "contents of all the elements so if you split up the attributes across multiple " +
- "elements, only one of them will take effect. To fix this, just merge all the " +
- "attributes from the various elements into a single <uses-sdk> element.";
-
- assertEquals(
- "The <code><uses-sdk></code> element should appear just once; the tools " +
- "will <b>not</b> merge the " +
- "contents of all the elements so if you split up the attributes across multiple " +
- "elements, only one of them will take effect. To fix this, just merge all the " +
- "attributes from the various elements into a single <uses-sdk> element.",
- convertMarkup(explanation, HTML));
- }
-
- public void testConvertMarkup6() throws Exception {
- // Embedded code next to attributes
- // From AlwaysShowActionDetector#ISSUE
- String explanation =
- "Using `showAsAction=\"always\"` in menu XML, or `MenuItem.SHOW_AS_ACTION_ALWAYS` in "+
- "Java code is usually a deviation from the user interface style guide." +
- "Use `ifRoom` or the corresponding `MenuItem.SHOW_AS_ACTION_IF_ROOM` instead.\n" +
- "\n" +
- "If `always` is used sparingly there are usually no problems and behavior is " +
- "roughly equivalent to `ifRoom` but with preference over other `ifRoom` " +
- "items. Using it more than twice in the same menu is a bad idea.\n" +
- "\n" +
- "This check looks for menu XML files that contain more than two `always` " +
- "actions, or some `always` actions and no `ifRoom` actions. In Java code, " +
- "it looks for projects that contain references to `MenuItem.SHOW_AS_ACTION_ALWAYS` " +
- "and no references to `MenuItem.SHOW_AS_ACTION_IF_ROOM`.";
-
- assertEquals(
- "Using <code>showAsAction=\"always\"</code> in menu XML, or " +
- "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> in Java code is usually a deviation " +
- "from the user interface style guide.Use <code>ifRoom</code> or the " +
- "corresponding <code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code> instead.<br/>\n" +
- "<br/>\n" +
- "If <code>always</code> is used sparingly there are usually no problems and " +
- "behavior is roughly equivalent to <code>ifRoom</code> but with preference over " +
- "other <code>ifRoom</code> items. Using it more than twice in the same menu " +
- "is a bad idea.<br/>\n" +
- "<br/>\n" +
- "This check looks for menu XML files that contain more than two <code>always</code> " +
- "actions, or some <code>always</code> actions and no <code>ifRoom</code> actions. " +
- "In Java code, it looks for projects that contain references to " +
- "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> and no references to " +
- "<code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code>.",
- convertMarkup(explanation, HTML));
- }
-
- public void testConvertSelf() throws Exception {
- // No changes
- assertEquals("`foo`<b>", RAW.convertTo("`foo`<b>", RAW));
- assertEquals("`foo`<b>", TEXT.convertTo("`foo`<b>", TEXT));
- assertEquals("`foo`<b>", HTML.convertTo("`foo`<b>", HTML));
- }
-
- public void testConvertFromHtml() throws Exception {
- assertEquals(""
- + "Line 1\n"
- + "Line 2 <div>\n",
- HTML.convertTo("<html>Line 1<br>Line 2\n<!-- comment --><div></html>",
- TEXT));
- }
-
- public void testConvertFromHtml2() throws Exception {
- assertEquals(""
- + "Using showAsAction=\"always\" in menu XML, or\n"
- + "MenuItem.SHOW_AS_ACTION_ALWAYS in Java code is usually a\n"
- + "deviation from the user interface style guide.Use ifRoom or\n"
- + "the corresponding MenuItem.SHOW_AS_ACTION_IF_ROOM instead.\n"
- + "If always is used sparingly there are usually no problems\n"
- + "and behavior is roughly equivalent to ifRoom but with\n"
- + "preference over other ifRoom items. Using it more than twice\n"
- + "in the same menu is a bad idea. This check looks for menu\n"
- + "XML files that contain more than two always actions, or some\n"
- + "always actions and no ifRoom actions. In Java code, it looks\n"
- + "for projects that contain references to\n"
- + "MenuItem.SHOW_AS_ACTION_ALWAYS and no references to\n"
- + "MenuItem.SHOW_AS_ACTION_IF_ROOM.\n",
- HTML.convertTo(
- "Using <code>showAsAction=\"always\"</code> in menu XML, or " +
- "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> in Java code is usually a deviation " +
- "from the user interface style guide.Use <code>ifRoom</code> or the " +
- "corresponding <code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code> instead.<br/>\n" +
- "<br/>\n" +
- "If <code>always</code> is used sparingly there are usually no problems and " +
- "behavior is roughly equivalent to <code>ifRoom</code> but with preference over " +
- "other <code>ifRoom</code> items. Using it more than twice in the same menu " +
- "is a bad idea.<br/>\n" +
- "<br/>\n" +
- "This check looks for menu XML files that contain more than two <code>always</code> " +
- "actions, or some <code>always</code> actions and no <code>ifRoom</code> actions. " +
- "In Java code, it looks for projects that contain references to " +
- "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> and no references to " +
- "<code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code>.",
- TEXT));
- }
-
- public void testNbsp() throws Exception {
- assertEquals(" text", RAW.convertTo("\u00a0\u00A0text", HTML));
- }
-}
\ No newline at end of file
diff --git a/lint/libs/lint-api/build.gradle b/lint/libs/lint-api/build.gradle
index 9a810ee..df9d553 100644
--- a/lint/libs/lint-api/build.gradle
+++ b/lint/libs/lint-api/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
+apply plugin: 'jacoco'
apply plugin: 'sdk-java-lib'
group = 'com.android.tools.lint'
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/DefaultConfiguration.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/DefaultConfiguration.java
index ca7e76e..79a4a39 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/DefaultConfiguration.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/DefaultConfiguration.java
@@ -267,7 +267,7 @@
}, mClient);
mClient.report(new Context(driver, mProject, mProject, mConfigFile),
IssueRegistry.LINT_ERROR,
- mProject.getConfiguration().getSeverity(IssueRegistry.LINT_ERROR),
+ mProject.getConfiguration(driver).getSeverity(IssueRegistry.LINT_ERROR),
Location.create(mConfigFile), message, TextFormat.RAW);
}
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaParser.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaParser.java
index 97917e9..f150eb2 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaParser.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/JavaParser.java
@@ -190,10 +190,15 @@
/** If the type is not primitive, returns the class of the type if known */
@Nullable
public abstract ResolvedClass getTypeClass();
+
+ @Override
+ public abstract boolean equals(Object o);
+
}
/** Convenience implementation of {@link TypeDescriptor} */
public static class DefaultTypeDescriptor extends TypeDescriptor {
+
private String mName;
public DefaultTypeDescriptor(String name) {
@@ -232,6 +237,26 @@
public ResolvedClass getTypeClass() {
return null;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DefaultTypeDescriptor that = (DefaultTypeDescriptor) o;
+
+ return !(mName != null ? !mName.equals(that.mName) : that.mName != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return mName != null ? mName.hashCode() : 0;
+ }
}
/** A resolved declaration from an AST Node reference */
@@ -269,6 +294,18 @@
return null;
}
+
+ /**
+ * Returns true if this element is in the given package (or optionally, in one of its sub
+ * packages)
+ *
+ * @param pkg the package name
+ * @param includeSubPackages whether to include subpackages
+ * @return true if the element is in the given package
+ */
+ public boolean isInPackage(@NonNull String pkg, boolean includeSubPackages) {
+ return getSignature().startsWith(pkg);
+ }
}
/** A resolved class declaration (class, interface, enumeration or annotation) */
@@ -327,9 +364,31 @@
@NonNull
public abstract Iterable<ResolvedMethod> getMethods(@NonNull String name, boolean includeInherited);
+ /** Returns the fields defined in this class, and optionally any fields declared in any superclasses as well */
+ @NonNull
+ public abstract Iterable<ResolvedField> getFields(boolean includeInherited);
+
/** Returns the named field defined in this class, or optionally inherited from a superclass */
@Nullable
public abstract ResolvedField getField(@NonNull String name, boolean includeInherited);
+
+ /** Returns the package containing this class */
+ @Nullable
+ public abstract ResolvedPackage getPackage();
+
+ @Override
+ public boolean isInPackage(@NonNull String pkg, boolean includeSubPackages) {
+ String packageName = getPackageName();
+
+ //noinspection SimplifiableIfStatement
+ if (pkg.equals(packageName)) {
+ return true;
+ }
+
+ return includeSubPackages && packageName.length() > pkg.length() &&
+ packageName.charAt(pkg.length()) == '.' &&
+ packageName.startsWith(pkg);
+ }
}
/** A method or constructor declaration */
@@ -410,6 +469,20 @@
return null;
}
+
+ @Override
+ public boolean isInPackage(@NonNull String pkg, boolean includeSubPackages) {
+ String packageName = getContainingClass().getPackageName();
+
+ //noinspection SimplifiableIfStatement
+ if (pkg.equals(packageName)) {
+ return true;
+ }
+
+ return includeSubPackages && packageName.length() > pkg.length() &&
+ packageName.charAt(pkg.length()) == '.' &&
+ packageName.startsWith(pkg);
+ }
}
/** A field declaration */
@@ -433,6 +506,20 @@
public String getContainingClassName() {
return getContainingClass().getName();
}
+
+ @Override
+ public boolean isInPackage(@NonNull String pkg, boolean includeSubPackages) {
+ String packageName = getContainingClass().getPackageName();
+
+ //noinspection SimplifiableIfStatement
+ if (pkg.equals(packageName)) {
+ return true;
+ }
+
+ return includeSubPackages && packageName.length() > pkg.length() &&
+ packageName.charAt(pkg.length()) == '.' &&
+ packageName.startsWith(pkg);
+ }
}
/**
@@ -469,7 +556,14 @@
public abstract List<Value> getValues();
@Nullable
- public abstract Object getValue(@NonNull String name);
+ public Object getValue(@NonNull String name) {
+ for (Value value : getValues()) {
+ if (name.equals(value.name)) {
+ return value.value;
+ }
+ }
+ return null;
+ }
@Nullable
public Object getValue() {
@@ -483,6 +577,15 @@
}
}
+ /** A package declaration */
+ public abstract static class ResolvedPackage extends ResolvedNode {
+ @NonNull
+ @Override
+ public Iterable<ResolvedAnnotation> getAnnotations() {
+ return Collections.emptyList();
+ }
+ }
+
/** A local variable or parameter declaration */
public abstract static class ResolvedVariable extends ResolvedNode {
@Override
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 0980af2..fbce577 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
@@ -151,6 +151,15 @@
private final Map<String, List<VisitingDetector>> mSuperClassDetectors =
new HashMap<String, List<VisitingDetector>>();
+ /**
+ * Number of fatal exceptions (internal errors, usually from ECJ) we've
+ * encountered; we don't log each and every one to avoid massive log spam
+ * in code which triggers this condition
+ */
+ private static int sExceptionCount;
+ /** Max number of logs to include */
+ private static final int MAX_REPORTED_CRASHES = 20;
+
JavaVisitor(@NonNull JavaParser parser, @NonNull List<Detector> detectors) {
mParser = parser;
mAllDetectors = new ArrayList<VisitingDetector>(detectors.size());
@@ -269,6 +278,43 @@
for (VisitingDetector v : mAllDetectors) {
v.getDetector().afterCheckFile(context);
}
+ } catch (RuntimeException e) {
+ if (sExceptionCount++ > MAX_REPORTED_CRASHES) {
+ // No need to keep spamming the user that a lot of the files
+ // are tripping up ECJ, they get the picture.
+ return;
+ }
+
+ // Work around ECJ bugs; see https://code.google.com/p/android/issues/detail?id=172268
+ // Don't allow lint bugs to take down the whole build. TRY to log this as a
+ // lint error instead!
+ StringBuilder sb = new StringBuilder(100);
+ sb.append("Unexpected failure during lint analysis of ");
+ sb.append(context.file.getName());
+ sb.append(" (this is a bug in lint or one of the libraries it depends on)\n");
+
+ StackTraceElement[] stackTrace = e.getStackTrace();
+ int count = 0;
+ for (StackTraceElement frame : stackTrace) {
+ if (count > 0) {
+ sb.append("->");
+ }
+
+ String className = frame.getClassName();
+ sb.append(className.substring(className.lastIndexOf('.') + 1));
+ sb.append('.').append(frame.getMethodName());
+ sb.append('(');
+ sb.append(frame.getFileName()).append(':').append(frame.getLineNumber());
+ sb.append(')');
+ count++;
+ // Only print the top 3-4 frames such that we can identify the bug
+ if (count == 4) {
+ break;
+ }
+ }
+ Throwable throwable = null; // NOT e: this makes for very noisy logs
+ //noinspection ConstantConditions
+ context.log(throwable, sb.toString());
} finally {
if (compilationUnit != null) {
mParser.dispose(context, compilationUnit);
@@ -343,13 +389,10 @@
ResolvedClass resolvedClass = (ResolvedClass) resolved;
ResolvedClass cls = resolvedClass;
while (cls != null) {
- String fqcn = cls.getSignature();
- if (fqcn != null) {
- List<VisitingDetector> list = mSuperClassDetectors.get(fqcn);
- if (list != null) {
- for (VisitingDetector v : list) {
- v.getJavaScanner().checkClass(mContext, node, node, resolvedClass);
- }
+ List<VisitingDetector> list = mSuperClassDetectors.get(cls.getName());
+ if (list != null) {
+ for (VisitingDetector v : list) {
+ v.getJavaScanner().checkClass(mContext, node, node, resolvedClass);
}
}
@@ -374,14 +417,11 @@
ResolvedClass resolvedClass = (ResolvedClass) resolved;
ResolvedClass cls = resolvedClass;
while (cls != null) {
- String fqcn = cls.getSignature();
- if (fqcn != null) {
- List<VisitingDetector> list = mSuperClassDetectors.get(fqcn);
- if (list != null) {
- for (VisitingDetector v : list) {
- v.getJavaScanner().checkClass(mContext, null, anonymous,
- resolvedClass);
- }
+ List<VisitingDetector> list = mSuperClassDetectors.get(cls.getName());
+ if (list != null) {
+ for (VisitingDetector v : list) {
+ v.getJavaScanner().checkClass(mContext, null, anonymous,
+ resolvedClass);
}
}
@@ -1331,7 +1371,7 @@
ResolvedNode resolved = mContext.resolve(node);
if (resolved instanceof ResolvedMethod) {
ResolvedMethod method = (ResolvedMethod) resolved;
- String type = method.getContainingClass().getSignature();
+ String type = method.getContainingClass().getName();
List<VisitingDetector> list = mConstructorDetectors.get(type);
if (list != null) {
for (VisitingDetector v : list) {
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintClient.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintClient.java
index fcb4dfe..473c95e 100755
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintClient.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintClient.java
@@ -28,6 +28,8 @@
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidLibrary;
+import com.android.builder.model.Variant;
import com.android.ide.common.repository.ResourceVisibilityLookup;
import com.android.ide.common.res2.AbstractResourceRepository;
import com.android.ide.common.res2.ResourceItem;
@@ -87,15 +89,17 @@
* By default this method returns a {@link DefaultConfiguration}.
*
* @param project the project to obtain a configuration for
+ * @param driver the current driver, if any
* @return a configuration, never null.
*/
- public Configuration getConfiguration(@NonNull Project project) {
+ @NonNull
+ public Configuration getConfiguration(@NonNull Project project, @Nullable LintDriver driver) {
return DefaultConfiguration.create(this, project, null);
}
/**
* Report the given issue. This method will only be called if the configuration
- * provided by {@link #getConfiguration(Project)} has reported the corresponding
+ * provided by {@link #getConfiguration(Project,LintDriver)} has reported the corresponding
* issue as enabled and has not filtered out the issue with its
* {@link Configuration#ignore(Context,Issue,Location,String)} method.
* <p>
@@ -920,10 +924,41 @@
@SuppressWarnings("MethodMayBeStatic") // Intentionally instance method so it can be overridden
@NonNull
public List<File> findRuleJars(@NonNull Project project) {
- if (project.getDir().getPath().endsWith(DOT_AAR)) {
- File lintJar = new File(project.getDir(), "lint.jar"); //$NON-NLS-1$
- if (lintJar.exists()) {
- return Collections.singletonList(lintJar);
+ if (project.isGradleProject()) {
+ if (project.isLibrary()) {
+ AndroidLibrary model = project.getGradleLibraryModel();
+ if (model != null) {
+ File lintJar = model.getLintJar();
+ if (lintJar.exists()) {
+ return Collections.singletonList(lintJar);
+ }
+ }
+ } else if (project.getSubset() != null) {
+ // Probably just analyzing a single file: we still want to look for custom
+ // rules applicable to the file
+ List<File> rules = null;
+ final Variant variant = project.getCurrentVariant();
+ if (variant != null) {
+ Collection<AndroidLibrary> libraries = variant.getMainArtifact()
+ .getDependencies().getLibraries();
+ for (AndroidLibrary library : libraries) {
+ File lintJar = library.getLintJar();
+ if (lintJar.exists()) {
+ if (rules == null) {
+ rules = Lists.newArrayListWithExpectedSize(4);
+ }
+ rules.add(lintJar);
+ }
+ }
+ if (rules != null) {
+ return rules;
+ }
+ }
+ } else if (project.getDir().getPath().endsWith(DOT_AAR)) {
+ File lintJar = new File(project.getDir(), "lint.jar"); //$NON-NLS-1$
+ if (lintJar.exists()) {
+ return Collections.singletonList(lintJar);
+ }
}
}
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.java
index d248236..b30cdb8 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/client/api/LintDriver.java
@@ -103,7 +103,6 @@
import lombok.ast.AnnotationElement;
import lombok.ast.AnnotationValue;
import lombok.ast.ArrayInitializer;
-import lombok.ast.ClassDeclaration;
import lombok.ast.ConstructorDeclaration;
import lombok.ast.Expression;
import lombok.ast.MethodDeclaration;
@@ -111,6 +110,7 @@
import lombok.ast.Node;
import lombok.ast.StrictListAccessor;
import lombok.ast.StringLiteral;
+import lombok.ast.TypeDeclaration;
import lombok.ast.TypeReference;
import lombok.ast.VariableDefinition;
@@ -409,7 +409,7 @@
return;
}
- registerCustomRules(projects);
+ registerCustomDetectors(projects);
if (mScope == null) {
mScope = Scope.infer(projects);
@@ -441,7 +441,21 @@
fireEvent(mCanceled ? EventType.CANCELED : EventType.COMPLETED, null);
}
- private void registerCustomRules(Collection<Project> projects) {
+ @Nullable
+ private Set<Issue> myCustomIssues;
+
+ /**
+ * Returns true if the given issue is an issue that was loaded as a custom rule
+ * (e.g. a 3rd-party library provided the detector, it's not built in)
+ *
+ * @param issue the issue to be looked up
+ * @return true if this is a custom (non-builtin) check
+ */
+ public boolean isCustomIssue(@NonNull Issue issue) {
+ return myCustomIssues != null && myCustomIssues.contains(issue);
+ }
+
+ private void registerCustomDetectors(Collection<Project> projects) {
// Look at the various projects, and if any of them provide a custom
// lint jar, "add" them (this will replace the issue registry with
// a CompositeIssueRegistry containing the original issue registry
@@ -449,6 +463,9 @@
Set<File> jarFiles = Sets.newHashSet();
for (Project project : projects) {
jarFiles.addAll(mClient.findRuleJars(project));
+ for (Project library : project.getAllLibraries()) {
+ jarFiles.addAll(mClient.findRuleJars(library));
+ }
}
jarFiles.addAll(mClient.findGlobalRuleJars());
@@ -458,7 +475,12 @@
registries.add(mRegistry);
for (File jarFile : jarFiles) {
try {
- registries.add(JarFileIssueRegistry.get(mClient, jarFile));
+ IssueRegistry registry = JarFileIssueRegistry.get(mClient, jarFile);
+ if (myCustomIssues == null) {
+ myCustomIssues = Sets.newHashSet();
+ }
+ myCustomIssues.addAll(registry.getIssues());
+ registries.add(registry);
} catch (Throwable e) {
mClient.log(e, "Could not load custom rule jar file %1$s", jarFile);
}
@@ -545,7 +567,7 @@
// and simultaneously build up the detectorToScope map which tracks
// the scopes each detector is affected by (this is used to populate
// the mScopeDetectors map which is used during iteration).
- Configuration configuration = project.getConfiguration();
+ Configuration configuration = project.getConfiguration(this);
for (Detector detector : detectors) {
Class<? extends Detector> detectorClass = detector.getClass();
Collection<Issue> detectorIssues = issueMap.get(detectorClass);
@@ -602,7 +624,7 @@
mCurrentFolderType = null;
mCurrentVisitor = null;
- Configuration configuration = project.getConfiguration();
+ Configuration configuration = project.getConfiguration(this);
mScopeDetectors = new EnumMap<Scope, List<Detector>>(Scope.class);
mApplicableDetectors = mRegistry.createDetectors(mClient, configuration,
mScope, mScopeDetectors);
@@ -1244,7 +1266,7 @@
Location location = Location.create(project.getDir());
mClient.report(new Context(this, project, main, project.getDir()),
IssueRegistry.LINT_ERROR,
- project.getConfiguration().getSeverity(IssueRegistry.LINT_ERROR),
+ project.getConfiguration(this).getSeverity(IssueRegistry.LINT_ERROR),
location, message, TextFormat.RAW);
classEntries = Collections.emptyList();
} else {
@@ -1848,11 +1870,11 @@
@Override
@NonNull
- public Configuration getConfiguration(@NonNull Project project) {
- return mDelegate.getConfiguration(project);
+ public Configuration getConfiguration(@NonNull Project project,
+ @Nullable LintDriver driver) {
+ return mDelegate.getConfiguration(project, driver);
}
-
@Override
public void log(@NonNull Severity severity, @Nullable Throwable exception,
@Nullable String format, @Nullable Object... args) {
@@ -2374,9 +2396,9 @@
if (isSuppressed(issue, declaration.astModifiers())) {
return true;
}
- } else if (type == ClassDeclaration.class) {
- // Class
- ClassDeclaration declaration = (ClassDeclaration) scope;
+ } else if (TypeDeclaration.class.isAssignableFrom(type)) {
+ // Class, annotation, enum, interface
+ TypeDeclaration declaration = (TypeDeclaration) scope;
if (isSuppressed(issue, declaration.astModifiers())) {
return true;
}
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java
index 3a0685a..4550cc5 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/ConstantEvaluator.java
@@ -419,6 +419,9 @@
}
}
+ // TODO: Check for MethodInvocation and perform some common operations -
+ // Math.* methods, String utility methods like notNullize, etc
+
return null;
}
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Context.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Context.java
index 3a04d71..6eca816 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Context.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Context.java
@@ -103,7 +103,7 @@
mDriver = driver;
mProject = project;
mMainProject = main;
- mConfiguration = project.getConfiguration();
+ mConfiguration = project.getConfiguration(driver);
}
/**
@@ -263,7 +263,7 @@
if (location != null && location.getFile() != null) {
Project project = mDriver.findProjectFor(location.getFile());
if (project != null) {
- configuration = project.getConfiguration();
+ configuration = project.getConfiguration(mDriver);
}
}
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 5000c26..58f66be 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
@@ -25,12 +25,15 @@
import com.android.tools.lint.client.api.JavaParser;
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
import com.android.tools.lint.client.api.LintDriver;
+import com.google.common.collect.Iterators;
import java.io.File;
import java.util.Iterator;
import lombok.ast.ClassDeclaration;
import lombok.ast.ConstructorDeclaration;
+import lombok.ast.ConstructorInvocation;
+import lombok.ast.EnumConstant;
import lombok.ast.Expression;
import lombok.ast.MethodDeclaration;
import lombok.ast.MethodInvocation;
@@ -228,6 +231,45 @@
return mParser.getType(this, node);
}
+ @Nullable
+ public static String getMethodName(@NonNull Node call) {
+ if (call instanceof MethodInvocation) {
+ return ((MethodInvocation)call).astName().astValue();
+ } else if (call instanceof ConstructorInvocation) {
+ return ((ConstructorInvocation)call).astTypeReference().getTypeName();
+ } else if (call instanceof EnumConstant) {
+ return ((EnumConstant)call).astName().astValue();
+ } else {
+ return null;
+ }
+ }
+
+ @NonNull
+ public static Iterator<Expression> getParameters(@NonNull Node call) {
+ if (call instanceof MethodInvocation) {
+ return ((MethodInvocation) call).astArguments().iterator();
+ } else if (call instanceof ConstructorInvocation) {
+ return ((ConstructorInvocation) call).astArguments().iterator();
+ } else if (call instanceof EnumConstant) {
+ return ((EnumConstant) call).astArguments().iterator();
+ } else {
+ return Iterators.emptyIterator();
+ }
+ }
+
+ @Nullable
+ public static Node getParameter(@NonNull Node call, int parameter) {
+ Iterator<Expression> iterator = getParameters(call);
+
+ for (int i = 0; i < parameter - 1; i++) {
+ if (!iterator.hasNext()) {
+ return null;
+ }
+ iterator.next();
+ }
+ return iterator.hasNext() ? iterator.next() : null;
+ }
+
/**
* Returns true if the given method invocation node corresponds to a call on a
* {@code android.content.Context}
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Location.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Location.java
index fb9d42e..284e41b 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Location.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Location.java
@@ -18,6 +18,7 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourcePosition;
import com.android.ide.common.res2.ResourceFile;
import com.android.ide.common.res2.ResourceItem;
import com.google.common.annotations.Beta;
@@ -189,6 +190,31 @@
}
/**
+ * Creates a new location for the given file and SourcePosition.
+ *
+ * @param file the file containing the positions
+ * @param position the source position
+ * @return a new location
+ */
+ @NonNull
+ public static Location create(
+ @NonNull File file,
+ @NonNull SourcePosition position) {
+ if (position.equals(SourcePosition.UNKNOWN)) {
+ return new Location(file, null /*start*/, null /*end*/);
+ }
+ return new Location(file,
+ new DefaultPosition(
+ position.getStartLine(),
+ position.getStartColumn(),
+ position.getStartOffset()),
+ new DefaultPosition(
+ position.getEndLine(),
+ position.getEndColumn(),
+ position.getEndOffset()));
+ }
+
+ /**
* Creates a new location for the given file and starting and ending
* positions.
*
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Project.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Project.java
index 57476ed..3831284 100755
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Project.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/Project.java
@@ -55,6 +55,7 @@
import com.android.tools.lint.client.api.CircularDependencyException;
import com.android.tools.lint.client.api.Configuration;
import com.android.tools.lint.client.api.LintClient;
+import com.android.tools.lint.client.api.LintDriver;
import com.android.tools.lint.client.api.SdkInfo;
import com.google.common.annotations.Beta;
import com.google.common.base.CharMatcher;
@@ -564,12 +565,13 @@
/**
* Gets the configuration associated with this project
*
+ * @param driver the current driver, if any
* @return the configuration associated with this project
*/
@NonNull
- public Configuration getConfiguration() {
+ public Configuration getConfiguration(@Nullable LintDriver driver) {
if (mConfiguration == null) {
- mConfiguration = mClient.getConfiguration(this);
+ mConfiguration = mClient.getConfiguration(this, driver);
}
return mConfiguration;
}
@@ -1353,4 +1355,24 @@
return mResourceVisibility;
}
+
+ /**
+ * Returns the associated client
+ *
+ * @return the client
+ */
+ @NonNull
+ public LintClient getClient() {
+ return mClient;
+ }
+
+ /**
+ * Returns the compile target to use for this project
+ *
+ * @return the compile target to use to build this project
+ */
+ @Nullable
+ public IAndroidTarget getCompileTarget() {
+ return mClient.getCompileTarget(this);
+ }
}
diff --git a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TextFormat.java b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TextFormat.java
index ed1bd3c..a66b326 100644
--- a/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TextFormat.java
+++ b/lint/libs/lint-api/src/main/java/com/android/tools/lint/detector/api/TextFormat.java
@@ -203,7 +203,7 @@
int n = text.length();
for (int i = 0; i < n; i++) {
char c = text.charAt(i);
- if ((c == '*' || c == '`' && i < n - 1)) {
+ if ((c == '*' || c == '`') && i < n - 1) {
// Scout ahead for range end
if (!Character.isLetterOrDigit(prev)
&& !Character.isWhitespace(text.charAt(i + 1))) {
diff --git a/lint/libs/lint-checks/build.gradle b/lint/libs/lint-checks/build.gradle
index 568c721..b71cb35 100644
--- a/lint/libs/lint-checks/build.gradle
+++ b/lint/libs/lint-checks/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
+apply plugin: 'jacoco'
apply plugin: 'sdk-java-lib'
group = 'com.android.tools.lint'
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java
index 66ece7e..9cd147f 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AnnotationDetector.java
@@ -16,35 +16,49 @@
package com.android.tools.lint.checks;
+import static com.android.SdkConstants.ATTR_VALUE;
import static com.android.SdkConstants.FQCN_SUPPRESS_LINT;
+import static com.android.SdkConstants.INT_DEF_ANNOTATION;
import static com.android.SdkConstants.SUPPRESS_LINT;
+import static com.android.SdkConstants.TYPE_DEF_FLAG_ATTRIBUTE;
+import static com.android.tools.lint.detector.api.JavaContext.findSurroundingClass;
+import static com.android.tools.lint.detector.api.JavaContext.getParentOfType;
import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
import com.android.tools.lint.client.api.IssueRegistry;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
+import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
+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 com.android.tools.lint.detector.api.Speed;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import java.io.File;
import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import lombok.ast.Annotation;
+import lombok.ast.AnnotationDeclaration;
import lombok.ast.AnnotationElement;
import lombok.ast.AnnotationValue;
import lombok.ast.ArrayInitializer;
import lombok.ast.AstVisitor;
import lombok.ast.Block;
+import lombok.ast.ClassDeclaration;
import lombok.ast.ConstructorDeclaration;
import lombok.ast.Expression;
import lombok.ast.ForwardingAstVisitor;
+import lombok.ast.IntegralLiteral;
import lombok.ast.MethodDeclaration;
import lombok.ast.Modifiers;
import lombok.ast.Node;
@@ -52,15 +66,23 @@
import lombok.ast.StrictListAccessor;
import lombok.ast.StringLiteral;
import lombok.ast.TypeBody;
+import lombok.ast.TypeMember;
+import lombok.ast.VariableDeclaration;
import lombok.ast.VariableDefinition;
import lombok.ast.VariableDefinitionEntry;
+import lombok.ast.VariableReference;
/**
* Checks annotations to make sure they are valid
*/
public class AnnotationDetector extends Detector implements Detector.JavaScanner {
+
+ public static final Implementation IMPLEMENTATION = new Implementation(
+ AnnotationDetector.class,
+ Scope.JAVA_FILE_SCOPE);
+
/** Placing SuppressLint on a local variable doesn't work for class-file based checks */
- public static final Issue ISSUE = Issue.create(
+ public static final Issue INSIDE_METHOD = Issue.create(
"LocalSuppress", //$NON-NLS-1$
"@SuppressLint on invalid element",
@@ -75,9 +97,42 @@
Category.CORRECTNESS,
3,
Severity.ERROR,
- new Implementation(
- AnnotationDetector.class,
- Scope.JAVA_FILE_SCOPE));
+ IMPLEMENTATION);
+
+ /** IntDef annotations should be unique */
+ public static final Issue UNIQUE = Issue.create(
+ "UniqueConstants", //$NON-NLS-1$
+ "Overlapping Enumeration Constants",
+
+ "The `@IntDef` annotation allows you to " +
+ "create a light-weight \"enum\" or type definition. However, it's possible to " +
+ "accidentally specify the same value for two or more of the values, which can " +
+ "lead to hard-to-detect bugs. This check looks for this scenario and flags any " +
+ "repeated constants.\n" +
+ "\n" +
+ "In some cases, the repeated constant is intentional (for example, renaming a " +
+ "constant to a more intuitive name, and leaving the old name in place for " +
+ "compatibility purposes.) In that case, simply suppress this check by adding a " +
+ "`@SuppressLint(\"UniqueConstants\")` annotation.",
+
+ Category.CORRECTNESS,
+ 3,
+ Severity.ERROR,
+ IMPLEMENTATION);
+
+ /** Flags should typically be specified as bit shifts */
+ public static final Issue FLAG_STYLE = Issue.create(
+ "ShiftFlags", //$NON-NLS-1$
+ "Dangerous Flag Constant Declaration",
+
+ "When defining multiple constants for use in flags, the recommended style is " +
+ "to use the form `1 << 2`, `1 << 3`, `1 << 4` and so on to ensure that the " +
+ "constants are unique and non-overlapping.",
+
+ Category.CORRECTNESS,
+ 3,
+ Severity.WARNING,
+ IMPLEMENTATION);
/** Constructs a new {@link AnnotationDetector} check */
public AnnotationDetector() {
@@ -139,9 +194,7 @@
if (expressions == null) {
continue;
}
- Iterator<Expression> arrayIterator = expressions.iterator();
- while (arrayIterator.hasNext()) {
- Expression arrayElement = arrayIterator.next();
+ for (Expression arrayElement : expressions) {
if (arrayElement instanceof StringLiteral) {
String id = ((StringLiteral) arrayElement).astValue();
if (!checkId(node, id)) {
@@ -153,11 +206,165 @@
}
}
}
+ } else if (INT_DEF_ANNOTATION.equals(type) || "IntDef".equals(type)) {
+ // Make sure that all the constants are unique
+ ResolvedNode resolved = mContext.resolve(node);
+ if (resolved instanceof ResolvedAnnotation) {
+ ensureUniqueValues(((ResolvedAnnotation)resolved), node);
+ }
}
return super.visitAnnotation(node);
}
+ private void ensureUniqueValues(@NonNull ResolvedAnnotation annotation,
+ @NonNull Annotation node) {
+ Object allowed = annotation.getValue();
+ if (allowed instanceof Object[]) {
+ Object[] allowedValues = (Object[]) allowed;
+ Map<Number,Integer> valueToIndex =
+ Maps.newHashMapWithExpectedSize(allowedValues.length);
+
+ List<Node> constants = null;
+ for (AnnotationElement element : node.astElements()) {
+ if (element.astName() == null
+ || ATTR_VALUE.equals(element.astName().astValue())) {
+ AnnotationValue value = element.astValue();
+ if (value instanceof ArrayInitializer) {
+ ArrayInitializer initializer = (ArrayInitializer)value;
+ constants = Lists.newArrayListWithExpectedSize(allowedValues.length);
+ for (Expression expression : initializer.astExpressions()) {
+ constants.add(expression);
+ }
+ }
+ break;
+ }
+ }
+ if (constants != null) {
+ if (constants.size() != allowedValues.length) {
+ constants = null;
+ } else {
+ boolean flag = annotation.getValue(TYPE_DEF_FLAG_ATTRIBUTE) == Boolean.TRUE;
+ if (flag) {
+ ensureUsingFlagStyle(constants);
+ }
+ }
+ }
+
+ for (int index = 0; index < allowedValues.length; index++) {
+ Object o = allowedValues[index];
+ if (o instanceof Number) {
+ Number number = (Number)o;
+ if (valueToIndex.containsKey(number)) {
+ @SuppressWarnings("UnnecessaryLocalVariable")
+ Number repeatedValue = number;
+
+ Location location;
+ String message;
+ if (constants != null) {
+ Node constant = constants.get(index);
+ int prevIndex = valueToIndex.get(number);
+ Node prevConstant = constants.get(prevIndex);
+ message = String.format(
+ "Constants `%1$s` and `%2$s` specify the same exact "
+ + "value (%3$s); this is usually a cut & paste or "
+ + "merge error",
+ constant.toString(), prevConstant.toString(),
+ repeatedValue.toString());
+ location = mContext.getLocation(constant);
+ Location secondary = mContext.getLocation(prevConstant);
+ secondary.setMessage("Previous same value");
+ location.setSecondary(secondary);
+ } else {
+ message = String.format(
+ "More than one constant specifies the same exact "
+ + "value (%1$s); this is usually a cut & paste or"
+ + "merge error",
+ repeatedValue.toString());
+ location = mContext.getLocation(node);
+ }
+ Node scope = getAnnotationScope(node);
+ mContext.report(UNIQUE, scope, location, message);
+ break;
+ }
+ valueToIndex.put(number, index);
+ }
+ }
+ }
+ }
+
+ @NonNull
+ private static List<VariableDefinitionEntry> findDeclarations(
+ @Nullable ClassDeclaration cls,
+ @NonNull List<VariableReference> references) {
+ if (cls == null) {
+ return Collections.emptyList();
+ }
+ Map<String, VariableReference> referenceMap = Maps.newHashMap();
+ for (VariableReference reference : references) {
+ String name = reference.astIdentifier().astValue();
+ referenceMap.put(name, reference);
+ }
+ List<VariableDefinitionEntry> declarations = Lists.newArrayList();
+ for (TypeMember member : cls.astBody().astMembers()) {
+ if (member instanceof VariableDeclaration) {
+ VariableDeclaration declaration = (VariableDeclaration)member;
+ VariableDefinitionEntry field = declaration.astDefinition().astVariables()
+ .first();
+ String name = field.astName().astValue();
+ if (referenceMap.containsKey(name)) {
+ // TODO: When the Lombok ECJ bridge properly handles resolving variable
+ // definitions into ECJ bindings this code should check that
+ // mContext.resolve(field) == mContext.resolve(referenceMap.get(name)) !
+ declarations.add(field);
+ }
+ }
+ }
+
+ return declarations;
+ }
+
+ private void ensureUsingFlagStyle(@NonNull List<Node> constants) {
+ if (constants.size() < 3) {
+ return;
+ }
+
+ List<VariableReference> references =
+ Lists.newArrayListWithExpectedSize(constants.size());
+ for (Node constant : constants) {
+ if (constant instanceof VariableReference) {
+ references.add((VariableReference) constant);
+ }
+ }
+ List<VariableDefinitionEntry> entries = findDeclarations(
+ findSurroundingClass(constants.get(0)), references);
+ for (VariableDefinitionEntry entry : entries) {
+ Expression declaration = entry.astInitializer();
+ if (declaration == null) {
+ continue;
+ }
+ if (declaration instanceof IntegralLiteral) {
+ IntegralLiteral literal = (IntegralLiteral) declaration;
+ // Allow -1, 0 and 1. You can write 1 as "1 << 0" but IntelliJ for
+ // example warns that that's a redundant shift.
+ long value = literal.astLongValue();
+ if (Math.abs(value) <= 1) {
+ continue;
+ }
+ // Only warn if we're setting a specific bit
+ if (Long.bitCount(value) != 1) {
+ continue;
+ }
+ int shift = Long.numberOfTrailingZeros(value);
+ String message = String.format(
+ "Consider declaring this constant using 1 << %1$d instead",
+ shift);
+ mContext.report(FLAG_STYLE, declaration, mContext.getLocation(declaration),
+ message);
+ }
+ }
+ }
+
private boolean checkId(Annotation node, String id) {
IssueRegistry registry = mContext.getDriver().getRegistry();
Issue issue = registry.getIssue(id);
@@ -193,7 +400,8 @@
// This issue doesn't have AST access: annotations are not
// available for local variables or parameters
- mContext.report(ISSUE, node, mContext.getLocation(node), String.format(
+ Node scope = getAnnotationScope(node);
+ mContext.report(INSIDE_METHOD, scope, mContext.getLocation(node), String.format(
"The `@SuppressLint` annotation cannot be used on a local " +
"variable with the lint check '%1$s': move out to the " +
"surrounding method", id));
@@ -203,4 +411,21 @@
return true;
}
}
+
+ /**
+ * Returns the node to use as the scope for the given annotation node.
+ * You can't annotate an annotation itself (with {@code @SuppressLint}), but
+ * you should be able to place an annotation next to it, as a sibling, to only
+ * suppress the error on this annotated element, not the whole surrounding class.
+ */
+ @NonNull
+ private static Node getAnnotationScope(@NonNull Annotation node) {
+ //
+ Node scope = getParentOfType(node,
+ AnnotationDeclaration.class, true);
+ if (scope == null) {
+ scope = node;
+ }
+ return scope;
+ }
}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java
index 1af53ce..8219e9b 100755
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ApiDetector.java
@@ -1764,7 +1764,7 @@
+ "API level %1$d (current min is %2$d)", api, minSdk);
LintDriver driver = mContext.getDriver();
if (!driver.isSuppressed(mContext, UNSUPPORTED, node)) {
- mContext.report(UNSUPPORTED, location, message);
+ mContext.report(UNSUPPORTED, node, location, message);
}
}
} else {
@@ -1792,7 +1792,7 @@
api, minSdk, fqcn);
LintDriver driver = mContext.getDriver();
if (!driver.isSuppressed(mContext, UNSUPPORTED, typeReference)) {
- mContext.report(UNSUPPORTED, location, message);
+ mContext.report(UNSUPPORTED, typeReference, location, message);
}
}
}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppIndexingApiDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppIndexingApiDetector.java
new file mode 100644
index 0000000..1a8dd67
--- /dev/null
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppIndexingApiDetector.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.lint.checks;
+
+import static com.android.SdkConstants.ANDROID_URI;
+import static com.android.SdkConstants.ATTR_HOST;
+import static com.android.SdkConstants.ATTR_PATH;
+import static com.android.SdkConstants.ATTR_PATH_PATTERN;
+import static com.android.SdkConstants.ATTR_PATH_PREFIX;
+import static com.android.SdkConstants.ATTR_SCHEME;
+
+import static com.android.xml.AndroidManifest.ATTRIBUTE_MIME_TYPE;
+import static com.android.xml.AndroidManifest.ATTRIBUTE_NAME;
+import static com.android.xml.AndroidManifest.ATTRIBUTE_PORT;
+import static com.android.xml.AndroidManifest.NODE_ACTION;
+import static com.android.xml.AndroidManifest.NODE_CATEGORY;
+import static com.android.xml.AndroidManifest.NODE_DATA;
+import static com.android.xml.AndroidManifest.NODE_INTENT;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.res2.AbstractResourceRepository;
+import com.android.ide.common.res2.ResourceItem;
+import com.android.ide.common.resources.ResourceUrl;
+import com.android.resources.ResourceType;
+import com.android.tools.lint.client.api.LintClient;
+import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Implementation;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Project;
+import com.android.tools.lint.detector.api.Scope;
+import com.android.tools.lint.detector.api.Severity;
+import com.android.tools.lint.detector.api.XmlContext;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Check if the usage of App Indexing is correct.
+ */
+public class AppIndexingApiDetector extends Detector implements Detector.XmlScanner {
+
+ private static final Implementation IMPLEMENTATION = new Implementation(
+ AppIndexingApiDetector.class, Scope.MANIFEST_SCOPE);
+
+ public static final Issue ISSUE_ERROR = Issue.create("AppIndexingError", //$NON-NLS-1$
+ "Wrong Usage of App Indexing",
+ "Ensures the app can correctly handle deep links and integrate with " +
+ "App Indexing for Google search.",
+ Category.USABILITY, 5, Severity.ERROR, IMPLEMENTATION)
+ .addMoreInfo("https://g.co/AppIndexing");
+
+ public static final Issue ISSUE_WARNING = Issue.create("AppIndexingWarning", //$NON-NLS-1$
+ "Missing App Indexing Support",
+ "Ensures the app can correctly handle deep links and integrate with " +
+ "App Indexing for Google search.",
+ Category.USABILITY, 5, Severity.WARNING, IMPLEMENTATION)
+ .addMoreInfo("https://g.co/AppIndexing");
+
+ private static final String[] PATH_ATTR_LIST = new String[]{ATTR_PATH_PREFIX, ATTR_PATH,
+ ATTR_PATH_PATTERN};
+
+ @Override
+ @Nullable
+ public Collection<String> getApplicableElements() {
+ return Collections.singletonList(NODE_INTENT);
+ }
+
+ @Override
+ public void visitElement(@NonNull XmlContext context, @NonNull Element intent) {
+ boolean actionView = hasActionView(intent);
+ boolean browsable = isBrowsable(intent);
+ boolean isHttp = false;
+ boolean hasScheme = false;
+ boolean hasHost = false;
+ boolean hasPort = false;
+ boolean hasPath = false;
+ boolean hasMimeType = false;
+ Element firstData = null;
+ NodeList children = intent.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+ if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals(NODE_DATA)) {
+ Element data = (Element) child;
+ if (firstData == null) {
+ firstData = data;
+ }
+ if (isHttpSchema(data)) {
+ isHttp = true;
+ }
+ checkSingleData(context, data);
+
+ for (String name : PATH_ATTR_LIST) {
+ if (data.hasAttributeNS(ANDROID_URI, name)) {
+ hasPath = true;
+ }
+ }
+
+ if (data.hasAttributeNS(ANDROID_URI, ATTR_SCHEME)) {
+ hasScheme = true;
+ }
+
+ if (data.hasAttributeNS(ANDROID_URI, ATTR_HOST)) {
+ hasHost = true;
+ }
+
+ if (data.hasAttributeNS(ANDROID_URI, ATTRIBUTE_PORT)) {
+ hasPort = true;
+ }
+
+ if (data.hasAttributeNS(ANDROID_URI, ATTRIBUTE_MIME_TYPE)) {
+ hasMimeType = true;
+ }
+ }
+ }
+
+ // In data field, a URL is consisted by
+ // <scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>]
+ // Each part of the URL should not have illegal character.
+ if ((hasPath || hasHost || hasPort) && !hasScheme) {
+ context.report(ISSUE_ERROR, firstData, context.getLocation(firstData),
+ "android:scheme missing");
+ }
+
+ if ((hasPath || hasPort) && !hasHost) {
+ context.report(ISSUE_ERROR, firstData, context.getLocation(firstData),
+ "android:host missing");
+ }
+
+ if (actionView && browsable) {
+ if (firstData == null) {
+ // If this activity is an ACTION_VIEW action with category BROWSABLE, but doesn't
+ // have data node, it may be a mistake and we will report error.
+ context.report(ISSUE_ERROR, intent, context.getLocation(intent),
+ "Missing data node?");
+ } else if (!hasScheme && !hasMimeType) {
+ // If this activity is an action view, is browsable, but has neither a
+ // URL nor mimeType, it may be a mistake and we will report error.
+ context.report(ISSUE_ERROR, firstData, context.getLocation(firstData),
+ "Missing URL for the intent filter?");
+ }
+ }
+
+ // If this activity is an ACTION_VIEW action, has a http URL but doesn't have
+ // BROWSABLE, it may be a mistake and and we will report warning.
+ if (actionView && isHttp && !browsable) {
+ context.report(ISSUE_WARNING, intent, context.getLocation(intent),
+ "Activity supporting ACTION_VIEW is not set as BROWSABLE");
+ }
+ }
+
+ private static boolean hasActionView(Element intent) {
+ NodeList children = intent.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+ if (child.getNodeType() == Node.ELEMENT_NODE &&
+ child.getNodeName().equals(NODE_ACTION)) {
+ Element action = (Element) child;
+ if (action.hasAttributeNS(ANDROID_URI, ATTRIBUTE_NAME)) {
+ Attr attr = action.getAttributeNodeNS(ANDROID_URI, ATTRIBUTE_NAME);
+ if (attr.getValue().equals("android.intent.action.VIEW")) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean isBrowsable(Element intent) {
+ NodeList children = intent.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+ if (child.getNodeType() == Node.ELEMENT_NODE &&
+ child.getNodeName().equals(NODE_CATEGORY)) {
+ Element e = (Element) child;
+ if (e.hasAttributeNS(ANDROID_URI, ATTRIBUTE_NAME)) {
+ Attr attr = e.getAttributeNodeNS(ANDROID_URI, ATTRIBUTE_NAME);
+ if (attr.getNodeValue().equals("android.intent.category.BROWSABLE")) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean isHttpSchema(Element data) {
+ if (data.hasAttributeNS(ANDROID_URI, ATTR_SCHEME)) {
+ String value = data.getAttributeNodeNS(ANDROID_URI, ATTR_SCHEME).getValue();
+ if (value.equalsIgnoreCase("http") || value.equalsIgnoreCase("https")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void checkSingleData(XmlContext context, Element data) {
+ // path, pathPrefix and pathPattern should starts with /.
+ for (String name : PATH_ATTR_LIST) {
+ if (data.hasAttributeNS(ANDROID_URI, name)) {
+ Attr attr = data.getAttributeNodeNS(ANDROID_URI, name);
+ String path = replaceUrlWithValue(context, attr.getValue());
+ if (!path.startsWith("/") && !path.startsWith(SdkConstants.PREFIX_RESOURCE_REF)) {
+ context.report(ISSUE_ERROR, attr, context.getLocation(attr),
+ "android:" + name + " attribute should start with '/', but it is : "
+ + path);
+ }
+ }
+ }
+
+ // port should be a legal number.
+ if (data.hasAttributeNS(ANDROID_URI, ATTRIBUTE_PORT)) {
+ Attr attr = data.getAttributeNodeNS(ANDROID_URI, ATTRIBUTE_PORT);
+ try {
+ String port = replaceUrlWithValue(context, attr.getValue());
+ Integer.parseInt(port);
+ } catch (NumberFormatException e) {
+ context.report(ISSUE_ERROR, attr, context.getLocation(attr),
+ "android:port is not a legal number");
+ }
+ }
+
+ // Each field should be non empty.
+ NamedNodeMap attrs = data.getAttributes();
+ for (int i = 0; i < attrs.getLength(); i++) {
+ Node item = attrs.item(i);
+ if (item.getNodeType() == Node.ATTRIBUTE_NODE) {
+ Attr attr = (Attr) attrs.item(i);
+ if (attr.getValue().isEmpty()) {
+ context.report(ISSUE_ERROR, attr, context.getLocation(attr),
+ attr.getName() + " cannot be empty");
+ }
+ }
+ }
+ }
+
+ private static String replaceUrlWithValue(@NonNull XmlContext context,
+ @NonNull String str) {
+ Project project = context.getProject();
+ LintClient client = context.getClient();
+ if (!client.supportsProjectResources()) {
+ return str;
+ }
+ ResourceUrl style = ResourceUrl.parse(str);
+ if (style == null || style.type != ResourceType.STRING || style.framework) {
+ return str;
+ }
+ AbstractResourceRepository resources = client.getProjectResources(project, true);
+ if (resources == null) {
+ return str;
+ }
+ List<ResourceItem> items = resources.getResourceItem(ResourceType.STRING, style.name);
+ if (items == null || items.isEmpty()) {
+ return str;
+ }
+ ResourceValue resourceValue = items.get(0).getResourceValue(false);
+ if (resourceValue == null) {
+ return str;
+ }
+ return resourceValue.getValue() == null ? str : resourceValue.getValue();
+ }
+}
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 0a352b3..4e50397 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
@@ -30,7 +30,7 @@
/** Registry which provides a list of checks to be performed on an Android project */
public class BuiltinIssueRegistry extends IssueRegistry {
private static final List<Issue> sIssues;
- static final int INITIAL_CAPACITY = 215;
+ static final int INITIAL_CAPACITY = 222;
static {
List<Issue> issues = new ArrayList<Issue>(INITIAL_CAPACITY);
@@ -39,13 +39,17 @@
issues.add(AddJavascriptInterfaceDetector.ISSUE);
issues.add(AlarmDetector.ISSUE);
issues.add(AlwaysShowActionDetector.ISSUE);
- issues.add(AnnotationDetector.ISSUE);
+ issues.add(AnnotationDetector.FLAG_STYLE);
+ issues.add(AnnotationDetector.INSIDE_METHOD);
+ issues.add(AnnotationDetector.UNIQUE);
issues.add(ApiDetector.INLINED);
issues.add(ApiDetector.OVERRIDE);
issues.add(ApiDetector.UNSUPPORTED);
issues.add(ApiDetector.UNUSED);
issues.add(AppCompatCallDetector.ISSUE);
issues.add(AppCompatResourceDetector.ISSUE);
+ issues.add(AppIndexingApiDetector.ISSUE_ERROR);
+ issues.add(AppIndexingApiDetector.ISSUE_WARNING);
issues.add(ArraySizeDetector.INCONSISTENT);
issues.add(AssertDetector.ISSUE);
issues.add(ButtonDetector.BACK_BUTTON);
@@ -74,6 +78,7 @@
issues.add(DuplicateResourceDetector.TYPE_MISMATCH);
issues.add(ExtraTextDetector.ISSUE);
issues.add(FieldGetterDetector.ISSUE);
+ issues.add(FullBackupContentDetector.ISSUE);
issues.add(FragmentDetector.ISSUE);
issues.add(GetSignaturesDetector.ISSUE);
issues.add(GradleDetector.COMPATIBILITY);
@@ -211,8 +216,10 @@
issues.add(SupportAnnotationDetector.CHECK_PERMISSION);
issues.add(SupportAnnotationDetector.CHECK_RESULT);
issues.add(SupportAnnotationDetector.COLOR_USAGE);
+ issues.add(SupportAnnotationDetector.MISSING_PERMISSION);
issues.add(SupportAnnotationDetector.RANGE);
issues.add(SupportAnnotationDetector.RESOURCE_TYPE);
+ issues.add(SupportAnnotationDetector.THREAD);
issues.add(SupportAnnotationDetector.TYPE_DEF);
issues.add(SystemPermissionsDetector.ISSUE);
issues.add(TextFieldDetector.ISSUE);
@@ -273,13 +280,13 @@
} else {
int initialSize = 12;
if (scope.contains(Scope.RESOURCE_FILE)) {
- initialSize += 70;
+ initialSize += 75;
} else if (scope.contains(Scope.ALL_RESOURCE_FILES)) {
initialSize += 10;
}
if (scope.contains(Scope.JAVA_FILE)) {
- initialSize += 48;
+ initialSize += 55;
} else if (scope.contains(Scope.CLASS_FILE)) {
initialSize += 15;
} else if (scope.contains(Scope.MANIFEST)) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ButtonDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ButtonDetector.java
index 68484af..568293d 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ButtonDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ButtonDetector.java
@@ -281,7 +281,7 @@
if (!label.equals(CANCEL_LABEL)
&& LintUtils.isEnglishResource(context, true)
&& context.isEnabled(CASE)) {
- assert label.equalsIgnoreCase(CANCEL_LABEL);
+ assert label.trim().equalsIgnoreCase(CANCEL_LABEL) : label;
context.report(CASE, child, context.getLocation(child),
String.format(
"The standard Android way to capitalize %1$s " +
@@ -298,7 +298,7 @@
if (!label.equals(OK_LABEL)
&& LintUtils.isEnglishResource(context, true)
&& context.isEnabled(CASE)) {
- assert text.trim().equalsIgnoreCase(OK_LABEL) : text;
+ assert label.trim().equalsIgnoreCase(OK_LABEL) : label;
context.report(CASE, child, context.getLocation(child),
String.format(
"The standard Android way to capitalize %1$s " +
@@ -380,7 +380,7 @@
isYes ? YES_LABEL : NO_LABEL,
isYes ? ANDROID_OK_RESOURCE : ANDROID_CANCEL_RESOURCE,
isYes ? YES_LABEL : NO_LABEL);
- context.report(CASE, element, location, message, null);
+ context.report(CASE, element, location, message);
}
}
}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CallSuperDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CallSuperDetector.java
index 36ae3c6..6dc6e67 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CallSuperDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CallSuperDetector.java
@@ -18,10 +18,11 @@
import static com.android.SdkConstants.CLASS_VIEW;
import static com.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.filterRelevantAnnotations;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
import com.android.tools.lint.detector.api.Category;
@@ -68,7 +69,7 @@
Category.CORRECTNESS,
9,
- Severity.WARNING,
+ Severity.ERROR,
IMPLEMENTATION);
/** Constructs a new {@link CallSuperDetector} check */
@@ -157,27 +158,24 @@
}
// Look up annotations metadata
- while (true) {
- ResolvedMethod superMethod = method.getSuperMethod();
- if (superMethod == null) {
- return null;
- }
-
- Iterable<JavaParser.ResolvedAnnotation> annotations = superMethod.getAnnotations();
- for (JavaParser.ResolvedAnnotation annotation : annotations) {
- annotation = SupportAnnotationDetector.getRelevantAnnotation(annotation);
- if (annotation != null) {
- String signature = annotation.getSignature();
- if (CALL_SUPER_ANNOTATION.equals(signature)) {
- return superMethod;
- } else if (signature.endsWith(".OverrideMustInvoke")) {
- // Handle findbugs annotation on the fly too
- return superMethod;
- }
+ ResolvedMethod directSuper = method.getSuperMethod();
+ ResolvedMethod superMethod = directSuper;
+ while (superMethod != null) {
+ Iterable<ResolvedAnnotation> annotations = superMethod.getAnnotations();
+ annotations = filterRelevantAnnotations(annotations);
+ for (ResolvedAnnotation annotation : annotations) {
+ String signature = annotation.getSignature();
+ if (CALL_SUPER_ANNOTATION.equals(signature)) {
+ return directSuper;
+ } else if (signature.endsWith(".OverrideMustInvoke")) {
+ // Handle findbugs annotation on the fly too
+ return directSuper;
}
}
- method = superMethod;
+ superMethod = superMethod.getSuperMethod();
}
+
+ return null;
}
/** Visits a method and determines whether the method calls its super method */
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java
index 24b174e..f16296f 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/CommentDetector.java
@@ -17,6 +17,7 @@
package com.android.tools.lint.checks;
import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
@@ -131,7 +132,7 @@
if (end == -1) {
end = n;
}
- checkComment(context, source, 0, start, end);
+ checkComment(context, null, source, 0, start, end);
} else if (next == '*') {
// Block comment
int start = i + 2;
@@ -139,7 +140,7 @@
if (end == -1) {
end = n;
}
- checkComment(context, source, 0, start, end);
+ checkComment(context, null, source, 0, start, end);
}
}
}
@@ -157,13 +158,14 @@
@Override
public boolean visitComment(Comment node) {
String contents = node.astContent();
- checkComment(mContext, contents, node.getPosition().getStart(), 0, contents.length());
+ checkComment(mContext, node, contents, node.getPosition().getStart(), 0, contents.length());
return super.visitComment(node);
}
}
private static void checkComment(
- @NonNull Context context,
+ @NonNull JavaContext context,
+ @Nullable Comment node,
@NonNull String source,
int offset,
int start,
@@ -178,7 +180,7 @@
0, ESCAPE_STRING.length())) {
Location location = Location.create(context.file, source,
offset + i - 1, offset + i - 1 + ESCAPE_STRING.length());
- context.report(EASTER_EGG, location,
+ context.report(EASTER_EGG, node, location,
"Code might be hidden here; found unicode escape sequence " +
"which is interpreted as comment end, compiled code follows");
}
@@ -190,7 +192,7 @@
// TODO: Only flag this issue in release mode??
Location location = Location.create(context.file, source,
offset + i - 1, offset + i - 1 + STOPSHIP_COMMENT.length());
- context.report(STOP_SHIP, location,
+ context.report(STOP_SHIP, node, location,
"`STOPSHIP` comment found; points to code which must be fixed prior " +
"to release");
}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DateFormatDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DateFormatDetector.java
index dd5bf77..1ad7853 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DateFormatDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DateFormatDetector.java
@@ -18,9 +18,7 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.tools.lint.client.api.JavaParser;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
-import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Detector;
@@ -38,8 +36,6 @@
import lombok.ast.AstVisitor;
import lombok.ast.ConstructorInvocation;
-import lombok.ast.ForwardingAstVisitor;
-import lombok.ast.Node;
/**
* Checks for errors related to Date Formats
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java
index ab06867..c7271fc 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/DetectMissingPrefix.java
@@ -25,6 +25,8 @@
import static com.android.SdkConstants.ATTR_LAYOUT_RESOURCE_PREFIX;
import static com.android.SdkConstants.ATTR_PACKAGE;
import static com.android.SdkConstants.ATTR_STYLE;
+import static com.android.SdkConstants.AUTO_URI;
+import static com.android.SdkConstants.TAG_LAYOUT;
import static com.android.SdkConstants.TOOLS_URI;
import static com.android.SdkConstants.VIEW_TAG;
import static com.android.resources.ResourceFolderType.ANIM;
@@ -130,6 +132,13 @@
Element element = attribute.getOwnerElement();
if (isCustomView(element) && context.getResourceFolderType() != null) {
return;
+ } else if (context.getResourceFolderType() == ResourceFolderType.LAYOUT) {
+ // Data binding: These look like Android framework views but
+ // are data binding directives not in the Android namespace
+ Element root = element.getOwnerDocument().getDocumentElement();
+ if (TAG_LAYOUT.equals(root.getTagName())) {
+ return;
+ }
}
if (name.indexOf(':') != -1) {
@@ -157,6 +166,15 @@
// ....&& !attribute.getLocalName().startsWith(ATTR_LAYOUT_RESOURCE_PREFIX)
&& attribute.getOwnerElement().getParentNode().getNodeType() == Node.ELEMENT_NODE
&& !isCustomView((Element) attribute.getOwnerElement().getParentNode())) {
+ if (context.getResourceFolderType() == ResourceFolderType.LAYOUT
+ && AUTO_URI.equals(uri)) {
+ // Data binding: Can add attributes like onClickListener to buttons etc.
+ Element root = attribute.getOwnerDocument().getDocumentElement();
+ if (TAG_LAYOUT.equals(root.getTagName())) {
+ return;
+ }
+ }
+
context.report(MISSING_NAMESPACE, attribute,
context.getLocation(attribute),
String.format("Unexpected namespace prefix \"%1$s\" found for tag `%2$s`",
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FullBackupContentDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FullBackupContentDetector.java
new file mode 100644
index 0000000..3b05589
--- /dev/null
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/FullBackupContentDetector.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.resources.ResourceFolderType;
+import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.Detector.JavaScanner;
+import com.android.tools.lint.detector.api.Implementation;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Location;
+import com.android.tools.lint.detector.api.ResourceXmlDetector;
+import com.android.tools.lint.detector.api.Scope;
+import com.android.tools.lint.detector.api.Severity;
+import com.android.tools.lint.detector.api.XmlContext;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Check which makes sure that a full-backup-content descriptor file is valid and logical
+ */
+public class FullBackupContentDetector extends ResourceXmlDetector implements JavaScanner {
+ /**
+ * Validation of {@code <full-backup-content>} XML elements
+ */
+ public static final Issue ISSUE = Issue.create(
+ "FullBackupContent", //$NON-NLS-1$
+ "Valid Full Backup Content File",
+
+ "Ensures that a `<full-backup-content>` file, which is pointed to by a " +
+ "`android:fullBackupContent attribute` in the manifest file, is valid",
+
+ Category.CORRECTNESS,
+ 5,
+ Severity.FATAL,
+ new Implementation(
+ FullBackupContentDetector.class,
+ Scope.RESOURCE_FILE_SCOPE));
+
+ @SuppressWarnings("SpellCheckingInspection")
+ private static final String DOMAIN_SHARED_PREF = "sharedpref";
+ private static final String DOMAIN_ROOT = "root";
+ private static final String DOMAIN_FILE = "file";
+ private static final String DOMAIN_DATABASE = "database";
+ private static final String DOMAIN_EXTERNAL = "external";
+ private static final String TAG_EXCLUDE = "exclude";
+ private static final String TAG_INCLUDE = "include";
+ private static final String TAG_FULL_BACKUP_CONTENT = "full-backup-content";
+ private static final String ATTR_PATH = "path";
+ private static final String ATTR_DOMAIN = "domain";
+
+ /**
+ * Constructs a new {@link FullBackupContentDetector}
+ */
+ public FullBackupContentDetector() {
+ }
+
+ @Override
+ public boolean appliesTo(@NonNull ResourceFolderType folderType) {
+ return folderType == ResourceFolderType.XML;
+ }
+
+ @Override
+ public void visitDocument(@NonNull XmlContext context, @NonNull Document document) {
+ Element root = document.getDocumentElement();
+ if (root == null) {
+ return;
+ }
+ if (!TAG_FULL_BACKUP_CONTENT.equals(root.getTagName())) {
+ return;
+ }
+
+ List<Element> includes = Lists.newArrayList();
+ List<Element> excludes = Lists.newArrayList();
+ NodeList children = root.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node child = children.item(i);
+ if (child.getNodeType() == Node.ELEMENT_NODE) {
+ Element element = (Element) child;
+ String tag = element.getTagName();
+ if (TAG_INCLUDE.equals(tag)) {
+ includes.add(element);
+ } else if (TAG_EXCLUDE.equals(tag)) {
+ excludes.add(element);
+ } else {
+ // See FullBackup#validateInnerTagContents
+ context.report(ISSUE, element, context.getNameLocation(element),
+ String.format("Unexpected element `<%1$s>`", tag));
+ }
+ }
+ }
+
+ Multimap<String, String> includePaths = ArrayListMultimap.create(includes.size(), 4);
+ for (Element include : includes) {
+ String domain = validateDomain(context, include);
+ String path = validatePath(context, include);
+ if (domain == null) {
+ continue;
+ }
+ includePaths.put(domain, path);
+ }
+
+ for (Element exclude : excludes) {
+ String excludePath = validatePath(context, exclude);
+ if (excludePath.isEmpty()) {
+ continue;
+ }
+ String domain = validateDomain(context, exclude);
+ if (domain == null) {
+ continue;
+ }
+ if (includePaths.isEmpty()) {
+ // There is no <include> anywhere: that means that everything
+ // is considered included and there's no potential prefix mismatch
+ continue;
+ }
+
+ boolean hasPrefix = false;
+ Collection<String> included = includePaths.get(domain);
+ if (included == null) {
+ continue;
+ }
+ for (String includePath : included) {
+ if (excludePath.startsWith(includePath)) {
+ if (excludePath.equals(includePath)) {
+ Attr pathNode = exclude.getAttributeNode(ATTR_PATH);
+ assert pathNode != null;
+ Location location = context.getValueLocation(pathNode);
+ // Find corresponding include path so we can link to it in the
+ // chained location list
+ for (Element include : includes) {
+ Attr includePathNode = include.getAttributeNode(ATTR_PATH);
+ String includeDomain = include.getAttribute(ATTR_DOMAIN);
+ if (includePathNode != null
+ && excludePath.equals(includePathNode.getValue())
+ && domain.equals(includeDomain)) {
+ Location earlier = context.getLocation(includePathNode);
+ earlier.setMessage("Unnecessary/conflicting <include>");
+ location.setSecondary(earlier);
+ }
+ }
+ context.report(ISSUE, exclude, location,
+ String.format("Include `%1$s` is also excluded", excludePath));
+ }
+ hasPrefix = true;
+ break;
+ }
+ }
+ if (!hasPrefix) {
+ Attr pathNode = exclude.getAttributeNode(ATTR_PATH);
+ assert pathNode != null;
+ context.report(ISSUE, exclude, context.getValueLocation(pathNode),
+ String.format("`%1$s` is not in an included path", excludePath));
+ }
+ }
+ }
+
+ @NonNull
+ private static String validatePath(@NonNull XmlContext context, @NonNull Element element) {
+ Attr pathNode = element.getAttributeNode(ATTR_PATH);
+ if (pathNode == null) {
+ return "";
+ }
+ String value = pathNode.getValue();
+ if (value.contains("//")) {
+ context.report(ISSUE, element, context.getValueLocation(pathNode),
+ "Paths are not allowed to contain `//`");
+ } else if (value.contains("..")) {
+ context.report(ISSUE, element, context.getValueLocation(pathNode),
+ "Paths are not allowed to contain `..`");
+ } else if (value.contains("/")) {
+ String domain = element.getAttribute(ATTR_DOMAIN);
+ if (DOMAIN_SHARED_PREF.equals(domain) || DOMAIN_DATABASE.equals(domain)) {
+ context.report(ISSUE, element, context.getValueLocation(pathNode),
+ String.format("Subdirectories are not allowed for domain `%1$s`",
+ domain));
+ }
+ }
+ return value;
+ }
+
+ @Nullable
+ private static String validateDomain(@NonNull XmlContext context, @NonNull Element element) {
+ Attr domainNode = element.getAttributeNode(ATTR_DOMAIN);
+ if (domainNode == null) {
+ context.report(ISSUE, element, context.getLocation(element),
+ String.format("Missing domain attribute, expected one of %1$s",
+ Joiner.on(", ").join(VALID_DOMAINS)));
+ return null;
+ }
+ String domain = domainNode.getValue();
+ for (String availableDomain : VALID_DOMAINS) {
+ if (availableDomain.equals(domain)) {
+ return domain;
+ }
+ }
+ context.report(ISSUE, element, context.getValueLocation(domainNode),
+ String.format("Unexpected domain `%1$s`, expected one of %2$s", domain,
+ Joiner.on(", ").join(VALID_DOMAINS)));
+
+ return domain;
+ }
+
+ /** Valid domains; see FullBackup#getTokenForXmlDomain for authoritative list */
+ private static final String[] VALID_DOMAINS = new String[] {
+ DOMAIN_ROOT, DOMAIN_FILE, DOMAIN_DATABASE, DOMAIN_SHARED_PREF, DOMAIN_EXTERNAL
+ };
+}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GradleDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GradleDetector.java
index 42562e4..33e49e6 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GradleDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/GradleDetector.java
@@ -851,7 +851,8 @@
// Network check for really up to date libraries? Only done in batch mode
if (context.getScope().size() > 1 && context.isEnabled(REMOTE_VERSION)) {
- PreciseRevision latest = getLatestVersion(context, dependency, dependency.isPreview());
+ PreciseRevision latest = getLatestVersionFromRemoteRepo(context.getClient(), dependency,
+ dependency.isPreview());
if (latest != null && isOlderThan(dependency, latest.getMajor(), latest.getMinor(),
latest.getMicro())) {
version = latest;
@@ -879,12 +880,14 @@
}
/** TODO: Cache these results somewhere! */
- private static PreciseRevision getLatestVersion(@NonNull Context context,
+ @Nullable
+ public static PreciseRevision getLatestVersionFromRemoteRepo(@NonNull LintClient client,
@NonNull GradleCoordinate dependency, boolean allowPreview) {
- return getLatestVersion(context, dependency, true, allowPreview);
+ return getLatestVersionFromRemoteRepo(client, dependency, true, allowPreview);
}
- private static PreciseRevision getLatestVersion(@NonNull Context context,
+ @Nullable
+ private static PreciseRevision getLatestVersionFromRemoteRepo(@NonNull LintClient client,
@NonNull GradleCoordinate dependency, boolean firstRowOnly, boolean allowPreview) {
StringBuilder query = new StringBuilder();
String encoding = UTF_8.name();
@@ -902,7 +905,7 @@
}
query.append("&wt=json");
- String response = readUrlData(context, dependency, query.toString());
+ String response = readUrlData(client, dependency, query.toString());
if (response == null) {
return null;
}
@@ -963,7 +966,7 @@
if (!allowPreview && foundPreview && firstRowOnly) {
// Recurse: search more than the first row this time to see if we can find a
// non-preview version
- return getLatestVersion(context, dependency, false, false);
+ return getLatestVersionFromRemoteRepo(client, dependency, false, false);
}
return null;
@@ -972,21 +975,20 @@
/** Normally null; used for testing */
@Nullable
@VisibleForTesting
- static Map<String,String> ourMockData;
+ static Map<String,String> sMockData;
@Nullable
private static String readUrlData(
- @NonNull Context context,
+ @NonNull LintClient client,
@NonNull GradleCoordinate dependency,
@NonNull String query) {
// For unit testing: avoid network as well as unexpected new versions
- if (ourMockData != null) {
- String value = ourMockData.get(query);
+ if (sMockData != null) {
+ String value = sMockData.get(query);
assert value != null : query;
return value;
}
- LintClient client = context.getClient();
try {
URL url = new URL(query);
@@ -1046,6 +1048,8 @@
// See if the support library version is lower than the targetSdkVersion
if (mTargetSdkVersion > 0 && dependency.getMajorVersion() < mTargetSdkVersion &&
dependency.getMajorVersion() != GradleCoordinate.PLUS_REV_VALUE &&
+ // The multidex library doesn't follow normal supportlib numbering scheme
+ !dependency.getArtifactId().startsWith("multidex") &&
context.isEnabled(COMPATIBILITY)) {
String message = "This support library should not use a lower version ("
+ dependency.getMajorVersion() + ") than the `targetSdkVersion` ("
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java
index 5f7967d..f436283 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/HandlerDetector.java
@@ -21,15 +21,23 @@
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
-import com.android.tools.lint.detector.api.*;
-
-import lombok.ast.ClassDeclaration;
-import lombok.ast.Node;
+import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Implementation;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.JavaContext;
+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 com.android.tools.lint.detector.api.Speed;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.List;
+import lombok.ast.ClassDeclaration;
+import lombok.ast.Node;
+
/**
* Checks that Handler implementations are top level classes or static.
* See the corresponding check in the android.os.Handler source code.
@@ -97,7 +105,7 @@
Node locationNode = node instanceof ClassDeclaration
? ((ClassDeclaration) node).astName() : node;
Location location = context.getLocation(locationNode);
- context.report(ISSUE, location, String.format(
+ context.report(ISSUE, locationNode, location, String.format(
"This Handler class should be static or leaks might occur (%1$s)",
cls.getName()));
}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleFolderDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleFolderDetector.java
index 9581682..c1f9962 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleFolderDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/LocaleFolderDetector.java
@@ -386,6 +386,7 @@
}
if (candidates.size() > 1) {
+ Collections.sort(candidates); // ensure stable test output
Location location = null;
List<String> folderNames = Lists.newArrayList();
for (int i = candidates.size() - 1; i >= 0; i--) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestDetector.java
index 61680dd..66537f5 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/ManifestDetector.java
@@ -52,6 +52,8 @@
import com.android.builder.model.ProductFlavorContainer;
import com.android.builder.model.SourceProviderContainer;
import com.android.builder.model.Variant;
+import com.android.ide.common.res2.AbstractResourceRepository;
+import com.android.ide.common.resources.ResourceUrl;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
@@ -814,6 +816,33 @@
(mApplicationTagHandle == null || isMainManifest(context, context.file))) {
mApplicationTagHandle = context.createLocationHandle(element);
}
+ Attr fullBackupNode = element.getAttributeNodeNS(ANDROID_URI, "fullBackupContent");
+ if (fullBackupNode != null &&
+ fullBackupNode.getValue().startsWith(PREFIX_RESOURCE_REF) &&
+ context.getClient().supportsProjectResources()) {
+ AbstractResourceRepository resources = context.getClient()
+ .getProjectResources(context.getProject(), true);
+ ResourceUrl url = ResourceUrl.parse(fullBackupNode.getValue());
+ if (url != null && !url.framework
+ && resources != null
+ && !resources.hasResourceItem(url.type, url.name)) {
+ Location location = context.getValueLocation(fullBackupNode);
+ context.report(ALLOW_BACKUP, fullBackupNode, location,
+ "Missing `<full-backup-content>` resource");
+ }
+ } else if (fullBackupNode == null && context.getMainProject().getTargetSdk() >= 23) {
+ Location location = context.getLocation(element);
+ context.report(ALLOW_BACKUP, element, location,
+ "Should explicitly set `android:fullBackupContent` to `true` or `false` "
+ + "to opt-in to or out of full app data back-up and restore, or "
+ + "alternatively to an `@xml` resource which specifies which "
+ + "files to backup");
+ } else if (fullBackupNode == null && hasGcmReceiver(element)) {
+ Location location = context.getLocation(element);
+ context.report(ALLOW_BACKUP, element, location,
+ "Should explicitly set `android:fullBackupContent` to avoid backing up "
+ + "the GCM device specific regId.");
+ }
} else if (mSeenApplication) {
if (context.isEnabled(ORDER)) {
context.report(ORDER, element, context.getLocation(element),
@@ -842,6 +871,41 @@
}
}
+ /**
+ * Returns true if the given application element has a receiver with an intent filter
+ * action for GCM receive
+ */
+ private static boolean hasGcmReceiver(@NonNull Element application) {
+ NodeList applicationChildren = application.getChildNodes();
+ for (int i1 = 0, n1 = applicationChildren.getLength(); i1 < n1; i1++) {
+ Node applicationChild = applicationChildren.item(i1);
+ if (applicationChild.getNodeType() == Node.ELEMENT_NODE
+ && TAG_RECEIVER.equals(applicationChild.getNodeName())) {
+ NodeList receiverChildren = applicationChild.getChildNodes();
+ for (int i2 = 0, n2 = receiverChildren.getLength(); i2 < n2; i2++) {
+ Node receiverChild = receiverChildren.item(i2);
+ if (receiverChild.getNodeType() == Node.ELEMENT_NODE
+ && TAG_INTENT_FILTER.equals(receiverChild.getNodeName())) {
+ NodeList filterChildren = receiverChild.getChildNodes();
+ for (int i3 = 0, n3 = filterChildren.getLength(); i3 < n3; i3++) {
+ Node filterChild = filterChildren.item(i3);
+ if (filterChild.getNodeType() == Node.ELEMENT_NODE
+ && NODE_ACTION.equals(filterChild.getNodeName())) {
+ Element action = (Element) filterChild;
+ String name = action.getAttributeNS(ANDROID_URI, ATTR_NAME);
+ if ("com.google.android.c2dm.intent.RECEIVE".equals(name)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
private static void checkMipmapIcon(@NonNull XmlContext context, @NonNull Element element) {
Attr attribute = element.getAttributeNodeNS(ANDROID_URI, ATTR_ICON);
if (attribute == null) {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java
index 78690fa..50bfc21 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NamespaceDetector.java
@@ -17,10 +17,14 @@
package com.android.tools.lint.checks;
import static com.android.SdkConstants.ANDROID_URI;
+import static com.android.SdkConstants.APP_PREFIX;
import static com.android.SdkConstants.AUTO_URI;
+import static com.android.SdkConstants.TOOLS_PREFIX;
+import static com.android.SdkConstants.TOOLS_URI;
import static com.android.SdkConstants.URI_PREFIX;
import static com.android.SdkConstants.XMLNS_PREFIX;
+import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Implementation;
@@ -136,7 +140,8 @@
NamedNodeMap attributes = root.getAttributes();
for (int i = 0, n = attributes.getLength(); i < n; i++) {
Node item = attributes.item(i);
- if (item.getNodeName().startsWith(XMLNS_PREFIX)) {
+ String prefix = item.getNodeName();
+ if (prefix.startsWith(XMLNS_PREFIX)) {
String value = item.getNodeValue();
if (!value.equals(ANDROID_URI)) {
@@ -147,7 +152,7 @@
if (mUnusedNamespaces == null) {
mUnusedNamespaces = new HashMap<String, Attr>();
}
- mUnusedNamespaces.put(item.getNodeName().substring(XMLNS_PREFIX.length()),
+ mUnusedNamespaces.put(prefix.substring(XMLNS_PREFIX.length()),
attribute);
} else if (value.startsWith("urn:")) { //$NON-NLS-1$
continue;
@@ -162,6 +167,11 @@
value.startsWith("http://schemas.android.com/")) { //$NON-NLS-1$
context.report(RES_AUTO, attribute, context.getValueLocation(attribute),
"Suspicious namespace: Did you mean `" + AUTO_URI + "`?");
+ } else if (value.equals(TOOLS_URI) && (prefix.equals(XMLNS_ANDROID) ||
+ prefix.endsWith(APP_PREFIX) && prefix.equals(
+ XMLNS_PREFIX + APP_PREFIX))) {
+ context.report(TYPO, attribute, context.getValueLocation(attribute),
+ "Suspicious namespace and prefix combination");
}
if (!context.isEnabled(TYPO)) {
@@ -209,6 +219,12 @@
"Unexpected namespace URI bound to the `\"android\"` " +
"prefix, was `%1$s`, expected `%2$s`", value, ANDROID_URI));
}
+ } else if (!prefix.equals(XMLNS_ANDROID) &&
+ ((prefix.endsWith(TOOLS_PREFIX) && prefix.equals(XMLNS_PREFIX + TOOLS_PREFIX)) ||
+ (prefix.endsWith(APP_PREFIX) && prefix.equals(XMLNS_PREFIX + APP_PREFIX)))) {
+ Attr attribute = (Attr) item;
+ context.report(TYPO, attribute, context.getValueLocation(attribute),
+ "Suspicious namespace and prefix combination");
}
}
}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java
index 60437aa..a664483 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/NestedScrollingWidgetDetector.java
@@ -40,7 +40,7 @@
import java.util.Collection;
/**
- * Checks whether a root FrameLayout can be replaced with a {@code <merge>} tag.
+ * Checks whether a scroll view contains a nested scrolling widget
*/
public class NestedScrollingWidgetDetector extends LayoutDetector {
private int mVisitingHorizontalScroll;
@@ -77,6 +77,7 @@
}
@Override
+ @NonNull
public Collection<String> getApplicableElements() {
return Arrays.asList(
SCROLL_VIEW,
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionFinder.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionFinder.java
new file mode 100644
index 0000000..6494e78
--- /dev/null
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionFinder.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.lint.checks;
+
+import static com.android.SdkConstants.CLASS_INTENT;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION_READ;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION_WRITE;
+import static com.android.tools.lint.detector.api.JavaContext.getParentOfType;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
+import com.android.tools.lint.client.api.JavaParser.ResolvedField;
+import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.detector.api.JavaContext;
+
+import java.util.ListIterator;
+
+import lombok.ast.BinaryExpression;
+import lombok.ast.BinaryOperator;
+import lombok.ast.Cast;
+import lombok.ast.ConstructorInvocation;
+import lombok.ast.Expression;
+import lombok.ast.ExpressionStatement;
+import lombok.ast.InlineIfExpression;
+import lombok.ast.Node;
+import lombok.ast.NullLiteral;
+import lombok.ast.Select;
+import lombok.ast.Statement;
+import lombok.ast.VariableDeclaration;
+import lombok.ast.VariableDefinition;
+import lombok.ast.VariableDefinitionEntry;
+import lombok.ast.VariableReference;
+
+/**
+ * Utility for locating permissions required by an intent or content resolver
+ */
+public class PermissionFinder {
+ /**
+ * Operation that has a permission requirement -- such as a method call,
+ * a content resolver read or write operation, an intent, etc.
+ */
+ public enum Operation {
+ CALL, ACTION, READ, WRITE;
+
+ /** Prefix to use when describing a name with a permission requirement */
+ public String prefix() {
+ switch (this) {
+ case ACTION:
+ return "by intent";
+ case READ:
+ return "to read";
+ case WRITE:
+ return "to write";
+ case CALL:
+ default:
+ return "by";
+ }
+ }
+ }
+
+ /** A permission requirement given a name and operation */
+ public static class Result {
+ @NonNull public final PermissionRequirement requirement;
+ @NonNull public final String name;
+ @NonNull public final Operation operation;
+
+ public Result(
+ @NonNull Operation operation,
+ @NonNull PermissionRequirement requirement,
+ @NonNull String name) {
+ this.operation = operation;
+ this.requirement = requirement;
+ this.name = name;
+ }
+ }
+
+ /**
+ * Searches for a permission requirement for the given parameter in the given call
+ *
+ * @param operation the operation to look up
+ * @param context the context to use for lookup
+ * @param parameter the parameter which contains the value which implies the permission
+ * @return the result with the permission requirement, or null if nothing is found
+ */
+ @Nullable
+ public static Result findRequiredPermissions(
+ @NonNull Operation operation,
+ @NonNull JavaContext context,
+ @NonNull Node parameter) {
+
+ // To find the permission required by an intent, we proceed in 3 steps:
+ // (1) Locate the parameter in the start call that corresponds to
+ // the Intent
+ //
+ // (2) Find the place where the intent is initialized, and figure
+ // out the action name being passed to it.
+ //
+ // (3) Find the place where the action is defined, and look for permission
+ // annotations on that action declaration!
+
+ return new PermissionFinder(context, operation).search(parameter);
+ }
+
+ private PermissionFinder(@NonNull JavaContext context, @NonNull Operation operation) {
+ mContext = context;
+ mOperation = operation;
+ }
+
+ @NonNull private final JavaContext mContext;
+ @NonNull private final Operation mOperation;
+
+ @Nullable
+ public Result search(@NonNull Node node) {
+ if (node instanceof NullLiteral) {
+ return null;
+ } else if (node instanceof InlineIfExpression) {
+ InlineIfExpression expression = (InlineIfExpression) node;
+ if (expression.astIfTrue() != null) {
+ Result result = search(expression.astIfTrue());
+ if (result != null) {
+ return result;
+ }
+ }
+ if (expression.astIfFalse() != null) {
+ Result result = search(expression.astIfFalse());
+ if (result != null) {
+ return result;
+ }
+ }
+ } else if (node instanceof Cast) {
+ Cast cast = (Cast) node;
+ return search(cast.astOperand());
+ } else if (node instanceof ConstructorInvocation && mOperation == Operation.ACTION) {
+ // Identifies "new Intent(argument)" calls and, if found, continues
+ // resolving the argument instead looking for the action definition
+ ConstructorInvocation call = (ConstructorInvocation) node;
+ String type = call.astTypeReference().getTypeName();
+ if (type.equals("Intent") || type.equals(CLASS_INTENT)) {
+ Expression action = call.astArguments().first();
+ if (action != null) {
+ return search(action);
+ }
+ }
+ return null;
+ } else if ((node instanceof VariableReference || node instanceof Select)) {
+ ResolvedNode resolved = mContext.resolve(node);
+ if (resolved instanceof ResolvedField) {
+ ResolvedField field = (ResolvedField) resolved;
+ if (mOperation == Operation.ACTION) {
+ ResolvedAnnotation annotation = field.getAnnotation(PERMISSION_ANNOTATION);
+ if (annotation != null) {
+ return getPermissionRequirement(field, annotation);
+ }
+ } else if (mOperation == Operation.READ || mOperation == Operation.WRITE) {
+ String fqn = mOperation == Operation.READ
+ ? PERMISSION_ANNOTATION_READ : PERMISSION_ANNOTATION_WRITE;
+ ResolvedAnnotation annotation = field.getAnnotation(fqn);
+ if (annotation != null) {
+ Object o = annotation.getValue();
+ if (o instanceof ResolvedAnnotation) {
+ annotation = (ResolvedAnnotation) o;
+ if (annotation.matches(PERMISSION_ANNOTATION)) {
+ return getPermissionRequirement(field, annotation);
+ }
+ } else {
+ // The complex annotations used for read/write cannot be
+ // expressed in the external annotations format, so they're inlined.
+ // (See Extractor.AnnotationData#write).
+ //
+ // Instead we've inlined the fields of the annotation on the
+ // outer one:
+ return getPermissionRequirement(field, annotation);
+ }
+ }
+ } else {
+ assert false : mOperation;
+ }
+ } else if (node instanceof VariableReference) {
+ Statement statement = getParentOfType(node, Statement.class, false);
+ if (statement != null) {
+ ListIterator<Node> iterator =
+ statement.getParent().getChildren().listIterator();
+ while (iterator.hasNext()) {
+ if (iterator.next() == statement) {
+ if (iterator.hasPrevious()) { // should always be true
+ iterator.previous();
+ }
+ break;
+ }
+ }
+
+ String targetName = ((VariableReference)node).astIdentifier().astValue();
+ while (iterator.hasPrevious()) {
+ Node previous = iterator.previous();
+ if (previous instanceof VariableDeclaration) {
+ VariableDeclaration declaration = (VariableDeclaration) previous;
+ VariableDefinition definition = declaration.astDefinition();
+ for (VariableDefinitionEntry entry : definition
+ .astVariables()) {
+ if (entry.astInitializer() != null
+ && entry.astName().astValue().equals(targetName)) {
+ return search(entry.astInitializer());
+ }
+ }
+ } else if (previous instanceof ExpressionStatement) {
+ ExpressionStatement expressionStatement =
+ (ExpressionStatement) previous;
+ Expression expression = expressionStatement.astExpression();
+ if (expression instanceof BinaryExpression &&
+ ((BinaryExpression) expression).astOperator()
+ == BinaryOperator.ASSIGN) {
+ BinaryExpression binaryExpression = (BinaryExpression) expression;
+ if (targetName.equals(binaryExpression.astLeft().toString())) {
+ return search(binaryExpression.astRight());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @NonNull
+ private Result getPermissionRequirement(
+ @NonNull ResolvedField field,
+ @NonNull ResolvedAnnotation annotation) {
+ PermissionRequirement requirement = PermissionRequirement.create(mContext, annotation);
+ String name = field.getContainingClass().getSimpleName() + "." + field.getName();
+ return new Result(mOperation, requirement, name);
+ }
+}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionHolder.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionHolder.java
new file mode 100644
index 0000000..632748c
--- /dev/null
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionHolder.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
+import com.android.sdklib.AndroidVersion;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * A {@linkplain PermissionHolder} knows which permissions are held/granted and can look up
+ * individual permissions and respond to queries about whether they are held or not.
+ */
+public interface PermissionHolder {
+
+ /** Returns true if the permission holder has been granted the given permission */
+ boolean hasPermission(@NonNull String permission);
+
+ /** Returns true if the given permission is known to be revocable for targetSdkVersion >= M */
+ boolean isRevocable(@NonNull String permission);
+
+ @NonNull
+ AndroidVersion getMinSdkVersion();
+
+ @NonNull
+ AndroidVersion getTargetSdkVersion();
+
+ /**
+ * A convenience implementation of {@link PermissionHolder} backed by a set
+ */
+ class SetPermissionLookup implements PermissionHolder {
+ private Set<String> mGrantedPermissions;
+ private Set<String> mRevocablePermissions;
+ private AndroidVersion mMinSdkVersion;
+ private AndroidVersion mTargetSdkVersion;
+
+ public SetPermissionLookup(
+ @NonNull Set<String> grantedPermissions,
+ @NonNull Set<String> revocablePermissions,
+ @NonNull AndroidVersion minSdkVersion,
+ @NonNull AndroidVersion targetSdkVersion) {
+ mGrantedPermissions = grantedPermissions;
+ mRevocablePermissions = revocablePermissions;
+ mMinSdkVersion = minSdkVersion;
+ mTargetSdkVersion = targetSdkVersion;
+ }
+
+ @VisibleForTesting
+ public SetPermissionLookup(@NonNull Set<String> grantedPermissions,
+ @NonNull Set<String> revocablePermissions) {
+ this(grantedPermissions, revocablePermissions, AndroidVersion.DEFAULT,
+ AndroidVersion.DEFAULT);
+ }
+
+ @VisibleForTesting
+ public SetPermissionLookup(@NonNull Set<String> grantedPermissions) {
+ this(grantedPermissions, Collections.<String>emptySet());
+ }
+
+ @Override
+ public boolean hasPermission(@NonNull String permission) {
+ return mGrantedPermissions.contains(permission);
+ }
+
+ @Override
+ public boolean isRevocable(@NonNull String permission) {
+ return mRevocablePermissions.contains(permission);
+ }
+
+ @NonNull
+ @Override
+ public AndroidVersion getMinSdkVersion() {
+ return mMinSdkVersion;
+ }
+
+ @NonNull
+ @Override
+ public AndroidVersion getTargetSdkVersion() {
+ return mTargetSdkVersion;
+ }
+
+ /**
+ * Creates a {@linkplain PermissionHolder} which combines the permissions
+ * held by the given holder, with the permissions implied by the given
+ * {@link PermissionRequirement}
+ */
+ @NonNull
+ public static PermissionHolder join(@NonNull PermissionHolder lookup,
+ @NonNull PermissionRequirement requirement) {
+ SetPermissionLookup empty = new SetPermissionLookup(Collections.<String>emptySet(),
+ Collections.<String>emptySet(), lookup.getMinSdkVersion(),
+ lookup.getTargetSdkVersion());
+ return join(lookup, requirement.getMissingPermissions(empty));
+ }
+
+ /**
+ * Creates a {@linkplain PermissionHolder} which combines the permissions
+ * held by the given holder, along with a set of additional permission names
+ */
+ @NonNull
+ public static PermissionHolder join(@NonNull final PermissionHolder lookup,
+ @Nullable final Set<String> permissions) {
+ if (permissions != null && !permissions.isEmpty()) {
+ return new PermissionHolder() {
+ @Override
+ public boolean hasPermission(@NonNull String permission) {
+ return lookup.hasPermission(permission)
+ || permissions.contains(permission);
+ }
+
+ @Override
+ public boolean isRevocable(@NonNull String permission) {
+ return lookup.isRevocable(permission);
+ }
+
+ @NonNull
+ @Override
+ public AndroidVersion getMinSdkVersion() {
+ return lookup.getMinSdkVersion();
+ }
+
+ @NonNull
+ @Override
+ public AndroidVersion getTargetSdkVersion() {
+ return lookup.getTargetSdkVersion();
+ }
+ };
+ }
+ return lookup;
+ }
+ }
+}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionRequirement.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionRequirement.java
new file mode 100644
index 0000000..8923fcb
--- /dev/null
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PermissionRequirement.java
@@ -0,0 +1,763 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.lint.checks;
+
+
+import static com.android.SdkConstants.ATTR_VALUE;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_ALL_OF;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_ANY_OF;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.ATTR_CONDITIONAL;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
+import com.android.sdklib.AndroidVersion;
+import com.android.tools.lint.client.api.JavaParser;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
+import com.android.tools.lint.detector.api.Context;
+import com.android.tools.lint.detector.api.JavaContext;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import lombok.ast.BinaryExpression;
+import lombok.ast.BinaryOperator;
+import lombok.ast.Expression;
+import lombok.ast.ForwardingAstVisitor;
+import lombok.ast.Node;
+import lombok.ast.Select;
+import lombok.ast.VariableDefinitionEntry;
+
+/**
+ * A permission requirement is a boolean expression of permission names that a
+ * caller must satisfy for a given Android API.
+ */
+public abstract class PermissionRequirement {
+ public static final String ATTR_PROTECTION_LEVEL = "protectionLevel"; //$NON-NLS-1$
+ public static final String VALUE_DANGEROUS = "dangerous"; //$NON-NLS-1$
+
+ protected final ResolvedAnnotation annotation;
+ private int firstApi;
+ private int lastApi;
+
+ @SuppressWarnings("ConstantConditions")
+ public static final PermissionRequirement NONE = new PermissionRequirement(null) {
+ @Override
+ public boolean isSatisfied(@NonNull PermissionHolder available) {
+ return true;
+ }
+
+ @Override
+ public boolean appliesTo(@NonNull PermissionHolder available) {
+ return false;
+ }
+
+ @Override
+ public boolean isConditional() {
+ return false;
+ }
+
+ @Override
+ public boolean isRevocable(@NonNull PermissionHolder revocable) {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "None";
+ }
+
+ @Override
+ protected void addMissingPermissions(@NonNull PermissionHolder available,
+ @NonNull Set<String> result) {
+ }
+
+ @Override
+ protected void addRevocablePermissions(@NonNull Set<String> result,
+ @NonNull PermissionHolder revocable) {
+ }
+
+ @Nullable
+ @Override
+ public BinaryOperator getOperator() {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public Iterable<PermissionRequirement> getChildren() {
+ return Collections.emptyList();
+ }
+ };
+
+ private PermissionRequirement(@NonNull ResolvedAnnotation annotation) {
+ this.annotation = annotation;
+ }
+
+ @NonNull
+ public static PermissionRequirement create(
+ @Nullable Context context,
+ @NonNull ResolvedAnnotation annotation) {
+ String value = (String)annotation.getValue(ATTR_VALUE);
+ if (value != null && !value.isEmpty()) {
+ for (int i = 0, n = value.length(); i < n; i++) {
+ char c = value.charAt(i);
+ // See if it's a complex expression and if so build it up
+ if (c == '&' || c == '|' || c == '^') {
+ return Complex.parse(annotation, context, value);
+ }
+ }
+
+ return new Single(annotation, value);
+ }
+
+ Object v = annotation.getValue(ATTR_ANY_OF);
+ if (v != null) {
+ if (v instanceof String[]) {
+ String[] anyOf = (String[])v;
+ if (anyOf.length > 0) {
+ return new Many(annotation, BinaryOperator.LOGICAL_OR, anyOf);
+ }
+ } else if (v instanceof String) {
+ String[] anyOf = new String[] { (String)v };
+ return new Many(annotation, BinaryOperator.LOGICAL_OR, anyOf);
+ }
+ }
+
+ v = annotation.getValue(ATTR_ALL_OF);
+ if (v != null) {
+ if (v instanceof String[]) {
+ String[] allOf = (String[])v;
+ if (allOf.length > 0) {
+ return new Many(annotation, BinaryOperator.LOGICAL_AND, allOf);
+ }
+ } else if (v instanceof String) {
+ String[] allOf = new String[] { (String)v };
+ return new Many(annotation, BinaryOperator.LOGICAL_AND, allOf);
+ }
+ }
+
+ return NONE;
+ }
+
+ /**
+ * Returns false if this permission does not apply given the specified minimum and
+ * target sdk versions
+ *
+ * @param minSdkVersion the minimum SDK version
+ * @param targetSdkVersion the target SDK version
+ * @return true if this permission requirement applies for the given versions
+ */
+ /**
+ * Returns false if this permission does not apply given the specified minimum and target
+ * sdk versions
+ *
+ * @param available the permission holder which also knows the min and target versions
+ * @return true if this permission requirement applies for the given versions
+ */
+ protected boolean appliesTo(@NonNull PermissionHolder available) {
+ if (firstApi == 0) { // initialized?
+ firstApi = -1; // initialized, not specified
+
+ // Not initialized
+ Object o = annotation.getValue("apis");
+ if (o instanceof String) {
+ String range = (String)o;
+ // Currently only support the syntax "a..b" where a and b are inclusive end points
+ // and where "a" and "b" are optional
+ int index = range.indexOf("..");
+ if (index != -1) {
+ try {
+ if (index > 0) {
+ firstApi = Integer.parseInt(range.substring(0, index));
+ } else {
+ firstApi = 1;
+ }
+ if (index + 2 < range.length()) {
+ lastApi = Integer.parseInt(range.substring(index + 2));
+ } else {
+ lastApi = Integer.MAX_VALUE;
+ }
+ } catch (NumberFormatException ignore) {
+ }
+ }
+ }
+ }
+
+ if (firstApi != -1) {
+ AndroidVersion minSdkVersion = available.getMinSdkVersion();
+ if (minSdkVersion.getFeatureLevel() > lastApi) {
+ return false;
+ }
+
+ AndroidVersion targetSdkVersion = available.getTargetSdkVersion();
+ if (targetSdkVersion.getFeatureLevel() < firstApi) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns whether this requirement is conditional, meaning that there are
+ * some circumstances in which the requirement is not necessary. For
+ * example, consider
+ * {@code android.app.backup.BackupManager.dataChanged(java.lang.String)} .
+ * Here the {@code android.permission.BACKUP} is required but only if the
+ * argument is not your own package.
+ * <p>
+ * This is used to handle permissions differently between the "missing" and
+ * "unused" checks. When checking for missing permissions, we err on the
+ * side of caution: if you are missing a permission, but the permission is
+ * conditional, you may not need it so we may not want to complain. However,
+ * when looking for unused permissions, we don't want to flag the
+ * conditional permissions as unused since they may be required.
+ *
+ * @return true if this requirement is conditional
+ */
+ public boolean isConditional() {
+ Object o = annotation.getValue(ATTR_CONDITIONAL);
+ if (o instanceof Boolean) {
+ return (Boolean)o;
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether this requirement is for a single permission (rather than
+ * a boolean expression such as one permission or another.)
+ *
+ * @return true if this requirement is just a simple permission name
+ */
+ public boolean isSingle() {
+ return true;
+ }
+
+ /**
+ * Whether the permission requirement is satisfied given the set of granted permissions
+ *
+ * @param available the available permissions
+ * @return true if all permissions specified by this requirement are available
+ */
+ public abstract boolean isSatisfied(@NonNull PermissionHolder available);
+
+ /** Describes the missing permissions (e.g. "P1, P2 and P3") */
+ public String describeMissingPermissions(@NonNull PermissionHolder available) {
+ return "";
+ }
+
+ /** Returns the missing permissions (e.g. {"P1", "P2", "P3"} */
+ public Set<String> getMissingPermissions(@NonNull PermissionHolder available) {
+ Set<String> result = Sets.newHashSet();
+ addMissingPermissions(available, result);
+ return result;
+ }
+
+ protected abstract void addMissingPermissions(@NonNull PermissionHolder available,
+ @NonNull Set<String> result);
+
+ /** Returns the permissions in the requirement that are revocable */
+ public Set<String> getRevocablePermissions(@NonNull PermissionHolder revocable) {
+ Set<String> result = Sets.newHashSet();
+ addRevocablePermissions(result, revocable);
+ return result;
+ }
+
+ protected abstract void addRevocablePermissions(@NonNull Set<String> result,
+ @NonNull PermissionHolder revocable);
+
+ /**
+ * Returns whether this permission is revocable
+ *
+ * @param revocable the set of revocable permissions
+ * @return true if a user can revoke the permission
+ */
+ public abstract boolean isRevocable(@NonNull PermissionHolder revocable);
+
+ /**
+ * For permission requirements that combine children, the operator to combine them with; null
+ * for leaf nodes
+ */
+ @Nullable
+ public abstract BinaryOperator getOperator();
+
+ /**
+ * Returns nested requirements, combined via {@link #getOperator()}
+ */
+ @NonNull
+ public abstract Iterable<PermissionRequirement> getChildren();
+
+ /** Require a single permission */
+ private static class Single extends PermissionRequirement {
+ public final String name;
+
+ public Single(@NonNull ResolvedAnnotation annotation, @NonNull String name) {
+ super(annotation);
+ this.name = name;
+ }
+
+ @Override
+ public boolean isRevocable(@NonNull PermissionHolder revocable) {
+ return revocable.isRevocable(name) || isRevocableSystemPermission(name);
+ }
+
+ @Nullable
+ @Override
+ public BinaryOperator getOperator() {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public Iterable<PermissionRequirement> getChildren() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean isSingle() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ @Override
+ public boolean isSatisfied(@NonNull PermissionHolder available) {
+ return available.hasPermission(name) || !appliesTo(available);
+ }
+
+ @Override
+ public String describeMissingPermissions(@NonNull PermissionHolder available) {
+ return isSatisfied(available) ? "" : name;
+ }
+
+ @Override
+ protected void addMissingPermissions(@NonNull PermissionHolder available,
+ @NonNull Set<String> missing) {
+ if (!isSatisfied(available)) {
+ missing.add(name);
+ }
+ }
+
+ @Override
+ protected void addRevocablePermissions(@NonNull Set<String> result,
+ @NonNull PermissionHolder revocable) {
+ if (isRevocable(revocable)) {
+ result.add(name);
+ }
+ }
+ }
+
+ protected static void appendOperator(StringBuilder sb, BinaryOperator operator) {
+ sb.append(' ');
+ if (operator == BinaryOperator.LOGICAL_AND) {
+ sb.append("and");
+ } else if (operator == BinaryOperator.LOGICAL_OR) {
+ sb.append("or");
+ } else {
+ assert operator == BinaryOperator.BITWISE_XOR : operator;
+ sb.append("xor");
+ }
+ sb.append(' ');
+ }
+
+ /**
+ * Require a series of permissions, all with the same operator.
+ */
+ private static class Many extends PermissionRequirement {
+ public final BinaryOperator operator;
+ public final List<PermissionRequirement> permissions;
+
+ public Many(
+ @NonNull ResolvedAnnotation annotation,
+ BinaryOperator operator,
+ String[] names) {
+ super(annotation);
+ assert operator == BinaryOperator.LOGICAL_OR
+ || operator == BinaryOperator.LOGICAL_AND : operator;
+ assert names.length >= 2;
+ this.operator = operator;
+ this.permissions = Lists.newArrayListWithExpectedSize(names.length);
+ for (String name : names) {
+ permissions.add(new Single(annotation, name));
+ }
+ }
+
+ @Override
+ public boolean isSingle() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(permissions.get(0));
+
+ for (int i = 1; i < permissions.size(); i++) {
+ appendOperator(sb, operator);
+ sb.append(permissions.get(i));
+ }
+
+ return sb.toString();
+ }
+
+ @Override
+ public boolean isSatisfied(@NonNull PermissionHolder available) {
+ if (operator == BinaryOperator.LOGICAL_AND) {
+ for (PermissionRequirement requirement : permissions) {
+ if (!requirement.isSatisfied(available) && requirement.appliesTo(available)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ assert operator == BinaryOperator.LOGICAL_OR : operator;
+ for (PermissionRequirement requirement : permissions) {
+ if (requirement.isSatisfied(available) || !requirement.appliesTo(available)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public String describeMissingPermissions(@NonNull PermissionHolder available) {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (PermissionRequirement requirement : permissions) {
+ if (!requirement.isSatisfied(available)) {
+ if (first) {
+ first = false;
+ } else {
+ appendOperator(sb, operator);
+ }
+ sb.append(requirement.describeMissingPermissions(available));
+ }
+ }
+ return sb.toString();
+ }
+
+ @Override
+ protected void addMissingPermissions(@NonNull PermissionHolder available,
+ @NonNull Set<String> missing) {
+ for (PermissionRequirement requirement : permissions) {
+ if (!requirement.isSatisfied(available)) {
+ requirement.addMissingPermissions(available, missing);
+ }
+ }
+ }
+
+ @Override
+ protected void addRevocablePermissions(@NonNull Set<String> result,
+ @NonNull PermissionHolder revocable) {
+ for (PermissionRequirement requirement : permissions) {
+ requirement.addRevocablePermissions(result, revocable);
+ }
+ }
+
+ @Override
+ public boolean isRevocable(@NonNull PermissionHolder revocable) {
+ // TODO: Pass in the available set of permissions here, and if
+ // the operator is BinaryOperator.LOGICAL_OR, only return revocable=true
+ // if an unsatisfied permission is also revocable. In other words,
+ // if multiple permissions are allowed, and some of them are satisfied and
+ // not revocable the overall permission requirement is not revocable.
+ for (PermissionRequirement requirement : permissions) {
+ if (requirement.isRevocable(revocable)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public BinaryOperator getOperator() {
+ return operator;
+ }
+
+ @NonNull
+ @Override
+ public Iterable<PermissionRequirement> getChildren() {
+ return permissions;
+ }
+ }
+
+ /**
+ * Require multiple permissions. This is a group of permissions with some
+ * associated boolean logic, such as "B or (C and (D or E))".
+ */
+ private static class Complex extends PermissionRequirement {
+ public final BinaryOperator operator;
+ public final PermissionRequirement left;
+ public final PermissionRequirement right;
+
+ public Complex(
+ @NonNull ResolvedAnnotation annotation,
+ BinaryOperator operator,
+ PermissionRequirement left,
+ PermissionRequirement right) {
+ super(annotation);
+ this.operator = operator;
+ this.left = left;
+ this.right = right;
+ }
+
+ @Override
+ public boolean isSingle() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ boolean needsParentheses = left instanceof Complex &&
+ ((Complex) left).operator != BinaryOperator.LOGICAL_AND;
+ if (needsParentheses) {
+ sb.append('(');
+ }
+ sb.append(left.toString());
+ if (needsParentheses) {
+ sb.append(')');
+ }
+
+ appendOperator(sb, operator);
+
+ needsParentheses = right instanceof Complex &&
+ ((Complex) right).operator != BinaryOperator.LOGICAL_AND;
+ if (needsParentheses) {
+ sb.append('(');
+ }
+ sb.append(right.toString());
+ if (needsParentheses) {
+ sb.append(')');
+ }
+
+ return sb.toString();
+ }
+
+ @Override
+ public boolean isSatisfied(@NonNull PermissionHolder available) {
+ boolean satisfiedLeft = left.isSatisfied(available) || !left.appliesTo(available);
+ boolean satisfiedRight = right.isSatisfied(available) || !right.appliesTo(available);
+ if (operator == BinaryOperator.LOGICAL_AND) {
+ return satisfiedLeft && satisfiedRight;
+ } else if (operator == BinaryOperator.LOGICAL_OR) {
+ return satisfiedLeft || satisfiedRight;
+ } else {
+ assert operator == BinaryOperator.BITWISE_XOR : operator;
+ return satisfiedLeft ^ satisfiedRight;
+ }
+ }
+
+ @Override
+ public String describeMissingPermissions(@NonNull PermissionHolder available) {
+ boolean satisfiedLeft = left.isSatisfied(available);
+ boolean satisfiedRight = right.isSatisfied(available);
+ if (operator == BinaryOperator.LOGICAL_AND || operator == BinaryOperator.LOGICAL_OR) {
+ if (satisfiedLeft) {
+ if (satisfiedRight) {
+ return "";
+ }
+ return right.describeMissingPermissions(available);
+ } else if (satisfiedRight) {
+ return left.describeMissingPermissions(available);
+ } else {
+ StringBuilder sb = new StringBuilder();
+ sb.append(left.describeMissingPermissions(available));
+ appendOperator(sb, operator);
+ sb.append(right.describeMissingPermissions(available));
+ return sb.toString();
+ }
+ } else {
+ assert operator == BinaryOperator.BITWISE_XOR : operator;
+ return toString();
+ }
+ }
+
+ @Override
+ protected void addMissingPermissions(@NonNull PermissionHolder available,
+ @NonNull Set<String> missing) {
+ boolean satisfiedLeft = left.isSatisfied(available);
+ boolean satisfiedRight = right.isSatisfied(available);
+ if (operator == BinaryOperator.LOGICAL_AND || operator == BinaryOperator.LOGICAL_OR) {
+ if (satisfiedLeft) {
+ if (satisfiedRight) {
+ return;
+ }
+ right.addMissingPermissions(available, missing);
+ } else if (satisfiedRight) {
+ left.addMissingPermissions(available, missing);
+ } else {
+ left.addMissingPermissions(available, missing);
+ right.addMissingPermissions(available, missing);
+ }
+ } else {
+ assert operator == BinaryOperator.BITWISE_XOR : operator;
+ left.addMissingPermissions(available, missing);
+ right.addMissingPermissions(available, missing);
+ }
+ }
+
+ @Override
+ protected void addRevocablePermissions(@NonNull Set<String> result,
+ @NonNull PermissionHolder revocable) {
+ left.addRevocablePermissions(result, revocable);
+ right.addRevocablePermissions(result, revocable);
+ }
+
+ @Override
+ public boolean isRevocable(@NonNull PermissionHolder revocable) {
+ // TODO: If operator == BinaryOperator.LOGICAL_OR only return
+ // revocable the there isn't a non-revocable term which is also satisfied.
+ return left.isRevocable(revocable) || right.isRevocable(revocable);
+ }
+
+ @NonNull
+ public static PermissionRequirement parse(@NonNull ResolvedAnnotation annotation,
+ @Nullable Context context, @NonNull final String value) {
+ // Parse an expression of the form (A op1 B op2 C) op3 (D op4 E) etc.
+ // We'll just use the Java parser to handle this to ensure that operator
+ // precedence etc is correct.
+ if (context == null) {
+ return NONE;
+ }
+ JavaParser javaParser = context.getClient().getJavaParser(null);
+ if (javaParser == null) {
+ return NONE;
+ }
+ try {
+ JavaContext javaContext = new JavaContext(context.getDriver(),
+ context.getProject(), context.getMainProject(), context.file,
+ javaParser) {
+ @Nullable
+ @Override
+ public String getContents() {
+ return ""
+ + "class Test { void test() {\n"
+ + "boolean result=" + value
+ + ";\n}\n}";
+ }
+ };
+ Node node = javaParser.parseJava(javaContext);
+ if (node != null) {
+ final AtomicReference<Expression> reference = new AtomicReference<Expression>();
+ node.accept(new ForwardingAstVisitor() {
+ @Override
+ public boolean visitVariableDefinitionEntry(VariableDefinitionEntry node) {
+ reference.set(node.astInitializer());
+ return true;
+ }
+ });
+ Expression expression = reference.get();
+ if (expression != null) {
+ return parse(annotation, expression);
+ }
+ }
+
+ return NONE;
+ } finally {
+ javaParser.dispose();
+ }
+ }
+
+ private static PermissionRequirement parse(
+ @NonNull ResolvedAnnotation annotation,
+ @NonNull Expression expression) {
+ if (expression instanceof Select) {
+ return new Single(annotation, expression.toString());
+ } else if (expression instanceof BinaryExpression) {
+ BinaryExpression binaryExpression = (BinaryExpression) expression;
+ BinaryOperator operator = binaryExpression.astOperator();
+ if (operator == BinaryOperator.LOGICAL_AND
+ || operator == BinaryOperator.LOGICAL_OR
+ || operator == BinaryOperator.BITWISE_XOR) {
+ PermissionRequirement left = parse(annotation, binaryExpression.astLeft());
+ PermissionRequirement right = parse(annotation, binaryExpression.astRight());
+ return new Complex(annotation, operator, left, right);
+ }
+ }
+ return NONE;
+ }
+
+ @Nullable
+ @Override
+ public BinaryOperator getOperator() {
+ return operator;
+ }
+
+ @NonNull
+ @Override
+ public Iterable<PermissionRequirement> getChildren() {
+ return Arrays.asList(left, right);
+ }
+ }
+
+ /**
+ * Returns true if the given permission name is a revocable permission for
+ * targetSdkVersion >= 23
+ *
+ * @param name permission name
+ * @return true if this is a revocable permission
+ */
+ public static boolean isRevocableSystemPermission(@NonNull String name) {
+ return Arrays.binarySearch(REVOCABLE_PERMISSION_NAMES, name) >= 0;
+ }
+
+ @VisibleForTesting
+ static final String[] REVOCABLE_PERMISSION_NAMES = new String[] {
+ "android.permission.ACCESS_COARSE_LOCATION",
+ "android.permission.ACCESS_FINE_LOCATION",
+ "android.permission.BODY_SENSORS",
+ "android.permission.CALL_PHONE",
+ "android.permission.CAMERA",
+ "android.permission.PROCESS_OUTGOING_CALLS",
+ "android.permission.READ_CALENDAR",
+ "android.permission.READ_CALL_LOG",
+ "android.permission.READ_CELL_BROADCASTS",
+ "android.permission.READ_CONTACTS",
+ "android.permission.READ_EXTERNAL_STORAGE",
+ "android.permission.READ_PHONE_STATE",
+ "android.permission.READ_PROFILE",
+ "android.permission.READ_SMS",
+ "android.permission.READ_SOCIAL_STREAM",
+ "android.permission.RECEIVE_MMS",
+ "android.permission.RECEIVE_SMS",
+ "android.permission.RECEIVE_WAP_PUSH",
+ "android.permission.RECORD_AUDIO",
+ "android.permission.SEND_SMS",
+ "android.permission.USE_FINGERPRINT",
+ "android.permission.USE_SIP",
+ "android.permission.WRITE_CALENDAR",
+ "android.permission.WRITE_CALL_LOG",
+ "android.permission.WRITE_CONTACTS",
+ "android.permission.WRITE_EXTERNAL_STORAGE",
+ "android.permission.WRITE_SETTINGS",
+ "android.permission.WRITE_PROFILE",
+ "android.permission.WRITE_SOCIAL_STREAM",
+ "com.android.voicemail.permission.ADD_VOICEMAIL",
+ };
+}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PreferenceActivityDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PreferenceActivityDetector.java
index b3bb0e1..749902e 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PreferenceActivityDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PreferenceActivityDetector.java
@@ -93,7 +93,7 @@
if (fqcn.equals(PREFERENCE_ACTIVITY) &&
!context.getDriver().isSuppressed(context, ISSUE, element)) {
String message = "`PreferenceActivity` should not be exported";
- context.report(ISSUE, context.getLocation(element), message);
+ context.report(ISSUE, element, context.getLocation(element), message);
}
mExportedActivities.put(fqcn, context.createLocationHandle(element));
}
@@ -151,7 +151,8 @@
String message = String.format(
"`PreferenceActivity` subclass `%1$s` should not be exported",
className);
- context.report(ISSUE, mExportedActivities.get(className).resolve(), message);
+ Location location = mExportedActivities.get(className).resolve();
+ context.report(ISSUE, declarationOrAnonymous, location, message);
}
}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java
index 68efcdf..a3e0400 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PrivateResourceDetector.java
@@ -239,7 +239,7 @@
context.report(ISSUE, item, context.getLocation(child), message);
}
} else {
- for (int j = 0, m = text.charAt(j); j < m; j++) {
+ for (int j = 0, m = text.length(); j < m; j++) {
char c = text.charAt(j);
if (c == '@') {
ResourceUrl url = ResourceUrl.parse(text.trim());
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PropertyFileDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PropertyFileDetector.java
index 19610b3..744e5a3 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PropertyFileDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/PropertyFileDetector.java
@@ -30,15 +30,10 @@
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.TextFormat;
-import com.android.utils.SdkUtils;
import com.google.common.base.Splitter;
-import com.google.common.io.Files;
import java.io.File;
-import java.io.IOException;
-import java.io.StringWriter;
import java.util.Iterator;
-import java.util.Properties;
/**
* Check for errors in .property files
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java
index 786a4f6..de262a0 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java
@@ -203,7 +203,7 @@
String.format(
"`%1$s` is a `<%2$s>` but is registered in the manifest as a `<%3$s>`",
className, tag, classToTag(wrongClass)));
- } else if (!tag.equals(TAG_RECEIVER)) { // don't need to be registered
+ } else if (!TAG_RECEIVER.equals(tag)) { // don't need to be registered
if (context.getMainProject().isGradleProject()) {
// Disabled for now; we need to formalize the difference between
// the *manifest* package and the variant package, since in some contexts
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RelativeOverlapDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RelativeOverlapDetector.java
index b348380..1fae9e6 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RelativeOverlapDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RelativeOverlapDetector.java
@@ -389,7 +389,7 @@
continue;
}
Set<LayoutNode> canGrowRight = left.canGrowRight();
- if (canGrowLeft.size() > 0 || canGrowRight.size() > 0) {
+ if (!canGrowLeft.isEmpty() || !canGrowRight.isEmpty()) {
canGrowRight.addAll(canGrowLeft);
LayoutNode nodeToBlame = right;
LayoutNode otherNode = left;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java
index 47af129..a78ca03 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RequiredAttributeDetector.java
@@ -44,6 +44,7 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
@@ -297,12 +298,13 @@
return false;
}
- private static boolean hasLayoutVariations(File file) {
+ @VisibleForTesting
+ static boolean hasLayoutVariations(File file) {
File parent = file.getParentFile();
if (parent == null) {
return false;
}
- File res = file.getParentFile();
+ File res = parent.getParentFile();
if (res == null) {
return false;
}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java
index c05973f..9071724 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SecurityDetector.java
@@ -50,7 +50,6 @@
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.XmlContext;
-import com.android.xml.AndroidManifest;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SignatureOrSystemDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SignatureOrSystemDetector.java
index 7647c2b..62ffd15 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SignatureOrSystemDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SignatureOrSystemDetector.java
@@ -17,22 +17,19 @@
package com.android.tools.lint.checks;
-import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
+import static com.android.tools.lint.checks.PermissionRequirement.ATTR_PROTECTION_LEVEL;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.XmlContext;
import org.w3c.dom.Attr;
-import java.io.File;
import java.util.Collection;
import java.util.Collections;
@@ -57,24 +54,12 @@
Scope.MANIFEST_SCOPE
));
private static final String SIGNATURE_OR_SYSTEM = "signatureOrSystem"; //$NON-NLS-1$
- private static final String PROTECTION_LEVEL = "protectionLevel"; //$NON-NLS-1$
- @NonNull
- @Override
- public Speed getSpeed() {
- return Speed.FAST;
- }
-
- @Override
- public boolean appliesTo(@NonNull Context context, @NonNull File file) {
- return file.getName().equals(ANDROID_MANIFEST_XML);
- }
-
- // ---- Implements Detector.XmlScanner ---- TAG_PERMISSION
+ // ---- Implements Detector.XmlScanner ----
@Override
public Collection<String> getApplicableAttributes() {
- return Collections.singletonList(PROTECTION_LEVEL);
+ return Collections.singletonList(ATTR_PROTECTION_LEVEL);
}
@Override
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java
index 9f7ca78..6b902a5 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/StringFormatDetector.java
@@ -43,7 +43,6 @@
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.res2.AbstractResourceRepository;
import com.android.ide.common.res2.ResourceItem;
-import com.android.ide.common.resources.configuration.LocaleQualifier;
import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.android.tools.lint.client.api.JavaParser;
@@ -402,7 +401,7 @@
* Checks whether the text begins with a non-unit word, pointing to a string
* that should probably be a plural instead. This
*/
- private boolean checkPotentialPlural(XmlContext context, Element element, String text,
+ private static boolean checkPotentialPlural(XmlContext context, Element element, String text,
int wordBegin) {
// This method should only be called if the text is known to start with a word
assert Character.isLetter(text.charAt(wordBegin));
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SupportAnnotationDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SupportAnnotationDetector.java
index 567f7a5..d34759a 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SupportAnnotationDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/SupportAnnotationDetector.java
@@ -16,57 +16,92 @@
package com.android.tools.lint.checks;
+import static com.android.SdkConstants.ANDROID_URI;
+import static com.android.SdkConstants.ATTR_NAME;
import static com.android.SdkConstants.ATTR_VALUE;
+import static com.android.SdkConstants.CLASS_INTENT;
import static com.android.SdkConstants.INT_DEF_ANNOTATION;
import static com.android.SdkConstants.R_CLASS;
import static com.android.SdkConstants.STRING_DEF_ANNOTATION;
import static com.android.SdkConstants.SUPPORT_ANNOTATIONS_PREFIX;
+import static com.android.SdkConstants.TAG_PERMISSION;
+import static com.android.SdkConstants.TAG_USES_PERMISSION;
import static com.android.SdkConstants.TYPE_DEF_FLAG_ATTRIBUTE;
import static com.android.resources.ResourceType.COLOR;
import static com.android.resources.ResourceType.DRAWABLE;
import static com.android.resources.ResourceType.MIPMAP;
+import static com.android.tools.lint.checks.PermissionFinder.Operation.ACTION;
+import static com.android.tools.lint.checks.PermissionFinder.Operation.READ;
+import static com.android.tools.lint.checks.PermissionFinder.Operation.WRITE;
+import static com.android.tools.lint.checks.PermissionRequirement.ATTR_PROTECTION_LEVEL;
+import static com.android.tools.lint.checks.PermissionRequirement.VALUE_DANGEROUS;
+import static com.android.tools.lint.detector.api.JavaContext.findSurroundingMethod;
import static com.android.tools.lint.detector.api.JavaContext.getParentOfType;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.resources.ResourceType;
+import com.android.sdklib.AndroidVersion;
+import com.android.tools.lint.checks.PermissionFinder.Operation;
+import com.android.tools.lint.checks.PermissionFinder.Result;
+import com.android.tools.lint.checks.PermissionHolder.SetPermissionLookup;
import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
import com.android.tools.lint.client.api.JavaParser.ResolvedField;
import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
+import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.ConstantEvaluator;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
+import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
+import com.android.utils.XmlUtils;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
+import java.util.Set;
import lombok.ast.ArrayCreation;
import lombok.ast.ArrayInitializer;
import lombok.ast.AstVisitor;
import lombok.ast.BinaryExpression;
import lombok.ast.BinaryOperator;
+import lombok.ast.Catch;
+import lombok.ast.ConstructorInvocation;
+import lombok.ast.EnumConstant;
import lombok.ast.Expression;
import lombok.ast.ExpressionStatement;
import lombok.ast.FloatingPointLiteral;
import lombok.ast.ForwardingAstVisitor;
import lombok.ast.InlineIfExpression;
import lombok.ast.IntegralLiteral;
+import lombok.ast.MethodDeclaration;
import lombok.ast.MethodInvocation;
import lombok.ast.Node;
import lombok.ast.NullLiteral;
import lombok.ast.Select;
import lombok.ast.Statement;
import lombok.ast.StringLiteral;
+import lombok.ast.Try;
+import lombok.ast.TypeReference;
import lombok.ast.UnaryExpression;
import lombok.ast.UnaryOperator;
import lombok.ast.VariableDeclaration;
@@ -173,14 +208,56 @@
Severity.WARNING,
IMPLEMENTATION);
+ /** Method result should be used */
+ public static final Issue MISSING_PERMISSION = Issue.create(
+ "MissingPermission", //$NON-NLS-1$
+ "Missing Permissions",
+
+ "This check scans through your code and libraries and looks at the APIs being used, " +
+ "and checks this against the set of permissions required to access those APIs. If " +
+ "the code using those APIs is called at runtime, then the program will crash.\n" +
+ "\n" +
+ "Furthermore, for permissions that are revocable (with targetSdkVersion 23), client " +
+ "code must also be prepared to handle the calls throwing an exception if the user " +
+ "rejects the request for permission at runtime.",
+
+ Category.CORRECTNESS,
+ 9,
+ Severity.ERROR,
+ IMPLEMENTATION);
+
+ /** Passing the wrong constant to an int or String method */
+ public static final Issue THREAD = Issue.create(
+ "WrongThread", //$NON-NLS-1$
+ "Wrong Thread",
+
+ "Ensures that a method which expects to be called on a specific thread, is actually " +
+ "called from that thread. For example, calls on methods in widgets should always " +
+ "be made on the UI thread.",
+
+ Category.CORRECTNESS,
+ 6,
+ Severity.ERROR,
+ IMPLEMENTATION)
+ .addMoreInfo(
+ "http://developer.android.com/guide/components/processes-and-threads.html#Threads");
+
public static final String CHECK_RESULT_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "CheckResult"; //$NON-NLS-1$
public static final String COLOR_INT_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "ColorInt"; //$NON-NLS-1$
public static final String INT_RANGE_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "IntRange"; //$NON-NLS-1$
public static final String FLOAT_RANGE_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "FloatRange"; //$NON-NLS-1$
public static final String SIZE_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "Size"; //$NON-NLS-1$
+ public static final String PERMISSION_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "RequiresPermission"; //$NON-NLS-1$
+ public static final String UI_THREAD_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "UiThread"; //$NON-NLS-1$
+ public static final String MAIN_THREAD_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "MainThread"; //$NON-NLS-1$
+ public static final String WORKER_THREAD_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "WorkerThread"; //$NON-NLS-1$
+ public static final String BINDER_THREAD_ANNOTATION = SUPPORT_ANNOTATIONS_PREFIX + "BinderThread"; //$NON-NLS-1$
+ public static final String PERMISSION_ANNOTATION_READ = PERMISSION_ANNOTATION + ".Read"; //$NON-NLS-1$
+ public static final String PERMISSION_ANNOTATION_WRITE = PERMISSION_ANNOTATION + ".Write"; //$NON-NLS-1$
- public static final String RES_SUFFIX = "Res"; //$NON-NLS-1$
- public static final String ATTR_SUGGEST = "suggest"; //$NON-NLS-1$
+ public static final String RES_SUFFIX = "Res";
+ public static final String THREAD_SUFFIX = "Thread";
+ public static final String ATTR_SUGGEST = "suggest";
public static final String ATTR_TO = "to";
public static final String ATTR_FROM = "from";
public static final String ATTR_FROM_INCLUSIVE = "fromInclusive";
@@ -188,6 +265,17 @@
public static final String ATTR_MULTIPLE = "multiple";
public static final String ATTR_MIN = "min";
public static final String ATTR_MAX = "max";
+ public static final String ATTR_ALL_OF = "allOf";
+ public static final String ATTR_ANY_OF = "anyOf";
+ public static final String ATTR_CONDITIONAL = "conditional";
+
+ /**
+ * Marker ResourceType used to signify that an expression is of type {@code @ColorInt},
+ * which isn't actually a ResourceType but one we want to specifically compare with.
+ * We're using {@link ResourceType#PUBLIC} because that one won't appear in the R
+ * class (and ResourceType is an enum we can't just create new constants for.)
+ */
+ public static final ResourceType COLOR_INT_MARKER_TYPE = ResourceType.PUBLIC;
/**
* Constructs a new {@link SupportAnnotationDetector} check
@@ -195,31 +283,47 @@
public SupportAnnotationDetector() {
}
- private static void checkMethodAnnotation(
+ private void checkMethodAnnotation(
@NonNull JavaContext context,
- @NonNull MethodInvocation node,
+ @NonNull ResolvedMethod method,
+ @NonNull Node node,
@NonNull ResolvedAnnotation annotation) {
String signature = annotation.getSignature();
if (CHECK_RESULT_ANNOTATION.equals(signature)
|| signature.endsWith(".CheckReturnValue")) { // support findbugs annotation too
checkResult(context, node, annotation);
+ } else if (signature.equals(PERMISSION_ANNOTATION)) {
+ PermissionRequirement requirement = PermissionRequirement.create(context, annotation);
+ checkPermission(context, node, method, null, requirement);
+ } else if (signature.endsWith(THREAD_SUFFIX)
+ && signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
+ checkThreading(context, node, method, signature);
}
}
- private static void checkParameterAnnotation(
+ private void checkParameterAnnotation(
@NonNull JavaContext context,
@NonNull Node argument,
- @NonNull ResolvedAnnotation annotation) {
+ @NonNull Node call,
+ @NonNull ResolvedMethod method,
+ @NonNull ResolvedAnnotation annotation,
+ @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
String signature = annotation.getSignature();
if (COLOR_INT_ANNOTATION.equals(signature)) {
checkColor(context, argument);
} else if (signature.equals(INT_RANGE_ANNOTATION)) {
- checkIntRange(context, annotation, argument);
+ checkIntRange(context, annotation, argument, allAnnotations);
} else if (signature.equals(FLOAT_RANGE_ANNOTATION)) {
checkFloatRange(context, annotation, argument);
} else if (signature.equals(SIZE_ANNOTATION)) {
checkSize(context, annotation, argument);
+ } else if (signature.startsWith(PERMISSION_ANNOTATION)) {
+ // PERMISSION_ANNOTATION, PERMISSION_ANNOTATION_READ, PERMISSION_ANNOTATION_WRITE
+ // When specified on a parameter, that indicates that we're dealing with
+ // a permission requirement on this *method* which depends on the value
+ // supplied by this parameter
+ checkParameterPermission(context, signature, call, method, argument);
} else {
// We only run @IntDef, @StringDef and @<Type>Res checks if we're not
// running inside Android Studio / IntelliJ where there are already inspections
@@ -228,9 +332,9 @@
// have to
if (signature.equals(INT_DEF_ANNOTATION)) {
boolean flag = annotation.getValue(TYPE_DEF_FLAG_ATTRIBUTE) == Boolean.TRUE;
- checkTypeDefConstant(context, annotation, argument, null, flag);
+ checkTypeDefConstant(context, annotation, argument, null, flag, allAnnotations);
} else if (signature.equals(STRING_DEF_ANNOTATION)) {
- checkTypeDefConstant(context, annotation, argument, null, false);
+ checkTypeDefConstant(context, annotation, argument, null, false, allAnnotations);
} else if (signature.endsWith(RES_SUFFIX)) {
String typeString = signature.substring(SUPPORT_ANNOTATIONS_PREFIX.length(),
signature.length() - RES_SUFFIX.length()).toLowerCase(Locale.US);
@@ -244,6 +348,35 @@
}
}
+ private void checkParameterPermission(
+ @NonNull JavaContext context,
+ @NonNull String signature,
+ @NonNull Node call,
+ @NonNull ResolvedMethod method,
+ @NonNull Node argument) {
+ Operation operation = null;
+ if (signature.equals(PERMISSION_ANNOTATION_READ)) {
+ operation = READ;
+ } else if (signature.equals(PERMISSION_ANNOTATION_WRITE)) {
+ operation = WRITE;
+ } else {
+ TypeDescriptor type = context.getType(argument);
+ if (type == null) {
+ return;
+ }
+ if (type.matchesSignature(CLASS_INTENT)) {
+ operation = ACTION;
+ }
+ }
+ if (operation == null) {
+ return;
+ }
+ Result result = PermissionFinder.findRequiredPermissions(operation, context, argument);
+ if (result != null) {
+ checkPermission(context, call, method, result, result.requirement);
+ }
+ }
+
private static void checkColor(@NonNull JavaContext context, @NonNull Node argument) {
if (argument instanceof InlineIfExpression) {
InlineIfExpression expression = (InlineIfExpression) argument;
@@ -252,9 +385,9 @@
return;
}
- ResourceType type = getResourceType(argument);
+ List<ResourceType> types = getResourceTypes(context, argument);
- if (type == ResourceType.COLOR) {
+ if (types != null && types.contains(ResourceType.COLOR)) {
String message = String.format(
"Should pass resolved color instead of resource id here: " +
"`getResources().getColor(%1$s)`", argument.toString());
@@ -262,10 +395,270 @@
}
}
- private static void checkResult(@NonNull JavaContext context, @NonNull MethodInvocation node,
+ private void checkPermission(
+ @NonNull JavaContext context,
+ @NonNull Node node,
+ @Nullable ResolvedMethod method,
+ @Nullable Result result,
+ @NonNull PermissionRequirement requirement) {
+ if (requirement.isConditional()) {
+ return;
+ }
+ PermissionHolder permissions = getPermissions(context);
+ if (!requirement.isSatisfied(permissions)) {
+ // See if it looks like we're holding the permission implicitly by @RequirePermission
+ // annotations in the surrounding context
+ permissions = addLocalPermissions(context, permissions, node);
+ if (!requirement.isSatisfied(permissions)) {
+ Operation operation;
+ String name;
+ if (result != null) {
+ name = result.name;
+ operation = result.operation;
+ } else {
+ assert method != null;
+ name = method.getContainingClass().getSimpleName() + "." + method.getName();
+ operation = Operation.CALL;
+ }
+ String message = getMissingPermissionMessage(requirement, name, permissions,
+ operation);
+ context.report(MISSING_PERMISSION, node, context.getLocation(node), message);
+ }
+ } else if (requirement.isRevocable(permissions) &&
+ context.getMainProject().getTargetSdkVersion().getFeatureLevel() >= 23) {
+ // Ensure that the caller is handling a security exception
+ // First check to see if we're inside a try/catch which catches a SecurityException
+ // (or some wider exception than that). Check for nested try/catches too.
+ boolean handlesMissingPermission = false;
+ Node parent = node;
+ while (true) {
+ Try tryCatch = getParentOfType(parent, Try.class);
+ if (tryCatch == null) {
+ break;
+ } else {
+ for (Catch aCatch : tryCatch.astCatches()) {
+ TypeReference catchType = aCatch.astExceptionDeclaration().
+ astTypeReference();
+ if (isSecurityException(context,
+ catchType)) {
+ handlesMissingPermission = true;
+ break;
+ }
+ }
+ parent = tryCatch;
+ }
+ }
+
+ // If not, check to see if the method itself declares that it throws a
+ // SecurityException or something wider.
+ if (!handlesMissingPermission) {
+ MethodDeclaration declaration = getParentOfType(parent, MethodDeclaration.class);
+ if (declaration != null) {
+ for (TypeReference typeReference : declaration.astThrownTypeReferences()) {
+ if (isSecurityException(context, typeReference)) {
+ handlesMissingPermission = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // If not, check to see if the code is deliberately checking to see if the
+ // given permission is available.
+ if (!handlesMissingPermission) {
+ Node methodNode = JavaContext.findSurroundingMethod(node);
+ if (methodNode != null) {
+ CheckPermissionVisitor visitor = new CheckPermissionVisitor(node);
+ methodNode.accept(visitor);
+ handlesMissingPermission = visitor.checksPermission();
+ }
+ }
+
+ if (!handlesMissingPermission) {
+ String message = getUnhandledPermissionMessage();
+ context.report(MISSING_PERMISSION, node, context.getLocation(node), message);
+ }
+ }
+ }
+
+ @NonNull
+ private static PermissionHolder addLocalPermissions(
+ @NonNull JavaContext context,
+ @NonNull PermissionHolder permissions,
+ @NonNull Node node) {
+ // Accumulate @RequirePermissions available in the local context
+ Node methodNode = JavaContext.findSurroundingMethod(node);
+ if (methodNode == null) {
+ return permissions;
+ }
+ ResolvedNode resolved = context.resolve(methodNode);
+ if (!(resolved instanceof ResolvedMethod)) {
+ return permissions;
+ }
+ ResolvedMethod method = (ResolvedMethod) resolved;
+ ResolvedAnnotation annotation = method.getAnnotation(PERMISSION_ANNOTATION);
+ permissions = mergeAnnotationPermissions(context, permissions, annotation);
+ annotation = method.getContainingClass().getAnnotation(PERMISSION_ANNOTATION);
+ permissions = mergeAnnotationPermissions(context, permissions, annotation);
+ return permissions;
+ }
+
+ @NonNull
+ private static PermissionHolder mergeAnnotationPermissions(
+ @NonNull JavaContext context,
+ @NonNull PermissionHolder permissions,
+ @Nullable ResolvedAnnotation annotation) {
+ if (annotation != null) {
+ PermissionRequirement requirement = PermissionRequirement.create(context, annotation);
+ permissions = SetPermissionLookup.join(permissions, requirement);
+ }
+
+ return permissions;
+ }
+
+ /** Returns the error message shown when a given call is missing one or more permissions */
+ public static String getMissingPermissionMessage(@NonNull PermissionRequirement requirement,
+ @NonNull String callName, @NonNull PermissionHolder permissions,
+ @NonNull Operation operation) {
+ return String.format("Missing permissions required %1$s %2$s: %3$s", operation.prefix(),
+ callName, requirement.describeMissingPermissions(permissions));
+ }
+
+ /** Returns the error message shown when a revocable permission call is not properly handled */
+ public static String getUnhandledPermissionMessage() {
+ return "Call requires permission which may be rejected by user: code should explicitly "
+ + "check to see if permission is available (with `checkPermission`) or handle "
+ + "a potential `SecurityException`";
+ }
+
+ /**
+ * Visitor which looks through a method, up to a given call (the one requiring a
+ * permission) and checks whether it's preceeded by a call to checkPermission or
+ * checkCallingPermission or enforcePermission etc.
+ * <p>
+ * Currently it only looks for the presence of this check; it does not perform
+ * flow analysis to determine whether the check actually affects program flow
+ * up to the permission call, or whether the check permission is checking for
+ * permissions sufficient to satisfy the permission requirement of the target call,
+ * or whether the check return value (== PERMISSION_GRANTED vs != PERMISSION_GRANTED)
+ * is handled correctly, etc.
+ */
+ private static class CheckPermissionVisitor extends ForwardingAstVisitor {
+ private boolean mChecksPermission;
+ private boolean mDone;
+ private final Node mTarget;
+
+ public CheckPermissionVisitor(@NonNull Node target) {
+ mTarget = target;
+ }
+
+ @Override
+ public boolean visitNode(Node node) {
+ return mDone;
+ }
+
+ @Override
+ public boolean visitMethodInvocation(MethodInvocation node) {
+ if (node == mTarget) {
+ mDone = true;
+ }
+
+ String name = node.astName().astValue();
+ if ((name.startsWith("check") || name.startsWith("enforce"))
+ && name.endsWith("Permission")) {
+ mChecksPermission = true;
+ mDone = true;
+ }
+ return super.visitMethodInvocation(node);
+ }
+
+ public boolean checksPermission() {
+ return mChecksPermission;
+ }
+ }
+
+ private static boolean isSecurityException(
+ @NonNull JavaContext context,
+ @NonNull TypeReference typeReference) {
+ TypeDescriptor type = context.getType(typeReference);
+ return type != null && (type.matchesSignature("java.lang.SecurityException") ||
+ type.matchesSignature("java.lang.RuntimeException") ||
+ type.matchesSignature("java.lang.Exception") ||
+ type.matchesSignature("java.lang.Throwable"));
+ }
+
+ private PermissionHolder mPermissions;
+
+ private PermissionHolder getPermissions(
+ @NonNull JavaContext context) {
+ if (mPermissions == null) {
+ Set<String> permissions = Sets.newHashSetWithExpectedSize(30);
+ Set<String> revocable = Sets.newHashSetWithExpectedSize(4);
+ LintClient client = context.getClient();
+ // Gather permissions from all projects that contribute to the
+ // main project.
+ Project mainProject = context.getMainProject();
+ for (File manifest : mainProject.getManifestFiles()) {
+ addPermissions(client, permissions, revocable, manifest);
+ }
+ for (Project library : mainProject.getAllLibraries()) {
+ for (File manifest : library.getManifestFiles()) {
+ addPermissions(client, permissions, revocable, manifest);
+ }
+ }
+
+ AndroidVersion minSdkVersion = mainProject.getMinSdkVersion();
+ AndroidVersion targetSdkVersion = mainProject.getTargetSdkVersion();
+ mPermissions = new SetPermissionLookup(permissions, revocable, minSdkVersion,
+ targetSdkVersion);
+ }
+
+ return mPermissions;
+ }
+
+ private static void addPermissions(@NonNull LintClient client,
+ @NonNull Set<String> permissions,
+ @NonNull Set<String> revocable,
+ @NonNull File manifest) {
+ Document document = XmlUtils.parseDocumentSilently(client.readFile(manifest), true);
+ if (document == null) {
+ return;
+ }
+ Element root = document.getDocumentElement();
+ if (root == null) {
+ return;
+ }
+ NodeList children = root.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ org.w3c.dom.Node item = children.item(i);
+ if (item.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) {
+ continue;
+ }
+ String nodeName = item.getNodeName();
+ if (nodeName.equals(TAG_USES_PERMISSION)) {
+ Element element = (Element)item;
+ String name = element.getAttributeNS(ANDROID_URI, ATTR_NAME);
+ if (!name.isEmpty()) {
+ permissions.add(name);
+ }
+ } else if (nodeName.equals(TAG_PERMISSION)) {
+ Element element = (Element)item;
+ String protectionLevel = element.getAttributeNS(ANDROID_URI,
+ ATTR_PROTECTION_LEVEL);
+ if (VALUE_DANGEROUS.equals(protectionLevel)) {
+ String name = element.getAttributeNS(ANDROID_URI, ATTR_NAME);
+ if (!name.isEmpty()) {
+ revocable.add(name);
+ }
+ }
+ }
+ }
+ }
+
+ private static void checkResult(@NonNull JavaContext context, @NonNull Node node,
@NonNull ResolvedAnnotation annotation) {
if (node.getParent() instanceof ExpressionStatement) {
- String methodName = node.astName().astValue();
+ String methodName = JavaContext.getMethodName(node);
Object suggested = annotation.getValue(ATTR_SUGGEST);
// Failing to check permissions is a potential security issue (and had an existing
@@ -273,7 +666,8 @@
// custom severity in their LintOptions etc) so continue to use that issue
// (which also has category Security rather than Correctness) for these:
Issue issue = CHECK_RESULT;
- if (methodName.startsWith("check") && methodName.contains("Permission")) {
+ if (methodName != null && methodName.startsWith("check")
+ && methodName.contains("Permission")) {
issue = CHECK_PERMISSION;
}
@@ -290,6 +684,101 @@
}
}
+ private static void checkThreading(
+ @NonNull JavaContext context,
+ @NonNull Node node,
+ @NonNull ResolvedMethod method,
+ @NonNull String annotation) {
+ String threadContext = getThreadContext(context, node);
+ if (threadContext != null && !isCompatibleThread(threadContext, annotation)) {
+ String message = String.format("Method %1$s must be called from the `%2$s` thread, currently inferred thread is `%3$s` thread",
+ method.getName(), describeThread(annotation), describeThread(threadContext));
+ context.report(THREAD, node, context.getLocation(node), message);
+ }
+ }
+
+ @NonNull
+ public static String describeThread(@NonNull String annotation) {
+ if (UI_THREAD_ANNOTATION.equals(annotation)) {
+ return "UI";
+ }
+ else if (MAIN_THREAD_ANNOTATION.equals(annotation)) {
+ return "main";
+ }
+ else if (BINDER_THREAD_ANNOTATION.equals(annotation)) {
+ return "binder";
+ }
+ else if (WORKER_THREAD_ANNOTATION.equals(annotation)) {
+ return "worker";
+ } else {
+ return "other";
+ }
+ }
+
+ /** returns true if the two threads are compatible */
+ public static boolean isCompatibleThread(@NonNull String thread1, @NonNull String thread2) {
+ if (thread1.equals(thread2)) {
+ return true;
+ }
+
+ // Allow @UiThread and @MainThread to be combined
+ if (thread1.equals(UI_THREAD_ANNOTATION)) {
+ if (thread2.equals(MAIN_THREAD_ANNOTATION)) {
+ return true;
+ }
+ } else if (thread1.equals(MAIN_THREAD_ANNOTATION)) {
+ if (thread2.equals(UI_THREAD_ANNOTATION)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /** Attempts to infer the current thread context at the site of the given method call */
+ @Nullable
+ private static String getThreadContext(@NonNull JavaContext context,
+ @NonNull Node methodCall) {
+ Node node = findSurroundingMethod(methodCall);
+ if (node != null) {
+ ResolvedNode resolved = context.resolve(node);
+ if (resolved instanceof ResolvedMethod) {
+ ResolvedMethod method = (ResolvedMethod) resolved;
+ ResolvedClass cls = method.getContainingClass();
+
+ while (method != null) {
+ for (ResolvedAnnotation annotation : method.getAnnotations()) {
+ String name = annotation.getSignature();
+ if (name.startsWith(SUPPORT_ANNOTATIONS_PREFIX)
+ && name.endsWith(THREAD_SUFFIX)) {
+ return name;
+ }
+ }
+ method = method.getSuperMethod();
+ }
+
+ // See if we're extending a class with a known threading context
+ while (cls != null) {
+ for (ResolvedAnnotation annotation : cls.getAnnotations()) {
+ String name = annotation.getSignature();
+ if (name.startsWith(SUPPORT_ANNOTATIONS_PREFIX)
+ && name.endsWith(THREAD_SUFFIX)) {
+ return name;
+ }
+ }
+ cls = cls.getSuperClass();
+ }
+ }
+ }
+
+ // In the future, we could also try to infer the threading context using
+ // other heuristics. For example, if we're in a method with unknown threading
+ // context, but we see that the method is called by another method with a known
+ // threading context, we can infer that that threading context is the context for
+ // this thread too (assuming the call is direct).
+
+ return null;
+ }
private static boolean isNumber(@NonNull Node argument) {
return argument instanceof IntegralLiteral || argument instanceof UnaryExpression
@@ -314,17 +803,22 @@
@NonNull JavaContext context,
@NonNull Node argument,
@Nullable ResourceType expectedType) {
- ResourceType actual = getResourceType(argument);
+ List<ResourceType> actual = getResourceTypes(context, argument);
if (actual == null && (!isNumber(argument) || isZero(argument) || isMinusOne(argument)) ) {
return;
} else if (actual != null && (expectedType == null
- || expectedType == actual
- || expectedType == DRAWABLE && (actual == COLOR || actual == MIPMAP))) {
+ || actual.contains(expectedType)
+ || expectedType == DRAWABLE && (actual.contains(COLOR) || actual.contains(MIPMAP)))) {
return;
}
String message;
- if (expectedType != null) {
+ if (actual != null && actual.size() == 1 && actual.get(0) == COLOR_INT_MARKER_TYPE) {
+ message = "Expected a color resource id (`R.color.`) but received an RGB integer";
+ } else if (expectedType == COLOR_INT_MARKER_TYPE) {
+ message = String.format("Should pass resolved color instead of resource id here: " +
+ "`getResources().getColor(%1$s)`", argument.toString());
+ } else if (expectedType != null) {
message = String.format(
"Expected resource of type %1$s", expectedType.getName());
} else {
@@ -334,7 +828,8 @@
}
@Nullable
- private static ResourceType getResourceType(@NonNull Node argument) {
+ private static List<ResourceType> getResourceTypes(@NonNull JavaContext context,
+ @NonNull Node argument) {
if (argument instanceof Select) {
Select node = (Select) argument;
if (node.astOperand() instanceof Select) {
@@ -342,15 +837,17 @@
if (select.astOperand() instanceof Select) { // android.R....
Select innerSelect = (Select) select.astOperand();
if (innerSelect.astIdentifier().astValue().equals(R_CLASS)) {
- String type = select.astIdentifier().astValue();
- return ResourceType.getEnum(type);
+ String typeName = select.astIdentifier().astValue();
+ ResourceType type = ResourceType.getEnum(typeName);
+ return type != null ? Collections.singletonList(type) : null;
}
}
if (select.astOperand() instanceof VariableReference) {
VariableReference reference = (VariableReference) select.astOperand();
if (reference.astIdentifier().astValue().equals(R_CLASS)) {
- String type = select.astIdentifier().astValue();
- return ResourceType.getEnum(type);
+ String typeName = select.astIdentifier().astValue();
+ ResourceType type = ResourceType.getEnum(typeName);
+ return type != null ? Collections.singletonList(type) : null;
}
}
}
@@ -365,8 +862,9 @@
Expression typeOperand = select.astOperand();
if (typeOperand instanceof Select) {
Select typeSelect = (Select) typeOperand;
- String type = typeSelect.astIdentifier().astValue();
- return ResourceType.getEnum(type);
+ String typeName = typeSelect.astIdentifier().astValue();
+ ResourceType type = ResourceType.getEnum(typeName);
+ return type != null ? Collections.singletonList(type) : null;
}
}
}
@@ -394,7 +892,7 @@
.astVariables()) {
if (entry.astInitializer() != null
&& entry.astName().astValue().equals(targetName)) {
- return getResourceType(entry.astInitializer());
+ return getResourceTypes(context, entry.astInitializer());
}
}
} else if (previous instanceof ExpressionStatement) {
@@ -405,12 +903,42 @@
== BinaryOperator.ASSIGN) {
BinaryExpression binaryExpression = (BinaryExpression) expression;
if (targetName.equals(binaryExpression.astLeft().toString())) {
- return getResourceType(binaryExpression.astRight());
+ return getResourceTypes(context, binaryExpression.astRight());
}
}
}
}
}
+ } else if (argument instanceof MethodInvocation) {
+ ResolvedNode resolved = context.resolve(argument);
+ if (resolved != null) {
+ for (ResolvedAnnotation annotation : resolved.getAnnotations()) {
+ String signature = annotation.getSignature();
+ if (signature.equals(COLOR_INT_ANNOTATION)) {
+ return Collections.singletonList(COLOR_INT_MARKER_TYPE);
+ }
+ if (signature.endsWith(RES_SUFFIX)
+ && signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
+ String typeString = signature.substring(SUPPORT_ANNOTATIONS_PREFIX.length(),
+ signature.length() - RES_SUFFIX.length()).toLowerCase(Locale.US);
+ ResourceType type = ResourceType.getEnum(typeString);
+ if (type != null) {
+ return Collections.singletonList(type);
+ } else if (typeString.equals("any")) { // @AnyRes
+ ResourceType[] types = ResourceType.values();
+ List<ResourceType> result = Lists.newArrayListWithExpectedSize(
+ types.length);
+ for (ResourceType t : types) {
+ if (t != COLOR_INT_MARKER_TYPE) {
+ result.add(t);
+ }
+ }
+
+ return result;
+ }
+ }
+ }
+ }
}
return null;
@@ -419,19 +947,35 @@
private static void checkIntRange(
@NonNull JavaContext context,
@NonNull ResolvedAnnotation annotation,
+ @NonNull Node argument,
+ @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
+ String message = getIntRangeError(context, annotation, argument);
+ if (message != null) {
+ if (findIntDef(allAnnotations) != null) {
+ // Don't flag int range errors if there is an int def annotation there too;
+ // there could be a valid @IntDef constant. (The @IntDef check will
+ // perform range validation by calling getIntRange.)
+ return;
+ }
+
+ context.report(RANGE, argument, context.getLocation(argument), message);
+ }
+ }
+
+ @Nullable
+ private static String getIntRangeError(
+ @NonNull JavaContext context,
+ @NonNull ResolvedAnnotation annotation,
@NonNull Node argument) {
Object object = ConstantEvaluator.evaluate(context, argument);
if (!(object instanceof Number)) {
- return;
+ return null;
}
long value = ((Number)object).longValue();
long from = getLongAttribute(annotation, ATTR_FROM, Long.MIN_VALUE);
long to = getLongAttribute(annotation, ATTR_TO, Long.MAX_VALUE);
- String message = getIntRangeError(value, from, to);
- if (message != null) {
- context.report(RANGE, argument, context.getLocation(argument), message);
- }
+ return getIntRangeError(value, from, to);
}
/**
@@ -622,12 +1166,37 @@
return message;
}
+ @Nullable
+ private static ResolvedAnnotation findIntRange(
+ @NonNull Iterable<ResolvedAnnotation> annotations) {
+ for (ResolvedAnnotation annotation : annotations) {
+ if (INT_RANGE_ANNOTATION.equals(annotation.getName())) {
+ return annotation;
+ }
+ }
+
+ return null;
+ }
+
+ @Nullable
+ private static ResolvedAnnotation findIntDef(
+ @NonNull Iterable<ResolvedAnnotation> annotations) {
+ for (ResolvedAnnotation annotation : annotations) {
+ if (INT_DEF_ANNOTATION.equals(annotation.getName())) {
+ return annotation;
+ }
+ }
+
+ return null;
+ }
+
private static void checkTypeDefConstant(
@NonNull JavaContext context,
@NonNull ResolvedAnnotation annotation,
@NonNull Node argument,
@Nullable Node errorNode,
- boolean flag) {
+ boolean flag,
+ @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
if (argument instanceof NullLiteral) {
// Accepted for @StringDef
return;
@@ -635,7 +1204,8 @@
if (argument instanceof StringLiteral) {
StringLiteral string = (StringLiteral) argument;
- checkTypeDefConstant(context, annotation, argument, errorNode, false, string.astValue());
+ checkTypeDefConstant(context, annotation, argument, errorNode, false, string.astValue(),
+ allAnnotations);
} else if (argument instanceof IntegralLiteral) {
IntegralLiteral literal = (IntegralLiteral) argument;
int value = literal.astIntValue();
@@ -643,35 +1213,68 @@
// Accepted for a flag @IntDef
return;
}
- checkTypeDefConstant(context, annotation, argument, errorNode, flag, value);
+
+ ResolvedAnnotation rangeAnnotation = findIntRange(allAnnotations);
+ if (rangeAnnotation != null) {
+ // Allow @IntRange on this number
+ if (getIntRangeError(context, rangeAnnotation, literal) == null) {
+ return;
+ }
+ }
+
+ checkTypeDefConstant(context, annotation, argument, errorNode, flag, value,
+ allAnnotations);
} else if (isMinusOne(argument)) {
// -1 is accepted unconditionally for flags
if (!flag) {
- reportTypeDef(context, annotation, argument, errorNode);
+ ResolvedAnnotation rangeAnnotation = findIntRange(allAnnotations);
+ if (rangeAnnotation != null) {
+ // Allow @IntRange on this number
+ if (getIntRangeError(context, rangeAnnotation, argument) == null) {
+ return;
+ }
+ }
+
+ reportTypeDef(context, annotation, argument, errorNode, allAnnotations);
}
} else if (argument instanceof InlineIfExpression) {
InlineIfExpression expression = (InlineIfExpression) argument;
if (expression.astIfTrue() != null) {
- checkTypeDefConstant(context, annotation, expression.astIfTrue(), errorNode, flag);
+ checkTypeDefConstant(context, annotation, expression.astIfTrue(), errorNode, flag,
+ allAnnotations);
}
if (expression.astIfFalse() != null) {
- checkTypeDefConstant(context, annotation, expression.astIfFalse(), errorNode, flag);
+ checkTypeDefConstant(context, annotation, expression.astIfFalse(), errorNode, flag,
+ allAnnotations);
}
} else if (argument instanceof UnaryExpression) {
UnaryExpression expression = (UnaryExpression) argument;
UnaryOperator operator = expression.astOperator();
if (flag) {
- checkTypeDefConstant(context, annotation, expression.astOperand(), errorNode, true);
+ checkTypeDefConstant(context, annotation, expression.astOperand(), errorNode, true,
+ allAnnotations);
} else if (operator == UnaryOperator.BINARY_NOT) {
context.report(TYPE_DEF, expression, context.getLocation(expression),
"Flag not allowed here");
+ } else if (operator == UnaryOperator.UNARY_MINUS) {
+ ResolvedAnnotation rangeAnnotation = findIntRange(allAnnotations);
+ if (rangeAnnotation != null) {
+ // Allow @IntRange on this number
+ if (getIntRangeError(context, rangeAnnotation, argument) == null) {
+ return;
+ }
+ }
+
+ reportTypeDef(context, annotation, argument, errorNode, allAnnotations);
}
} else if (argument instanceof BinaryExpression) {
// If it's ?: then check both the if and else clauses
BinaryExpression expression = (BinaryExpression) argument;
if (flag) {
- checkTypeDefConstant(context, annotation, expression.astLeft(), errorNode, true);
- checkTypeDefConstant(context, annotation, expression.astRight(), errorNode, true);
+ checkTypeDefConstant(context, annotation, expression.astLeft(), errorNode, true,
+ allAnnotations);
+ checkTypeDefConstant(context, annotation, expression.astRight(), errorNode, true,
+ allAnnotations);
} else {
BinaryOperator operator = expression.astOperator();
if (operator == BinaryOperator.BITWISE_AND
@@ -684,7 +1287,8 @@
} else {
ResolvedNode resolved = context.resolve(argument);
if (resolved instanceof ResolvedField) {
- checkTypeDefConstant(context, annotation, argument, errorNode, flag, resolved);
+ checkTypeDefConstant(context, annotation, argument, errorNode, flag, resolved,
+ allAnnotations);
} else if (argument instanceof VariableReference) {
Statement statement = getParentOfType(argument, Statement.class, false);
if (statement != null) {
@@ -710,7 +1314,8 @@
&& entry.astName().astValue().equals(targetName)) {
checkTypeDefConstant(context, annotation,
entry.astInitializer(),
- errorNode != null ? errorNode : argument, flag);
+ errorNode != null ? errorNode : argument, flag,
+ allAnnotations);
return;
}
}
@@ -724,7 +1329,8 @@
if (targetName.equals(binaryExpression.astLeft().toString())) {
checkTypeDefConstant(context, annotation,
binaryExpression.astRight(),
- errorNode != null ? errorNode : argument, flag);
+ errorNode != null ? errorNode : argument, flag,
+ allAnnotations);
return;
}
}
@@ -737,7 +1343,8 @@
private static void checkTypeDefConstant(@NonNull JavaContext context,
@NonNull ResolvedAnnotation annotation, @NonNull Node argument,
- @Nullable Node errorNode, boolean flag, Object value) {
+ @Nullable Node errorNode, boolean flag, Object value,
+ @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
Object allowed = annotation.getValue();
if (allowed instanceof Object[]) {
Object[] allowedValues = (Object[]) allowed;
@@ -746,22 +1353,23 @@
return;
}
}
- reportTypeDef(context, argument, errorNode, flag, allowedValues);
+ reportTypeDef(context, argument, errorNode, flag, allowedValues, allAnnotations);
}
}
private static void reportTypeDef(@NonNull JavaContext context,
@NonNull ResolvedAnnotation annotation, @NonNull Node argument,
- @Nullable Node errorNode) {
+ @Nullable Node errorNode, @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
Object allowed = annotation.getValue();
if (allowed instanceof Object[]) {
Object[] allowedValues = (Object[]) allowed;
- reportTypeDef(context, argument, errorNode, false, allowedValues);
+ reportTypeDef(context, argument, errorNode, false, allowedValues, allAnnotations);
}
}
private static void reportTypeDef(@NonNull JavaContext context, @NonNull Node node,
- @Nullable Node errorNode, boolean flag, @NonNull Object[] allowedValues) {
+ @Nullable Node errorNode, boolean flag, @NonNull Object[] allowedValues,
+ @NonNull Iterable<ResolvedAnnotation> allAnnotations) {
String values = listAllowedValues(allowedValues);
String message;
if (flag) {
@@ -769,6 +1377,17 @@
} else {
message = "Must be one of: " + values;
}
+
+ ResolvedAnnotation rangeAnnotation = findIntRange(allAnnotations);
+ if (rangeAnnotation != null) {
+ // Allow @IntRange on this number
+ String rangeError = getIntRangeError(context, rangeAnnotation, node);
+ if (rangeError != null && !rangeError.isEmpty()) {
+ message += " or " + Character.toLowerCase(rangeError.charAt(0))
+ + rangeError.substring(1);
+ }
+ }
+
if (errorNode == null) {
errorNode = node;
}
@@ -832,41 +1451,66 @@
return defaultValue;
}
- @Nullable
- static ResolvedAnnotation getRelevantAnnotation(@NonNull ResolvedAnnotation annotation) {
- String signature = annotation.getSignature();
- if (signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
- // Bail on the nullness annotations early since they're the most commonly
- // defined ones. They're not analyzed in lint yet.
- if (signature.endsWith(".Nullable") || signature.endsWith(".NonNull")) {
- return null;
+ @NonNull
+ static Iterable<ResolvedAnnotation> filterRelevantAnnotations(
+ @NonNull Iterable<ResolvedAnnotation> annotations) {
+ List<ResolvedAnnotation> result = null;
+ Iterator<ResolvedAnnotation> iterator = annotations.iterator();
+ int index = 0;
+ while (iterator.hasNext()) {
+ ResolvedAnnotation annotation = iterator.next();
+ index++;
+
+ String signature = annotation.getSignature();
+ if (signature.startsWith("java.")) {
+ // @Override, @SuppressWarnings etc. Ignore
+ continue;
}
+ if (signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) {
+ // Bail on the nullness annotations early since they're the most commonly
+ // defined ones. They're not analyzed in lint yet.
+ if (signature.endsWith(".Nullable") || signature.endsWith(".NonNull")) {
+ continue;
+ }
- return annotation;
- }
+ // Common case: there's just one annotation; no need to create a list copy
+ if (!iterator.hasNext() && index == 1) {
+ return annotations;
+ }
+ if (result == null) {
+ result = new ArrayList<ResolvedAnnotation>(2);
+ }
+ result.add(annotation);
+ }
- if (signature.startsWith("java.")) {
- // @Override, @SuppressWarnings etc. Ignore
- return null;
- }
-
- // Special case @IntDef and @StringDef: These are used on annotations
- // themselves. For example, you create a new annotation named @foo.bar.Baz,
- // annotate it with @IntDef, and then use @foo.bar.Baz in your signatures.
- // Here we want to map from @foo.bar.Baz to the corresponding int def.
- // Don't need to compute this if performing @IntDef or @StringDef lookup
- ResolvedClass type = annotation.getClassType();
- if (type != null) {
- for (ResolvedAnnotation inner : type.getAnnotations()) {
- if (inner.matches(INT_DEF_ANNOTATION)
- || inner.matches(STRING_DEF_ANNOTATION)) {
- return inner;
+ // Special case @IntDef and @StringDef: These are used on annotations
+ // themselves. For example, you create a new annotation named @foo.bar.Baz,
+ // annotate it with @IntDef, and then use @foo.bar.Baz in your signatures.
+ // Here we want to map from @foo.bar.Baz to the corresponding int def.
+ // Don't need to compute this if performing @IntDef or @StringDef lookup
+ ResolvedClass type = annotation.getClassType();
+ if (type != null) {
+ Iterator<ResolvedAnnotation> iterator2 = type.getAnnotations().iterator();
+ while (iterator2.hasNext()) {
+ ResolvedAnnotation inner = iterator2.next();
+ if (inner.matches(INT_DEF_ANNOTATION)
+ || inner.matches(PERMISSION_ANNOTATION)
+ || inner.matches(INT_RANGE_ANNOTATION)
+ || inner.matches(STRING_DEF_ANNOTATION)) {
+ if (!iterator.hasNext() && !iterator2.hasNext() && index == 1) {
+ return annotations;
+ }
+ if (result == null) {
+ result = new ArrayList<ResolvedAnnotation>(2);
+ }
+ result.add(inner);
+ }
}
}
}
- return null;
+ return result != null ? result : Collections.<ResolvedAnnotation>emptyList();
}
// ---- Implements JavaScanner ----
@@ -874,7 +1518,11 @@
@Override
public
List<Class<? extends Node>> getApplicableNodeTypes() {
- return Collections.<Class<? extends Node>>singletonList(MethodInvocation.class);
+ //noinspection unchecked
+ return Arrays.<Class<? extends Node>>asList(
+ MethodInvocation.class,
+ ConstructorInvocation.class,
+ EnumConstant.class);
}
@Nullable
@@ -883,7 +1531,7 @@
return new CallVisitor(context);
}
- private static class CallVisitor extends ForwardingAstVisitor {
+ private class CallVisitor extends ForwardingAstVisitor {
private final JavaContext mContext;
public CallVisitor(JavaContext context) {
@@ -895,31 +1543,63 @@
ResolvedNode resolved = mContext.resolve(call);
if (resolved instanceof ResolvedMethod) {
ResolvedMethod method = (ResolvedMethod) resolved;
- Iterable<ResolvedAnnotation> annotations = method.getAnnotations();
- for (ResolvedAnnotation annotation : annotations) {
- annotation = getRelevantAnnotation(annotation);
- if (annotation != null) {
- checkMethodAnnotation(mContext, call, annotation);
- }
- }
-
- Iterator<Expression> arguments = call.astArguments().iterator();
- for (int i = 0, n = method.getArgumentCount();
- i < n && arguments.hasNext();
- i++) {
- Expression argument = arguments.next();
-
- annotations = method.getParameterAnnotations(i);
- for (ResolvedAnnotation annotation : annotations) {
- annotation = getRelevantAnnotation(annotation);
- if (annotation != null) {
- checkParameterAnnotation(mContext, argument, annotation);
- }
- }
- }
+ checkCall(call, method);
}
return false;
}
+
+ @Override
+ public boolean visitConstructorInvocation(@NonNull ConstructorInvocation call) {
+ ResolvedNode resolved = mContext.resolve(call);
+ if (resolved instanceof ResolvedMethod) {
+ ResolvedMethod method = (ResolvedMethod) resolved;
+ checkCall(call, method);
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean visitEnumConstant(EnumConstant node) {
+ ResolvedNode resolved = mContext.resolve(node);
+ if (resolved instanceof ResolvedMethod) {
+ ResolvedMethod method = (ResolvedMethod) resolved;
+ checkCall(node, method);
+ }
+
+ return false;
+ }
+
+ private void checkCall(@NonNull Node call, ResolvedMethod method) {
+ Iterable<ResolvedAnnotation> annotations = method.getAnnotations();
+ annotations = filterRelevantAnnotations(annotations);
+ for (ResolvedAnnotation annotation : annotations) {
+ checkMethodAnnotation(mContext, method, call, annotation);
+ }
+
+ // Look for annotations on the class as well: these trickle
+ // down to all the methods in the class
+ ResolvedClass containingClass = method.getContainingClass();
+ annotations = containingClass.getAnnotations();
+ annotations = filterRelevantAnnotations(annotations);
+ for (ResolvedAnnotation annotation : annotations) {
+ checkMethodAnnotation(mContext, method, call, annotation);
+ }
+
+ Iterator<Expression> arguments = JavaContext.getParameters(call);
+ for (int i = 0, n = method.getArgumentCount();
+ i < n && arguments.hasNext();
+ i++) {
+ Expression argument = arguments.next();
+
+ annotations = method.getParameterAnnotations(i);
+ annotations = filterRelevantAnnotations(annotations);
+ for (ResolvedAnnotation annotation : annotations) {
+ checkParameterAnnotation(mContext, argument, call, method, annotation,
+ annotations);
+ }
+ }
+ }
}
}
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TooManyViewsDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TooManyViewsDetector.java
index c3d4324..c059fd6 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TooManyViewsDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/TooManyViewsDetector.java
@@ -32,7 +32,7 @@
import java.util.Collection;
/**
- * Checks whether a root FrameLayout can be replaced with a {@code <merge>} tag.
+ * Checks whether a view hierarchy has too many views or has a suspiciously deep hierarchy
*/
public class TooManyViewsDetector extends LayoutDetector {
diff --git a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WakelockDetector.java b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WakelockDetector.java
index 594822b..7ec48e1 100644
--- a/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WakelockDetector.java
+++ b/lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WakelockDetector.java
@@ -185,7 +185,7 @@
}
}
- private void checkFlow(@NonNull ClassContext context, @NonNull ClassNode classNode,
+ private static void checkFlow(@NonNull ClassContext context, @NonNull ClassNode classNode,
@NonNull MethodNode method, @NonNull MethodInsnNode acquire) {
// Track allocations such that we know whether the type of the call
// is on a SecureRandom rather than a Random
diff --git a/lint/libs/lint-tests/build.gradle b/lint/libs/lint-tests/build.gradle
index cbebcca..7781b8a 100644
--- a/lint/libs/lint-tests/build.gradle
+++ b/lint/libs/lint-tests/build.gradle
@@ -1,14 +1,5 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
-apply plugin: 'sdk-java-lib'
+apply plugin: 'jacoco'
group = 'com.android.tools.lint'
archivesBaseName = 'lint-tests'
@@ -19,6 +10,10 @@
compile 'junit:junit:4.12'
compile project(':base:testutils')
+
+
+ testCompile 'org.mockito:mockito-all:1.9.5'
+ testCompile 'org.codehaus.groovy:groovy-all:2.2.1'
}
sourceSets {
diff --git a/lint/libs/lint-tests/lint-tests.iml b/lint/libs/lint-tests/lint-tests.iml
index ca46d9d..66d4ddf 100644
--- a/lint/libs/lint-tests/lint-tests.iml
+++ b/lint/libs/lint-tests/lint-tests.iml
@@ -4,8 +4,10 @@
<exclude-output />
<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" />
<excludeFolder url="file://$MODULE_DIR$/.settings" />
<excludeFolder url="file://$MODULE_DIR$/build" />
+ <excludeFolder url="file://$MODULE_DIR$/src/test/.settings" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="inheritedJdk" />
@@ -20,5 +22,7 @@
<orderEntry type="module" module-name="layoutlib-api-base" exported="" />
<orderEntry type="module" module-name="sdklib-base" exported="" />
<orderEntry type="library" exported="" name="intellij-annotations" level="project" />
+ <orderEntry type="library" scope="TEST" name="groovy" level="project" />
+ <orderEntry type="library" scope="TEST" name="mockito" level="project" />
</component>
</module>
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java b/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java
index 4344cd7..e103a08 100644
--- a/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java
+++ b/lint/libs/lint-tests/src/main/java/com/android/tools/lint/checks/infrastructure/LintDetectorTest.java
@@ -115,7 +115,7 @@
private Detector mDetector;
- private Detector getDetectorInstance() {
+ protected final Detector getDetectorInstance() {
if (mDetector == null) {
mDetector = getDetector();
}
@@ -123,20 +123,7 @@
return mDetector;
}
- protected List<Issue> getIssues() {
- List<Issue> issues = new ArrayList<Issue>();
- Class<? extends Detector> detectorClass = getDetectorInstance().getClass();
- // Get the list of issues from the registry and filter out others, to make sure
- // issues are properly registered
- List<Issue> candidates = new BuiltinIssueRegistry().getIssues();
- for (Issue issue : candidates) {
- if (issue.getImplementation().getDetectorClass() == detectorClass) {
- issues.add(issue);
- }
- }
-
- return issues;
- }
+ protected abstract List<Issue> getIssues();
public class CustomIssueRegistry extends IssueRegistry {
@NonNull
@@ -242,6 +229,16 @@
return checkLint(client, Collections.singletonList(projectDir));
}
+ protected String lintProjectIncrementally(String currentFile, TestFile... files)
+ throws Exception {
+ File projectDir = getProjectDir(null, files);
+ File current = new File(projectDir, currentFile.replace('/', File.separatorChar));
+ assertTrue(current.exists());
+ TestLintClient client = createClient();
+ client.setIncremental(current);
+ return checkLint(client, Collections.singletonList(projectDir));
+ }
+
/**
* Run lint on the given files when constructed as a separate project
* @return The output of the lint check. On Windows, this transforms all directory
@@ -543,8 +540,10 @@
}
}
+ @NonNull
@Override
- public Configuration getConfiguration(@NonNull Project project) {
+ public Configuration getConfiguration(@NonNull Project project,
+ @Nullable LintDriver driver) {
return LintDetectorTest.this.getConfiguration(this, project);
}
@@ -647,8 +646,11 @@
}
};
// Only support 1 resource folder in test setup right now
- assertEquals(1, project.getResourceFolders().size());
- resourceSet.addSource(project.getResourceFolders().get(0));
+ int size = project.getResourceFolders().size();
+ assertTrue("Found " + size + " test resources folders", size <= 1);
+ if (size == 1) {
+ resourceSet.addSource(project.getResourceFolders().get(0));
+ }
try {
resourceSet.loadFromFiles(logger);
merger.addDataSet(resourceSet);
@@ -737,9 +739,6 @@
}
}
}
- catch (DuplicateDataException e) {
- fail(e.getMessage());
- }
catch (MergingException e) {
fail(e.getMessage());
}
diff --git a/lint/libs/lint-tests/src/test/.settings/org.eclipse.core.resources.prefs b/lint/libs/lint-tests/src/test/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..609db57
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+encoding//java/com/android/tools/lint/checks/TypoDetectorTest.java=UTF-8
+encoding//java/com/android/tools/lint/checks/TypoLookupTest.java=UTF-8
+encoding//java/com/android/tools/lint/checks/TypographyDetectorTest.java=UTF-8
+encoding//java/com/android/tools/lint/checks/data/res/values-nb/typos.xml=UTF-8
diff --git a/lint/libs/lint-tests/src/test/.settings/org.moreunit.prefs b/lint/libs/lint-tests/src/test/.settings/org.moreunit.prefs
new file mode 100644
index 0000000..1d51ca3
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/.settings/org.moreunit.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+org.moreunit.prefixes=
+org.moreunit.unitsourcefolder=lint-check-tests\:java\:lint-api\:src/main/java\#lint-check-tests\:java\:lint-checks\:src/main/java\#lint-check-tests\:java\:lint-cli\:src/main/java
+org.moreunit.useprojectsettings=true
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/EcjParserTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/EcjParserTest.java
new file mode 100644
index 0000000..457a447
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/EcjParserTest.java
@@ -0,0 +1,916 @@
+/*
+ * 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;
+
+import static com.android.tools.lint.EcjParser.equalsCompound;
+import static com.android.tools.lint.EcjParser.startsWithCompound;
+import static com.android.tools.lint.client.api.JavaParser.ResolvedClass;
+import static com.android.tools.lint.client.api.JavaParser.ResolvedField;
+import static com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
+import static com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import static com.android.tools.lint.client.api.JavaParser.ResolvedVariable;
+
+import com.android.annotations.NonNull;
+import com.android.tools.lint.checks.AbstractCheckTest;
+import com.android.tools.lint.checks.SdCardDetector;
+import com.android.tools.lint.client.api.JavaParser;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.JavaContext;
+import com.android.tools.lint.detector.api.LintUtilsTest;
+import com.android.tools.lint.detector.api.Project;
+import com.google.common.collect.Lists;
+
+import org.intellij.lang.annotations.Language;
+import org.junit.Assert;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import lombok.ast.AnnotationElement;
+import lombok.ast.BinaryExpression;
+import lombok.ast.Block;
+import lombok.ast.ClassDeclaration;
+import lombok.ast.DescribedNode;
+import lombok.ast.ExpressionStatement;
+import lombok.ast.ForwardingAstVisitor;
+import lombok.ast.Identifier;
+import lombok.ast.KeywordModifier;
+import lombok.ast.MethodInvocation;
+import lombok.ast.Modifiers;
+import lombok.ast.Node;
+import lombok.ast.NormalTypeBody;
+import lombok.ast.Select;
+import lombok.ast.TypeReferencePart;
+import lombok.ast.VariableDeclaration;
+import lombok.ast.VariableDefinition;
+import lombok.ast.VariableReference;
+import lombok.ast.printer.SourceFormatter;
+import lombok.ast.printer.SourcePrinter;
+import lombok.ast.printer.TextFormatter;
+
+public class EcjParserTest extends AbstractCheckTest {
+ public void testTryCatchHang() throws Exception {
+ // Ensure that we're really using this parser
+ JavaParser javaParser = createClient().getJavaParser(null);
+ assertNotNull(javaParser);
+ assertTrue(javaParser.getClass().getName(), javaParser instanceof EcjParser);
+
+ // See https://code.google.com/p/projectlombok/issues/detail?id=573#c6
+ // With lombok.ast 0.2.1 and the parboiled-based Java parser this test will hang forever.
+ assertEquals(
+ "No warnings.",
+
+ lintProject("src/test/pkg/TryCatchHang.java.txt=>src/test/pkg/TryCatchHang.java"));
+ }
+
+ public void testKitKatLanguageFeatures() throws Exception {
+ String testClass = "" +
+ "package test.pkg;\n" +
+ "\n" +
+ "import java.io.BufferedReader;\n" +
+ "import java.io.FileReader;\n" +
+ "import java.io.IOException;\n" +
+ "import java.lang.reflect.InvocationTargetException;\n" +
+ "import java.util.List;\n" +
+ "import java.util.Map;\n" +
+ "import java.util.TreeMap;\n" +
+ "\n" +
+ "public class Java7LanguageFeatureTest {\n" +
+ " public void testDiamondOperator() {\n" +
+ " Map<String, List<Integer>> map = new TreeMap<>();\n" +
+ " }\n" +
+ "\n" +
+ " public int testStringSwitches(String value) {\n" +
+ " final String first = \"first\";\n" +
+ " final String second = \"second\";\n" +
+ "\n" +
+ " switch (value) {\n" +
+ " case first:\n" +
+ " return 41;\n" +
+ " case second:\n" +
+ " return 42;\n" +
+ " default:\n" +
+ " return 0;\n" +
+ " }\n" +
+ " }\n" +
+ "\n" +
+ " public String testTryWithResources(String path) throws IOException {\n" +
+ " try (BufferedReader br = new BufferedReader(new FileReader(path))) {\n" +
+ " return br.readLine();\n" +
+ " }\n" +
+ " }\n" +
+ "\n" +
+ " public void testNumericLiterals() {\n" +
+ " int thousand = 1_000;\n" +
+ " int million = 1_000_000;\n" +
+ " int binary = 0B01010101;\n" +
+ " }\n" +
+ "\n" +
+ " public void testMultiCatch() {\n" +
+ "\n" +
+ " try {\n" +
+ " Class.forName(\"java.lang.Integer\").getMethod(\"toString\").invoke(null);\n" +
+ " } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {\n" +
+ " e.printStackTrace();\n" +
+ " } catch (ClassNotFoundException e) {\n" +
+ " // TODO: Logging here\n" +
+ " }\n" +
+ " }\n" +
+ "}\n";
+
+ Node unit = LintUtilsTest.getCompilationUnit(testClass);
+ assertNotNull(unit);
+
+ // Now print the AST back and make sure that it contains at least the essence of the AST
+ TextFormatter formatter = new TextFormatter();
+ unit.accept(new SourcePrinter(formatter));
+ String actual = formatter.finish();
+ assertEquals(""
+ + "package test.pkg;\n"
+ + "\n"
+ + "import java.io.BufferedReader;\n"
+ + "import java.io.FileReader;\n"
+ + "import java.io.IOException;\n"
+ + "import java.lang.reflect.InvocationTargetException;\n"
+ + "import java.util.List;\n"
+ + "import java.util.Map;\n"
+ + "import java.util.TreeMap;\n"
+ + "\n"
+ + "public class Java7LanguageFeatureTest {\n"
+ + " public void testDiamondOperator() {\n"
+ + " Map<String, List<Integer>> map = new TreeMap();\n" // missing types on rhs
+ + " }\n"
+ + " \n"
+ + " public int testStringSwitches(String value) {\n"
+ + " final String first = \"first\";\n"
+ + " final String second = \"second\";\n"
+ + " switch (value) {\n"
+ + " case first:\n"
+ + " return 41;\n"
+ + " case second:\n"
+ + " return 42;\n"
+ + " default:\n"
+ + " return 0;\n"
+ + " }\n"
+ + " }\n"
+ + " \n"
+ + " public String testTryWithResources(String path) throws IOException {\n"
+ + " try {\n" // Note how the initialization clause is gone here
+ + " return br.readLine();\n"
+ + " }\n"
+ + " }\n"
+ + " \n"
+ + " public void testNumericLiterals() {\n"
+ + " int thousand = 1_000;\n"
+ + " int million = 1_000_000;\n"
+ + " int binary = 0B01010101;\n"
+ + " }\n"
+ + " \n"
+ + " public void testMultiCatch() {\n"
+ + " try {\n"
+ + " Class.forName(\"java.lang.Integer\").getMethod(\"toString\").invoke(null);\n"
+ + " } catch (IllegalAccessException e) {\n" // Note: missing other union types
+ + " e.printStackTrace();\n"
+ + " } catch (ClassNotFoundException e) {\n"
+ + " }\n"
+ + " }\n"
+ + "}",
+ actual);
+ }
+
+ @SuppressWarnings("ClassNameDiffersFromFileName")
+ public void testGetFields() throws Exception {
+ @Language("JAVA")
+ String source = ""
+ + "public class FieldTest {\n"
+ + " public int field1 = 1;\n"
+ + " public int field2 = 3;\n"
+ + " public int field3 = 5;\n"
+ + " \n"
+ + " public static class Inner extends FieldTest {\n"
+ + " public int field2 = 5;\n"
+ + " }\n"
+ + "}\n";
+
+ final JavaContext context = LintUtilsTest.parse(source,
+ new File("src/test/pkg/FieldTest.java"));
+ assertNotNull(context);
+
+ Node compilationUnit = context.getCompilationUnit();
+ assertNotNull(compilationUnit);
+ final AtomicBoolean found = new AtomicBoolean();
+ compilationUnit.accept(new ForwardingAstVisitor() {
+ @Override
+ public boolean visitClassDeclaration(ClassDeclaration node) {
+ if (node.astName().astValue().equals("Inner")) {
+ found.set(true);
+ ResolvedNode resolved = context.resolve(node);
+ assertNotNull(resolved);
+ ResolvedClass cls = (ResolvedClass) resolved;
+ List<ResolvedField> declaredFields = Lists.newArrayList(cls.getFields(false));
+ assertEquals(1, declaredFields.size());
+ assertEquals("field2", declaredFields.get(0).getName());
+
+ declaredFields = Lists.newArrayList(cls.getFields(true));
+ assertEquals(3, declaredFields.size());
+ assertEquals("field2", declaredFields.get(0).getName());
+ assertEquals("FieldTest.Inner", declaredFields.get(0).getContainingClassName());
+ assertEquals("field1", declaredFields.get(1).getName());
+ assertEquals("FieldTest", declaredFields.get(1).getContainingClassName());
+ assertEquals("field3", declaredFields.get(2).getName());
+ assertEquals("FieldTest", declaredFields.get(2).getContainingClassName());
+ }
+
+ return super.visitClassDeclaration(node);
+ }
+ });
+ assertTrue(found.get());
+ }
+
+ public void testResolution() throws Exception {
+ String source =
+ "package test.pkg;\n" +
+ "\n" +
+ "import java.io.File;\n" +
+ "\n" +
+ "public class TypeResolutionTest {\n" +
+ " public static class Inner extends File {\n" +
+ " public float myField = 5f;\n" +
+ " public int[] myInts;\n" +
+ "\n" +
+ " public Inner(File dir, String name) {\n" +
+ " super(dir, name);\n" +
+ " }\n" +
+ "\n" +
+ " public void call(int arg1, double arg2) {\n" +
+ " boolean x = super.canRead();\n" +
+ " System.out.println(x);\n" +
+ " }\n" +
+ " }\n" +
+ "\n" +
+ " @SuppressWarnings(\"all\")\n" +
+ " public static class Other {\n" +
+ " private void client(int z) {\n" +
+ " int x = z;\n" +
+ " int y = x + 5;\n" +
+ " Inner inner = new Inner(null, null);\n" +
+ " inner.myField = 6;\n" +
+ " System.out.println(inner.myInts);\n" +
+ " }\n" +
+ " }\n" +
+ "}\n";
+
+ Node unit = LintUtilsTest.getCompilationUnit(source,
+ new File("src/test/pkg/TypeResolutionTest.java"));
+
+ // Visit all nodes and assert nativeNode != null unless I expect it!
+ unit.accept(new ForwardingAstVisitor() {
+ @SuppressWarnings("Contract")
+ @Override
+ public boolean visitNode(Node node) {
+ if (node.getNativeNode() == null && requiresNativeNode(node)) {
+ fail("Expected native node on node of type " +
+ node.getClass().getSimpleName());
+ }
+ return super.visitNode(node);
+ }
+
+ private boolean requiresNativeNode(Node node) {
+ if (node instanceof TypeReferencePart &&
+ node.getParent().getNativeNode() != null) {
+ return false;
+ }
+
+ if (node instanceof Identifier
+ || node instanceof NormalTypeBody
+ || node instanceof Block
+ || node instanceof VariableDeclaration
+ || node instanceof VariableDefinition
+ || node instanceof AnnotationElement
+ || node instanceof BinaryExpression
+ || node instanceof Modifiers
+ || node instanceof KeywordModifier) {
+ return false;
+ }
+
+ if (node instanceof VariableReference) {
+ VariableReference reference = (VariableReference)node;
+ if (reference.getParent() instanceof Select) {
+ return false;
+ }
+ } else if (node instanceof MethodInvocation) {
+ Node parent = node.getParent();
+ if (parent instanceof ExpressionStatement &&
+ parent.getNativeNode() != null) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ });
+
+ JavaParser parser = new EcjParser(new LintCliClient(), null);
+ AstPrettyPrinter astPrettyPrinter = new AstPrettyPrinter(parser);
+ unit.accept(new SourcePrinter(astPrettyPrinter));
+ String actual = astPrettyPrinter.finish();
+ assertEquals(
+ "[CompilationUnit]\n" +
+ " [PackageDeclaration]\n" +
+ " [Identifier test]\n" +
+ " PROPERTY: name = test\n" +
+ " [Identifier pkg]\n" +
+ " PROPERTY: name = pkg\n" +
+ " [ImportDeclaration]\n" +
+ " PROPERTY: static = false\n" +
+ " PROPERTY: star = false\n" +
+ " [Identifier java]\n" +
+ " PROPERTY: name = java\n" +
+ " [Identifier io]\n" +
+ " PROPERTY: name = io\n" +
+ " [Identifier File]\n" +
+ " PROPERTY: name = File\n" +
+ " [ClassDeclaration TypeResolutionTest], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
+ " [Modifiers], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
+ " [KeywordModifier public]\n" +
+ " PROPERTY: modifier = public\n" +
+ " typeName: [Identifier TypeResolutionTest], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
+ " PROPERTY: name = TypeResolutionTest\n" +
+ " [NormalTypeBody], type: test.pkg.TypeResolutionTest, resolved class: test.pkg.TypeResolutionTest \n" +
+ " [ClassDeclaration Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+ " [Modifiers], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+ " [KeywordModifier public]\n" +
+ " PROPERTY: modifier = public\n" +
+ " [KeywordModifier static]\n" +
+ " PROPERTY: modifier = static\n" +
+ " typeName: [Identifier Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+ " PROPERTY: name = Inner\n" +
+ " extends: [TypeReference File], type: java.io.File, resolved class: java.io.File \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: java.io.File, resolved class: java.io.File \n" +
+ " [Identifier File]\n" +
+ " PROPERTY: name = File\n" +
+ " [NormalTypeBody], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+ " [VariableDeclaration], type: float, resolved class: float \n" +
+ " [VariableDefinition]\n" +
+ " PROPERTY: varargs = false\n" +
+ " [Modifiers]\n" +
+ " [KeywordModifier public]\n" +
+ " PROPERTY: modifier = public\n" +
+ " type: [TypeReference float], type: float, resolved class: float \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: float, resolved class: float \n" +
+ " [Identifier float]\n" +
+ " PROPERTY: name = float\n" +
+ " [VariableDefinitionEntry], resolved field: myField test.pkg.TypeResolutionTest.Inner\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " varName: [Identifier myField], resolved field: myField test.pkg.TypeResolutionTest.Inner\n" +
+ " PROPERTY: name = myField\n" +
+ " [FloatingPointLiteral 5.0], type: float\n" +
+ " PROPERTY: value = 5f\n" +
+ " [VariableDeclaration], type: int[], resolved class: int[] \n" +
+ " [VariableDefinition]\n" +
+ " PROPERTY: varargs = false\n" +
+ " [Modifiers]\n" +
+ " [KeywordModifier public]\n" +
+ " PROPERTY: modifier = public\n" +
+ " type: [TypeReference int[]], type: int[], resolved class: int[] \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 1\n" +
+ " [TypeReferencePart], type: int[], resolved class: int[] \n" +
+ " [Identifier int]\n" +
+ " PROPERTY: name = int\n" +
+ " [VariableDefinitionEntry], resolved field: myInts test.pkg.TypeResolutionTest.Inner\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " varName: [Identifier myInts], resolved field: myInts test.pkg.TypeResolutionTest.Inner\n" +
+ " PROPERTY: name = myInts\n" +
+ " [ConstructorDeclaration], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
+ " [Modifiers], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
+ " [KeywordModifier public]\n" +
+ " PROPERTY: modifier = public\n" +
+ " typeName: [Identifier Inner], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
+ " PROPERTY: name = Inner\n" +
+ " parameter: [VariableDefinition], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
+ " PROPERTY: varargs = false\n" +
+ " [Modifiers]\n" +
+ " type: [TypeReference File], type: java.io.File, resolved class: java.io.File \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: java.io.File, resolved class: java.io.File \n" +
+ " [Identifier File]\n" +
+ " PROPERTY: name = File\n" +
+ " [VariableDefinitionEntry], resolved variable: dir java.io.File\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " varName: [Identifier dir], resolved variable: dir java.io.File\n" +
+ " PROPERTY: name = dir\n" +
+ " parameter: [VariableDefinition], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
+ " PROPERTY: varargs = false\n" +
+ " [Modifiers]\n" +
+ " type: [TypeReference String], type: java.lang.String, resolved class: java.lang.String \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: java.lang.String, resolved class: java.lang.String \n" +
+ " [Identifier String]\n" +
+ " PROPERTY: name = String\n" +
+ " [VariableDefinitionEntry], resolved variable: name java.lang.String\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " varName: [Identifier name], resolved variable: name java.lang.String\n" +
+ " PROPERTY: name = name\n" +
+ " [Block], type: void, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
+ " [SuperConstructorInvocation], resolved method: java.io.File java.io.File\n" +
+ " [VariableReference], type: java.io.File, resolved variable: dir java.io.File\n" +
+ " [Identifier dir], type: java.io.File, resolved variable: dir java.io.File\n" +
+ " PROPERTY: name = dir\n" +
+ " [VariableReference], type: java.lang.String, resolved variable: name java.lang.String\n" +
+ " [Identifier name], type: java.lang.String, resolved variable: name java.lang.String\n" +
+ " PROPERTY: name = name\n" +
+ " [MethodDeclaration call], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
+ " [Modifiers], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
+ " [KeywordModifier public]\n" +
+ " PROPERTY: modifier = public\n" +
+ " returnType: [TypeReference void], type: void, resolved class: void \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: void, resolved class: void \n" +
+ " [Identifier void]\n" +
+ " PROPERTY: name = void\n" +
+ " methodName: [Identifier call], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
+ " PROPERTY: name = call\n" +
+ " parameter: [VariableDefinition], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
+ " PROPERTY: varargs = false\n" +
+ " [Modifiers]\n" +
+ " type: [TypeReference int], type: int, resolved class: int \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: int, resolved class: int \n" +
+ " [Identifier int]\n" +
+ " PROPERTY: name = int\n" +
+ " [VariableDefinitionEntry], resolved variable: arg1 int\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " varName: [Identifier arg1], resolved variable: arg1 int\n" +
+ " PROPERTY: name = arg1\n" +
+ " parameter: [VariableDefinition], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
+ " PROPERTY: varargs = false\n" +
+ " [Modifiers]\n" +
+ " type: [TypeReference double], type: double, resolved class: double \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: double, resolved class: double \n" +
+ " [Identifier double]\n" +
+ " PROPERTY: name = double\n" +
+ " [VariableDefinitionEntry], resolved variable: arg2 double\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " varName: [Identifier arg2], resolved variable: arg2 double\n" +
+ " PROPERTY: name = arg2\n" +
+ " [Block], type: void, resolved method: call test.pkg.TypeResolutionTest.Inner\n" +
+ " [VariableDeclaration], type: boolean, resolved class: boolean \n" +
+ " [VariableDefinition]\n" +
+ " PROPERTY: varargs = false\n" +
+ " [Modifiers]\n" +
+ " type: [TypeReference boolean], type: boolean, resolved class: boolean \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: boolean, resolved class: boolean \n" +
+ " [Identifier boolean]\n" +
+ " PROPERTY: name = boolean\n" +
+ " [VariableDefinitionEntry], resolved variable: x boolean\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " varName: [Identifier x], resolved variable: x boolean\n" +
+ " PROPERTY: name = x\n" +
+ " [MethodInvocation canRead], type: boolean, resolved method: canRead java.io.File\n" +
+ " operand: [Super], type: java.io.File\n" +
+ " methodName: [Identifier canRead], type: boolean, resolved method: canRead java.io.File\n" +
+ " PROPERTY: name = canRead\n" +
+ " [ExpressionStatement], type: void, resolved method: println java.io.PrintStream\n" +
+ " [MethodInvocation println], type: void, resolved method: println java.io.PrintStream\n" +
+ " operand: [Select], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
+ " operand: [VariableReference], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
+ " [Identifier System]\n" +
+ " PROPERTY: name = System\n" +
+ " selected: [Identifier out], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
+ " PROPERTY: name = out\n" +
+ " methodName: [Identifier println]\n" +
+ " PROPERTY: name = println\n" +
+ " [VariableReference], type: boolean, resolved variable: x boolean\n" +
+ " [Identifier x], type: boolean, resolved variable: x boolean\n" +
+ " PROPERTY: name = x\n" +
+ " [ClassDeclaration Other], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
+ " [Modifiers], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
+ " [Annotation SuppressWarnings], type: java.lang.SuppressWarnings, resolved annotation: java.lang.SuppressWarnings \n" +
+ " [TypeReference SuppressWarnings], type: java.lang.SuppressWarnings, resolved class: java.lang.SuppressWarnings \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: java.lang.SuppressWarnings, resolved class: java.lang.SuppressWarnings \n" +
+ " [Identifier SuppressWarnings]\n" +
+ " PROPERTY: name = SuppressWarnings\n" +
+ " [AnnotationElement null], type: java.lang.SuppressWarnings, resolved annotation: java.lang.SuppressWarnings \n" +
+ " [StringLiteral all], type: java.lang.String\n" +
+ " PROPERTY: value = \"all\"\n" +
+ " [KeywordModifier public]\n" +
+ " PROPERTY: modifier = public\n" +
+ " [KeywordModifier static]\n" +
+ " PROPERTY: modifier = static\n" +
+ " typeName: [Identifier Other], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
+ " PROPERTY: name = Other\n" +
+ " [NormalTypeBody], type: test.pkg.TypeResolutionTest.Other, resolved class: test.pkg.TypeResolutionTest.Other \n" +
+ " [MethodDeclaration client], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
+ " [Modifiers], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
+ " [KeywordModifier private]\n" +
+ " PROPERTY: modifier = private\n" +
+ " returnType: [TypeReference void], type: void, resolved class: void \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: void, resolved class: void \n" +
+ " [Identifier void]\n" +
+ " PROPERTY: name = void\n" +
+ " methodName: [Identifier client], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
+ " PROPERTY: name = client\n" +
+ " parameter: [VariableDefinition], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
+ " PROPERTY: varargs = false\n" +
+ " [Modifiers]\n" +
+ " type: [TypeReference int], type: int, resolved class: int \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: int, resolved class: int \n" +
+ " [Identifier int]\n" +
+ " PROPERTY: name = int\n" +
+ " [VariableDefinitionEntry], resolved variable: z int\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " varName: [Identifier z], resolved variable: z int\n" +
+ " PROPERTY: name = z\n" +
+ " [Block], type: void, resolved method: client test.pkg.TypeResolutionTest.Other\n" +
+ " [VariableDeclaration], type: int, resolved class: int \n" +
+ " [VariableDefinition]\n" +
+ " PROPERTY: varargs = false\n" +
+ " [Modifiers]\n" +
+ " type: [TypeReference int], type: int, resolved class: int \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: int, resolved class: int \n" +
+ " [Identifier int]\n" +
+ " PROPERTY: name = int\n" +
+ " [VariableDefinitionEntry], resolved variable: x int\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " varName: [Identifier x], resolved variable: x int\n" +
+ " PROPERTY: name = x\n" +
+ " [VariableReference], type: int, resolved variable: z int\n" +
+ " [Identifier z], type: int, resolved variable: z int\n" +
+ " PROPERTY: name = z\n" +
+ " [VariableDeclaration], type: int, resolved class: int \n" +
+ " [VariableDefinition]\n" +
+ " PROPERTY: varargs = false\n" +
+ " [Modifiers]\n" +
+ " type: [TypeReference int], type: int, resolved class: int \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: int, resolved class: int \n" +
+ " [Identifier int]\n" +
+ " PROPERTY: name = int\n" +
+ " [VariableDefinitionEntry], resolved variable: y int\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " varName: [Identifier y], resolved variable: y int\n" +
+ " PROPERTY: name = y\n" +
+ " [BinaryExpression +], type: int\n" +
+ " PROPERTY: operator = +\n" +
+ " left: [VariableReference], type: int, resolved variable: x int\n" +
+ " [Identifier x], type: int, resolved variable: x int\n" +
+ " PROPERTY: name = x\n" +
+ " right: [IntegralLiteral 5], type: int\n" +
+ " PROPERTY: value = 5\n" +
+ " [VariableDeclaration], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+ " [VariableDefinition]\n" +
+ " PROPERTY: varargs = false\n" +
+ " [Modifiers]\n" +
+ " type: [TypeReference Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+ " [Identifier Inner]\n" +
+ " PROPERTY: name = Inner\n" +
+ " [VariableDefinitionEntry], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " varName: [Identifier inner], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+ " PROPERTY: name = inner\n" +
+ " [ConstructorInvocation Inner], type: test.pkg.TypeResolutionTest.Inner, resolved method: test.pkg.TypeResolutionTest.Inner test.pkg.TypeResolutionTest.Inner\n" +
+ " type: [TypeReference Inner], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+ " PROPERTY: WildcardKind = NONE\n" +
+ " PROPERTY: arrayDimensions = 0\n" +
+ " [TypeReferencePart], type: test.pkg.TypeResolutionTest.Inner, resolved class: test.pkg.TypeResolutionTest.Inner \n" +
+ " [Identifier Inner]\n" +
+ " PROPERTY: name = Inner\n" +
+ " [NullLiteral], type: null\n" +
+ " [NullLiteral], type: null\n" +
+ " [ExpressionStatement], type: float\n" +
+ " [BinaryExpression =], type: float\n" +
+ " PROPERTY: operator = =\n" +
+ " left: [Select], type: float, resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+ " operand: [VariableReference], type: float, resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+ " [Identifier inner]\n" +
+ " PROPERTY: name = inner\n" +
+ " selected: [Identifier myField], type: float, resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+ " PROPERTY: name = myField\n" +
+ " right: [IntegralLiteral 6], type: int\n" +
+ " PROPERTY: value = 6\n" +
+ " [ExpressionStatement], type: void, resolved method: println java.io.PrintStream\n" +
+ " [MethodInvocation println], type: void, resolved method: println java.io.PrintStream\n" +
+ " operand: [Select], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
+ " operand: [VariableReference], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
+ " [Identifier System]\n" +
+ " PROPERTY: name = System\n" +
+ " selected: [Identifier out], type: java.io.PrintStream, resolved field: out java.lang.System\n" +
+ " PROPERTY: name = out\n" +
+ " methodName: [Identifier println]\n" +
+ " PROPERTY: name = println\n" +
+ " [Select], type: int[], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+ " operand: [VariableReference], type: int[], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+ " [Identifier inner]\n" +
+ " PROPERTY: name = inner\n" +
+ " selected: [Identifier myInts], type: int[], resolved variable: inner test.pkg.TypeResolutionTest.Inner\n" +
+ " PROPERTY: name = myInts\n",
+ actual);
+ }
+
+ public void testStartsWithCompound() throws Exception {
+ assertTrue(startsWithCompound("test.pkg",
+ new char[][]{
+ "test".toCharArray(),
+ "pkg".toCharArray()
+ }));
+
+ assertTrue(startsWithCompound("test.pkg",
+ new char[][]{
+ "test".toCharArray(),
+ "pkg".toCharArray(),
+ "other".toCharArray(),
+ }));
+
+ assertFalse(startsWithCompound("test.pkg",
+ new char[][]{
+ "test".toCharArray(),
+ "other".toCharArray()
+ }));
+
+ // Corner cases
+ assertFalse(startsWithCompound("test.pk",
+ new char[][]{
+ "test".toCharArray(),
+ "pkg".toCharArray(),
+ }));
+ assertFalse(startsWithCompound("test.pkg",
+ new char[][]{
+ "test".toCharArray()
+ }));
+ assertTrue(startsWithCompound("test.",
+ new char[][]{
+ "test".toCharArray(),
+ "pkg".toCharArray()
+ }));
+ assertFalse(startsWithCompound("test.pkg",
+ new char[][]{
+ "test".toCharArray(),
+ "pk".toCharArray()
+ }));
+ }
+
+ public void testEqualsWithCompound() throws Exception {
+ assertTrue(equalsCompound("test.pkg",
+ new char[][]{
+ "test".toCharArray(),
+ "pkg".toCharArray()
+ }));
+
+ assertFalse(equalsCompound("test.pkg",
+ new char[][]{
+ "test".toCharArray(),
+ "pkg".toCharArray(),
+ "other".toCharArray(),
+ }));
+
+ assertFalse(equalsCompound("test.pkg",
+ new char[][]{
+ "test".toCharArray(),
+ "other".toCharArray()
+ }));
+
+ // Corner cases
+ assertFalse(equalsCompound("test.pk",
+ new char[][]{
+ "test".toCharArray(),
+ "pkg".toCharArray(),
+ }));
+ assertFalse(equalsCompound("test.pkg",
+ new char[][]{
+ "test".toCharArray()
+ }));
+ assertFalse(equalsCompound("test.",
+ new char[][]{
+ "test".toCharArray(),
+ "pkg".toCharArray()
+ }));
+ assertFalse(equalsCompound("test.pkg",
+ new char[][]{
+ "test".toCharArray(),
+ "pk".toCharArray()
+ }));
+ }
+
+ @Override
+ protected Detector getDetector() {
+ return new SdCardDetector();
+ }
+
+ @Override
+ protected TestLintClient createClient() {
+ return new TestLintClient() {
+ @NonNull
+ @Override
+ protected ClassPathInfo getClassPath(@NonNull Project project) {
+ ClassPathInfo classPath = super.getClassPath(project);
+ // Insert fake classpath entries (non existent directories) to
+ // make sure the parser handles that gracefully. See issue 87740.
+ classPath.getLibraries().add(new File("nonexistent path"));
+ return classPath;
+ }
+ };
+ }
+
+ public static class AstPrettyPrinter implements SourceFormatter {
+
+ private final StringBuilder mOutput = new StringBuilder(1000);
+
+ private final JavaParser mResolver;
+
+ private int mIndent;
+
+ private String mName;
+
+ public AstPrettyPrinter(JavaParser resolver) {
+ mResolver = resolver;
+ }
+
+ private void add(String in, Object... args) {
+ for (int i = 0; i < mIndent; i++) {
+ mOutput.append(" ");
+ }
+ if (mName != null) {
+ mOutput.append(mName).append(": ");
+ mName = null;
+ }
+ if (args.length == 0) {
+ mOutput.append(in);
+ } else {
+ mOutput.append(String.format(in, args));
+ }
+ }
+
+ @Override
+ public void buildInline(Node node) {
+ buildNode(node);
+ }
+
+ @Override
+ public void buildBlock(Node node) {
+ buildNode(node);
+ }
+
+ private void buildNode(Node node) {
+ if (node == null) {
+ mIndent++;
+ return;
+ }
+ String name = node.getClass().getSimpleName();
+ String description = "";
+ if (node instanceof DescribedNode) {
+ description = " " + ((DescribedNode) node).getDescription();
+ }
+
+ String typeDescription = "";
+ String resolutionDescription = "";
+ JavaParser.TypeDescriptor t = mResolver.getType(null, node);
+ if (t != null) {
+ typeDescription = ", type: " + t.getName();
+ }
+ ResolvedNode resolved = mResolver.resolve(null, node);
+ if (resolved != null) {
+ String c = "unknown";
+ String extra = "";
+ if (resolved instanceof ResolvedClass) {
+ c = "class";
+ } else if (resolved instanceof ResolvedMethod) {
+ c = "method";
+ ResolvedMethod method = (ResolvedMethod) resolved;
+ extra = method.getContainingClass().getName();
+ } else if (resolved instanceof ResolvedField) {
+ c = "field";
+ ResolvedField field = (ResolvedField) resolved;
+ extra = field.getContainingClass().getName();
+ } else if (resolved instanceof ResolvedVariable) {
+ c = "variable";
+ ResolvedVariable variable = (ResolvedVariable) resolved;
+ extra = variable.getType().getName();
+ } else if (resolved instanceof ResolvedAnnotation) {
+ c = "annotation";
+ }
+ resolutionDescription = String.format(", resolved %1$s: %2$s %3$s",
+ c, resolved.getName(), extra);
+ }
+
+ add("[%1$s%2$s]%3$s%4$s\n", name, description, typeDescription, resolutionDescription);
+
+ mIndent++;
+ }
+
+ @Override
+ public void fail(String fail) {
+ Assert.fail(fail);
+ }
+
+ @Override
+ public void property(String name, Object value) {
+ add("PROPERTY: %s = %s\n", name, value);
+ }
+
+ @Override
+ public void keyword(String text) {
+ }
+
+ @Override
+ public void operator(String text) {
+ }
+
+ @Override
+ public void verticalSpace() {
+ }
+
+ @Override
+ public void space() {
+ }
+
+ @Override
+ public void append(String text) {
+ }
+
+ @Override
+ public void startSuppressBlock() {
+ }
+
+ @Override
+ public void endSuppressBlock() {
+ }
+
+ @Override
+ public void startSuppressIndent() {
+ }
+
+ @Override
+ public void endSuppressIndent() {
+ }
+
+ @Override
+ public void closeInline() {
+ mIndent--;
+ }
+
+ @Override
+ public void closeBlock() {
+ mIndent--;
+ }
+
+ @Override
+ public void addError(int start, int end, String message) {
+ fail(message);
+ }
+
+ @Override
+ public String finish() {
+ return mOutput.toString();
+ }
+
+ @Override
+ public void setTimeTaken(long taken) {
+ }
+
+ @Override
+ public void nameNextElement(String name) {
+ mName = name;
+ }
+ }
+}
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/ExternalAnnotationRepositoryTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/ExternalAnnotationRepositoryTest.java
new file mode 100644
index 0000000..774a4c8
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/ExternalAnnotationRepositoryTest.java
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint;
+
+import static com.android.tools.lint.ExternalAnnotationRepository.FN_ANNOTATIONS_XML;
+import static com.google.common.base.Charsets.UTF_8;
+import static java.io.File.separatorChar;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.testutils.SdkTestCase;
+import com.android.tools.lint.client.api.JavaParser.DefaultTypeDescriptor;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
+import com.android.tools.lint.client.api.JavaParser.ResolvedClass;
+import com.android.tools.lint.client.api.JavaParser.ResolvedField;
+import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
+import com.android.tools.lint.client.api.JavaParser.ResolvedNode;
+import com.android.tools.lint.client.api.JavaParser.ResolvedPackage;
+import com.android.tools.lint.client.api.JavaParser.TypeDescriptor;
+import com.android.tools.lint.detector.api.JavaContext;
+import com.android.tools.lint.detector.api.LintUtilsTest;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import lombok.ast.ClassDeclaration;
+import lombok.ast.ForwardingAstVisitor;
+import lombok.ast.MethodDeclaration;
+import lombok.ast.MethodInvocation;
+import lombok.ast.Node;
+
+public class ExternalAnnotationRepositoryTest extends SdkTestCase {
+
+ @Nullable
+ private ExternalAnnotationRepository getSdkAnnotations() {
+ File annotations = findSrcRelativeDir("tools/adt/idea/android/annotations");
+ if (annotations != null) {
+ List<File> files = Collections.singletonList(annotations);
+ ExternalAnnotationRepository manager = ExternalAnnotationRepository.create(null, files);
+ assertNotNull(manager);
+ return manager;
+ } else {
+ // Can't find it when running from Gradle; ignore for now
+ //fail("Could not find annotations database");
+ }
+
+ return null;
+ }
+
+ @Nullable
+ private ExternalAnnotationRepository getExternalAnnotations(@NonNull String pkg,
+ @NonNull String contents) throws IOException {
+ File dir = Files.createTempDir();
+ try {
+ File pkgDir = new File(dir, pkg.replace('.', separatorChar));
+ boolean mkdirs = pkgDir.mkdirs();
+ assertTrue(mkdirs);
+ Files.write(contents, new File(pkgDir, FN_ANNOTATIONS_XML), UTF_8);
+
+ List<File> files = Collections.singletonList(dir);
+ ExternalAnnotationRepository manager = ExternalAnnotationRepository.create(null, files);
+ assertNotNull(manager);
+ return manager;
+
+ } finally {
+ deleteFile(dir);
+ }
+ }
+
+ public void testFields() throws Exception {
+ ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<root>\n"
+ + " <item name=\"android.graphics.Color\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation1\" />\n"
+ + " </item>\n"
+ + " <item name=\"android.graphics.Color BLUE\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation3\" />\n"
+ + " </item>\n"
+ + " <item name=\"android.graphics.Color TRANSPARENT\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ + " </item>\n"
+ + "</root>\n");
+ assertNotNull(manager);
+ ResolvedClass cls = createClass("android.graphics.Color");
+ assertNotNull(manager.getAnnotation(cls, "android.support.annotation.Annotation1"));
+ ResolvedField blueField = createField("android.graphics.Color", "BLUE");
+ ResolvedField transparentField = createField("android.graphics.Color", "TRANSPARENT");
+ assertNotNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation3"));
+ assertNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation4"));
+ assertNull(manager.getAnnotation(blueField, "android.support.annotation.Annotation5"));
+
+ assertNotNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation5"));
+ assertNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation3"));
+ assertNull(manager.getAnnotation(transparentField, "android.support.annotation.Annotation4"));
+ }
+
+ public void testMethods1() throws Exception {
+ ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<root>\n"
+ + " <item name=\"android.graphics.Color int HSVToColor(float[]) 0\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ + " </item>\n"
+ + " <item name=\"android.graphics.Color int HSVToColor(int, float[]) 1\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation7\">\n"
+ + " <val name=\"value\" val=\"3\" />\n"
+ + " </annotation>\n"
+ + " </item>\n"
+ + " <item name=\"android.graphics.Color int alpha(int)\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation3\" />\n"
+ + " </item>\n"
+ + " <item name=\"android.graphics.Color int argb(int, int, int, int)\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation3\" />\n"
+ + " </item>\n"
+ + " <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode) 4\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ + " </item>\n"
+ + " <item name=\"android.graphics.ArrayAdapter ArrayAdapter(android.content.Context, int, int, java.util.List<T>) 3\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation4\" />\n"
+ + " </item>"
+ + "</root>\n");
+ assertNotNull(manager);
+ ResolvedMethod method1 = createMethod("android.graphics.Color", "int", "HSVToColor",
+ "float[]");
+ assertNotNull(manager.getAnnotation(method1, 0, "android.support.annotation.Annotation5"));
+
+ // Generic types
+ ResolvedMethod method2 = createConstructor("android.graphics.ArrayAdapter", "ArrayAdapter",
+ "android.content.Context, int, int, java.util.List<T>");
+ assertNotNull(manager.getAnnotation(method2, 3, "android.support.annotation.Annotation4"));
+
+ // Raw types
+ method2 = createConstructor("android.graphics.ArrayAdapter", "ArrayAdapter",
+ "android.content.Context, int, int, java.util.List");
+ assertNotNull(manager.getAnnotation(method2, 3, "android.support.annotation.Annotation4"));
+ }
+
+ public void testMethods2() throws Exception {
+ ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<root>\n"
+ + " <item name=\"test.pkg.Test java.lang.Object myMethod()\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ + " <annotation name=\"android.support.annotation.Annotation6\">\n"
+ + " <val name=\"suggest\" val=\""#other(String,String)"\" />\n"
+ + " </annotation>\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.Test java.lang.Object myMethod(int) 0\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation7\">\n"
+ + " <val name=\"value\" val=\"3\" />\n"
+ + " </annotation>\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.Test java.lang.Object myMethod(int[]) 0\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation3\" />\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.Test java.lang.Object myMethod(int,java.lang.Object) 1\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.Test java.lang.Object myMethod(android.content.Context, int, int, java.util.List<T>) 0\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.Test java.lang.Object myMethod(android.content.Context, int, int, java.util.List<T>) 1\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation3\" />\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.Test java.lang.Object myMethod(java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.String>>,int) 0\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation4\" />\n"
+ + " </item>\n"
+ + "</root>\n");
+ assertNotNull(manager);
+ ResolvedMethod method;
+ method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "");
+ assertNotNull(manager.getAnnotation(method, "android.support.annotation.Annotation5"));
+ assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
+ assertNotNull(manager.getAnnotation(method, "android.support.annotation.Annotation6"));
+
+ method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "int");
+ assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
+ assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation7"));
+
+ method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod", "int[]");
+ assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
+ assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation3"));
+ assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
+ assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation3"));
+
+ method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
+ "int,java.lang.Object");
+ assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation5"));
+
+ method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
+ "android.content.Context, int, int, java.util.List<T>");
+ assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation4"));
+ assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation5"));
+ assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
+ assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation3"));
+ assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation5"));
+ assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation3"));
+ assertNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation5"));
+
+ method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
+ "android.content.Context, int, int, java.util.List");
+ assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation5"));
+ assertNotNull(manager.getAnnotation(method, 1, "android.support.annotation.Annotation3"));
+
+ method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
+ Arrays.asList("java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.String>>",
+ "int"), false);
+ assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
+ method = createMethod("test.pkg.Test", "java.lang.Object", "myMethod",
+ "java.util.Map,int");
+ assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
+ }
+
+ // test intdef!
+
+
+ public void testAnnotationAttributes() throws Exception {
+ ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<root>\n"
+ + " <item name=\"android.graphics.Color int HSVToColor(float[]) 0\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation7\">\n"
+ + " <val name=\"value\" val=\"3\" />\n"
+ + " </annotation>\n"
+ + " </item>\n"
+ + " <item name=\"android.graphics.Color int HSVToColor(int, float[]) 1\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation7\">\n"
+ + " <val name=\"value\" val=\"3\" />\n"
+ + " </annotation>\n"
+ + " </item>\n"
+ + " <item name=\"android.graphics.Canvas void drawLines(float[], android.graphics.Paint) 0\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation7\">\n"
+ + " <val name=\"min\" val=\"4\" />\n"
+ + " <val name=\"multiple\" val=\"2\" />\n"
+ + " </annotation>\n"
+ + " <annotation name=\"android.support.annotation.Annotation4\" />\n"
+ + " </item>\n"
+ + " <item name=\"android.graphics.Canvas int saveLayer(android.graphics.RectF, android.graphics.Paint, int) 2\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation8\">\n"
+ + " <val name=\"value\" val=\"{android.graphics.Canvas.MATRIX_SAVE_FLAG, android.graphics.Canvas.CLIP_SAVE_FLAG, android.graphics.Canvas.HAS_ALPHA_LAYER_SAVE_FLAG, android.graphics.Canvas.FULL_COLOR_LAYER_SAVE_FLAG, android.graphics.Canvas.CLIP_TO_LAYER_SAVE_FLAG, android.graphics.Canvas.ALL_SAVE_FLAG}\" />\n"
+ + " <val name=\"flag\" val=\"true\" />\n"
+ + " </annotation>\n"
+ + " </item>\n"
+ + "</root>\n");
+ assertNotNull(manager);
+ ResolvedMethod method;
+ ResolvedAnnotation annotation;
+
+ // Size 1
+ method = createMethod("android.graphics.Color", "int", "HSVToColor", "int, float[]");
+ assertNull(manager.getAnnotation(method, "android.support.annotation.Annotation7"));
+ assertNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation7"));
+ annotation = manager.getAnnotation(method, 1, "android.support.annotation.Annotation7");
+ assertNotNull(annotation);
+ assertEquals(3L, annotation.getValue());
+ assertEquals(3L, annotation.getValue("value"));
+ //noinspection ConstantConditions
+ assertEquals(3, ((Number)annotation.getValue("value")).intValue());
+ assertNotNull(annotation);
+
+ // Size 2
+ method = createMethod("android.graphics.Canvas", "void", "drawLines", "float[], android.graphics.Paint");
+ assertNotNull(manager.getAnnotation(method, 0, "android.support.annotation.Annotation4"));
+ annotation = manager.getAnnotation(method, 0, "android.support.annotation.Annotation7");
+ assertNotNull(annotation);
+ assertEquals(4L, annotation.getValue("min"));
+ assertEquals(2L, annotation.getValue("multiple"));
+ assertNotNull(annotation);
+
+ // Intdef
+ method = createMethod("android.graphics.Canvas", "int", "saveLayer",
+ "android.graphics.RectF, android.graphics.Paint, int");
+ annotation = manager.getAnnotation(method, 2, "android.support.annotation.Annotation8");
+ assertNotNull(annotation);
+ assertEquals(true, annotation.getValue("flag"));
+ Object[] values = (Object[]) annotation.getValue("value");
+ assertNotNull(values);
+ assertEquals(6, values.length);
+ assertTrue(values[0] instanceof ResolvedField);
+ assertFalse(values[0].equals(createField("android.graphics.Canvas", "WRONG_NAME")));
+ assertEquals(values[0], createField("android.graphics.Canvas", "MATRIX_SAVE_FLAG"));
+ assertEquals(values[1], createField("android.graphics.Canvas", "CLIP_SAVE_FLAG"));
+ assertEquals(values[2], createField("android.graphics.Canvas", "HAS_ALPHA_LAYER_SAVE_FLAG"));
+ assertEquals(values[3], createField("android.graphics.Canvas", "FULL_COLOR_LAYER_SAVE_FLAG"));
+ assertEquals(values[4], createField("android.graphics.Canvas", "CLIP_TO_LAYER_SAVE_FLAG"));
+ assertEquals(values[5], createField("android.graphics.Canvas", "ALL_SAVE_FLAG"));
+
+ ResolvedField field = (ResolvedField)values[0];
+ assertEquals("android.graphics.Canvas.MATRIX_SAVE_FLAG", field.getSignature());
+ assertEquals("android.graphics.Canvas", field.getContainingClassName());
+ assertEquals("MATRIX_SAVE_FLAG", field.getName());
+ }
+
+ public void testConstructors() throws Exception {
+ ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<root>\n"
+ + " <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode) 4\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ + " </item>\n"
+ + "</root>\n");
+ assertNotNull(manager);
+ ResolvedMethod method = createConstructor("android.graphics.RadialGradient",
+ "RadialGradient",
+ "float, float, float, int[], float[], android.graphics.Shader.TileMode");
+ assertNull(manager.getAnnotation(method, 4, "android.support.annotation.Annotation4"));
+ assertNotNull(manager.getAnnotation(method, 4, "android.support.annotation.Annotation5"));
+ }
+
+ public void testVarArgs() throws Exception {
+ ExternalAnnotationRepository manager = getExternalAnnotations("android.graphics", ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<root>\n"
+ + " <item name=\"android.graphics.RadialGradient RadialGradient(float, float, float, int...) 3\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ + " </item>\n"
+ + " <item name=\"android.graphics.Bitmap android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]) 2\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ + " </item>\n"
+ + "</root>\n");
+ assertNotNull(manager);
+ // Match "..." in external annotation with ... in code lookup
+ ResolvedMethod method = createConstructor("android.graphics.RadialGradient",
+ "RadialGradient",
+ "float, float, float, int...");
+ assertNotNull(manager.getAnnotation(method, 3, "android.support.annotation.Annotation5"));
+ // Match "..." in external annotation with [] in code lookup
+ method = createConstructor("android.graphics.RadialGradient",
+ "RadialGradient",
+ "float, float, float, int[]");
+ assertNotNull(manager.getAnnotation(method, 3, "android.support.annotation.Annotation5"));
+
+ // Match "[]" in external annotation with [] in code lookup
+ method = createMethod("android.graphics.Bitmap",
+ "android.graphics.Bitmap",
+ "extractAlpha",
+ "android.graphics.Paint, int[]");
+ assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.Annotation5"));
+
+ // Match "[]" in external annotation with ... in code lookup
+ method = createMethod("android.graphics.Bitmap",
+ "android.graphics.Bitmap",
+ "extractAlpha",
+ "android.graphics.Paint, int...");
+ assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.Annotation5"));
+ }
+
+ public void testPackage() throws Exception {
+ ExternalAnnotationRepository manager = getExternalAnnotations("foo.bar.baz", ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<root>\n"
+ + " <item name=\"foo.bar.baz.package-info\">\n"
+ + " <annotation name=\"my.pkg.MyAnnotation\"/>\n"
+ + " </item>\n"
+ + "</root>\n");
+ assertNotNull(manager);
+ ResolvedClass cls = createClass("foo.bar.baz.AdView");
+ ResolvedPackage pkg = cls.getPackage();
+ assertNotNull(pkg);
+ assertNull(manager.getAnnotation(pkg, "foo.bar.Baz"));
+ assertNotNull(manager.getAnnotation(pkg, "my.pkg.MyAnnotation"));
+ }
+
+ public void testMatchWithEcj() throws Exception {
+ try {
+ ExternalAnnotationRepository manager = getExternalAnnotations("test.pkg", ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<root>\n"
+ + " <item name=\"test.pkg.Test\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation1\" />\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.Test.Inner\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation2\" />\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.Test void foo(int, int[], int...)\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation6\" />\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.Test void foo(int, int[], int...) 0\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation3\" />\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.Test void foo(int, int[], int...) 1\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation3\" />\n"
+ + " <annotation name=\"android.support.annotation.Annotation4\" />\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.Test void foo(int, int[], int...) 2\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation5\" />\n"
+ + " </item>\n"
+ + "</root>\n");
+ assertNotNull(manager);
+ ExternalAnnotationRepository.set(manager);
+
+ String source =
+ "package test.pkg;\n" +
+ "\n" +
+ "public class Test {\n" +
+ " public void foo(int a, int[] b, int... c) {\n" +
+ " }\n" +
+ " public static class Inner {\n" +
+ " }\n" +
+ "}\n";
+
+ final JavaContext context = LintUtilsTest.parse(source,
+ new File("src/test/pkg/Test.java"));
+ assertNotNull(context);
+ Node unit = context.getCompilationUnit();
+ assertNotNull(unit);
+ unit.accept(new ForwardingAstVisitor() {
+ @Override
+ public boolean visitClassDeclaration(ClassDeclaration node) {
+ ResolvedNode resolved = context.resolve(node);
+ assertNotNull(resolved);
+ assertTrue(resolved.getClass().getName(), resolved instanceof ResolvedClass);
+ ResolvedClass cls = (ResolvedClass) resolved;
+ if (cls.getName().endsWith(".Inner")) {
+ assertNull(cls.getAnnotation("android.support.annotation.Annotation1"));
+ assertNotNull(cls.getAnnotation("android.support.annotation.Annotation2"));
+ } else {
+ assertNotNull(cls.getAnnotation("android.support.annotation.Annotation1"));
+ assertNull(cls.getAnnotation("android.support.annotation.Annotation2"));
+ }
+
+ return super.visitClassDeclaration(node);
+ }
+
+ @Override
+ public boolean visitMethodDeclaration(MethodDeclaration node) {
+ ResolvedNode resolved = context.resolve(node);
+ assertNotNull(resolved);
+ assertTrue(resolved.getClass().getName(), resolved instanceof ResolvedMethod);
+ ResolvedMethod method = (ResolvedMethod) resolved;
+ assertNull(method.getAnnotation("android.support.annotation.Annotation5"));
+ assertNotNull(method.getAnnotation("android.support.annotation.Annotation6"));
+ assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation3", 0));
+ assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation4", 1));
+ assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation3", 1));
+ assertNotNull(method.getParameterAnnotation("android.support.annotation.Annotation5", 2));
+
+ return super.visitMethodDeclaration(node);
+ }
+ });
+ } finally {
+ ExternalAnnotationRepository.set(null);
+ }
+ }
+
+ public void testMergeParameters() throws Exception {
+ try {
+ ExternalAnnotationRepository manager = getExternalAnnotations("test.pkg", ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<root>\n"
+ + " <item name=\"test.pkg.Test.Parent void testMethod(int)\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation1\" />\n"
+ + " </item>\n"
+ + " <item name=\"test.pkg.Test.Child void testMethod(int)\">\n"
+ + " <annotation name=\"android.support.annotation.Annotation2\" />\n"
+ + " </item>\n"
+ + "</root>\n");
+ assertNotNull(manager);
+ ExternalAnnotationRepository.set(manager);
+
+ String source = ""
+ + "package test.pkg;\n"
+ + "\n"
+ + "public class Test {\n"
+ + " public void test(Child child) {\n"
+ + " child.testMethod(5);\n"
+ + " }\n"
+ + "\n"
+ + " public static class Parent {\n"
+ + " public void testMethod(int parameter) {\n"
+ + " }\n"
+ + " }\n"
+ + "\n"
+ + " public static class Child extends Parent {\n"
+ + " @Override\n"
+ + " public void testMethod(int parameter) {\n"
+ + " }\n"
+ + " }\n"
+ + "}\n";
+
+ final JavaContext context = LintUtilsTest.parse(source,
+ new File("src/test/pkg/Test.java"));
+ assertNotNull(context);
+ Node unit = context.getCompilationUnit();
+ assertNotNull(unit);
+ final AtomicBoolean foundMethod = new AtomicBoolean();
+ unit.accept(new ForwardingAstVisitor() {
+ @Override
+ public boolean visitMethodInvocation(MethodInvocation node) {
+ foundMethod.set(true);
+ assertEquals("testMethod", node.astName().astValue());
+ ResolvedNode resolved = context.resolve(node);
+ assertTrue(resolved instanceof ResolvedMethod);
+ ResolvedMethod method = (ResolvedMethod)resolved;
+ List<ResolvedAnnotation> annotations =
+ Lists.newArrayList(method.getAnnotations());
+ assertEquals(3, annotations.size());
+ Collections.sort(annotations,
+ new Comparator<ResolvedAnnotation>() {
+ @Override
+ public int compare(ResolvedAnnotation a1,
+ ResolvedAnnotation a2) {
+ return a1.getName().compareTo(a2.getName());
+ }
+ });
+ assertEquals("android.support.annotation.Annotation1", annotations.get(0).getName());
+ assertEquals("android.support.annotation.Annotation2", annotations.get(1).getName());
+ assertEquals("java.lang.Override", annotations.get(2).getName());
+ return super.visitMethodInvocation(node);
+ }
+ });
+ assertTrue(foundMethod.get());
+ } finally {
+ ExternalAnnotationRepository.set(null);
+ }
+ }
+
+ public void testSdkAnnotations() throws Exception {
+ ExternalAnnotationRepository manager = getSdkAnnotations();
+ if (manager == null) {
+ // Can't find it when running from Gradle; ignore for now
+ return;
+ }
+ ResolvedMethod method = createMethod("android.view.LayoutInflater", "android.view.View",
+ "createView", "java.lang.String, java.lang.String, android.util.AttributeSet");
+ assertNotNull(manager.getAnnotation(method, 2, "android.support.annotation.NonNull"));
+ }
+
+ private static ResolvedClass createClass(String name) {
+ ResolvedClass mock = mock(ResolvedClass.class);
+ when(mock.getName()).thenReturn(name);
+ when(mock.getSignature()).thenReturn(name);
+ assertTrue(name, name.indexOf('.') != -1);
+ ResolvedPackage pkg = createPackage(name.substring(0, name.lastIndexOf('.')));
+ when(mock.getPackage()).thenReturn(pkg);
+ return mock;
+ }
+
+ private static ResolvedMethod createConstructor(String containingClass, String name,
+ String parameters) {
+ return createMethod(containingClass, null, name, parameters, true);
+ }
+
+ public static ResolvedMethod createMethod(String containingClass, String returnType,
+ String name, String parameters) {
+ return createMethod(containingClass, returnType, name, parameters, false);
+ }
+
+ public static ResolvedMethod createMethod(String containingClass, String returnType,
+ String name, String parameters, boolean isConstructor) {
+ return createMethod(containingClass, returnType, name,
+ Splitter.on(',').trimResults().split(parameters), isConstructor);
+ }
+
+ public static ResolvedMethod createMethod(String containingClass, String returnType,
+ String name, Iterable<String> parameters, boolean isConstructor) {
+ ResolvedMethod mock = mock(ResolvedMethod.class);
+ when(mock.isConstructor()).thenReturn(isConstructor);
+ when(mock.getName()).thenReturn(name);
+ if (!isConstructor) {
+ DefaultTypeDescriptor typeDescriptor = new DefaultTypeDescriptor(returnType);
+ when(mock.getReturnType()).thenReturn(typeDescriptor);
+ }
+ ResolvedClass cls = createClass(containingClass);
+ when(mock.getContainingClass()).thenReturn(cls);
+ int index = 0;
+ for (String argument : parameters) {
+ TypeDescriptor typeDescriptor = new DefaultTypeDescriptor(argument);
+ when(mock.getArgumentType(index)).thenReturn(typeDescriptor);
+ index++;
+ }
+ when(mock.getArgumentCount()).thenReturn(index);
+ return mock;
+ }
+
+ public static ResolvedField createField(String containingClass, String name) {
+ ResolvedField mock = mock(ResolvedField.class);
+ when(mock.getName()).thenReturn(name);
+ ResolvedClass cls = createClass(containingClass);
+ when(mock.getContainingClass()).thenReturn(cls);
+ return mock;
+ }
+
+ public static ResolvedPackage createPackage(String pkgName) {
+ ResolvedPackage mock = mock(ResolvedPackage.class);
+ when(mock.getName()).thenReturn(pkgName);
+ return mock;
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/HtmlReporterTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/HtmlReporterTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/HtmlReporterTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/HtmlReporterTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/LintCliXmlParserTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/LintCliXmlParserTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/LintCliXmlParserTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/LintCliXmlParserTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/MainTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/MainTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/MainTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/MainTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/MultiProjectHtmlReporterTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/MultiProjectHtmlReporterTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/MultiProjectHtmlReporterTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/MultiProjectHtmlReporterTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/ReporterTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/ReporterTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/ReporterTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/ReporterTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/TextReporterTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/TextReporterTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/TextReporterTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/TextReporterTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/WarningTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/WarningTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/WarningTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/WarningTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/XmlReporterTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/XmlReporterTest.java
new file mode 100644
index 0000000..ea2efe8
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/XmlReporterTest.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import com.android.tools.lint.checks.AbstractCheckTest;
+import com.android.tools.lint.checks.HardcodedValuesDetector;
+import com.android.tools.lint.checks.ManifestDetector;
+import com.android.tools.lint.checks.TypographyDetector;
+import com.android.tools.lint.detector.api.DefaultPosition;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Location;
+import com.android.tools.lint.detector.api.Project;
+import com.android.tools.lint.detector.api.Severity;
+import com.android.tools.lint.detector.api.TextFormat;
+import com.android.utils.PositionXmlParser;
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+@SuppressWarnings("javadoc")
+public class XmlReporterTest extends AbstractCheckTest {
+ public void test() throws Exception {
+ File file = new File(getTargetDir(), "report");
+ try {
+ LintCliClient client = new LintCliClient() {
+ @Override
+ String getRevision() {
+ return "unittest"; // Hardcode version to keep unit test output stable
+ }
+ };
+ //noinspection ResultOfMethodCallIgnored
+ file.getParentFile().mkdirs();
+ XmlReporter reporter = new XmlReporter(client, file);
+ Project project = Project.create(client, new File("/foo/bar/Foo"),
+ new File("/foo/bar/Foo"));
+
+ Warning warning1 = new Warning(ManifestDetector.USES_SDK,
+ "<uses-sdk> tag should specify a target API level (the highest verified " +
+ "version; when running on later versions, compatibility behaviors may " +
+ "be enabled) with android:targetSdkVersion=\"?\"",
+ Severity.WARNING, project);
+ warning1.line = 6;
+ warning1.file = new File("/foo/bar/Foo/AndroidManifest.xml");
+ warning1.errorLine = " <uses-sdk android:minSdkVersion=\"8\" />\n ^\n";
+ warning1.path = "AndroidManifest.xml";
+ warning1.location = Location.create(warning1.file,
+ new DefaultPosition(6, 4, 198), new DefaultPosition(6, 42, 236));
+
+ Warning warning2 = new Warning(HardcodedValuesDetector.ISSUE,
+ "[I18N] Hardcoded string \"Fooo\", should use @string resource",
+ Severity.WARNING, project);
+ warning2.line = 11;
+ warning2.file = new File("/foo/bar/Foo/res/layout/main.xml");
+ warning2.errorLine = " android:text=\"Fooo\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~\n";
+ warning2.path = "res/layout/main.xml";
+ warning2.location = Location.create(warning2.file,
+ new DefaultPosition(11, 8, 377), new DefaultPosition(11, 27, 396));
+
+ List<Warning> warnings = new ArrayList<Warning>();
+ warnings.add(warning1);
+ warnings.add(warning2);
+
+ reporter.write(0, 2, warnings);
+
+ String report = Files.toString(file, Charsets.UTF_8);
+ assertEquals(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+ "<issues format=\"4\" by=\"lint unittest\">\n" +
+ "\n" +
+ " <issue\n" +
+ " id=\"UsesMinSdkAttributes\"\n" +
+ " severity=\"Warning\"\n" +
+ " message=\"<uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion="?"\"\n" +
+ " category=\"Correctness\"\n" +
+ " priority=\"9\"\n" +
+ " summary=\"Minimum SDK and target SDK attributes not defined\"\n" +
+ " explanation=\"The manifest should contain a `<uses-sdk>` element which defines the minimum API Level required for the application to run, as well as the target version (the highest API level you have tested the version for.)\"\n" +
+ " url=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
+ " urls=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
+ " errorLine1=\" <uses-sdk android:minSdkVersion="8" />\"\n" +
+ " errorLine2=\" ^\">\n" +
+ " <location\n" +
+ " file=\"AndroidManifest.xml\"\n" +
+ " line=\"7\"\n" +
+ " column=\"5\"/>\n" +
+ " </issue>\n" +
+ "\n" +
+ " <issue\n" +
+ " id=\"HardcodedText\"\n" +
+ " severity=\"Warning\"\n" +
+ " message=\"[I18N] Hardcoded string "Fooo", should use @string resource\"\n" +
+ " category=\"Internationalization\"\n" +
+ " priority=\"5\"\n" +
+ " summary=\"Hardcoded text\"\n" +
+ " explanation=\"Hardcoding text attributes directly in layout files is bad for several reasons:\n" +
+ "\n" +
+ "* When creating configuration variations (for example for landscape or portrait)you have to repeat the actual text (and keep it up to date when making changes)\n" +
+ "\n" +
+ "* The application cannot be translated to other languages by just adding new translations for existing string resources.\n" +
+ "\n" +
+ "In Android Studio and Eclipse there are quickfixes to automatically extract this hardcoded string into a resource lookup.\"\n" +
+ " errorLine1=\" android:text="Fooo" />\"\n" +
+ " errorLine2=\" ~~~~~~~~~~~~~~~~~~~\">\n" +
+ " <location\n" +
+ " file=\"res/layout/main.xml\"\n" +
+ " line=\"12\"\n" +
+ " column=\"9\"/>\n" +
+ " </issue>\n" +
+ "\n" +
+ "</issues>\n",
+ report);
+
+ // Make sure the XML is valid
+ Document document = PositionXmlParser.parse(report);
+ assertNotNull(document);
+ assertEquals(2, document.getElementsByTagName("issue").getLength());
+ } finally {
+ //noinspection ResultOfMethodCallIgnored
+ file.delete();
+ }
+ }
+
+ public void testFullPaths() throws Exception {
+ File file = new File(getTargetDir(), "report");
+ try {
+ LintCliClient client = new LintCliClient() {
+ @Override
+ String getRevision() {
+ return "unittest"; // Hardcode version to keep unit test output stable
+ }
+ };
+ client.mFlags.setFullPath(true);
+
+ //noinspection ResultOfMethodCallIgnored
+ file.getParentFile().mkdirs();
+ XmlReporter reporter = new XmlReporter(client, file);
+ Project project = Project.create(client, new File("/foo/bar/Foo"),
+ new File("/foo/bar/Foo"));
+
+ Warning warning1 = new Warning(ManifestDetector.USES_SDK,
+ "<uses-sdk> tag should specify a target API level (the highest verified " +
+ "version; when running on later versions, compatibility behaviors may " +
+ "be enabled) with android:targetSdkVersion=\"?\"",
+ Severity.WARNING, project);
+ warning1.line = 6;
+ warning1.file = new File("/foo/bar/../Foo/AndroidManifest.xml");
+ warning1.errorLine = " <uses-sdk android:minSdkVersion=\"8\" />\n ^\n";
+ warning1.path = "AndroidManifest.xml";
+ warning1.location = Location.create(warning1.file,
+ new DefaultPosition(6, 4, 198), new DefaultPosition(6, 42, 236));
+
+ Warning warning2 = new Warning(HardcodedValuesDetector.ISSUE,
+ "[I18N] Hardcoded string \"Fooo\", should use @string resource",
+ Severity.WARNING, project);
+ warning2.line = 11;
+ warning2.file = new File("/foo/bar/Foo/res/layout/main.xml");
+ warning2.errorLine = " android:text=\"Fooo\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~\n";
+ warning2.path = "res/layout/main.xml";
+ warning2.location = Location.create(warning2.file,
+ new DefaultPosition(11, 8, 377), new DefaultPosition(11, 27, 396));
+
+ List<Warning> warnings = new ArrayList<Warning>();
+ warnings.add(warning1);
+ warnings.add(warning2);
+
+ reporter.write(0, 2, warnings);
+
+ String report = Files.toString(file, Charsets.UTF_8);
+ assertEquals(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+ "<issues format=\"4\" by=\"lint unittest\">\n" +
+ "\n" +
+ " <issue\n" +
+ " id=\"UsesMinSdkAttributes\"\n" +
+ " severity=\"Warning\"\n" +
+ " message=\"<uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion="?"\"\n" +
+ " category=\"Correctness\"\n" +
+ " priority=\"9\"\n" +
+ " summary=\"Minimum SDK and target SDK attributes not defined\"\n" +
+ " explanation=\"The manifest should contain a `<uses-sdk>` element which defines the minimum API Level required for the application to run, as well as the target version (the highest API level you have tested the version for.)\"\n" +
+ " url=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
+ " urls=\"http://developer.android.com/guide/topics/manifest/uses-sdk-element.html\"\n" +
+ " errorLine1=\" <uses-sdk android:minSdkVersion="8" />\"\n" +
+ " errorLine2=\" ^\">\n" +
+ " <location\n" +
+ " file=\"/foo/Foo/AndroidManifest.xml\"\n" +
+ " line=\"7\"\n" +
+ " column=\"5\"/>\n" +
+ " </issue>\n" +
+ "\n" +
+ " <issue\n" +
+ " id=\"HardcodedText\"\n" +
+ " severity=\"Warning\"\n" +
+ " message=\"[I18N] Hardcoded string "Fooo", should use @string resource\"\n" +
+ " category=\"Internationalization\"\n" +
+ " priority=\"5\"\n" +
+ " summary=\"Hardcoded text\"\n" +
+ " explanation=\"Hardcoding text attributes directly in layout files is bad for several reasons:\n" +
+ "\n" +
+ "* When creating configuration variations (for example for landscape or portrait)you have to repeat the actual text (and keep it up to date when making changes)\n" +
+ "\n" +
+ "* The application cannot be translated to other languages by just adding new translations for existing string resources.\n" +
+ "\n" +
+ "In Android Studio and Eclipse there are quickfixes to automatically extract this hardcoded string into a resource lookup.\"\n" +
+ " errorLine1=\" android:text="Fooo" />\"\n" +
+ " errorLine2=\" ~~~~~~~~~~~~~~~~~~~\">\n" +
+ " <location\n" +
+ " file=\"/foo/bar/Foo/res/layout/main.xml\"\n" +
+ " line=\"12\"\n" +
+ " column=\"9\"/>\n" +
+ " </issue>\n" +
+ "\n" +
+ "</issues>\n",
+ report);
+
+ // Make sure the XML is valid
+ Document document = PositionXmlParser.parse(report);
+ assertNotNull(document);
+ assertEquals(2, document.getElementsByTagName("issue").getLength());
+ } finally {
+ //noinspection ResultOfMethodCallIgnored
+ file.delete();
+ }
+ }
+
+ public void testNonPrintableChars() throws Exception {
+ // See https://code.google.com/p/android/issues/detail?id=56205
+ File file = new File(getTargetDir(), "report");
+ try {
+ LintCliClient client = new LintCliClient() {
+ @Override
+ String getRevision() {
+ return "unittest"; // Hardcode version to keep unit test output stable
+ }
+ };
+ //noinspection ResultOfMethodCallIgnored
+ file.getParentFile().mkdirs();
+ XmlReporter reporter = new XmlReporter(client, file);
+ Project project = Project.create(client, new File("/foo/bar/Foo"),
+ new File("/foo/bar/Foo"));
+
+ Warning warning1 = new Warning(TypographyDetector.FRACTIONS,
+ String.format("Use fraction character %1$c (%2$s) instead of %3$s ?",
+ '\u00BC', "¼", "1/4"), Severity.WARNING, project);
+ warning1.line = 592;
+ warning1.file = new File("/foo/bar/Foo/AndroidManifest.xml");
+ warning1.errorLine =
+ " <string name=\"user_registration_name3_3\">Register 3/3</string>\n" +
+ " ^";
+ warning1.path = "res/values-en/common_strings.xml";
+ warning1.location = Location.create(warning1.file,
+ new DefaultPosition(592, 46, -1), null);
+
+ List<Warning> warnings = new ArrayList<Warning>();
+ warnings.add(warning1);
+
+ reporter.write(0, 2, warnings);
+
+ String report = Files.toString(file, Charsets.UTF_8);
+ assertEquals(""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<issues format=\"4\" by=\"lint unittest\">\n"
+ + "\n"
+ + " <issue\n"
+ + " id=\"TypographyFractions\"\n"
+ + " severity=\"Warning\"\n"
+ + " message=\"Use fraction character ¼ (&#188;) instead of 1/4 ?\"\n"
+ + " category=\"Usability:Typography\"\n"
+ + " priority=\"5\"\n"
+ + " summary=\"Fraction string can be replaced with fraction character\"\n"
+ + " explanation=\"You can replace certain strings, such as 1/2, and 1/4, with dedicated characters for these, such as ½ (&#189;) and ¼ (&#188;). This can help make the text more readable.\"\n"
+ + " url=\"http://en.wikipedia.org/wiki/Number_Forms\"\n"
+ + " urls=\"http://en.wikipedia.org/wiki/Number_Forms\">\n"
+ + " <location\n"
+ + " file=\"AndroidManifest.xml\"\n"
+ + " line=\"593\"\n"
+ + " column=\"47\"/>\n"
+ + " </issue>\n"
+ + "\n"
+ + "</issues>\n",
+ report);
+
+ // Make sure the XML is valid
+ Document document = PositionXmlParser.parse(report);
+ assertNotNull(document);
+ assertEquals(1, document.getElementsByTagName("issue").getLength());
+ String explanation = ((Element)document.getElementsByTagName("issue").item(0)).
+ getAttribute("explanation");
+ assertEquals(TypographyDetector.FRACTIONS.getExplanation(TextFormat.RAW),
+ explanation);
+ } finally {
+ //noinspection ResultOfMethodCallIgnored
+ file.delete();
+ }
+ }
+
+ @Override
+ protected Detector getDetector() {
+ fail("Not used in this test");
+ return null;
+ }
+}
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java
new file mode 100644
index 0000000..d50ffe4
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AbstractCheckTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Issue;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class AbstractCheckTest extends LintDetectorTest {
+ @Override
+ protected List<Issue> getIssues() {
+ List<Issue> issues = new ArrayList<Issue>();
+ Class<? extends Detector> detectorClass = getDetectorInstance().getClass();
+ // Get the list of issues from the registry and filter out others, to make sure
+ // issues are properly registered
+ List<Issue> candidates = new BuiltinIssueRegistry().getIssues();
+ for (Issue issue : candidates) {
+ if (issue.getImplementation().getDetectorClass() == detectorClass) {
+ issues.add(issue);
+ }
+ }
+
+ return issues;
+ }
+
+ @Override
+ protected InputStream getTestResource(String relativePath, boolean expectExists) {
+ String path = "data" + File.separator + relativePath; //$NON-NLS-1$
+ InputStream stream = AbstractCheckTest.class.getResourceAsStream(path);
+ if (stream == null) {
+ File root = getRootDir();
+ assertNotNull(root);
+ String pkg = AbstractCheckTest.class.getName();
+ pkg = pkg.substring(0, pkg.lastIndexOf('.'));
+ File f = new File(root,
+ "tools/base/lint/libs/lint-tests/src/test/java/".replace('/', File.separatorChar)
+ + pkg.replace('.', File.separatorChar)
+ + File.separatorChar + path);
+ if (f.exists()) {
+ try {
+ return new BufferedInputStream(new FileInputStream(f));
+ } catch (FileNotFoundException e) {
+ stream = null;
+ if (expectExists) {
+ fail("Could not find file " + relativePath);
+ }
+ }
+ }
+ }
+ if (!expectExists && stream == null) {
+ return null;
+ }
+ return stream;
+ }
+
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AccessibilityDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AccessibilityDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/AccessibilityDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AccessibilityDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AddJavascriptInterfaceDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AlarmDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AlarmDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/AlarmDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AlarmDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AlwaysShowActionDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AlwaysShowActionDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/AlwaysShowActionDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AlwaysShowActionDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java
new file mode 100644
index 0000000..1831953
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AnnotationDetectorTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+import com.android.tools.lint.detector.api.Issue;
+
+import java.util.List;
+
+@SuppressWarnings("javadoc")
+public class AnnotationDetectorTest extends AbstractCheckTest {
+ public void test() throws Exception {
+ assertEquals(
+ "src/test/pkg/WrongAnnotation.java:9: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+ " public static void foobar(View view, @SuppressLint(\"NewApi\") int foo) { // Invalid: class-file check\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "src/test/pkg/WrongAnnotation.java:10: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+ " @SuppressLint(\"NewApi\") // Invalid\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "src/test/pkg/WrongAnnotation.java:12: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+ " @SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid: class-file based check on local variable\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "src/test/pkg/WrongAnnotation.java:14: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+ " @android.annotation.SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid (FQN)\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "src/test/pkg/WrongAnnotation.java:28: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
+ " @SuppressLint(\"NewApi\")\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "5 errors, 0 warnings\n",
+
+ lintProject(
+ "src/test/pkg/WrongAnnotation.java.txt=>src/test/pkg/WrongAnnotation.java"
+ ));
+ }
+
+ @SuppressWarnings("ClassNameDiffersFromFileName")
+ public void testUniqueValues() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/IntDefTest.java:9: Error: Constants STYLE_NO_INPUT and STYLE_NO_FRAME specify the same exact value (2); this is usually a cut & paste or merge error [UniqueConstants]\n"
+ + " @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})\n"
+ + " ~~~~~~~~~~~~~~\n"
+ + " src/test/pkg/IntDefTest.java:9: Previous same value\n"
+ + "src/test/pkg/IntDefTest.java:28: Error: Constants FLAG3 and FLAG2 specify the same exact value (562949953421312); this is usually a cut & paste or merge error [UniqueConstants]\n"
+ + " @IntDef({FLAG2, FLAG3, FLAG1})\n"
+ + " ~~~~~\n"
+ + " src/test/pkg/IntDefTest.java:28: Previous same value\n"
+ + "2 errors, 0 warnings\n",
+
+ lintProject(
+ java("src/test/pkg/IntDefTest.java", ""
+ + "package test.pkg;\n"
+ + "import android.support.annotation.IntDef;\n"
+ + "\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.RetentionPolicy;\n"
+ + "\n"
+ + "@SuppressLint(\"UnusedDeclaration\")\n"
+ + "public class IntDefTest {\n"
+ + " @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})\n"
+ + " @Retention(RetentionPolicy.SOURCE)\n"
+ + " private @interface DialogStyle {}\n"
+ + "\n"
+ + " public static final int STYLE_NORMAL = 0;\n"
+ + " public static final int STYLE_NO_TITLE = 1;\n"
+ + " public static final int STYLE_NO_FRAME = 2;\n"
+ + " public static final int STYLE_NO_INPUT = 2;\n"
+ + "\n"
+ + " @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})\n"
+ + " @SuppressWarnings(\"UniqueConstants\")\n"
+ + " @Retention(RetentionPolicy.SOURCE)\n"
+ + " private @interface SuppressedDialogStyle {}\n"
+ + "\n"
+ + "\n"
+ + " public static final long FLAG1 = 0x100000000000L;\n"
+ + " public static final long FLAG2 = 0x0002000000000000L;\n"
+ + " public static final long FLAG3 = 0x2000000000000L;\n"
+ + "\n"
+ + " @IntDef({FLAG2, FLAG3, FLAG1})\n"
+ + " @Retention(RetentionPolicy.SOURCE)\n"
+ + " private @interface Flags {}\n"
+ + "\n"
+
+ + ""
+ + "}"),
+ copy("src/android/support/annotation/IntDef.java.txt",
+ "src/android/support/annotation/IntDef.java")));
+ }
+
+ @SuppressWarnings("ClassNameDiffersFromFileName")
+ public void testFlagStyle() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/IntDefTest.java:13: Warning: Consider declaring this constant using 1 << 44 instead [ShiftFlags]\n"
+ + " public static final long FLAG5 = 0x100000000000L;\n"
+ + " ~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:14: Warning: Consider declaring this constant using 1 << 49 instead [ShiftFlags]\n"
+ + " public static final long FLAG6 = 0x0002000000000000L;\n"
+ + " ~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:15: Warning: Consider declaring this constant using 1 << 3 instead [ShiftFlags]\n"
+ + " public static final long FLAG7 = 8L;\n"
+ + " ~~\n"
+ + "0 errors, 3 warnings\n",
+ lintProject(
+ java("src/test/pkg/IntDefTest.java", ""
+ + "package test.pkg;\n"
+ + "import android.support.annotation.IntDef;\n"
+ + "\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.RetentionPolicy;\n"
+ + "\n"
+ + "@SuppressWarnings(\"unused\")\n"
+ + "public class IntDefTest {\n"
+ + " public static final long FLAG1 = 1;\n"
+ + " public static final long FLAG2 = 2;\n"
+ + " public static final long FLAG3 = 1 << 2;\n"
+ + " public static final long FLAG4 = 1 << 3;\n"
+ + " public static final long FLAG5 = 0x100000000000L;\n"
+ + " public static final long FLAG6 = 0x0002000000000000L;\n"
+ + " public static final long FLAG7 = 8L;\n"
+ + " public static final long FLAG8 = 9L;\n"
+ + " public static final long FLAG9 = 0;\n"
+ + " public static final long FLAG10 = 1;\n"
+ + " public static final long FLAG11 = -1;\n"
+ + "\n"
+ // Not a flag (missing flag=true)
+ + " @IntDef({FLAG1, FLAG2, FLAG3})\n"
+ + " @Retention(RetentionPolicy.SOURCE)\n"
+ + " private @interface Flags1 {}\n"
+ + "\n"
+ // OK: Too few values
+ + " @IntDef(flag = true, value={FLAG1, FLAG2})\n"
+ + " @Retention(RetentionPolicy.SOURCE)\n"
+ + " private @interface Flags2 {}\n"
+ + "\n"
+ // OK: Allow 0, 1, -1
+ + " @IntDef(flag = true, value={FLAG9, FLAG10, FLAG11})\n"
+ + " @Retention(RetentionPolicy.SOURCE)\n"
+ + " private @interface Flags3 {}\n"
+ + "\n"
+ // OK: Already using shifts
+ + " @IntDef(flag = true, value={FLAG1, FLAG3, FLAG4})\n"
+ + " @Retention(RetentionPolicy.SOURCE)\n"
+ + " private @interface Flags4 {}\n"
+ + "\n"
+ // Wrong: should be flagged
+ + " @IntDef(flag = true, value={FLAG5, FLAG6, FLAG7, FLAG8})\n"
+ + " @Retention(RetentionPolicy.SOURCE)\n"
+ + " private @interface Flags5 {}\n"
+ + "}"),
+ copy("src/android/support/annotation/IntDef.java.txt",
+ "src/android/support/annotation/IntDef.java")));
+ }
+
+ @Override
+ protected Detector getDetector() {
+ return new AnnotationDetector();
+ }
+
+ @Override
+ protected List<Issue> getIssues() {
+ List<Issue> issues = super.getIssues();
+
+ // Need these issues on to be found by the registry as well to look up scope
+ // in id references (these ids are referenced in the unit test java file below)
+ issues.add(ApiDetector.UNSUPPORTED);
+ issues.add(SdCardDetector.ISSUE);
+
+ return issues;
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ApiLookupTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiLookupTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ApiLookupTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ApiLookupTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AppCompatCallDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppCompatCallDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/AppCompatCallDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppCompatCallDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AppCompatResourceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppCompatResourceDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/AppCompatResourceDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppCompatResourceDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppIndexingApiDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppIndexingApiDetectorTest.java
new file mode 100644
index 0000000..4c0a56a
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppIndexingApiDetectorTest.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings("javadoc")
+public class AppIndexingApiDetectorTest extends AbstractCheckTest {
+
+ @Override
+ protected Detector getDetector() {
+ return new AppIndexingApiDetector();
+ }
+
+ public void testOk() throws Exception {
+ assertEquals("No warnings.",
+ lintProject(xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:icon=\"@mipmap/ic_launcher\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " <activity\n"
+ + " android:name=\".FullscreenActivity\"\n"
+ + " android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+ + " android:label=\"@string/title_activity_fullscreen\"\n"
+ + " android:theme=\"@style/FullscreenTheme\" >\n"
+ + " <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+ + " <action android:name=\"android.intent.action.VIEW\" />\n"
+ + " <data android:scheme=\"http\"\n"
+ + " android:host=\"example.com\"\n"
+ + " android:pathPrefix=\"/gizmos\" />\n"
+ + " <category android:name=\"android.intent.category.DEFAULT\" />\n"
+ + " <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+ + " </intent-filter>\n"
+ + " </activity>\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testDataMissing() throws Exception {
+ assertEquals(""
+ + "AndroidManifest.xml:15: Error: Missing data node? [AppIndexingError]\n"
+ + " <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+ + " ^\n"
+ + "1 errors, 0 warnings\n",
+ lintProject(xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:icon=\"@mipmap/ic_launcher\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " <activity\n"
+ + " android:name=\".FullscreenActivity\"\n"
+ + " android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+ + " android:label=\"@string/title_activity_fullscreen\"\n"
+ + " android:theme=\"@style/FullscreenTheme\" >\n"
+ + " <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+ + " <action android:name=\"android.intent.action.VIEW\" />\n"
+ + " <category android:name=\"android.intent.category.DEFAULT\" />\n"
+ + " <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+ + " </intent-filter>\n"
+ + " </activity>\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testNoUrl() throws Exception {
+ assertEquals(""
+ + "AndroidManifest.xml:17: Error: Missing URL for the intent filter? [AppIndexingError]\n"
+ + " <data />\n"
+ + " ~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+ lintProject(xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:icon=\"@mipmap/ic_launcher\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " <activity\n"
+ + " android:name=\".FullscreenActivity\"\n"
+ + " android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+ + " android:label=\"@string/title_activity_fullscreen\"\n"
+ + " android:theme=\"@style/FullscreenTheme\" >\n"
+ + " <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+ + " <action android:name=\"android.intent.action.VIEW\" />\n"
+ + " <data />\n"
+ + " <category android:name=\"android.intent.category.DEFAULT\" />\n"
+ + " <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+ + " </intent-filter>\n"
+ + " </activity>\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testMimeType() throws Exception {
+ assertEquals("No warnings.",
+ lintProject(xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:icon=\"@mipmap/ic_launcher\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " <activity\n"
+ + " android:name=\".FullscreenActivity\"\n"
+ + " android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+ + " android:label=\"@string/title_activity_fullscreen\"\n"
+ + " android:theme=\"@style/FullscreenTheme\" >\n"
+ + " <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+ + " <action android:name=\"android.intent.action.VIEW\" />\n"
+ + " <data android:mimeType=\"mimetype\" /> "
+ + " <category android:name=\"android.intent.category.DEFAULT\" />\n"
+ + " <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+ + " </intent-filter>\n"
+ + " </activity>\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+
+ public void testNoActivity() throws Exception {
+ assertEquals(
+ "No warnings.",
+ lintProject(xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:icon=\"@mipmap/ic_launcher\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testNotBrowsable() throws Exception {
+ assertEquals(""
+ + "AndroidManifest.xml:25: Warning: Activity supporting ACTION_VIEW is not set as BROWSABLE [AppIndexingWarning]\n"
+ + " <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+ + " ^\n"
+ + "0 errors, 1 warnings\n",
+ lintProject(xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:icon=\"@mipmap/ic_launcher\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " <activity\n"
+ + " android:name=\".MainActivity\"\n"
+ + " android:label=\"@string/app_name\" >\n"
+ + " <intent-filter>\n"
+ + " <action android:name=\"android.intent.action.MAIN\" />\n"
+ + "\n"
+ + " <category android:name=\"android.intent.category.LAUNCHER\" />\n"
+ + " </intent-filter>\n"
+ + " </activity>\n"
+ + "\n"
+ + " <activity\n"
+ + " android:name=\".FullscreenActivity\"\n"
+ + " android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+ + " android:label=\"@string/title_activity_fullscreen\"\n"
+ + " android:theme=\"@style/FullscreenTheme\" >\n"
+ + " <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+ + " <action android:name=\"android.intent.action.VIEW\" />\n"
+ + " <data android:scheme=\"http\"\n"
+ + " android:host=\"example.com\"\n"
+ + " android:pathPrefix=\"/gizmos\" />\n"
+ + " <category android:name=\"android.intent.category.DEFAULT\" />\n"
+ + " </intent-filter>\n"
+ + " </activity>\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testWrongPathPrefix() throws Exception {
+ assertEquals(""
+ + "AndroidManifest.xml:19: Error: android:pathPrefix attribute should start with '/', but it is : gizmos [AppIndexingError]\n"
+ + " android:pathPrefix=\"gizmos\" />\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+ lintProject(xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:icon=\"@mipmap/ic_launcher\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " <activity\n"
+ + " android:name=\".FullscreenActivity\"\n"
+ + " android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+ + " android:label=\"@string/title_activity_fullscreen\"\n"
+ + " android:theme=\"@style/FullscreenTheme\" >\n"
+ + " <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+ + " <action android:name=\"android.intent.action.VIEW\" />\n"
+ + " <data android:scheme=\"http\"\n"
+ + " android:host=\"example.com\"\n"
+ + " android:pathPrefix=\"gizmos\" />\n"
+ + " <category android:name=\"android.intent.category.DEFAULT\" />\n"
+ + " <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+ + " </intent-filter>\n"
+ + " </activity>\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testWrongPort() throws Exception {
+ assertEquals(""
+ + "AndroidManifest.xml:19: Error: android:port is not a legal number [AppIndexingError]\n"
+ + " android:port=\"ABCD\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+ lintProject(xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:icon=\"@mipmap/ic_launcher\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " <activity\n"
+ + " android:name=\".FullscreenActivity\"\n"
+ + " android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+ + " android:label=\"@string/title_activity_fullscreen\"\n"
+ + " android:theme=\"@style/FullscreenTheme\" >\n"
+ + " <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+ + " <action android:name=\"android.intent.action.VIEW\" />\n"
+ + " <data android:scheme=\"http\"\n"
+ + " android:host=\"example.com\"\n"
+ + " android:port=\"ABCD\"\n"
+ + " android:pathPrefix=\"/gizmos\" />\n"
+ + " <category android:name=\"android.intent.category.DEFAULT\" />\n"
+ + " <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+ + " </intent-filter>\n"
+ + " </activity>\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testSchemeAndHostMissing() throws Exception {
+ assertEquals(""
+ + "AndroidManifest.xml:17: Error: Missing URL for the intent filter? [AppIndexingError]\n"
+ + " <data android:pathPrefix=\"/gizmos\" />\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "AndroidManifest.xml:17: Error: android:host missing [AppIndexingError]\n"
+ + " <data android:pathPrefix=\"/gizmos\" />\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "AndroidManifest.xml:17: Error: android:scheme missing [AppIndexingError]\n"
+ + " <data android:pathPrefix=\"/gizmos\" />\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "3 errors, 0 warnings\n",
+ lintProject(xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:icon=\"@mipmap/ic_launcher\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " <activity\n"
+ + " android:name=\".FullscreenActivity\"\n"
+ + " android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+ + " android:label=\"@string/title_activity_fullscreen\"\n"
+ + " android:theme=\"@style/FullscreenTheme\" >\n"
+ + " <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+ + " <action android:name=\"android.intent.action.VIEW\" />\n"
+ + " <data android:pathPrefix=\"/gizmos\" />\n"
+ + " <category android:name=\"android.intent.category.DEFAULT\" />\n"
+ + " <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+ + " </intent-filter>\n"
+ + " </activity>\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testMultiData() throws Exception {
+ assertEquals("No warnings.",
+ lintProject(xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:icon=\"@mipmap/ic_launcher\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " <activity\n"
+ + " android:name=\".FullscreenActivity\"\n"
+ + " android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+ + " android:label=\"@string/title_activity_fullscreen\"\n"
+ + " android:theme=\"@style/FullscreenTheme\" >\n"
+ + " <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+ + " <action android:name=\"android.intent.action.VIEW\" />\n"
+ + " <data android:scheme=\"http\" />\n"
+ + " <data android:host=\"example.com\" />\n"
+ + " <data android:pathPrefix=\"/gizmos\" />\n"
+ + " <category android:name=\"android.intent.category.DEFAULT\" />\n"
+ + " <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+ + " </intent-filter>\n"
+ + " </activity>\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testMultiIntent() throws Exception {
+ assertEquals("No warnings.",
+ lintProject(xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:icon=\"@mipmap/ic_launcher\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " <activity\n"
+ + " android:name=\".FullscreenActivity\"\n"
+ + " android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+ + " android:label=\"@string/title_activity_fullscreen\"\n"
+ + " android:theme=\"@style/FullscreenTheme\" >\n"
+ + " <intent-filter>\n"
+ + " <action android:name=\"android.intent.action.MAIN\" />\n"
+ + " <category android:name=\"android.intent.category.LAUNCHER\" />\n"
+ + " </intent-filter>"
+ + " <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+ + " <action android:name=\"android.intent.action.VIEW\" />\n"
+ + " <data android:scheme=\"http\"\n"
+ + " android:host=\"example.com\"\n"
+ + " android:pathPrefix=\"/gizmos\" />\n"
+ + " <category android:name=\"android.intent.category.DEFAULT\" />\n"
+ + " <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+ + " </intent-filter>\n"
+ + " </activity>\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testMultiIntentWithError() throws Exception {
+ assertEquals(""
+ + "AndroidManifest.xml:20: Error: android:host missing [AppIndexingError]\n"
+ + " <data android:scheme=\"http\"\n"
+ + " ^\n"
+ + "1 errors, 0 warnings\n",
+ lintProject(xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:icon=\"@mipmap/ic_launcher\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " <activity\n"
+ + " android:name=\".FullscreenActivity\"\n"
+ + " android:configChanges=\"orientation|keyboardHidden|screenSize\"\n"
+ + " android:label=\"@string/title_activity_fullscreen\"\n"
+ + " android:theme=\"@style/FullscreenTheme\" >\n"
+ + " <intent-filter>\n"
+ + " <action android:name=\"android.intent.action.MAIN\" />\n"
+ + " <category android:name=\"android.intent.category.LAUNCHER\" />\n"
+ + " </intent-filter>"
+ + " <intent-filter android:label=\"@string/title_activity_fullscreen\">\n"
+ + " <action android:name=\"android.intent.action.VIEW\" />\n"
+ + " <data android:scheme=\"http\"\n"
+ + " android:pathPrefix=\"/gizmos\" />\n"
+ + " <category android:name=\"android.intent.category.DEFAULT\" />\n"
+ + " <category android:name=\"android.intent.category.BROWSABLE\" />\n"
+ + " </intent-filter>\n"
+ + " </activity>\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testOkWithResource() throws Exception {
+ assertEquals("No warnings.",
+ lintProjectIncrementally(
+ "AndroidManifest.xml",
+ "appindexing_manifest.xml=>AndroidManifest.xml",
+ "res/values/appindexing_strings.xml"));
+ }
+
+ public void testWrongWithResource() throws Exception {
+ assertEquals("" + "AndroidManifest.xml:18: Error: android:pathPrefix attribute should start with '/', but it is : pathprefix [AppIndexingError]\n"
+ + " android:pathPrefix=\"@string/path_prefix\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "AndroidManifest.xml:19: Error: android:port is not a legal number [AppIndexingError]\n"
+ + " android:port=\"@string/port\"/>\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "2 errors, 0 warnings\n",
+ lintProjectIncrementally(
+ "AndroidManifest.xml",
+ "appindexing_manifest.xml=>AndroidManifest.xml",
+ "res/values/appindexing_wrong_strings.xml"));
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ArraySizeDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ArraySizeDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ArraySizeDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ArraySizeDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/AssertDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AssertDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/AssertDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AssertDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/BuiltinIssueRegistryTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/BuiltinIssueRegistryTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/BuiltinIssueRegistryTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/BuiltinIssueRegistryTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ButtonDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ButtonDetectorTest.java
new file mode 100644
index 0000000..98405cc
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ButtonDetectorTest.java
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+import com.android.tools.lint.detector.api.Issue;
+
+@SuppressWarnings("javadoc")
+public class ButtonDetectorTest extends AbstractCheckTest {
+ private static Issue sTestIssue;
+
+ @Override
+ protected boolean isEnabled(Issue issue) {
+ return super.isEnabled(issue) && sTestIssue == null || issue == sTestIssue;
+ }
+
+ @Override
+ protected Detector getDetector() {
+ return new ButtonDetector();
+ }
+
+ public void testButtonOrder() throws Exception {
+ sTestIssue = ButtonDetector.ORDER;
+ assertEquals(
+ "res/layout/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "0 errors, 7 warnings\n" +
+ "",
+
+ lintProject(
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "res/layout/buttonbar.xml",
+ "res/values/buttonbar-values.xml"));
+ }
+
+ public void testButtonOrder2() throws Exception {
+ // If the layout is in v14, it had better have the right order
+ sTestIssue = ButtonDetector.ORDER;
+ assertEquals(
+ "res/layout-v14/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-v14/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-v14/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-v14/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-v14/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-v14/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-v14/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "0 errors, 7 warnings\n" +
+ "",
+
+ lintProject(
+ "minsdk5targetsdk14.xml=>AndroidManifest.xml",
+ "res/layout/buttonbar.xml=>res/layout-v14/buttonbar.xml",
+ "res/values/buttonbar-values.xml"));
+ }
+
+ public void testButtonOrder3() throws Exception {
+ // Similar to test 3, but also complain if the -v version is *higher* than 14
+ sTestIssue = ButtonDetector.ORDER;
+ assertEquals(
+ "res/layout-v16/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-v16/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-v16/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-v16/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-v16/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-v16/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-v16/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "0 errors, 7 warnings\n" +
+ "",
+
+ lintProject(
+ "minsdk5targetsdk14.xml=>AndroidManifest.xml",
+ "res/layout/buttonbar.xml=>res/layout-v16/buttonbar.xml",
+ "res/values/buttonbar-values.xml"));
+ }
+
+ public void testButtonOrder4() throws Exception {
+ // Targeting 14 but using a layout that also needs to work for older platforms:
+ sTestIssue = ButtonDetector.ORDER;
+ assertEquals(
+ "res/layout/buttonbar.xml:12: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:44: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:92: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:124: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:140: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:156: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:177: Warning: Layout uses the wrong button order for API >= 14: Create a layout-v14/buttonbar.xml file with opposite order: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "0 errors, 7 warnings\n" +
+ "",
+
+ lintProject(
+ "minsdk5targetsdk14.xml=>AndroidManifest.xml",
+ "res/layout/buttonbar.xml",
+ "res/values/buttonbar-values.xml"));
+ }
+
+ public void testButtonOrder5() throws Exception {
+ // If the layout is in a non-ICS folder and has the wrong button order,
+ // but there is a v14 version of the layout, don't complain about the non-v14 version
+ sTestIssue = ButtonDetector.ORDER;
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "minsdk5targetsdk14.xml=>AndroidManifest.xml",
+ "res/layout/buttonbar.xml",
+ "res/layout/layout1.xml=>res/layout-v14/buttonbar.xml",
+ "res/values/buttonbar-values.xml"));
+ }
+
+ public void testSuppressed() throws Exception {
+ sTestIssue = ButtonDetector.ORDER;
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "res/layout/buttonbar_suppressed.xml",
+ "res/values/buttonbar-values.xml"));
+ }
+
+ public void testButtonOrderRelativeLayout() throws Exception {
+ sTestIssue = ButtonDetector.ORDER;
+ assertEquals(
+ "No warnings.",
+
+ lintProject("res/layout/buttonbar2.xml", "res/values/buttonbar-values.xml"));
+ }
+
+ public void testButtonOrderRelativeLayout2() throws Exception {
+ sTestIssue = ButtonDetector.ORDER;
+ assertEquals(
+ "res/layout/buttonbar3.xml:27: Warning: Cancel button should be on the left [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "0 errors, 1 warnings\n" +
+ "",
+
+ lintProject(
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "res/layout/buttonbar3.xml",
+ "res/values/buttonbar-values.xml"));
+ }
+
+ public void testButtonOrderRelativeLayout3() throws Exception {
+ sTestIssue = ButtonDetector.ORDER;
+ assertEquals(
+ "No warnings.",
+
+ lintProject("res/layout/buttonbar4.xml", "res/values/buttonbar-values.xml"));
+ }
+
+
+ public void testCase() throws Exception {
+ sTestIssue = ButtonDetector.CASE;
+ assertEquals(
+ "res/values/buttonbar-values.xml:9: Warning: The standard Android way to capitalize Ok is \"OK\" (tip: use @android:string/ok instead) [ButtonCase]\n" +
+ " <string name=\"resume2\"> Ok </string>\n" +
+ " ^\n" +
+ "res/values/buttonbar-values.xml:10: Warning: The standard Android way to capitalize CANCEL is \"Cancel\" (tip: use @android:string/cancel instead) [ButtonCase]\n" +
+ " <string name=\"giveup2\">\"CANCEL\"</string>\n" +
+ " ^\n" +
+ "0 errors, 2 warnings\n" +
+ "",
+
+ lintProject("res/layout/buttonbar.xml", "res/values/buttonbar-values.xml"));
+ }
+
+ public void testBack() throws Exception {
+ sTestIssue = ButtonDetector.BACK_BUTTON;
+ assertEquals(
+ "res/layout/buttonbar.xml:183: Warning: Back buttons are not standard on Android; see design guide's navigation section [BackButton]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "0 errors, 1 warnings\n" +
+ "",
+
+ lintProject("res/layout/buttonbar.xml", "res/values/buttonbar-values.xml"));
+ }
+
+ public void testOldApp() throws Exception {
+ // Target SDK < 14 - no warnings on button order
+ sTestIssue = ButtonDetector.ORDER;
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "minsdk5targetsdk9.xml=>AndroidManifest.xml",
+ "res/layout/buttonbar.xml",
+ "res/values/buttonbar-values.xml"));
+ }
+
+ public void testEnglishLocales() throws Exception {
+ sTestIssue = ButtonDetector.ORDER;
+ assertEquals(
+ "res/layout-en-rGB/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-en-rGB/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-en-rGB/buttonbar.xml:92: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-en-rGB/buttonbar.xml:124: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-en-rGB/buttonbar.xml:140: Warning: OK button should be on the right (was \"Ok | CANCEL\", should be \"CANCEL | Ok\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-en-rGB/buttonbar.xml:156: Warning: OK button should be on the right (was \"OK | Abort\", should be \"Abort | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-en-rGB/buttonbar.xml:177: Warning: Cancel button should be on the left (was \"Send | Cancel\", should be \"Cancel | Send\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "0 errors, 7 warnings\n" +
+ "",
+
+ lintProject(
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "res/layout/buttonbar.xml=>res/layout-en-rGB/buttonbar.xml",
+ "res/values/buttonbar-values.xml=>res/values-en-rGB/buttonbar-values.xml"));
+ }
+
+ public void testOtherLocales() throws Exception {
+ sTestIssue = ButtonDetector.ORDER;
+ assertEquals(
+ // Hardcoded values only
+ "res/layout-de/buttonbar.xml:12: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout-de/buttonbar.xml:44: Warning: OK button should be on the right (was \"OK | Cancel\", should be \"Cancel | OK\") [ButtonOrder]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "0 errors, 2 warnings\n" +
+ "",
+
+ lintProject(
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "res/layout/buttonbar.xml=>res/layout-de/buttonbar.xml",
+ "res/values/buttonbar-values.xml=>res/values-de/buttonbar-values.xml"));
+ }
+
+ public void testOtherLocales2() throws Exception {
+ sTestIssue = ButtonDetector.CASE;
+ assertEquals(
+ "No warnings.",
+
+ lintProject("res/layout/buttonbar.xml=>res/layout-de/buttonbar.xml",
+ "res/values/buttonbar-values.xml=>res/values-de/buttonbar-values.xml"));
+ }
+
+ public void testButtonStyle() throws Exception {
+ sTestIssue = ButtonDetector.STYLE;
+ assertEquals(
+ "res/layout/buttonbar.xml:12: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:17: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:28: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:33: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:44: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:49: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:60: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:65: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:76: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:81: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:92: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:97: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:108: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:113: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:124: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:129: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:140: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:145: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:156: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/buttonbar.xml:161: Warning: Buttons in button bars should be borderless; use style=\"?android:attr/buttonBarButtonStyle\" (and ?android:attr/buttonBarStyle on the parent) [ButtonStyle]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "0 errors, 20 warnings\n",
+
+ lintProject(
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "res/layout/buttonbar.xml",
+ "res/layout/buttonbar2.xml",
+ "res/layout/buttonbar3.xml",
+ "res/values/buttonbar-values.xml"));
+ }
+
+ public void testButtonStyleOldMinSdk() throws Exception {
+ sTestIssue = ButtonDetector.STYLE;
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "apicheck/minsdk4.xml=>AndroidManifest.xml",
+ "res/layout/buttonbar.xml",
+ "res/layout/buttonbar2.xml",
+ "res/layout/buttonbar3.xml",
+ "res/values/buttonbar-values.xml"));
+ }
+
+ public void testYesNo() throws Exception {
+ sTestIssue = ButtonDetector.CASE;
+ assertEquals(""
+ + "res/layout/yesno.xml:10: Warning: @android:string/yes actually returns \"OK\", not \"Yes\"; use @android:string/ok instead or create a local string resource for Yes [ButtonCase]\n"
+ + " android:text=\"@android:string/yes\" />\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "res/layout/yesno.xml:15: Warning: @android:string/no actually returns \"Cancel\", not \"No\"; use @android:string/cancel instead or create a local string resource for No [ButtonCase]\n"
+ + " android:text=\"@android:string/no\" />\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 2 warnings\n",
+
+ lintProject(
+ "apicheck/minsdk4.xml=>AndroidManifest.xml",
+ "res/layout/yesno.xml"));
+ }
+
+ public void testIssue180417() throws Exception {
+ // Regression test for
+ // https://code.google.com/p/android/issues/detail?id=180417
+ sTestIssue = ButtonDetector.CASE;
+ assertEquals(""
+ + "res/values/buttonbar-values.xml:4: Warning: The standard Android way to capitalize Ok is \"OK\" (tip: use @android:string/ok instead) [ButtonCase]\n"
+ + " <string name=\"ok\">\"Ok\"</string>\n"
+ + " ^\n"
+ + "0 errors, 1 warnings\n",
+ lintProject(
+ xml("res/values/buttonbar-values.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<resources>\n"
+ + "\n"
+ + " <string name=\"ok\">\"Ok\"</string>\n"
+ + "\n"
+ + "</resources>\n"),
+ xml("res/layout/buttonbar", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " android:layout_width=\"match_parent\"\n"
+ + " android:layout_height=\"match_parent\"\n"
+ + " android:orientation=\"vertical\" >\n"
+ + " <LinearLayout\n"
+ + " android:layout_width=\"match_parent\"\n"
+ + " android:layout_height=\"wrap_content\" >\n"
+ + "\n"
+ + " <Button\n"
+ + " android:layout_width=\"wrap_content\"\n"
+ + " android:layout_height=\"wrap_content\"\n"
+ + " android:text=\"@string/cancel\" />\n"
+ + "\n"
+ + " <Button\n"
+ + " android:layout_width=\"wrap_content\"\n"
+ + " android:layout_height=\"wrap_content\"\n"
+ + " android:text=\"@string/ok\" />\n"
+ + " </LinearLayout>\n"
+ + "</LinearLayout>\n"))
+ );
+ }
+
+ public void testIssue101279() throws Exception {
+ // Regression test for https://code.google.com/p/android/issues/detail?id=101279
+ sTestIssue = ButtonDetector.STYLE;
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "apicheck/minsdk4.xml=>AndroidManifest.xml",
+ "res/layout/buttonbar5.xml"));
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ByteOrderMarkDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ByteOrderMarkDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ByteOrderMarkDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ByteOrderMarkDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CallSuperDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CallSuperDetectorTest.java
new file mode 100644
index 0000000..1b885f3
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CallSuperDetectorTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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 CallSuperDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new CallSuperDetector();
+ }
+
+ public void testCallSuper() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/CallSuperTest.java:11: Error: Overriding method should call super.test1 [MissingSuperCall]\n"
+ + " protected void test1() { // ERROR\n"
+ + " ~~~~~~~\n"
+ + "src/test/pkg/CallSuperTest.java:14: Error: Overriding method should call super.test2 [MissingSuperCall]\n"
+ + " protected void test2() { // ERROR\n"
+ + " ~~~~~~~\n"
+ + "src/test/pkg/CallSuperTest.java:17: Error: Overriding method should call super.test3 [MissingSuperCall]\n"
+ + " protected void test3() { // ERROR\n"
+ + " ~~~~~~~\n"
+ + "src/test/pkg/CallSuperTest.java:20: Error: Overriding method should call super.test4 [MissingSuperCall]\n"
+ + " protected void test4(int arg) { // ERROR\n"
+ + " ~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/CallSuperTest.java:26: Error: Overriding method should call super.test5 [MissingSuperCall]\n"
+ + " protected void test5(int arg1, boolean arg2, Map<List<String>,?> arg3, // ERROR\n"
+ + " ^\n"
+ + "src/test/pkg/CallSuperTest.java:30: Error: Overriding method should call super.test5 [MissingSuperCall]\n"
+ + " protected void test5() { // ERROR\n"
+ + " ~~~~~~~\n"
+ + "6 errors, 0 warnings\n",
+
+ lintProject("src/test/pkg/CallSuperTest.java.txt=>src/test/pkg/CallSuperTest.java",
+ "src/android/support/annotation/CallSuper.java.txt=>src/android/support/annotation/CallSuper.java"));
+ }
+
+ @SuppressWarnings("ClassNameDiffersFromFileName")
+ public void testCallSuperIndirect() throws Exception {
+ // Ensure that when the @CallSuper is on an indirect super method,
+ // we correctly check that you call the direct super method, not the ancestor.
+ //
+ // Regression test for
+ // https://code.google.com/p/android/issues/detail?id=174964
+ assertEquals("No warnings.",
+ lintProject(
+ java("src/test/pkg/CallSuperTest.java", ""
+ + "package test.pkg;\n"
+ + "\n"
+ + "import android.support.annotation.CallSuper;\n"
+ + "\n"
+ + "import java.util.List;\n"
+ + "import java.util.Map;\n"
+ + "\n"
+ + "@SuppressWarnings(\"UnusedDeclaration\")\n"
+ + "public class CallSuperTest {\n"
+ + " private static class Child extends Parent {\n"
+ + " @Override\n"
+ + " protected void test1() {\n"
+ + " super.test1();\n"
+ + " }\n"
+ + " }\n"
+ + "\n"
+ + " private static class Parent extends ParentParent {\n"
+ + " @Override\n"
+ + " protected void test1() {\n"
+ + " super.test1();\n"
+ + " }\n"
+ + " }\n"
+ + "\n"
+ + " private static class ParentParent extends ParentParentParent {\n"
+ + " @CallSuper\n"
+ + " protected void test1() {\n"
+ + " }\n"
+ + " }\n"
+ + "\n"
+ + " private static class ParentParentParent {\n"
+ + "\n"
+ + " }\n"
+ + "}\n"),
+ copy("src/android/support/annotation/CallSuper.java.txt",
+ "src/android/support/annotation/CallSuper.java")));
+ }
+
+ public void testDetachFromWindow() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/DetachedFromWindow.java:7: Error: Overriding method should call super.onDetachedFromWindow [MissingSuperCall]\n"
+ + " protected void onDetachedFromWindow() {\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/DetachedFromWindow.java:26: Error: Overriding method should call super.onDetachedFromWindow [MissingSuperCall]\n"
+ + " protected void onDetachedFromWindow() {\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "2 errors, 0 warnings\n",
+
+ lintProject("src/test/pkg/DetachedFromWindow.java.txt=>" +
+ "src/test/pkg/DetachedFromWindow.java"));
+ }
+
+ public void testWatchFaceVisibility() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/WatchFaceTest.java:9: Error: Overriding method should call super.onVisibilityChanged [MissingSuperCall]\n"
+ + " public void onVisibilityChanged(boolean visible) { // ERROR: Missing super call\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+
+ lintProject(
+ "src/test/pkg/WatchFaceTest.java.txt=>src/test/pkg/WatchFaceTest.java",
+ "stubs/WatchFaceService.java.txt=>src/android/support/wearable/watchface/WatchFaceService.java",
+ "stubs/CanvasWatchFaceService.java.txt=>src/android/support/wearable/watchface/CanvasWatchFaceService.java"
+ ));
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ChildCountDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ChildCountDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ChildCountDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ChildCountDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/CipherGetInstanceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CipherGetInstanceDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/CipherGetInstanceDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CipherGetInstanceDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/CleanupDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CleanupDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/CleanupDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CleanupDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ClickableViewAccessibilityDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ClickableViewAccessibilityDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ClickableViewAccessibilityDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ClickableViewAccessibilityDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/CommentDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CommentDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/CommentDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CommentDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/CustomViewDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CustomViewDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/CustomViewDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CustomViewDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/CutPasteDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CutPasteDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/CutPasteDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/CutPasteDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/DateFormatDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DateFormatDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/DateFormatDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DateFormatDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/DeprecationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DeprecationDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/DeprecationDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DeprecationDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DetectMissingPrefixTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DetectMissingPrefixTest.java
new file mode 100644
index 0000000..ed10742
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DetectMissingPrefixTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings("javadoc")
+public class DetectMissingPrefixTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new DetectMissingPrefix();
+ }
+
+ public void test() throws Exception {
+ assertEquals(
+ "res/layout/namespace.xml:2: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" xmlns:other=\"http://foo.bar\" android:id=\"@+id/newlinear\" android:orientation=\"vertical\" android:layout_width=\"match_parent\" android:layout_height=\"match_parent\" orientation=\"true\">\n" +
+ " ~~~~~~~~~~~~~~~~~~\n" +
+ "res/layout/namespace.xml:3: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+ " <Button style=\"@style/setupWizardOuterFrame\" android.text=\"Button\" android:id=\"@+id/button1\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\"></Button>\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~\n" +
+ "res/layout/namespace.xml:5: Error: Unexpected namespace prefix \"other\" found for tag LinearLayout [MissingPrefix]\n" +
+ " <LinearLayout other:orientation=\"horizontal\"/>\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "3 errors, 0 warnings\n",
+
+ lintFiles("res/layout/namespace.xml"));
+ }
+
+ public void testCustomNamespace() throws Exception {
+ assertEquals(
+ "res/layout/namespace2.xml:9: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+ " orientation=\"true\">\n" +
+ " ~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 0 warnings\n",
+
+ lintFiles("res/layout/namespace2.xml"));
+ }
+
+ public void testManifest() throws Exception {
+ assertEquals(
+ "AndroidManifest.xml:4: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+ " versionCode=\"1\"\n" +
+ " ~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:11: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+ " android.label=\"@string/app_name\" >\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:18: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+ " <category name=\"android.intent.category.LAUNCHER\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "3 errors, 0 warnings\n",
+
+ lintFiles("missingprefix.xml=>AndroidManifest.xml"));
+ }
+
+ public void testLayoutAttributes() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintFiles("res/layout/namespace3.xml"));
+ }
+
+ public void testLayoutAttributes2() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintFiles("res/layout/namespace4.xml"));
+ }
+
+ public void testUnusedNamespace() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject("res/layout/message_edit_detail.xml"));
+ }
+
+ public void testMissingLayoutAttribute() throws Exception {
+ assertEquals(
+ "res/layout/rtl.xml:7: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+ " layout_gravity=\"left\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~\n" +
+ "res/layout/rtl.xml:8: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+ " layout_alignParentLeft=\"true\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "res/layout/rtl.xml:9: Error: Attribute is missing the Android namespace prefix [MissingPrefix]\n" +
+ " editable=\"false\"\n" +
+ " ~~~~~~~~~~~~~~~~\n" +
+ "3 errors, 0 warnings\n",
+
+ lintProject(
+ "overdraw/project.properties=>project.properties",
+ "rtl/minsdk5targetsdk17.xml=>AndroidManifest.xml",
+ "rtl/rtl_noprefix.xml=>res/layout/rtl.xml"
+ ));
+ }
+
+ public void testDataBinding() throws Exception {
+ assertEquals("No warnings.",
+ lintProject(xml("res/layout/test.xml", "\n"
+ + "<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " xmlns:bind=\"http://schemas.android.com/apk/res-auto\"\n"
+ + " xmlns:tools=\"http://schemas.android.com/tools\">\n"
+ + " <data>\n"
+ + " <variable name=\"activity\" type=\"com.android.example.bindingdemo.MainActivity\"/>\n"
+ + " <!---->\n"
+ + " <import\n"
+ + " type=\"android.view.View\"\n"
+ + " />\n"
+ + " <!---->\n"
+ + " <import type=\"com.android.example.bindingdemo.R.string\" alias=\"Strings\"/>\n"
+ + " <import type=\"com.android.example.bindingdemo.vo.User\"/>\n"
+ + " </data>\n"
+ + " <LinearLayout\n"
+ + " android:layout_width=\"match_parent\"\n"
+ + " android:layout_height=\"match_parent\"\n"
+ + " android:orientation=\"vertical\"\n"
+ + " android:id=\"@+id/activityRoot\"\n"
+ + " android:clickable=\"true\"\n"
+ + " android:onClickListener=\"@{activity.onUnselect}\">\n"
+ + " <android.support.v7.widget.CardView\n"
+ + " android:id=\"@+id/selected_card\"\n"
+ + " bind:contentPadding=\"@{activity.selected == null ? 5 : activity.selected.name.length()}\"\n"
+ + " android:layout_width=\"match_parent\"\n"
+ + " android:layout_height=\"wrap_content\"\n"
+ + " bind:visibility=\"@{activity.selected == null ? View.INVISIBLE : View.VISIBLE}\">\n"
+ + "\n"
+ + " <GridLayout\n"
+ + " android:layout_width=\"match_parent\"\n"
+ + " android:layout_height=\"wrap_content\"\n"
+ + " android:columnCount=\"2\"\n"
+ + " android:rowCount=\"4\">\n"
+ + " <Button\n"
+ + " android:id=\"@+id/edit_button\"\n"
+ + " bind:onClickListener=\"@{activity.onSave}\"\n"
+ + " android:text='@{\"Save changes to \" + activity.selected.name}'\n"
+ + " android:layout_width=\"wrap_content\"\n"
+ + " android:layout_height=\"wrap_content\"\n"
+ + " android:layout_column=\"1\"\n"
+ + " android:layout_gravity=\"right\"\n"
+ + " android:layout_row=\"2\"/>\n"
+ + " </GridLayout>\n"
+ + " </android.support.v7.widget.CardView>"
+ + " </LinearLayout>\n"
+ + "</layout>")));
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/DosLineEndingDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DosLineEndingDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/DosLineEndingDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DosLineEndingDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/DuplicateIdDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DuplicateIdDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/DuplicateIdDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DuplicateIdDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/DuplicateResourceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DuplicateResourceDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/DuplicateResourceDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/DuplicateResourceDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ExtraTextDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ExtraTextDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ExtraTextDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ExtraTextDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/FieldGetterDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FieldGetterDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/FieldGetterDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FieldGetterDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/FragmentDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FragmentDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/FragmentDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FragmentDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FullBackupContentDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FullBackupContentDetectorTest.java
new file mode 100644
index 0000000..844e5cb
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/FullBackupContentDetectorTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+public class FullBackupContentDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new FullBackupContentDetector();
+ }
+
+ public void testOk() throws Exception {
+ assertEquals("No warnings.",
+
+ lintProject(xml("res/xml/backup.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<full-backup-content>\n"
+ + " <include domain=\"file\" path=\"dd\"/>\n"
+ + " <exclude domain=\"file\" path=\"dd/fo3o.txt\"/>\n"
+ + " <exclude domain=\"file\" path=\"dd/ss/foo.txt\"/>\n"
+ + "</full-backup-content>")));
+ }
+
+ public void test20890435() throws Exception {
+ assertEquals(""
+ + "res/xml/backup.xml:6: Error: foo.xml is not in an included path [FullBackupContent]\n"
+ + " <exclude domain=\"sharedpref\" path=\"foo.xml\"/>\n"
+ + " ~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+
+ lintProject(xml("res/xml/backup.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<full-backup-content>\n"
+ + " <include domain=\"file\" path=\"dd\"/>\n"
+ + " <exclude domain=\"file\" path=\"dd/fo3o.txt\"/>\n"
+ + " <exclude domain=\"file\" path=\"dd/ss/foo.txt\"/>\n"
+ + " <exclude domain=\"sharedpref\" path=\"foo.xml\"/>\n"
+ + "</full-backup-content>")));
+ }
+
+ public void testImplicitInclude() throws Exception {
+ // If there is no include, then everything is considered included
+ assertEquals("No warnings.",
+
+ lintProject(xml("res/xml/backup.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<full-backup-content>\n"
+ + " <exclude domain=\"file\" path=\"dd/fo3o.txt\"/>\n"
+ + "</full-backup-content>")));
+ }
+
+ public void testImplicitPath() throws Exception {
+ // If you specify an include, but no path attribute, that's defined to mean include
+ // everything
+ assertEquals("No warnings.",
+
+ lintProject(xml("res/xml/backup.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<full-backup-content>\n"
+ + " <include domain=\"file\"/>\n"
+ + " <exclude domain=\"file\" path=\"dd/fo3o.txt\"/>\n"
+ + " <include domain=\"sharedpref\" path=\"something\"/>\n"
+ + "</full-backup-content>")));
+ }
+
+ public void testSuppressed() throws Exception {
+ assertEquals("No warnings.",
+
+ lintProject(xml("res/xml/backup.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<full-backup-content xmlns:tools=\"http://schemas.android.com/tools\">\n"
+ + " <include domain=\"file\" path=\"dd\"/>\n"
+ + " <exclude domain=\"file\" path=\"dd/fo3o.txt\"/>\n"
+ + " <exclude domain=\"file\" path=\"dd/ss/foo.txt\"/>\n"
+ + " <exclude domain=\"sharedpref\" path=\"foo.xml\" tools:ignore=\"FullBackupContent\"/>\n"
+ + "</full-backup-content>")));
+ }
+
+ public void testIncludeWrongDomain() throws Exception {
+ // Ensure that the path prefix check is done independently for each domain
+ assertEquals(""
+ + "res/xml/backup.xml:4: Error: abc/def.txt is not in an included path [FullBackupContent]\n"
+ + " <exclude domain=\"external\" path=\"abc/def.txt\"/>\n"
+ + " ~~~~~~~~~~~\n"
+ + "res/xml/backup.xml:6: Error: def/ghi.txt is not in an included path [FullBackupContent]\n"
+ + " <exclude domain=\"external\" path=\"def/ghi.txt\"/>\n"
+ + " ~~~~~~~~~~~\n"
+ + "2 errors, 0 warnings\n",
+
+ lintProject(xml("res/xml/backup.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<full-backup-content>\n"
+ + " <include domain=\"file\" path=\"abc\"/>\n"
+ + " <exclude domain=\"external\" path=\"abc/def.txt\"/>\n"
+ + " <include domain=\"file\" path=\"def\"/>\n"
+ + " <exclude domain=\"external\" path=\"def/ghi.txt\"/>\n"
+ + "</full-backup-content>")));
+ }
+
+ public void testValidation() throws Exception {
+ assertEquals(""
+ + "res/xml/backup.xml:7: Error: Subdirectories are not allowed for domain sharedpref [FullBackupContent]\n"
+ + " <include domain=\"sharedpref\" path=\"dd/subdir\"/>\n"
+ + " ~~~~~~~~~\n"
+ + "res/xml/backup.xml:8: Error: Paths are not allowed to contain .. [FullBackupContent]\n"
+ + " <include domain=\"file\" path=\"../outside\"/>\n"
+ + " ~~~~~~~~~~\n"
+ + "res/xml/backup.xml:9: Error: Paths are not allowed to contain // [FullBackupContent]\n"
+ + " <include domain=\"file\" path=\"//wrong\"/>\n"
+ + " ~~~~~~~\n"
+ + "res/xml/backup.xml:11: Error: Include dd is also excluded [FullBackupContent]\n"
+ + " <exclude domain=\"external\" path=\"dd\"/>\n"
+ + " ~~\n"
+ + " res/xml/backup.xml:10: Unnecessary/conflicting <include>\n"
+ + "res/xml/backup.xml:12: Error: Unexpected domain unknown-domain, expected one of root, file, database, sharedpref, external [FullBackupContent]\n"
+ + " <exclude domain=\"unknown-domain\" path=\"dd\"/>\n"
+ + " ~~~~~~~~~~~~~~\n"
+ + "res/xml/backup.xml:12: Error: dd is not in an included path [FullBackupContent]\n"
+ + " <exclude domain=\"unknown-domain\" path=\"dd\"/>\n"
+ + " ~~\n"
+ + "res/xml/backup.xml:13: Error: Missing domain attribute, expected one of root, file, database, sharedpref, external [FullBackupContent]\n"
+ + " <include path=\"dd\"/>\n"
+ + " ~~~~~~~~~~~~~~~~~~~~\n"
+ + "res/xml/backup.xml:15: Error: Unexpected element <wrongtag> [FullBackupContent]\n"
+ + " <wrongtag />\n"
+ + " ~~~~~~~~\n"
+ + "8 errors, 0 warnings\n",
+
+ lintProject(xml("res/xml/backup.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<full-backup-content>\n"
+ + " <include domain=\"root\" path=\"dd\"/>\n" // OK
+ + " <include domain=\"file\" path=\"dd\"/>\n" // OK
+ + " <include domain=\"database\" path=\"dd\"/>\n" // OK
+ + " <include domain=\"sharedpref\" path=\"dd\"/>\n" // OK
+ + " <include domain=\"sharedpref\" path=\"dd/subdir\"/>\n" // Not allowed
+ + " <include domain=\"file\" path=\"../outside\"/>\n" // Not allowed
+ + " <include domain=\"file\" path=\"//wrong\"/>\n" // Not allowed
+ + " <include domain=\"external\" path=\"dd\"/>\n" // OK
+ + " <exclude domain=\"external\" path=\"dd\"/>\n" // same as included
+ + " <exclude domain=\"unknown-domain\" path=\"dd\"/>\n"
+ + " <include path=\"dd\"/>\n" // No domain
+ + " <include domain=\"root\" />\n" // OK (means include everything
+ + " <wrongtag />\n"
+ + "</full-backup-content>")));
+ }
+}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/GetSignaturesDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GetSignaturesDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/GetSignaturesDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GetSignaturesDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GradleDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GradleDetectorTest.java
new file mode 100644
index 0000000..71e07a2
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GradleDetectorTest.java
@@ -0,0 +1,882 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import static com.android.SdkConstants.GRADLE_PLUGIN_MINIMUM_VERSION;
+import static com.android.SdkConstants.GRADLE_PLUGIN_RECOMMENDED_VERSION;
+import static com.android.tools.lint.checks.GradleDetector.ACCIDENTAL_OCTAL;
+import static com.android.tools.lint.checks.GradleDetector.COMPATIBILITY;
+import static com.android.tools.lint.checks.GradleDetector.DEPENDENCY;
+import static com.android.tools.lint.checks.GradleDetector.DEPRECATED;
+import static com.android.tools.lint.checks.GradleDetector.GRADLE_GETTER;
+import static com.android.tools.lint.checks.GradleDetector.GRADLE_PLUGIN_COMPATIBILITY;
+import static com.android.tools.lint.checks.GradleDetector.PATH;
+import static com.android.tools.lint.checks.GradleDetector.PLUS;
+import static com.android.tools.lint.checks.GradleDetector.REMOTE_VERSION;
+import static com.android.tools.lint.checks.GradleDetector.STRING_INTEGER;
+import static com.android.tools.lint.checks.GradleDetector.getNamedDependency;
+import static com.android.tools.lint.checks.GradleDetector.getNewValue;
+import static com.android.tools.lint.checks.GradleDetector.getOldValue;
+import static com.android.tools.lint.detector.api.TextFormat.TEXT;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidArtifact;
+import com.android.builder.model.AndroidLibrary;
+import com.android.builder.model.Dependencies;
+import com.android.builder.model.MavenCoordinates;
+import com.android.builder.model.Variant;
+import com.android.tools.lint.client.api.LintClient;
+import com.android.tools.lint.detector.api.Context;
+import com.android.tools.lint.detector.api.DefaultPosition;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Implementation;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Location;
+import com.android.tools.lint.detector.api.Project;
+import com.android.tools.lint.detector.api.Scope;
+import com.android.tools.lint.detector.api.Severity;
+import com.android.utils.Pair;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.builder.AstBuilder;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.NamedArgumentListExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * <b>NOTE</b>: Most GradleDetector unit tests are in the Studio plugin, as tests
+ * for IntellijGradleDetector
+ */
+public class GradleDetectorTest extends AbstractCheckTest {
+
+ private File mSdkDir;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ if (mSdkDir != null) {
+ deleteFile(mSdkDir);
+ mSdkDir = null;
+ }
+ }
+
+ /** Creates a mock SDK installation structure, containing a fixed set of dependencies */
+ private File getMockSupportLibraryInstallation() {
+ if (mSdkDir == null) {
+ // Make fake SDK "installation" such that we can predict the set
+ // of Maven repositories discovered by this test
+ mSdkDir = Files.createTempDir();
+
+ String[] paths = new String[]{
+ // Android repository
+ "extras/android/m2repository/com/android/support/appcompat-v7/18.0.0/appcompat-v7-18.0.0.aar",
+ "extras/android/m2repository/com/android/support/appcompat-v7/19.0.0/appcompat-v7-19.0.0.aar",
+ "extras/android/m2repository/com/android/support/appcompat-v7/19.0.1/appcompat-v7-19.0.1.aar",
+ "extras/android/m2repository/com/android/support/appcompat-v7/19.1.0/appcompat-v7-19.1.0.aar",
+ "extras/android/m2repository/com/android/support/appcompat-v7/20.0.0/appcompat-v7-20.0.0.aar",
+ "extras/android/m2repository/com/android/support/appcompat-v7/21.0.0/appcompat-v7-21.0.0.aar",
+ "extras/android/m2repository/com/android/support/appcompat-v7/21.0.2/appcompat-v7-21.0.2.aar",
+ "extras/android/m2repository/com/android/support/cardview-v7/21.0.0/cardview-v7-21.0.0.aar",
+ "extras/android/m2repository/com/android/support/cardview-v7/21.0.2/cardview-v7-21.0.2.aar",
+ "extras/android/m2repository/com/android/support/support-v13/20.0.0/support-v13-20.0.0.aar",
+ "extras/android/m2repository/com/android/support/support-v13/21.0.0/support-v13-21.0.0.aar",
+ "extras/android/m2repository/com/android/support/support-v13/21.0.2/support-v13-21.0.2.aar",
+ "extras/android/m2repository/com/android/support/support-v4/20.0.0/support-v4-20.0.0.aar",
+ "extras/android/m2repository/com/android/support/support-v4/21.0.0/support-v4-21.0.0.aar",
+ "extras/android/m2repository/com/android/support/support-v4/21.0.2/support-v4-21.0.2.aar",
+
+ // Google repository
+ "extras/google/m2repository/com/google/android/gms/play-services/3.1.36/play-services-3.1.36.aar",
+ "extras/google/m2repository/com/google/android/gms/play-services/3.1.59/play-services-3.1.59.aar",
+ "extras/google/m2repository/com/google/android/gms/play-services/3.2.25/play-services-3.2.25.aar",
+ "extras/google/m2repository/com/google/android/gms/play-services/3.2.65/play-services-3.2.65.aar",
+ "extras/google/m2repository/com/google/android/gms/play-services/4.0.30/play-services-4.0.30.aar",
+ "extras/google/m2repository/com/google/android/gms/play-services/4.1.32/play-services-4.1.32.aar",
+ "extras/google/m2repository/com/google/android/gms/play-services/4.2.42/play-services-4.2.42.aar",
+ "extras/google/m2repository/com/google/android/gms/play-services/4.3.23/play-services-4.3.23.aar",
+ "extras/google/m2repository/com/google/android/gms/play-services/4.4.52/play-services-4.4.52.aar",
+ "extras/google/m2repository/com/google/android/gms/play-services/5.0.89/play-services-5.0.89.aar",
+ "extras/google/m2repository/com/google/android/gms/play-services/6.1.11/play-services-6.1.11.aar",
+ "extras/google/m2repository/com/google/android/gms/play-services/6.1.71/play-services-6.1.71.aar",
+ "extras/google/m2repository/com/google/android/gms/play-services-wearable/5.0.77/play-services-wearable-5.0.77.aar",
+ "extras/google/m2repository/com/google/android/gms/play-services-wearable/6.1.11/play-services-wearable-6.1.11.aar",
+ "extras/google/m2repository/com/google/android/gms/play-services-wearable/6.1.71/play-services-wearable-6.1.71.aar",
+ "extras/google/m2repository/com/google/android/support/wearable/1.0.0/wearable-1.0.0.aar"
+ };
+
+ for (String path : paths) {
+ File file = new File(mSdkDir, path.replace('/', File.separatorChar));
+ File parent = file.getParentFile();
+ if (!parent.exists()) {
+ boolean ok = parent.mkdirs();
+ assertTrue(ok);
+ }
+ try {
+ boolean created = file.createNewFile();
+ assertTrue(created);
+ } catch (IOException e) {
+ fail(e.toString());
+ }
+ }
+ }
+
+ return mSdkDir;
+ }
+
+ public void testGetOldValue() {
+ assertEquals("11.0.2", getOldValue(DEPENDENCY,
+ "A newer version of com.google.guava:guava than 11.0.2 is available: 17.0.0",
+ TEXT));
+ assertNull(getOldValue(DEPENDENCY, "Bogus", TEXT));
+ assertNull(getOldValue(DEPENDENCY, "bogus", TEXT));
+ // targetSdkVersion 20, compileSdkVersion 19: Should replace targetVersion 20 with 19
+ assertEquals("20", getOldValue(DEPENDENCY,
+ "The targetSdkVersion (20) should not be higher than the compileSdkVersion (19)",
+ TEXT));
+ assertEquals("'19'", getOldValue(STRING_INTEGER,
+ "Use an integer rather than a string here (replace '19' with just 19)", TEXT));
+ assertEquals("android", getOldValue(DEPRECATED,
+ "'android' is deprecated; use 'com.android.application' instead", TEXT));
+ assertEquals("android-library", getOldValue(DEPRECATED,
+ "'android-library' is deprecated; use 'com.android.library' instead", TEXT));
+ assertEquals("packageName", getOldValue(DEPRECATED,
+ "Deprecated: Replace 'packageName' with 'applicationId'", TEXT));
+ assertEquals("packageNameSuffix", getOldValue(DEPRECATED,
+ "Deprecated: Replace 'packageNameSuffix' with 'applicationIdSuffix'", TEXT));
+ assertEquals("18.0.0", getOldValue(DEPENDENCY,
+ "Old buildToolsVersion 18.0.0; recommended version is 19.1 or later", TEXT));
+ }
+
+ public void testGetNewValue() {
+ assertEquals("17.0.0", getNewValue(DEPENDENCY,
+ "A newer version of com.google.guava:guava than 11.0.2 is available: 17.0.0",
+ TEXT));
+ assertNull(getNewValue(DEPENDENCY,
+ "A newer version of com.google.guava:guava than 11.0.2 is available", TEXT));
+ assertNull(getNewValue(DEPENDENCY, "bogus", TEXT));
+ // targetSdkVersion 20, compileSdkVersion 19: Should replace targetVersion 20 with 19
+ assertEquals("19", getNewValue(DEPENDENCY,
+ "The targetSdkVersion (20) should not be higher than the compileSdkVersion (19)",
+ TEXT));
+ assertEquals("19", getNewValue(STRING_INTEGER,
+ "Use an integer rather than a string here (replace '19' with just 19)", TEXT));
+ assertEquals("com.android.application", getNewValue(DEPRECATED,
+ "'android' is deprecated; use 'com.android.application' instead", TEXT));
+ assertEquals("com.android.library", getNewValue(DEPRECATED,
+ "'android-library' is deprecated; use 'com.android.library' instead", TEXT));
+ assertEquals("applicationId", getNewValue(DEPRECATED,
+ "Deprecated: Replace 'packageName' with 'applicationId'", TEXT));
+ assertEquals("applicationIdSuffix", getNewValue(DEPRECATED,
+ "Deprecated: Replace 'packageNameSuffix' with 'applicationIdSuffix'", TEXT));
+ assertEquals("19.1", getNewValue(DEPENDENCY,
+ "Old buildToolsVersion 18.0.0; recommended version is 19.1 or later", TEXT));
+ }
+
+ public void test() throws Exception {
+ mEnabled = Sets.newHashSet(COMPATIBILITY, DEPRECATED, DEPENDENCY, PLUS);
+ assertEquals(""
+ + "build.gradle:25: Error: This support library should not use a lower version (13) than the targetSdkVersion (17) [GradleCompatible]\n"
+ + " compile 'com.android.support:appcompat-v7:13.0.0'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:1: Warning: 'android' is deprecated; use 'com.android.application' instead [GradleDeprecated]\n"
+ + "apply plugin: 'android'\n"
+ + "~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:5: Warning: Old buildToolsVersion 19.0.0; recommended version is 19.1 or later [GradleDependency]\n"
+ + " buildToolsVersion \"19.0.0\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:24: Warning: A newer version of com.google.guava:guava than 11.0.2 is available: 18.0 [GradleDependency]\n"
+ + " freeCompile 'com.google.guava:guava:11.0.2'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:25: Warning: A newer version of com.android.support:appcompat-v7 than 13.0.0 is available: 21.0.2 [GradleDependency]\n"
+ + " compile 'com.android.support:appcompat-v7:13.0.0'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:23: Warning: Avoid using + in version numbers; can lead to unpredictable and unrepeatable builds (com.android.support:appcompat-v7:+) [GradleDynamicVersion]\n"
+ + " compile 'com.android.support:appcompat-v7:+'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 5 warnings\n",
+
+ lintProject("gradle/Dependencies.gradle=>build.gradle"));
+ }
+
+ public void testCompatibility() throws Exception {
+ mEnabled = Collections.singleton(COMPATIBILITY);
+ assertEquals(""
+ + "build.gradle:16: Error: This support library should not use a lower version (18) than the targetSdkVersion (19) [GradleCompatible]\n"
+ + " compile 'com.android.support:support-v4:18.0.0'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+
+ lintProject("gradle/Compatibility.gradle=>build.gradle"));
+ }
+
+ public void testIncompatiblePlugin() throws Exception {
+ mEnabled = Collections.singleton(GRADLE_PLUGIN_COMPATIBILITY);
+ assertEquals(""
+ + "build.gradle:6: Error: You must use a newer version of the Android Gradle plugin. The minimum supported version is " + GRADLE_PLUGIN_MINIMUM_VERSION + " and the recommended version is " + GRADLE_PLUGIN_RECOMMENDED_VERSION + " [AndroidGradlePluginVersion]\n"
+ + " classpath 'com.android.tools.build:gradle:0.1.0'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+
+ lintProject("gradle/IncompatiblePlugin.gradle=>build.gradle"));
+ }
+
+ public void testSetter() throws Exception {
+ mEnabled = Collections.singleton(GRADLE_GETTER);
+ assertEquals(""
+ + "build.gradle:18: Error: Bad method name: pick a unique method name which does not conflict with the implicit getters for the defaultConfig properties. For example, try using the prefix compute- instead of get-. [GradleGetter]\n"
+ + " versionCode getVersionCode\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:19: Error: Bad method name: pick a unique method name which does not conflict with the implicit getters for the defaultConfig properties. For example, try using the prefix compute- instead of get-. [GradleGetter]\n"
+ + " versionName getVersionName\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "2 errors, 0 warnings\n",
+
+ lintProject("gradle/Setter.gradle=>build.gradle"));
+ }
+
+ public void testDependencies() throws Exception {
+ mEnabled = Collections.singleton(DEPENDENCY);
+ assertEquals(""
+ + "build.gradle:5: Warning: Old buildToolsVersion 19.0.0; recommended version is 19.1 or later [GradleDependency]\n"
+ + " buildToolsVersion \"19.0.0\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:24: Warning: A newer version of com.google.guava:guava than 11.0.2 is available: 18.0 [GradleDependency]\n"
+ + " freeCompile 'com.google.guava:guava:11.0.2'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:25: Warning: A newer version of com.android.support:appcompat-v7 than 13.0.0 is available: 21.0.2 [GradleDependency]\n"
+ + " compile 'com.android.support:appcompat-v7:13.0.0'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 3 warnings\n",
+
+ lintProject("gradle/Dependencies.gradle=>build.gradle"));
+ }
+
+ public void testLongHandDependencies() throws Exception {
+ mEnabled = Collections.singleton(DEPENDENCY);
+ assertEquals(""
+ + "build.gradle:9: Warning: A newer version of com.android.support:support-v4 than 19.0 is available: 21.0.2 [GradleDependency]\n"
+ + " compile group: 'com.android.support', name: 'support-v4', version: '19.0'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 1 warnings\n",
+
+ lintProject("gradle/DependenciesProps.gradle=>build.gradle"));
+ }
+
+ public void testDependenciesMinSdkVersion() throws Exception {
+ mEnabled = Collections.singleton(DEPENDENCY);
+ assertEquals(""
+ + "build.gradle:13: Warning: Using the appcompat library when minSdkVersion >= 14 and compileSdkVersion < 21 is not necessary [GradleDependency]\n"
+ + " compile 'com.android.support:appcompat-v7:+'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 1 warnings\n",
+
+ lintProject("gradle/Dependencies14.gradle=>build.gradle"));
+ }
+
+ public void testDependenciesMinSdkVersionLollipop() throws Exception {
+ mEnabled = Collections.singleton(DEPENDENCY);
+ assertEquals("No warnings.",
+ lintProject("gradle/Dependencies14_21.gradle=>build.gradle"));
+ }
+
+ public void testDependenciesNoMicroVersion() throws Exception {
+ // Regression test for https://code.google.com/p/android/issues/detail?id=77594
+ mEnabled = Collections.singleton(DEPENDENCY);
+ assertEquals(""
+ + "build.gradle:13: Warning: A newer version of com.google.code.gson:gson than 2.2 is available: 2.3 [GradleDependency]\n"
+ + " compile 'com.google.code.gson:gson:2.2'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 1 warnings\n",
+
+ lintProject("gradle/DependenciesGson.gradle=>build.gradle"));
+ }
+
+ public void testPaths() throws Exception {
+ mEnabled = Collections.singleton(PATH);
+ assertEquals(""
+ + "build.gradle:4: Warning: Do not use Windows file separators in .gradle files; use / instead [GradlePath]\n"
+ + " compile files('my\\\\libs\\\\http.jar')\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:5: Warning: Avoid using absolute paths in .gradle files [GradlePath]\n"
+ + " compile files('/libs/android-support-v4.jar')\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 2 warnings\n",
+
+ lintProject("gradle/Paths.gradle=>build.gradle"));
+ }
+
+ public void testIdSuffix() throws Exception {
+ mEnabled = Collections.singleton(PATH);
+ assertEquals(""
+ + "build.gradle:6: Warning: Package suffix should probably start with a \".\" [GradlePath]\n"
+ + " applicationIdSuffix \"debug\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 1 warnings\n",
+
+ lintProject("gradle/IdSuffix.gradle=>build.gradle"));
+ }
+
+ public void testPackage() throws Exception {
+ mEnabled = Collections.singleton(DEPRECATED);
+ assertEquals(""
+ + "build.gradle:5: Warning: Deprecated: Replace 'packageName' with 'applicationId' [GradleDeprecated]\n"
+ + " packageName 'my.pkg'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:9: Warning: Deprecated: Replace 'packageNameSuffix' with 'applicationIdSuffix' [GradleDeprecated]\n"
+ + " packageNameSuffix \".debug\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 2 warnings\n",
+
+ lintProject("gradle/Package.gradle=>build.gradle"));
+ }
+
+ public void testPlus() throws Exception {
+ mEnabled = Collections.singleton(PLUS);
+ assertEquals(""
+ + "build.gradle:9: Warning: Avoid using + in version numbers; can lead to unpredictable and unrepeatable builds (com.android.support:appcompat-v7:+) [GradleDynamicVersion]\n"
+ + " compile 'com.android.support:appcompat-v7:+'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:10: Warning: Avoid using + in version numbers; can lead to unpredictable and unrepeatable builds (com.android.support:support-v4:21.0.+) [GradleDynamicVersion]\n"
+ + " compile group: 'com.android.support', name: 'support-v4', version: '21.0.+'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 2 warnings\n",
+
+ lintProject("gradle/Plus.gradle=>build.gradle"));
+ }
+
+ public void testStringInt() throws Exception {
+ mEnabled = Collections.singleton(STRING_INTEGER);
+ assertEquals(""
+ + "build.gradle:4: Error: Use an integer rather than a string here (replace '19' with just 19) [StringShouldBeInt]\n"
+ + " compileSdkVersion '19'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:7: Error: Use an integer rather than a string here (replace '8' with just 8) [StringShouldBeInt]\n"
+ + " minSdkVersion '8'\n"
+ + " ~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:8: Error: Use an integer rather than a string here (replace '16' with just 16) [StringShouldBeInt]\n"
+ + " targetSdkVersion '16'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~\n"
+ + "3 errors, 0 warnings\n",
+
+ lintProject("gradle/StringInt.gradle=>build.gradle"));
+ }
+
+ public void testSuppressLine2() throws Exception {
+ mEnabled = null;
+ assertEquals("No warnings.",
+
+ lintProject("gradle/SuppressLine2.gradle=>build.gradle"));
+ }
+
+ public void testDeprecatedPluginId() throws Exception {
+ mEnabled = Sets.newHashSet(DEPRECATED);
+ assertEquals(""
+ + "build.gradle:4: Warning: 'android' is deprecated; use 'com.android.application' instead [GradleDeprecated]\n"
+ + "apply plugin: 'android'\n"
+ + "~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:5: Warning: 'android-library' is deprecated; use 'com.android.library' instead [GradleDeprecated]\n"
+ + "apply plugin: 'android-library'\n"
+ + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 2 warnings\n",
+
+ lintProject("gradle/DeprecatedPluginId.gradle=>build.gradle"));
+ }
+
+ public void testIgnoresGStringsInDependencies() throws Exception {
+ mEnabled = null;
+ assertEquals("No warnings.",
+
+ lintProject("gradle/IgnoresGStringsInDependencies.gradle=>build.gradle"));
+ }
+
+ public void testAccidentalOctal() throws Exception {
+ mEnabled = Collections.singleton(ACCIDENTAL_OCTAL);
+ assertEquals(""
+ + "build.gradle:13: Error: The leading 0 turns this number into octal which is probably not what was intended (interpreted as 8) [AccidentalOctal]\n"
+ + " versionCode 010\n"
+ + " ~~~~~~~~~~~~~~~\n"
+ + "build.gradle:16: Error: The leading 0 turns this number into octal which is probably not what was intended (and it is not a valid octal number) [AccidentalOctal]\n"
+ + " versionCode 01 // line suffix comments are not handled correctly\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "2 errors, 0 warnings\n",
+
+ lintProject("gradle/AccidentalOctal.gradle=>build.gradle"));
+ }
+
+ public void testBadPlayServicesVersion() throws Exception {
+ mEnabled = Collections.singleton(COMPATIBILITY);
+ assertEquals(""
+ + "build.gradle:5: Error: Version 5.2.08 should not be used; the app can not be published with this version. Use version 6.1.71 instead. [GradleCompatible]\n"
+ + " compile 'com.google.android.gms:play-services:5.2.08'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+
+ lintProject("gradle/PlayServices.gradle=>build.gradle"));
+ }
+
+ public void testRemoteVersions() throws Exception {
+ mEnabled = Collections.singleton(REMOTE_VERSION);
+ try {
+ HashMap<String, String> data = Maps.newHashMap();
+ GradleDetector.sMockData = data;
+ data.put("http://search.maven.org/solrsearch/select?q=g:%22joda-time%22+AND+a:%22joda-time%22&core=gav&rows=1&wt=json",
+ "{\"responseHeader\":{\"status\":0,\"QTime\":1,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"joda-time\\\" AND a:\\\"joda-time\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"rows\":\"1\",\"version\":\"2.2\"}},\"response\":{\"numFound\":17,\"start\":0,\"docs\":[{\"id\":\"joda-time:joda-time:2.3\",\"g\":\"joda-time\",\"a\":\"joda-time\",\"v\":\"2.3\",\"p\":\"jar\",\"timestamp\":1376674285000,\"tags\":[\"replace\",\"time\",\"library\",\"date\",\"handling\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\".pom\"]}]}}");
+ data.put("http://search.maven.org/solrsearch/select?q=g:%22com.squareup.dagger%22+AND+a:%22dagger%22&core=gav&rows=1&wt=json",
+ "{\"responseHeader\":{\"status\":0,\"QTime\":1,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"com.squareup.dagger\\\" AND a:\\\"dagger\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"rows\":\"1\",\"version\":\"2.2\"}},\"response\":{\"numFound\":5,\"start\":0,\"docs\":[{\"id\":\"com.squareup.dagger:dagger:1.2.1\",\"g\":\"com.squareup.dagger\",\"a\":\"dagger\",\"v\":\"1.2.1\",\"p\":\"jar\",\"timestamp\":1392614597000,\"tags\":[\"dependency\",\"android\",\"injector\",\"java\",\"fast\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\"-tests.jar\",\".jar\",\".pom\"]}]}}");
+
+ assertEquals(""
+ + "build.gradle:9: Warning: A newer version of joda-time:joda-time than 2.1 is available: 2.3 [NewerVersionAvailable]\n"
+ + " compile 'joda-time:joda-time:2.1'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:10: Warning: A newer version of com.squareup.dagger:dagger than 1.2.0 is available: 1.2.1 [NewerVersionAvailable]\n"
+ + " compile 'com.squareup.dagger:dagger:1.2.0'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 2 warnings\n",
+
+ lintProject("gradle/RemoteVersions.gradle=>build.gradle"));
+ } finally {
+ GradleDetector.sMockData = null;
+ }
+ }
+
+ public void testRemoteVersionsWithPreviews() throws Exception {
+ // If the most recent version is a rc version, query for all versions
+ mEnabled = Collections.singleton(REMOTE_VERSION);
+ try {
+ HashMap<String, String> data = Maps.newHashMap();
+ GradleDetector.sMockData = data;
+ data.put("http://search.maven.org/solrsearch/select?q=g:%22com.google.guava%22+AND+a:%22guava%22&core=gav&rows=1&wt=json",
+ "{\"responseHeader\":{\"status\":0,\"QTime\":0,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"com.google.guava\\\" AND a:\\\"guava\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"rows\":\"1\",\"version\":\"2.2\"}},\"response\":{\"numFound\":38,\"start\":0,\"docs\":[{\"id\":\"com.google.guava:guava:18.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"18.0-rc1\",\"p\":\"bundle\",\"timestamp\":1407266204000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]}]}}");
+ data.put("http://search.maven.org/solrsearch/select?q=g:%22com.google.guava%22+AND+a:%22guava%22&core=gav&wt=json",
+ "{\"responseHeader\":{\"status\":0,\"QTime\":1,\"params\":{\"fl\":\"id,g,a,v,p,ec,timestamp,tags\",\"sort\":\"score desc,timestamp desc,g asc,a asc,v desc\",\"indent\":\"off\",\"q\":\"g:\\\"com.google.guava\\\" AND a:\\\"guava\\\"\",\"core\":\"gav\",\"wt\":\"json\",\"version\":\"2.2\"}},\"response\":{\"numFound\":38,\"start\":0,\"docs\":[{\"id\":\"com.google.guava:guava:18.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"18.0-rc1\",\"p\":\"bundle\",\"timestamp\":1407266204000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:17.0\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"17.0\",\"p\":\"bundle\",\"timestamp\":1398199666000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:17.0-rc2\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"17.0-rc2\",\"p\":\"bundle\",\"timestamp\":1397162341000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:17.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"17.0-rc1\",\"p\":\"bundle\",\"timestamp\":1396985408000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:16.0.1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"16.0.1\",\"p\":\"bundle\",\"timestamp\":1391467528000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:16.0\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"16.0\",\"p\":\"bundle\",\"timestamp\":1389995088000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:16.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"16.0-rc1\",\"p\":\"bundle\",\"timestamp\":1387495574000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"dependency\",\"that\",\"more\",\"utility\",\"guava\",\"javax\",\"only\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:15.0\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"15.0\",\"p\":\"bundle\",\"timestamp\":1378497169000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"inject\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"that\",\"more\",\"utility\",\"guava\",\"dependencies\",\"javax\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\",\"-cdi1.0.jar\"]},{\"id\":\"com.google.guava:guava:15.0-rc1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"15.0-rc1\",\"p\":\"bundle\",\"timestamp\":1377542588000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"inject\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"that\",\"more\",\"utility\",\"guava\",\"dependencies\",\"javax\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]},{\"id\":\"com.google.guava:guava:14.0.1\",\"g\":\"com.google.guava\",\"a\":\"guava\",\"v\":\"14.0.1\",\"p\":\"bundle\",\"timestamp\":1363305439000,\"tags\":[\"spec\",\"libraries\",\"classes\",\"google\",\"inject\",\"code\",\"expanded\",\"much\",\"include\",\"annotation\",\"that\",\"more\",\"utility\",\"guava\",\"dependencies\",\"javax\",\"core\",\"suite\",\"collections\"],\"ec\":[\"-javadoc.jar\",\"-sources.jar\",\".jar\",\"-site.jar\",\".pom\"]}]}}");
+
+ assertEquals(""
+ + "build.gradle:9: Warning: A newer version of com.google.guava:guava than 11.0.2 is available: 17.0 [NewerVersionAvailable]\n"
+ + " compile 'com.google.guava:guava:11.0.2'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "build.gradle:10: Warning: A newer version of com.google.guava:guava than 16.0-rc1 is available: 18.0.0-rc1 [NewerVersionAvailable]\n"
+ + " compile 'com.google.guava:guava:16.0-rc1'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 2 warnings\n",
+
+ lintProject("gradle/RemoteVersions2.gradle=>build.gradle"));
+ } finally {
+ GradleDetector.sMockData = null;
+ }
+ }
+
+ public void testPreviewVersions() throws Exception {
+ mEnabled = Collections.singleton(DEPENDENCY);
+ // This test only works when SdkConstants.GRADLE_PLUGIN_RECOMMENDED_VERSION contains
+ // a preview string:
+ if (!GRADLE_PLUGIN_RECOMMENDED_VERSION.startsWith("1.0.0-rc")) {
+ return;
+ }
+ assertEquals(""
+ + "build.gradle:6: Warning: A newer version of com.android.tools.build:gradle than 1.0.0-rc0 is available: " + GRADLE_PLUGIN_RECOMMENDED_VERSION + " [GradleDependency]\n"
+ + " classpath 'com.android.tools.build:gradle:1.0.0-rc0'\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 1 warnings\n",
+
+ lintProject("gradle/PreviewDependencies.gradle=>build.gradle"));
+ }
+
+
+ public void testDependenciesInVariables() throws Exception {
+ mEnabled = Collections.singleton(DEPENDENCY);
+ assertEquals(""
+ + "build.gradle:10: Warning: A newer version of com.google.android.gms:play-services-wearable than 5.0.77 is available: 6.1.71 [GradleDependency]\n"
+ + " compile \"com.google.android.gms:play-services-wearable:${GPS_VERSION}\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 1 warnings\n",
+
+ lintProject("gradle/DependenciesVariable.gradle=>build.gradle"));
+ }
+
+ @Override
+ protected void checkReportedError(@NonNull Context context, @NonNull Issue issue,
+ @NonNull Severity severity, @Nullable Location location, @NonNull String message) {
+ if (issue == DEPENDENCY && message.startsWith("Using the appcompat library when ")) {
+ // No data embedded in this specific message
+ return;
+ }
+
+ // Issues we're supporting getOldFrom
+ if (issue == DEPENDENCY
+ || issue == STRING_INTEGER
+ || issue == DEPRECATED
+ || issue == PLUS) {
+ assertNotNull("Could not extract message tokens from " + message,
+ GradleDetector.getOldValue(issue, message, TEXT));
+ }
+
+ if (issue == DEPENDENCY
+ || issue == STRING_INTEGER
+ || issue == DEPRECATED) {
+ assertNotNull("Could not extract message tokens from " + message,
+ GradleDetector.getNewValue(issue, message, TEXT));
+ }
+
+ if (issue == COMPATIBILITY) {
+ if (message.startsWith("Version ")) {
+ assertNotNull("Could not extract message tokens from " + message,
+ GradleDetector.getNewValue(issue, message, TEXT));
+ }
+ }
+ }
+
+ public void testGetNamedDependency() {
+ assertEquals("com.android.support:support-v4:21.0.+", getNamedDependency(
+ "group: 'com.android.support', name: 'support-v4', version: '21.0.+'"
+ ));
+ assertEquals("com.android.support:support-v4:21.0.+", getNamedDependency(
+ "name:'support-v4', group: \"com.android.support\", version: '21.0.+'"
+ ));
+ assertEquals("junit:junit:4.+", getNamedDependency(
+ "group: 'junit', name: 'junit', version: '4.+'"
+ ));
+ assertEquals("com.android.support:support-v4:19.0.+", getNamedDependency(
+ "group: 'com.android.support', name: 'support-v4', version: '19.0.+'"
+ ));
+ assertEquals("com.google.guava:guava:11.0.1", getNamedDependency(
+ "group: 'com.google.guava', name: 'guava', version: '11.0.1', transitive: false"
+ ));
+ assertEquals("com.google.api-client:google-api-client:1.6.0-beta", getNamedDependency(
+ "group: 'com.google.api-client', name: 'google-api-client', version: '1.6.0-beta', transitive: false"
+ ));
+ assertEquals("org.robolectric:robolectric:2.3-SNAPSHOT", getNamedDependency(
+ "group: 'org.robolectric', name: 'robolectric', version: '2.3-SNAPSHOT'"
+ ));
+ }
+
+ // -------------------------------------------------------------------------------------------
+ // Test infrastructure below here
+ // -------------------------------------------------------------------------------------------
+
+ static final Implementation IMPLEMENTATION = new Implementation(
+ GroovyGradleDetector.class,
+ Scope.GRADLE_SCOPE);
+ static {
+ for (Issue issue : new BuiltinIssueRegistry().getIssues()) {
+ if (issue.getImplementation().getDetectorClass() == GradleDetector.class) {
+ issue.setImplementation(IMPLEMENTATION);
+ }
+ }
+ }
+
+ @Override
+ protected Detector getDetector() {
+ return new GroovyGradleDetector();
+ }
+
+ private Set<Issue> mEnabled;
+
+ @Override
+ protected TestConfiguration getConfiguration(LintClient client, Project project) {
+ return new TestConfiguration(client, project, null) {
+ @Override
+ public boolean isEnabled(@NonNull Issue issue) {
+ return super.isEnabled(issue) && (mEnabled == null || mEnabled.contains(issue));
+ }
+ };
+ }
+
+ @Override
+ protected TestLintClient createClient() {
+ return new TestLintClient() {
+ @Nullable
+ @Override
+ public File getSdkHome() {
+ return getMockSupportLibraryInstallation();
+ }
+
+ @NonNull
+ @Override
+ protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+ if (!"testDependenciesInVariables".equals(getName())) {
+ return super.createProject(dir, referenceDir);
+ }
+
+ return new Project(this, dir, referenceDir) {
+ @Override
+ public boolean isGradleProject() {
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public Variant getCurrentVariant() {
+ /*
+ Simulate variant which has an AndroidLibrary with
+ resolved coordinates
+
+ com.google.android.gms:play-services-wearable:5.0.77"
+ */
+ MavenCoordinates coordinates = mock(MavenCoordinates.class);
+ when(coordinates.getGroupId()).thenReturn("com.google.android.gms");
+ when(coordinates.getArtifactId()).thenReturn("play-services-wearable");
+ when(coordinates.getVersion()).thenReturn("5.0.77");
+
+ AndroidLibrary library = mock(AndroidLibrary.class);
+ when(library.getResolvedCoordinates()).thenReturn(coordinates);
+ List<AndroidLibrary> libraries = Collections.singletonList(library);
+
+ Dependencies dependencies = mock(Dependencies.class);
+ when(dependencies.getLibraries()).thenReturn(libraries);
+
+ AndroidArtifact artifact = mock(AndroidArtifact.class);
+ when(artifact.getDependencies()).thenReturn(dependencies);
+
+ Variant variant = mock(Variant.class);
+ when(variant.getMainArtifact()).thenReturn(artifact);
+ return variant;
+ }
+ };
+ }
+ };
+ }
+
+ // Copy of com.android.build.gradle.tasks.GroovyGradleDetector (with "static" added as
+ // a modifier, and the unused field IMPLEMENTATION removed, and with fail(t.toString())
+ // inserted into visitBuildScript's catch handler.
+ //
+ // THIS CODE DUPLICATION IS NOT AN IDEAL SITUATION! But, it's preferable to a lack of
+ // tests.
+ //
+ // A more proper fix would be to extract the groovy detector into a library shared by
+ // the testing framework and the gradle plugin.
+
+ public static class GroovyGradleDetector extends GradleDetector {
+ @Override
+ public void visitBuildScript(@NonNull final Context context, Map<String, Object> sharedData) {
+ try {
+ visitQuietly(context, sharedData);
+ } catch (Throwable t) {
+ // ignore
+ // Parsing the build script can involve class loading that we sometimes can't
+ // handle. This happens for example when running lint in build-system/tests/api/.
+ // This is a lint limitation rather than a user error, so don't complain
+ // about these. Consider reporting a Issue#LINT_ERROR.
+ fail(t.toString());
+ }
+ }
+
+ private void visitQuietly(@NonNull final Context context,
+ @SuppressWarnings("UnusedParameters") Map<String, Object> sharedData) {
+ String source = context.getContents();
+ if (source == null) {
+ return;
+ }
+
+ List<ASTNode> astNodes = new AstBuilder().buildFromString(source);
+ GroovyCodeVisitor visitor = new CodeVisitorSupport() {
+ private List<MethodCallExpression> mMethodCallStack = Lists.newArrayList();
+ @Override
+ public void visitMethodCallExpression(MethodCallExpression expression) {
+ mMethodCallStack.add(expression);
+ super.visitMethodCallExpression(expression);
+ Expression arguments = expression.getArguments();
+ String parent = expression.getMethodAsString();
+ String parentParent = getParentParent();
+ if (arguments instanceof ArgumentListExpression) {
+ ArgumentListExpression ale = (ArgumentListExpression)arguments;
+ List<Expression> expressions = ale.getExpressions();
+ if (expressions.size() == 1 &&
+ expressions.get(0) instanceof ClosureExpression) {
+ if (isInterestingBlock(parent, parentParent)) {
+ ClosureExpression closureExpression =
+ (ClosureExpression)expressions.get(0);
+ Statement block = closureExpression.getCode();
+ if (block instanceof BlockStatement) {
+ BlockStatement bs = (BlockStatement)block;
+ for (Statement statement : bs.getStatements()) {
+ if (statement instanceof ExpressionStatement) {
+ ExpressionStatement e = (ExpressionStatement)statement;
+ if (e.getExpression() instanceof MethodCallExpression) {
+ checkDslProperty(parent,
+ (MethodCallExpression)e.getExpression(),
+ parentParent);
+ }
+ } else if (statement instanceof ReturnStatement) {
+ // Single item in block
+ ReturnStatement e = (ReturnStatement)statement;
+ if (e.getExpression() instanceof MethodCallExpression) {
+ checkDslProperty(parent,
+ (MethodCallExpression)e.getExpression(),
+ parentParent);
+ }
+ }
+ }
+ }
+ }
+ }
+ } else if (arguments instanceof TupleExpression) {
+ if (isInterestingStatement(parent, parentParent)) {
+ TupleExpression te = (TupleExpression) arguments;
+ Map<String, String> namedArguments = Maps.newHashMap();
+ List<String> unnamedArguments = Lists.newArrayList();
+ for (Expression subExpr : te.getExpressions()) {
+ if (subExpr instanceof NamedArgumentListExpression) {
+ NamedArgumentListExpression nale = (NamedArgumentListExpression) subExpr;
+ for (MapEntryExpression mae : nale.getMapEntryExpressions()) {
+ namedArguments.put(mae.getKeyExpression().getText(),
+ mae.getValueExpression().getText());
+ }
+ }
+ }
+ checkMethodCall(context, parent, parentParent, namedArguments, unnamedArguments, expression);
+ }
+ }
+ assert !mMethodCallStack.isEmpty();
+ assert mMethodCallStack.get(mMethodCallStack.size() - 1) == expression;
+ mMethodCallStack.remove(mMethodCallStack.size() - 1);
+ }
+
+ private String getParentParent() {
+ for (int i = mMethodCallStack.size() - 2; i >= 0; i--) {
+ MethodCallExpression expression = mMethodCallStack.get(i);
+ Expression arguments = expression.getArguments();
+ if (arguments instanceof ArgumentListExpression) {
+ ArgumentListExpression ale = (ArgumentListExpression)arguments;
+ List<Expression> expressions = ale.getExpressions();
+ if (expressions.size() == 1 &&
+ expressions.get(0) instanceof ClosureExpression) {
+ return expression.getMethodAsString();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private void checkDslProperty(String parent, MethodCallExpression c,
+ String parentParent) {
+ String property = c.getMethodAsString();
+ if (isInterestingProperty(property, parent, getParentParent())) {
+ String value = getText(c.getArguments());
+ checkDslPropertyAssignment(context, property, value, parent, parentParent, c, c);
+ }
+ }
+
+ private String getText(ASTNode node) {
+ String source = context.getContents();
+ Pair<Integer, Integer> offsets = getOffsets(node, context);
+ return source.substring(offsets.getFirst(), offsets.getSecond());
+ }
+ };
+
+ for (ASTNode node : astNodes) {
+ node.visit(visitor);
+ }
+ }
+
+ @NonNull
+ private static Pair<Integer, Integer> getOffsets(ASTNode node, Context context) {
+ if (node.getLastLineNumber() == -1 && node instanceof TupleExpression) {
+ // Workaround: TupleExpressions yield bogus offsets, so use its
+ // children instead
+ TupleExpression exp = (TupleExpression) node;
+ List<Expression> expressions = exp.getExpressions();
+ if (!expressions.isEmpty()) {
+ return Pair.of(
+ getOffsets(expressions.get(0), context).getFirst(),
+ getOffsets(expressions.get(expressions.size() - 1), context).getSecond());
+ }
+ }
+ String source = context.getContents();
+ assert source != null; // because we successfully parsed
+ int start = 0;
+ int end = source.length();
+ int line = 1;
+ int startLine = node.getLineNumber();
+ int startColumn = node.getColumnNumber();
+ int endLine = node.getLastLineNumber();
+ int endColumn = node.getLastColumnNumber();
+ int column = 1;
+ for (int index = 0, len = end; index < len; index++) {
+ if (line == startLine && column == startColumn) {
+ start = index;
+ }
+ if (line == endLine && column == endColumn) {
+ end = index;
+ break;
+ }
+
+ char c = source.charAt(index);
+ if (c == '\n') {
+ line++;
+ column = 1;
+ } else {
+ column++;
+ }
+ }
+
+ return Pair.of(start, end);
+ }
+
+ @Override
+ protected int getStartOffset(@NonNull Context context, @NonNull Object cookie) {
+ ASTNode node = (ASTNode) cookie;
+ Pair<Integer, Integer> offsets = getOffsets(node, context);
+ return offsets.getFirst();
+ }
+
+ @Override
+ protected Location createLocation(@NonNull Context context, @NonNull Object cookie) {
+ ASTNode node = (ASTNode) cookie;
+ Pair<Integer, Integer> offsets = getOffsets(node, context);
+ int fromLine = node.getLineNumber() - 1;
+ int fromColumn = node.getColumnNumber() - 1;
+ int toLine = node.getLastLineNumber() - 1;
+ int toColumn = node.getLastColumnNumber() - 1;
+ return Location.create(context.file,
+ new DefaultPosition(fromLine, fromColumn, offsets.getFirst()),
+ new DefaultPosition(toLine, toColumn, offsets.getSecond()));
+ }
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/GridLayoutDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GridLayoutDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/GridLayoutDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/GridLayoutDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java
new file mode 100644
index 0000000..aa52ba2
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HandlerDetectorTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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({"javadoc", "ClassNameDiffersFromFileName"})
+public class HandlerDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new HandlerDetector();
+ }
+
+ public void testRegistered() throws Exception {
+ assertEquals(
+ "src/test/pkg/HandlerTest.java:12: Warning: This Handler class should be static or leaks might occur (test.pkg.HandlerTest.Inner) [HandlerLeak]\n" +
+ " public class Inner extends Handler { // ERROR\n" +
+ " ~~~~~\n" +
+ "src/test/pkg/HandlerTest.java:18: Warning: This Handler class should be static or leaks might occur (new android.os.Handler(){}) [HandlerLeak]\n" +
+ " Handler anonymous = new Handler() { // ERROR\n" +
+ " ^\n" +
+ "0 errors, 2 warnings\n",
+
+ lintProject(
+ "bytecode/HandlerTest.java.txt=>src/test/pkg/HandlerTest.java",
+ "bytecode/HandlerTest.class.data=>bin/classes/test/pkg/HandlerTest.class",
+ "bytecode/HandlerTest$Inner.class.data=>bin/classes/test/pkg/HandlerTest$Inner.class",
+ "bytecode/HandlerTest$StaticInner.class.data=>bin/classes/test/pkg/HandlerTest$StaticInner.class",
+ "bytecode/HandlerTest$WithArbitraryLooper.class.data=>bin/classes/test/pkg/HandlerTest$WithArbitraryLooper.class",
+ "bytecode/HandlerTest$1.class.data=>bin/classes/test/pkg/HandlerTest$1.class",
+ "bytecode/HandlerTest$2.class.data=>bin/classes/test/pkg/HandlerTest$2.class"));
+ }
+
+ public void testSuppress() throws Exception {
+ assertEquals("No warnings.",
+ lintProject(
+ java("src/test/pkg/CheckActivity.java", ""
+ + "package test.pkg;\n"
+ + "import android.annotation.SuppressLint;\n"
+ + "import android.app.Activity;\n"
+ + "import android.os.Handler;\n"
+ + "import android.os.Message;\n"
+ + "\n"
+ + "public class CheckActivity extends Activity {\n"
+ + "\n"
+ + " @SuppressWarnings(\"unused\")\n"
+ + " @SuppressLint(\"HandlerLeak\")\n"
+ + " Handler handler = new Handler() {\n"
+ + "\n"
+ + " public void handleMessage(Message msg) {\n"
+ + "\n"
+ + " }\n"
+ + " };\n"
+ + "\n"
+ + "}")
+ ));
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/HardcodedDebugModeDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HardcodedDebugModeDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/HardcodedDebugModeDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HardcodedDebugModeDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/HardcodedValuesDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HardcodedValuesDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/HardcodedValuesDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/HardcodedValuesDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/IconDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/IconDetectorTest.java
new file mode 100644
index 0000000..17703af
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/IconDetectorTest.java
@@ -0,0 +1,812 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.build.FilterData;
+import com.android.build.OutputFile;
+import com.android.builder.model.AndroidArtifact;
+import com.android.builder.model.AndroidArtifactOutput;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.ProductFlavor;
+import com.android.builder.model.ProductFlavorContainer;
+import com.android.builder.model.Variant;
+import com.android.tools.lint.client.api.LintClient;
+import com.android.tools.lint.client.api.LintDriver;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Project;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import org.mockito.stubbing.OngoingStubbing;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@SuppressWarnings("javadoc")
+public class IconDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new IconDetector();
+ }
+
+ private Set<Issue> mEnabled = new HashSet<Issue>();
+ private boolean mAbbreviate;
+
+ private static final Set<Issue> ALL = new HashSet<Issue>();
+ static {
+ ALL.add(IconDetector.DUPLICATES_CONFIGURATIONS);
+ ALL.add(IconDetector.DUPLICATES_NAMES);
+ ALL.add(IconDetector.GIF_USAGE);
+ ALL.add(IconDetector.ICON_DENSITIES);
+ ALL.add(IconDetector.ICON_DIP_SIZE);
+ ALL.add(IconDetector.ICON_EXTENSION);
+ ALL.add(IconDetector.ICON_LOCATION);
+ ALL.add(IconDetector.ICON_MISSING_FOLDER);
+ ALL.add(IconDetector.ICON_NODPI);
+ ALL.add(IconDetector.ICON_COLORS);
+ ALL.add(IconDetector.ICON_XML_AND_PNG);
+ ALL.add(IconDetector.ICON_LAUNCHER_SHAPE);
+ ALL.add(IconDetector.ICON_MIX_9PNG);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mAbbreviate = true;
+ }
+
+ @Override
+ protected void configureDriver(LintDriver driver) {
+ driver.setAbbreviating(mAbbreviate);
+ }
+
+ @Override
+ protected TestConfiguration getConfiguration(LintClient client, Project project) {
+ return new TestConfiguration(client, project, null) {
+ @Override
+ public boolean isEnabled(@NonNull Issue issue) {
+ return super.isEnabled(issue) && mEnabled.contains(issue);
+ }
+ };
+ }
+
+ public void test() throws Exception {
+ mEnabled = ALL;
+ assertEquals(
+ "res/drawable-mdpi/sample_icon.gif: Warning: Using the .gif format for bitmaps is discouraged [GifUsage]\n" +
+ "res/drawable/ic_launcher.png: Warning: The ic_launcher.png icon has identical contents in the following configuration folders: drawable-mdpi, drawable [IconDuplicatesConfig]\n" +
+ " res/drawable-mdpi/ic_launcher.png: <No location-specific message\n" +
+ "res/drawable/ic_launcher.png: Warning: Found bitmap drawable res/drawable/ic_launcher.png in densityless folder [IconLocation]\n" +
+ "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: sample_icon.gif (found in drawable-mdpi) [IconDensities]\n" +
+ "res: Warning: Missing density variation folders in res: drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
+ "0 errors, 5 warnings\n" +
+ "",
+
+ lintProject(
+ // Use minSDK4 to ensure that we get warnings about missing drawables
+ "apicheck/minsdk4.xml=>AndroidManifest.xml",
+ "res/drawable/ic_launcher.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher.png",
+ "res/drawable-mdpi/sample_icon.gif",
+ // Make a dummy file named .svn to make sure it doesn't get seen as
+ // an icon name
+ "res/drawable-mdpi/sample_icon.gif=>res/drawable-hdpi/.svn",
+ "res/drawable-hdpi/ic_launcher.png"));
+ }
+
+ public void testMixed() throws Exception {
+ mEnabled = Collections.singleton(IconDetector.ICON_XML_AND_PNG);
+ assertEquals(
+ "res/drawable/background.xml: Warning: The following images appear both as density independent .xml files and as bitmap files: res/drawable-mdpi/background.png, res/drawable/background.xml [IconXmlAndPng]\n" +
+ " res/drawable-mdpi/background.png: <No location-specific message\n" +
+ "0 errors, 1 warnings\n",
+
+ lintProject(
+ "apicheck/minsdk4.xml=>AndroidManifest.xml",
+ "apicheck/minsdk4.xml=>res/drawable/background.xml",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/background.png"));
+ }
+
+ public void testApi1() throws Exception {
+ mEnabled = ALL;
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ // manifest file which specifies uses sdk = 2
+ "apicheck/minsdk2.xml=>AndroidManifest.xml",
+ "res/drawable/ic_launcher.png"));
+ }
+
+ public void test2() throws Exception {
+ mEnabled = ALL;
+ assertEquals(
+ "res/drawable-hdpi/other.9.png: Warning: The following unrelated icon files have identical contents: appwidget_bg.9.png, other.9.png [IconDuplicates]\n" +
+ " res/drawable-hdpi/appwidget_bg.9.png: <No location-specific message\n" +
+ "res/drawable-hdpi/unrelated.png: Warning: The following unrelated icon files have identical contents: ic_launcher.png, unrelated.png [IconDuplicates]\n" +
+ " res/drawable-hdpi/ic_launcher.png: <No location-specific message\n" +
+ "res: Warning: Missing density variation folders in res: drawable-mdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
+ "0 errors, 3 warnings\n",
+
+ lintProject(
+ "res/drawable-hdpi/unrelated.png",
+ "res/drawable-hdpi/appwidget_bg.9.png",
+ "res/drawable-hdpi/appwidget_bg_focus.9.png",
+ "res/drawable-hdpi/other.9.png",
+ "res/drawable-hdpi/ic_launcher.png"
+ ));
+ }
+
+ public void testNoDpi() throws Exception {
+ mEnabled = ALL;
+ assertEquals(
+ "res/drawable-mdpi/frame.png: Warning: The following images appear in both -nodpi and in a density folder: frame.png [IconNoDpi]\n" +
+ "res/drawable-xlarge-nodpi-v11/frame.png: Warning: The frame.png icon has identical contents in the following configuration folders: drawable-mdpi, drawable-nodpi, drawable-xlarge-nodpi-v11 [IconDuplicatesConfig]\n" +
+ " res/drawable-nodpi/frame.png: <No location-specific message\n" +
+ " res/drawable-mdpi/frame.png: <No location-specific message\n" +
+ "res: Warning: Missing density variation folders in res: drawable-hdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
+ "0 errors, 3 warnings\n" +
+ "",
+
+ lintProject(
+ "res/drawable-mdpi/frame.png",
+ "res/drawable-nodpi/frame.png",
+ "res/drawable-xlarge-nodpi-v11/frame.png"));
+ }
+
+ public void testNoDpi2() throws Exception {
+ mEnabled = ALL;
+ // Having additional icon names in the no-dpi folder should not cause any complaints
+ assertEquals(
+ "res/drawable-xxxhdpi/frame.png: Warning: The image frame.png varies significantly in its density-independent (dip) size across the various density versions: drawable-ldpi/frame.png: 629x387 dp (472x290 px), drawable-mdpi/frame.png: 472x290 dp (472x290 px), drawable-hdpi/frame.png: 315x193 dp (472x290 px), drawable-xhdpi/frame.png: 236x145 dp (472x290 px), drawable-xxhdpi/frame.png: 157x97 dp (472x290 px), drawable-xxxhdpi/frame.png: 118x73 dp (472x290 px) [IconDipSize]\n" +
+ " res/drawable-xxhdpi/frame.png: <No location-specific message\n" +
+ " res/drawable-xhdpi/frame.png: <No location-specific message\n" +
+ " res/drawable-hdpi/frame.png: <No location-specific message\n" +
+ " res/drawable-mdpi/frame.png: <No location-specific message\n" +
+ " res/drawable-ldpi/frame.png: <No location-specific message\n" +
+ "res/drawable-xxxhdpi/frame.png: Warning: The following unrelated icon files have identical contents: frame.png, frame.png, frame.png, file1.png, file2.png, frame.png, frame.png, frame.png [IconDuplicates]\n" +
+ " res/drawable-xxhdpi/frame.png: <No location-specific message\n" +
+ " res/drawable-xhdpi/frame.png: <No location-specific message\n" +
+ " res/drawable-nodpi/file2.png: <No location-specific message\n" +
+ " res/drawable-nodpi/file1.png: <No location-specific message\n" +
+ " res/drawable-mdpi/frame.png: <No location-specific message\n" +
+ " res/drawable-ldpi/frame.png: <No location-specific message\n" +
+ " res/drawable-hdpi/frame.png: <No location-specific message\n" +
+ "0 errors, 2 warnings\n" +
+ "",
+
+ lintProject(
+ "res/drawable-mdpi/frame.png=>res/drawable-mdpi/frame.png",
+ "res/drawable-mdpi/frame.png=>res/drawable-hdpi/frame.png",
+ "res/drawable-mdpi/frame.png=>res/drawable-ldpi/frame.png",
+ "res/drawable-mdpi/frame.png=>res/drawable-xhdpi/frame.png",
+ "res/drawable-mdpi/frame.png=>res/drawable-xxhdpi/frame.png",
+ "res/drawable-mdpi/frame.png=>res/drawable-xxxhdpi/frame.png",
+ "res/drawable-mdpi/frame.png=>res/drawable-nodpi/file1.png",
+ "res/drawable-mdpi/frame.png=>res/drawable-nodpi/file2.png"));
+ }
+
+ public void testNoDpiMix() throws Exception {
+ mEnabled = ALL;
+ assertEquals(
+ "res/drawable-mdpi/frame.xml: Warning: The following images appear in both -nodpi and in a density folder: frame.png, frame.xml [IconNoDpi]\n" +
+ " res/drawable-mdpi/frame.png: <No location-specific message\n" +
+ "res/drawable-nodpi/frame.xml: Warning: The following images appear both as density independent .xml files and as bitmap files: res/drawable-mdpi/frame.png, res/drawable-nodpi/frame.xml [IconXmlAndPng]\n" +
+ " res/drawable-mdpi/frame.png: <No location-specific message\n" +
+ "res: Warning: Missing density variation folders in res: drawable-hdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi [IconMissingDensityFolder]\n" +
+ "0 errors, 3 warnings\n",
+
+ lintProject(
+ "res/drawable-mdpi/frame.png",
+ "res/drawable/states.xml=>res/drawable-nodpi/frame.xml"));
+ }
+
+
+ public void testMixedFormat() throws Exception {
+ mEnabled = ALL;
+ // Test having a mixture of .xml and .png resources for the same name
+ // Make sure we don't get:
+ // drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: f.png (found in drawable-mdpi)
+ // drawable-xhdpi: Warning: Missing the following drawables in drawable-xhdpi: f.png (found in drawable-mdpi)
+ assertEquals(
+ "res/drawable-xxxhdpi/f.xml: Warning: The following images appear both as density independent .xml files and as bitmap files: res/drawable-hdpi/f.xml, res/drawable-mdpi/f.png [IconXmlAndPng]\n" +
+ " res/drawable-xxhdpi/f.xml: <No location-specific message\n" +
+ " res/drawable-xhdpi/f.xml: <No location-specific message\n" +
+ " res/drawable-mdpi/f.png: <No location-specific message\n" +
+ " res/drawable-hdpi/f.xml: <No location-specific message\n" +
+ "0 errors, 1 warnings\n",
+
+ lintProject(
+ "res/drawable-mdpi/frame.png=>res/drawable-mdpi/f.png",
+ "res/drawable/states.xml=>res/drawable-hdpi/f.xml",
+ "res/drawable/states.xml=>res/drawable-xhdpi/f.xml",
+ "res/drawable/states.xml=>res/drawable-xxhdpi/f.xml",
+ "res/drawable/states.xml=>res/drawable-xxxhdpi/f.xml"));
+ }
+
+ public void testMisleadingFileName() throws Exception {
+ mEnabled = Collections.singleton(IconDetector.ICON_EXTENSION);
+ assertEquals(
+ "res/drawable-mdpi/frame.gif: Warning: Misleading file extension; named .gif but the file format is png [IconExtension]\n" +
+ "res/drawable-mdpi/frame.jpg: Warning: Misleading file extension; named .jpg but the file format is png [IconExtension]\n" +
+ "res/drawable-mdpi/myjpg.png: Warning: Misleading file extension; named .png but the file format is JPEG [IconExtension]\n" +
+ "res/drawable-mdpi/sample_icon.jpeg: Warning: Misleading file extension; named .jpeg but the file format is gif [IconExtension]\n" +
+ "res/drawable-mdpi/sample_icon.jpg: Warning: Misleading file extension; named .jpg but the file format is gif [IconExtension]\n" +
+ "res/drawable-mdpi/sample_icon.png: Warning: Misleading file extension; named .png but the file format is gif [IconExtension]\n" +
+ "0 errors, 6 warnings\n",
+
+ lintProject(
+ "res/drawable-mdpi/sample_icon.jpg=>res/drawable-mdpi/myjpg.jpg", // VALID
+ "res/drawable-mdpi/sample_icon.jpg=>res/drawable-mdpi/myjpg.jpeg", // VALID
+ "res/drawable-mdpi/frame.png=>res/drawable-mdpi/frame.gif",
+ "res/drawable-mdpi/frame.png=>res/drawable-mdpi/frame.jpg",
+ "res/drawable-mdpi/sample_icon.jpg=>res/drawable-mdpi/myjpg.png",
+ "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/sample_icon.jpg",
+ "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/sample_icon.jpeg",
+ "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/sample_icon.png"));
+ }
+
+ public void testColors() throws Exception {
+ mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
+ assertEquals(
+ "res/drawable-mdpi/ic_menu_my_action.png: Warning: Action Bar icons should use a single gray color (#333333 for light themes (with 60%/30% opacity for enabled/disabled), and #FFFFFF with opacity 80%/30% for dark themes [IconColors]\n" +
+ "res/drawable-mdpi-v11/ic_stat_my_notification.png: Warning: Notification icons must be entirely white [IconColors]\n" +
+ "res/drawable-mdpi-v9/ic_stat_my_notification2.png: Warning: Notification icons must be entirely white [IconColors]\n" +
+ "0 errors, 3 warnings\n",
+
+ lintProject(
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_menu_my_action.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi-v11/ic_stat_my_notification.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi-v9/ic_stat_my_notification2.png",
+ "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
+ }
+
+ public void testNotActionBarIcons() throws Exception {
+ mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
+ assertEquals(
+ "No warnings.",
+
+ // No Java code designates the menu as an action bar menu
+ lintProject(
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "res/menu/menu.xml",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon2.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png", // Not action bar
+ "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
+ }
+
+ public void testActionBarIcons() throws Exception {
+ mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
+ assertEquals(
+ "res/drawable-mdpi/icon1.png: Warning: Action Bar icons should use a single gray color (#333333 for light themes (with 60%/30% opacity for enabled/disabled), and #FFFFFF with opacity 80%/30% for dark themes [IconColors]\n" +
+ "res/drawable-mdpi/icon2.png: Warning: Action Bar icons should use a single gray color (#333333 for light themes (with 60%/30% opacity for enabled/disabled), and #FFFFFF with opacity 80%/30% for dark themes [IconColors]\n" +
+ "0 errors, 2 warnings\n",
+
+ lintProject(
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "res/menu/menu.xml",
+ "src/test/pkg/ActionBarTest.java.txt=>src/test/pkg/ActionBarTest.java",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon2.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png", // Not action bar
+ "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
+ }
+
+ public void testOkActionBarIcons() throws Exception {
+ mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "res/menu/menu.xml",
+ "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon1.png",
+ "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon2.png"));
+ }
+
+ public void testNotificationIcons() throws Exception {
+ mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
+ assertEquals(
+ "res/drawable-mdpi/icon1.png: Warning: Notification icons must be entirely white [IconColors]\n" +
+ "res/drawable-mdpi/icon2.png: Warning: Notification icons must be entirely white [IconColors]\n" +
+ "res/drawable-mdpi/icon3.png: Warning: Notification icons must be entirely white [IconColors]\n" +
+ "res/drawable-mdpi/icon4.png: Warning: Notification icons must be entirely white [IconColors]\n" +
+ "res/drawable-mdpi/icon5.png: Warning: Notification icons must be entirely white [IconColors]\n" +
+ "0 errors, 5 warnings\n",
+
+ lintProject(
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "src/test/pkg/NotificationTest.java.txt=>src/test/pkg/NotificationTest.java",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon2.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon4.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon5.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon6.png", // not a notification
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon7.png", // ditto
+ "res/drawable-mdpi/ic_menu_add_clip_normal.png")); // OK
+ }
+
+ public void testOkNotificationIcons() throws Exception {
+ mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "src/test/pkg/NotificationTest.java.txt=>src/test/pkg/NotificationTest.java",
+ "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon1.png",
+ "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon2.png",
+ "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon3.png",
+ "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon4.png",
+ "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon5.png"));
+ }
+
+ public void testExpectedSize() throws Exception {
+ mEnabled = Collections.singleton(IconDetector.ICON_EXPECTED_SIZE);
+ assertEquals(
+ "res/drawable-mdpi/ic_launcher.png: Warning: Incorrect icon size for drawable-mdpi/ic_launcher.png: expected 48x48, but was 24x24 [IconExpectedSize]\n" +
+ "res/drawable-mdpi/icon1.png: Warning: Incorrect icon size for drawable-mdpi/icon1.png: expected 32x32, but was 48x48 [IconExpectedSize]\n" +
+ "res/drawable-mdpi/icon3.png: Warning: Incorrect icon size for drawable-mdpi/icon3.png: expected 24x24, but was 48x48 [IconExpectedSize]\n" +
+ "0 errors, 3 warnings\n",
+
+ lintProject(
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "src/test/pkg/NotificationTest.java.txt=>src/test/pkg/NotificationTest.java",
+ "res/menu/menu.xml",
+ "src/test/pkg/ActionBarTest.java.txt=>src/test/pkg/ActionBarTest.java",
+
+ // 3 wrong-sized icons:
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon1.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/icon3.png",
+ "res/drawable-mdpi/stat_notify_alarm.png=>res/drawable-mdpi/ic_launcher.png",
+
+ // OK sizes
+ "res/drawable-mdpi/ic_menu_add_clip_normal.png=>res/drawable-mdpi/icon2.png",
+ "res/drawable-mdpi/stat_notify_alarm.png=>res/drawable-mdpi/icon4.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png"
+ ));
+ }
+
+ public void testAbbreviate() throws Exception {
+ mEnabled = Collections.singleton(IconDetector.ICON_DENSITIES);
+ assertEquals(
+ "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: " +
+ "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png, " +
+ "ic_launcher3.png... (6 more) [IconDensities]\n" +
+ "res/drawable-xhdpi: Warning: Missing the following drawables in drawable-xhdpi: " +
+ "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png, " +
+ "ic_launcher3.png... (6 more) [IconDensities]\n" +
+ "0 errors, 2 warnings\n",
+
+ lintProject(
+ // Use minSDK4 to ensure that we get warnings about missing drawables
+ "apicheck/minsdk4.xml=>AndroidManifest.xml",
+ "res/drawable/ic_launcher.png=>res/drawable-hdpi/ic_launcher1.png",
+ "res/drawable/ic_launcher.png=>res/drawable-xhdpi/ic_launcher1.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher1.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher3.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher4.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher5.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher6.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher7.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher8.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher9.webp",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher10.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher11.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher12.png"
+ ));
+ }
+
+ public void testShowAll() throws Exception {
+ mEnabled = Collections.singleton(IconDetector.ICON_DENSITIES);
+ mAbbreviate = false;
+ assertEquals(
+ "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: " +
+ "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png, " +
+ "ic_launcher3.png, ic_launcher4.png, ic_launcher5.png, ic_launcher6.png, " +
+ "ic_launcher7.png, ic_launcher8.png, ic_launcher9.png [IconDensities]\n" +
+ "res/drawable-xhdpi: Warning: Missing the following drawables in drawable-xhdpi: " +
+ "ic_launcher10.png, ic_launcher11.png, ic_launcher12.png, ic_launcher2.png," +
+ " ic_launcher3.png, ic_launcher4.png, ic_launcher5.png, ic_launcher6.png, " +
+ "ic_launcher7.png, ic_launcher8.png, ic_launcher9.png [IconDensities]\n" +
+ "0 errors, 2 warnings\n",
+
+ lintProject(
+ // Use minSDK4 to ensure that we get warnings about missing drawables
+ "apicheck/minsdk4.xml=>AndroidManifest.xml",
+ "res/drawable/ic_launcher.png=>res/drawable-hdpi/ic_launcher1.png",
+ "res/drawable/ic_launcher.png=>res/drawable-xhdpi/ic_launcher1.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher1.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher3.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher4.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher5.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher6.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher7.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher8.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher9.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher10.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher11.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher12.png"
+ ));
+ }
+
+ public void testIgnoreMissingFolders() throws Exception {
+ mEnabled = Collections.singleton(IconDetector.ICON_DENSITIES);
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ // Use minSDK4 to ensure that we get warnings about missing drawables
+ "apicheck/minsdk4.xml=>AndroidManifest.xml",
+ "ignoremissing.xml=>lint.xml",
+ "res/drawable/ic_launcher.png=>res/drawable-hdpi/ic_launcher1.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher1.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher2.png"
+ ));
+ }
+
+ public void testSquareLauncher() throws Exception {
+ mEnabled = Collections.singleton(IconDetector.ICON_LAUNCHER_SHAPE);
+ assertEquals(
+ "res/drawable-hdpi/ic_launcher_filled.png: Warning: Launcher icons should not fill every pixel of their square region; see the design guide for details [IconLauncherShape]\n" +
+ "0 errors, 1 warnings\n",
+
+ lintProject(
+ "apicheck/minsdk4.xml=>AndroidManifest.xml",
+ "res/drawable-hdpi/filled.png=>res/drawable-hdpi/ic_launcher_filled.png",
+ "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/ic_launcher_2.gif"
+ ));
+ }
+
+ public void testMixNinePatch() throws Exception {
+ // https://code.google.com/p/android/issues/detail?id=43075
+ mEnabled = Collections.singleton(IconDetector.ICON_MIX_9PNG);
+ assertEquals(""
+ + "res/drawable-mdpi/ic_launcher_filled.png: Warning: The files ic_launcher_filled.png and ic_launcher_filled.9.png clash; both will map to @drawable/ic_launcher_filled [IconMixedNinePatch]\n"
+ + " res/drawable-hdpi/ic_launcher_filled.png: <No location-specific message\n"
+ + " res/drawable-hdpi/ic_launcher_filled.9.png: <No location-specific message\n"
+ + "0 errors, 1 warnings\n",
+
+ lintProject(
+ "apicheck/minsdk4.xml=>AndroidManifest.xml",
+ "res/drawable-hdpi/filled.png=>res/drawable-mdpi/ic_launcher_filled.png",
+ "res/drawable-hdpi/filled.png=>res/drawable-hdpi/ic_launcher_filled.png",
+ "res/drawable-hdpi/filled.png=>res/drawable-hdpi/ic_launcher_filled.9.png",
+ "res/drawable-mdpi/sample_icon.gif=>res/drawable-mdpi/ic_launcher_2.gif"
+ ));
+ }
+
+ public void test67486() throws Exception {
+ // Regression test for https://code.google.com/p/android/issues/detail?id=67486
+ mEnabled = Collections.singleton(IconDetector.ICON_COLORS);
+ assertEquals("No warnings.",
+
+ lintProject(
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "res/drawable-xhdpi/ic_stat_notify.png=>res/drawable-xhdpi/ic_stat_notify.png"
+ ));
+ }
+
+ public void testDuplicatesWithDpNames() throws Exception {
+ // Regression test for https://code.google.com/p/android/issues/detail?id=74584
+ mEnabled = Collections.singleton(IconDetector.DUPLICATES_NAMES);
+ assertEquals("No warnings.",
+
+ lintProject(
+ "res/drawable-hdpi/unrelated.png=>res/drawable-mdpi/foo_72dp.png",
+ "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_36dp.png"
+ ));
+ }
+
+ public void testClaimedSize() throws Exception {
+ // Check that icons which declare a dp size actually correspond to that dp size
+ mEnabled = Collections.singleton(IconDetector.ICON_DIP_SIZE);
+ assertEquals(""
+ + "res/drawable-xhdpi/foo_30dp.png: Warning: Suspicious file name foo_30dp.png: The implied 30 dp size does not match the actual dp size (pixel size 72\u00d772 in a drawable-xhdpi folder computes to 36\u00d736 dp) [IconDipSize]\n"
+ + "res/drawable-mdpi/foo_80dp.png: Warning: Suspicious file name foo_80dp.png: The implied 80 dp size does not match the actual dp size (pixel size 72\u00d772 in a drawable-mdpi folder computes to 72\u00d772 dp) [IconDipSize]\n"
+ + "0 errors, 2 warnings\n",
+
+ lintProject(
+ "res/drawable-hdpi/unrelated.png=>res/drawable-mdpi/foo_72dp.png", // ok
+ "res/drawable-hdpi/unrelated.png=>res/drawable-mdpi/foo_80dp.png", // wrong
+ "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_36dp.png", // ok
+ "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_35dp.png", // ~ok
+ "res/drawable-hdpi/unrelated.png=>res/drawable-xhdpi/foo_30dp.png" // wrong
+ ));
+ }
+
+ public void testResConfigs1() throws Exception {
+ // resConfigs in the Gradle model sets up the specific set of resource configs
+ // that are included in the packaging: we use this to limit the set of required
+ // densities
+ mEnabled = Sets.newHashSet(IconDetector.ICON_DENSITIES, IconDetector.ICON_MISSING_FOLDER);
+ assertEquals(""
+ + "res: Warning: Missing density variation folders in res: drawable-hdpi [IconMissingDensityFolder]\n"
+ + "0 errors, 1 warnings\n",
+
+ lintProject(
+ "res/drawable-mdpi/frame.png",
+ "res/drawable-nodpi/frame.png",
+ "res/drawable-xlarge-nodpi-v11/frame.png"));
+ }
+
+ public void testResConfigs2() throws Exception {
+ mEnabled = Sets.newHashSet(IconDetector.ICON_DENSITIES, IconDetector.ICON_MISSING_FOLDER);
+ assertEquals(""
+ + "res/drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: sample_icon.gif (found in drawable-mdpi) [IconDensities]\n"
+ + "0 errors, 1 warnings\n",
+
+ lintProject(
+ // Use minSDK4 to ensure that we get warnings about missing drawables
+ "apicheck/minsdk4.xml=>AndroidManifest.xml",
+ "res/drawable/ic_launcher.png",
+ "res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher.png",
+ "res/drawable/ic_launcher.png=>res/drawable-xhdpi/ic_launcher.png",
+ "res/drawable-mdpi/sample_icon.gif",
+ "res/drawable-hdpi/ic_launcher.png"));
+ }
+
+ public void testSplits1() throws Exception {
+ // splits in the Gradle model sets up the specific set of resource configs
+ // that are included in the packaging: we use this to limit the set of required
+ // densities
+ mEnabled = Sets.newHashSet(IconDetector.ICON_DENSITIES, IconDetector.ICON_MISSING_FOLDER);
+ assertEquals(""
+ + "res: Warning: Missing density variation folders in res: drawable-hdpi [IconMissingDensityFolder]\n"
+ + "0 errors, 1 warnings\n",
+
+ lintProject(
+ "res/drawable-mdpi/frame.png",
+ "res/drawable-nodpi/frame.png",
+ "res/drawable-xlarge-nodpi-v11/frame.png"));
+ }
+
+ @Override
+ protected TestLintClient createClient() {
+ String testName = getName();
+ if (testName.startsWith("testResConfigs")) {
+ return createClientForTestResConfigs();
+ } else if (testName.startsWith("testSplits")) {
+ return createClientForTestSplits();
+ } else {
+ return super.createClient();
+ }
+ }
+
+ private TestLintClient createClientForTestResConfigs() {
+
+ // Set up a mock project model for the resource configuration test(s)
+ // where we provide a subset of densities to be included
+
+ return new TestLintClient() {
+ @NonNull
+ @Override
+ protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+ return new Project(this, dir, referenceDir) {
+ @Override
+ public boolean isGradleProject() {
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public AndroidProject getGradleProjectModel() {
+ /*
+ Simulate variant freeBetaDebug in this setup:
+ defaultConfig {
+ ...
+ resConfigs "mdpi"
+ }
+ flavorDimensions "pricing", "releaseType"
+ productFlavors {
+ beta {
+ flavorDimension "releaseType"
+ resConfig "en"
+ resConfigs "nodpi", "hdpi"
+ }
+ normal { flavorDimension "releaseType" }
+ free { flavorDimension "pricing" }
+ paid { flavorDimension "pricing" }
+ }
+ */
+ ProductFlavor flavorFree = mock(ProductFlavor.class);
+ when(flavorFree.getName()).thenReturn("free");
+ when(flavorFree.getResourceConfigurations())
+ .thenReturn(Collections.<String>emptyList());
+
+ ProductFlavor flavorNormal = mock(ProductFlavor.class);
+ when(flavorNormal.getName()).thenReturn("normal");
+ when(flavorNormal.getResourceConfigurations())
+ .thenReturn(Collections.<String>emptyList());
+
+ ProductFlavor flavorPaid = mock(ProductFlavor.class);
+ when(flavorPaid.getName()).thenReturn("paid");
+ when(flavorPaid.getResourceConfigurations())
+ .thenReturn(Collections.<String>emptyList());
+
+ ProductFlavor flavorBeta = mock(ProductFlavor.class);
+ when(flavorBeta.getName()).thenReturn("beta");
+ List<String> resConfigs = Arrays.asList("hdpi", "en", "nodpi");
+ when(flavorBeta.getResourceConfigurations()).thenReturn(resConfigs);
+
+ ProductFlavor defaultFlavor = mock(ProductFlavor.class);
+ when(defaultFlavor.getName()).thenReturn("main");
+ when(defaultFlavor.getResourceConfigurations()).thenReturn(
+ Collections.singleton("mdpi"));
+
+ ProductFlavorContainer containerBeta =
+ mock(ProductFlavorContainer.class);
+ when(containerBeta.getProductFlavor()).thenReturn(flavorBeta);
+
+ ProductFlavorContainer containerFree =
+ mock(ProductFlavorContainer.class);
+ when(containerFree.getProductFlavor()).thenReturn(flavorFree);
+
+ ProductFlavorContainer containerPaid =
+ mock(ProductFlavorContainer.class);
+ when(containerPaid.getProductFlavor()).thenReturn(flavorPaid);
+
+ ProductFlavorContainer containerNormal =
+ mock(ProductFlavorContainer.class);
+ when(containerNormal.getProductFlavor()).thenReturn(flavorNormal);
+
+ ProductFlavorContainer defaultContainer =
+ mock(ProductFlavorContainer.class);
+ when(defaultContainer.getProductFlavor()).thenReturn(defaultFlavor);
+
+ List<ProductFlavorContainer> containers = Arrays.asList(
+ containerPaid, containerFree, containerNormal, containerBeta
+ );
+
+ AndroidProject project = mock(AndroidProject.class);
+ when(project.getProductFlavors()).thenReturn(containers);
+ when(project.getDefaultConfig()).thenReturn(defaultContainer);
+ return project;
+ }
+
+ @Nullable
+ @Override
+ public Variant getCurrentVariant() {
+ List<String> productFlavorNames = Arrays.asList("free", "beta");
+ Variant mock = mock(Variant.class);
+ when(mock.getProductFlavors()).thenReturn(productFlavorNames);
+ return mock;
+ }
+ };
+ }
+ };
+ }
+
+ private TestLintClient createClientForTestSplits() {
+
+ // Set up a mock project model for the resource configuration test(s)
+ // where we provide a subset of densities to be included
+
+ return new TestLintClient() {
+ @NonNull
+ @Override
+ protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+ return new Project(this, dir, referenceDir) {
+ @Override
+ public boolean isGradleProject() {
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public AndroidProject getGradleProjectModel() {
+ /*
+ Simulate variant debug in this setup:
+ splits {
+ density {
+ enable true
+ reset()
+ include "mdpi", "hdpi"
+ }
+ }
+ */
+
+ ProductFlavor defaultFlavor = mock(ProductFlavor.class);
+ when(defaultFlavor.getName()).thenReturn("main");
+ when(defaultFlavor.getResourceConfigurations()).thenReturn(
+ Collections.<String>emptyList());
+
+ ProductFlavorContainer defaultContainer =
+ mock(ProductFlavorContainer.class);
+ when(defaultContainer.getProductFlavor()).thenReturn(defaultFlavor);
+
+ AndroidProject project = mock(AndroidProject.class);
+ when(project.getProductFlavors()).thenReturn(
+ Collections.<ProductFlavorContainer>emptyList());
+ when(project.getDefaultConfig()).thenReturn(defaultContainer);
+ return project;
+ }
+
+ @Nullable
+ @Override
+ public Variant getCurrentVariant() {
+ Collection<AndroidArtifactOutput> outputs = Lists.newArrayList();
+
+ outputs.add(createAndroidArtifactOutput("", ""));
+ outputs.add(createAndroidArtifactOutput("DENSITY", "mdpi"));
+ outputs.add(createAndroidArtifactOutput("DENSITY", "hdpi"));
+
+ AndroidArtifact mainArtifact = mock(AndroidArtifact.class);
+ when(mainArtifact.getOutputs()).thenReturn(outputs);
+
+ List<String> productFlavorNames = Collections.emptyList();
+ Variant mock = mock(Variant.class);
+ when(mock.getProductFlavors()).thenReturn(productFlavorNames);
+ when(mock.getMainArtifact()).thenReturn(mainArtifact);
+ return mock;
+ }
+
+ private AndroidArtifactOutput createAndroidArtifactOutput(
+ @NonNull String filterType,
+ @NonNull String identifier) {
+ AndroidArtifactOutput artifactOutput = mock(
+ AndroidArtifactOutput.class);
+
+ OutputFile outputFile = mock(OutputFile.class);
+ if (filterType.isEmpty()) {
+ when(outputFile.getFilterTypes())
+ .thenReturn(Collections.<String>emptyList());
+ when(outputFile.getFilters())
+ .thenReturn(Collections.<FilterData>emptyList());
+ } else {
+ when(outputFile.getFilterTypes())
+ .thenReturn(Collections.singletonList(filterType));
+ List<FilterData> filters = Lists.newArrayList();
+ FilterData filter = mock(FilterData.class);
+ when(filter.getFilterType()).thenReturn(filterType);
+ when(filter.getIdentifier()).thenReturn(identifier);
+ filters.add(filter);
+ when(outputFile.getFilters()).thenReturn(filters);
+ }
+
+ // Work around wildcard capture
+ //when(artifactOutput.getOutputs()).thenReturn(outputFiles);
+ List<OutputFile> outputFiles = Collections.singletonList(outputFile);
+ OngoingStubbing<? extends Collection<? extends OutputFile>> when = when(
+ artifactOutput.getOutputs());
+ //noinspection unchecked,RedundantCast
+ ((OngoingStubbing<Collection<? extends OutputFile>>) (OngoingStubbing<?>) when)
+ .thenReturn(outputFiles);
+
+ return artifactOutput;
+ }
+ };
+ }
+ };
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/IncludeDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/IncludeDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/IncludeDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/IncludeDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/InefficientWeightDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/InefficientWeightDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/InefficientWeightDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/InefficientWeightDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/InvalidPackageDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/InvalidPackageDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/InvalidPackageDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/InvalidPackageDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaPerformanceDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/JavaScriptInterfaceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaScriptInterfaceDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/JavaScriptInterfaceDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/JavaScriptInterfaceDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/LabelForDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LabelForDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/LabelForDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LabelForDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/LayoutConsistencyDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LayoutConsistencyDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/LayoutConsistencyDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LayoutConsistencyDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/LayoutInflationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LayoutInflationDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/LayoutInflationDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LayoutInflationDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/LocaleDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LocaleDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/LocaleDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LocaleDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/LocaleFolderDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LocaleFolderDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/LocaleFolderDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LocaleFolderDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/LogDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LogDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/LogDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/LogDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ManifestDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ManifestDetectorTest.java
new file mode 100644
index 0000000..31fedb1
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ManifestDetectorTest.java
@@ -0,0 +1,945 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import static com.android.SdkConstants.ANDROID_MANIFEST_XML;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.ApiVersion;
+import com.android.builder.model.BuildType;
+import com.android.builder.model.BuildTypeContainer;
+import com.android.builder.model.ProductFlavor;
+import com.android.builder.model.ProductFlavorContainer;
+import com.android.builder.model.SourceProvider;
+import com.android.builder.model.SourceProviderContainer;
+import com.android.builder.model.Variant;
+import com.android.tools.lint.client.api.LintClient;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Project;
+import com.google.common.collect.Lists;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@SuppressWarnings("javadoc")
+public class ManifestDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new ManifestDetector();
+ }
+
+ private Set<Issue> mEnabled = new HashSet<Issue>();
+
+ @Override
+ protected TestConfiguration getConfiguration(LintClient client, Project project) {
+ return new TestConfiguration(client, project, null) {
+ @Override
+ public boolean isEnabled(@NonNull Issue issue) {
+ return super.isEnabled(issue) && mEnabled.contains(issue);
+ }
+ };
+ }
+
+ public void testOrderOk() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.ORDER);
+ assertEquals(
+ "No warnings.",
+ lintProject(
+ "AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testBrokenOrder() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.ORDER);
+ assertEquals(
+ "AndroidManifest.xml:16: Warning: <uses-sdk> tag appears after <application> tag [ManifestOrder]\n" +
+ " <uses-sdk android:minSdkVersion=\"Froyo\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "0 errors, 1 warnings\n" +
+ "",
+
+ lintProject(
+ "broken-manifest.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testMissingUsesSdk() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.USES_SDK);
+ assertEquals(
+ "AndroidManifest.xml: Warning: Manifest should specify a minimum API level with <uses-sdk android:minSdkVersion=\"?\" />; if it really supports all versions of Android set it to 1. [UsesMinSdkAttributes]\n" +
+ "0 errors, 1 warnings\n",
+ lintProject(
+ "missingusessdk.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testMissingUsesSdkInGradle() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.SET_VERSION);
+ assertEquals(""
+ + "No warnings.",
+ lintProject("missingusessdk.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>build.gradle")); // dummy; only name counts
+ }
+
+ public void testMissingMinSdk() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.USES_SDK);
+ assertEquals(
+ "AndroidManifest.xml:7: Warning: <uses-sdk> tag should specify a minimum API level with android:minSdkVersion=\"?\" [UsesMinSdkAttributes]\n" +
+ " <uses-sdk android:targetSdkVersion=\"10\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "0 errors, 1 warnings\n" +
+ "",
+ lintProject(
+ "missingmin.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testMissingTargetSdk() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.USES_SDK);
+ assertEquals(
+ "AndroidManifest.xml:7: Warning: <uses-sdk> tag should specify a target API level (the highest verified version; when running on later versions, compatibility behaviors may be enabled) with android:targetSdkVersion=\"?\" [UsesMinSdkAttributes]\n" +
+ " <uses-sdk android:minSdkVersion=\"10\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "0 errors, 1 warnings\n",
+ lintProject(
+ "missingtarget.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testOldTargetSdk() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.TARGET_NEWER);
+ assertEquals(
+ "AndroidManifest.xml:7: Warning: Not targeting the latest versions of Android; compatibility modes apply. Consider testing and updating this version. Consult the android.os.Build.VERSION_CODES javadoc for details. [OldTargetApi]\n" +
+ " <uses-sdk android:minSdkVersion=\"10\" android:targetSdkVersion=\"14\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "0 errors, 1 warnings\n",
+ lintProject(
+ "oldtarget.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testMultipleSdk() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.MULTIPLE_USES_SDK);
+ assertEquals(
+ "AndroidManifest.xml:8: Error: There should only be a single <uses-sdk> element in the manifest: merge these together [MultipleUsesSdk]\n" +
+ " <uses-sdk android:targetSdkVersion=\"14\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ " AndroidManifest.xml:7: Also appears here\n" +
+ " AndroidManifest.xml:9: Also appears here\n" +
+ "1 errors, 0 warnings\n",
+
+ lintProject(
+ "multiplesdk.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testWrongLocation() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.WRONG_PARENT);
+ assertEquals(
+ "AndroidManifest.xml:8: Error: The <uses-sdk> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+ " <uses-sdk android:minSdkVersion=\"Froyo\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:9: Error: The <uses-permission> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+ " <uses-permission />\n" +
+ " ~~~~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:10: Error: The <permission> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+ " <permission />\n" +
+ " ~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:11: Error: The <permission-tree> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+ " <permission-tree />\n" +
+ " ~~~~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:12: Error: The <permission-group> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+ " <permission-group />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:14: Error: The <uses-sdk> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+ " <uses-sdk />\n" +
+ " ~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:15: Error: The <uses-configuration> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+ " <uses-configuration />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:16: Error: The <uses-feature> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+ " <uses-feature />\n" +
+ " ~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:17: Error: The <supports-screens> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+ " <supports-screens />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:18: Error: The <compatible-screens> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+ " <compatible-screens />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:19: Error: The <supports-gl-texture> element must be a direct child of the <manifest> root element [WrongManifestParent]\n" +
+ " <supports-gl-texture />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:24: Error: The <uses-library> element must be a direct child of the <application> element [WrongManifestParent]\n" +
+ " <uses-library />\n" +
+ " ~~~~~~~~~~~~~~~~\n" +
+ "AndroidManifest.xml:25: Error: The <activity> element must be a direct child of the <application> element [WrongManifestParent]\n" +
+ " <activity android:name=\".HelloWorld\"\n" +
+ " ^\n" +
+ "13 errors, 0 warnings\n" +
+ "",
+
+ lintProject("broken-manifest2.xml=>AndroidManifest.xml"));
+ }
+
+ public void testDuplicateActivity() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_ACTIVITY);
+ assertEquals(
+ "AndroidManifest.xml:16: Error: Duplicate registration for activity com.example.helloworld.HelloWorld [DuplicateActivity]\n" +
+ " <activity android:name=\"com.example.helloworld.HelloWorld\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 0 warnings\n" +
+ "",
+
+ lintProject(
+ "duplicate-manifest.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testDuplicateActivityAcrossSourceSets() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_ACTIVITY);
+ File master = getProjectDir("MasterProject",
+ // Master project
+ "AndroidManifest.xml=>AndroidManifest.xml",
+ "multiproject/main-merge.properties=>project.properties",
+ "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
+ );
+ File library = getProjectDir("LibraryProject",
+ // Library project
+ "AndroidManifest.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>project.properties",
+ "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
+ "multiproject/strings.xml=>res/values/strings.xml"
+ );
+ assertEquals("No warnings.",
+ checkLint(Arrays.asList(master, library)));
+ }
+
+ public void testIgnoreDuplicateActivity() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_ACTIVITY);
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "duplicate-manifest-ignore.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testAllowBackup() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+ assertEquals(
+ "AndroidManifest.xml:9: Warning: Should explicitly set android:allowBackup to " +
+ "true or false (it's true by default, and that can have some security " +
+ "implications for the application's data) [AllowBackup]\n" +
+ " <application\n" +
+ " ^\n" +
+ "0 errors, 1 warnings\n",
+ lintProject(
+ "AndroidManifest.xml",
+ "apicheck/minsdk14.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testAllowBackupOk() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+ assertEquals(
+ "No warnings.",
+ lintProject(
+ "allowbackup.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testAllowBackupOk2() throws Exception {
+ // Requires build api >= 4
+ mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+ assertEquals(
+ "No warnings.",
+ lintProject(
+ "apicheck/minsdk1.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testAllowBackupOk3() throws Exception {
+ // Not flagged in library projects
+ mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+ assertEquals(
+ "No warnings.",
+ lintProject(
+ "AndroidManifest.xml",
+ "multiproject/library.properties=>project.properties",
+ "res/values/strings.xml"));
+ }
+
+ public void testAllowIgnore() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+ assertEquals(
+ "No warnings.",
+ lintProject(
+ "allowbackup_ignore.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testDuplicatePermissions() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.UNIQUE_PERMISSION);
+ assertEquals(
+ "AndroidManifest.xml:12: Error: Permission name SEND_SMS is not unique (appears in both foo.permission.SEND_SMS and bar.permission.SEND_SMS) [UniquePermission]\n" +
+ " <permission android:name=\"bar.permission.SEND_SMS\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ " AndroidManifest.xml:9: Previous permission here\n" +
+ "1 errors, 0 warnings\n",
+
+ lintProject(
+ "duplicate_permissions1.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testDuplicatePermissionsMultiProject() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.UNIQUE_PERMISSION);
+
+ File master = getProjectDir("MasterProject",
+ // Master project
+ "duplicate_permissions2.xml=>AndroidManifest.xml",
+ "multiproject/main-merge.properties=>project.properties",
+ "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
+ );
+ File library = getProjectDir("LibraryProject",
+ // Library project
+ "duplicate_permissions3.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>project.properties",
+ "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
+ "multiproject/strings.xml=>res/values/strings.xml"
+ );
+ assertEquals(
+ "LibraryProject/AndroidManifest.xml:9: Error: Permission name SEND_SMS is not unique (appears in both foo.permission.SEND_SMS and bar.permission.SEND_SMS) [UniquePermission]\n" +
+ " <permission android:name=\"bar.permission.SEND_SMS\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 0 warnings\n",
+
+ checkLint(Arrays.asList(master, library)));
+ }
+
+ public void testMissingVersion() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.SET_VERSION);
+ assertEquals(""
+ + "AndroidManifest.xml:2: Warning: Should set android:versionCode to specify the application version [MissingVersion]\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + "^\n"
+ + "AndroidManifest.xml:2: Warning: Should set android:versionName to specify the application version [MissingVersion]\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + "^\n"
+ + "0 errors, 2 warnings\n",
+ lintProject("no_version.xml=>AndroidManifest.xml"));
+ }
+
+ public void testVersionNotMissingInGradleProjects() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.SET_VERSION);
+ assertEquals(""
+ + "No warnings.",
+ lintProject("no_version.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>build.gradle")); // dummy; only name counts
+ }
+
+ public void testIllegalReference() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.ILLEGAL_REFERENCE);
+ assertEquals(""
+ + "AndroidManifest.xml:4: Warning: The android:versionCode cannot be a resource url, it must be a literal integer [IllegalResourceRef]\n"
+ + " android:versionCode=\"@dimen/versionCode\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "AndroidManifest.xml:7: Warning: The android:minSdkVersion cannot be a resource url, it must be a literal integer (or string if a preview codename) [IllegalResourceRef]\n"
+ + " <uses-sdk android:minSdkVersion=\"@dimen/minSdkVersion\" android:targetSdkVersion=\"@dimen/targetSdkVersion\" />\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "AndroidManifest.xml:7: Warning: The android:targetSdkVersion cannot be a resource url, it must be a literal integer (or string if a preview codename) [IllegalResourceRef]\n"
+ + " <uses-sdk android:minSdkVersion=\"@dimen/minSdkVersion\" android:targetSdkVersion=\"@dimen/targetSdkVersion\" />\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 3 warnings\n",
+
+ lintProject("illegal_version.xml=>AndroidManifest.xml"));
+ }
+
+ public void testDuplicateUsesFeature() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_USES_FEATURE);
+ assertEquals(
+ "AndroidManifest.xml:11: Warning: Duplicate declaration of uses-feature android.hardware.camera [DuplicateUsesFeature]\n" +
+ " <uses-feature android:name=\"android.hardware.camera\"/>\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "0 errors, 1 warnings\n",
+ lintProject(
+ "duplicate_uses_feature.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testDuplicateUsesFeatureOk() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.DUPLICATE_USES_FEATURE);
+ assertEquals(
+ "No warnings.",
+ lintProject(
+ "duplicate_uses_feature_ok.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testMissingApplicationIcon() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.APPLICATION_ICON);
+ assertEquals(
+ "AndroidManifest.xml:9: Warning: Should explicitly set android:icon, there is no default [MissingApplicationIcon]\n" +
+ " <application\n" +
+ " ^\n" +
+ "0 errors, 1 warnings\n",
+ lintProject(
+ "missing_application_icon.xml=>AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testMissingApplicationIconInLibrary() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.APPLICATION_ICON);
+ assertEquals(
+ "No warnings.",
+ lintProject(
+ "missing_application_icon.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>project.properties",
+ "res/values/strings.xml"));
+ }
+
+ public void testMissingApplicationIconOk() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.APPLICATION_ICON);
+ assertEquals(
+ "No warnings.",
+ lintProject(
+ "AndroidManifest.xml",
+ "res/values/strings.xml"));
+ }
+
+ public void testDeviceAdmin() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.DEVICE_ADMIN);
+ assertEquals(""
+ + "AndroidManifest.xml:31: Warning: You must have an intent filter for action android.app.action.DEVICE_ADMIN_ENABLED [DeviceAdmin]\n"
+ + " <meta-data android:name=\"android.app.device_admin\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "AndroidManifest.xml:44: Warning: You must have an intent filter for action android.app.action.DEVICE_ADMIN_ENABLED [DeviceAdmin]\n"
+ + " <meta-data android:name=\"android.app.device_admin\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "AndroidManifest.xml:56: Warning: You must have an intent filter for action android.app.action.DEVICE_ADMIN_ENABLED [DeviceAdmin]\n"
+ + " <meta-data android:name=\"android.app.device_admin\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 3 warnings\n",
+ lintProject("deviceadmin.xml=>AndroidManifest.xml"));
+ }
+
+ public void testMockLocations() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.MOCK_LOCATION);
+ assertEquals(""
+ + "AndroidManifest.xml:9: Error: Mock locations should only be requested in a test or debug-specific manifest file (typically src/debug/AndroidManifest.xml) [MockLocation]\n"
+ + " <uses-permission android:name=\"android.permission.ACCESS_MOCK_LOCATION\" /> \n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+ lintProject(
+ "mock_location.xml=>AndroidManifest.xml",
+ "mock_location.xml=>debug/AndroidManifest.xml",
+ "mock_location.xml=>test/AndroidManifest.xml",
+ "multiproject/library.properties=>build.gradle")); // dummy; only name counts
+ // TODO: When we have an instantiatable gradle model, test with real model and verify
+ // that a manifest file in a debug build type does not get flagged.
+ }
+
+ public void testMockLocationsOk() throws Exception {
+ // Not a Gradle project
+ mEnabled = Collections.singleton(ManifestDetector.MOCK_LOCATION);
+ assertEquals(""
+ + "No warnings.",
+ lintProject(
+ "mock_location.xml=>AndroidManifest.xml"));
+ }
+
+ public void testGradleOverrides() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.GRADLE_OVERRIDES);
+ assertEquals(""
+ + "AndroidManifest.xml:4: Warning: This versionCode value (1) is not used; it is always overridden by the value specified in the Gradle build script (2) [GradleOverrides]\n"
+ + " android:versionCode=\"1\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "AndroidManifest.xml:5: Warning: This versionName value (1.0) is not used; it is always overridden by the value specified in the Gradle build script (MyName) [GradleOverrides]\n"
+ + " android:versionName=\"1.0\" >\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "AndroidManifest.xml:7: Warning: This minSdkVersion value (14) is not used; it is always overridden by the value specified in the Gradle build script (5) [GradleOverrides]\n"
+ + " <uses-sdk android:minSdkVersion=\"14\" android:targetSdkVersion=\"17\" />\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "AndroidManifest.xml:7: Warning: This targetSdkVersion value (17) is not used; it is always overridden by the value specified in the Gradle build script (16) [GradleOverrides]\n"
+ + " <uses-sdk android:minSdkVersion=\"14\" android:targetSdkVersion=\"17\" />\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 4 warnings\n",
+ lintProject(
+ "gradle_override.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>build.gradle")); // dummy; only name counts
+ }
+
+ public void testGradleOverridesOk() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.GRADLE_OVERRIDES);
+ // (See custom logic in #createClient which returns -1/null for the merged flavor
+ // from this test, and not from testGradleOverrides)
+ assertEquals(""
+ + "No warnings.",
+ lintProject(
+ "gradle_override.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>build.gradle")); // dummy; only name counts
+ }
+
+ public void testManifestPackagePlaceholder() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.GRADLE_OVERRIDES);
+ assertEquals(""
+ + "AndroidManifest.xml:3: Warning: Cannot use placeholder for the package in the manifest; set applicationId in build.gradle instead [GradleOverrides]\n"
+ + " package=\"${packageName}\" >\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 1 warnings\n",
+
+ lintProject(
+ "gradle_override_placeholder.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>build.gradle")); // dummy; only name counts
+ }
+
+ public void testMipMap() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.MIPMAP);
+ assertEquals("No warnings.",
+
+ lintProject(
+ "mipmap.xml=>AndroidManifest.xml"));
+ }
+
+ public void testMipMapWithDensityFiltering() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.MIPMAP);
+ assertEquals(""
+ + "AndroidManifest.xml:9: Warning: Should use @mipmap instead of @drawable for launcher icons [MipmapIcons]\n"
+ + " android:icon=\"@drawable/ic_launcher\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "AndroidManifest.xml:14: Warning: Should use @mipmap instead of @drawable for launcher icons [MipmapIcons]\n"
+ + " android:icon=\"@drawable/activity1\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 2 warnings\n",
+
+ lintProject(
+ "mipmap.xml=>AndroidManifest.xml"));
+ }
+
+ public void testFullBackupContentBoolean() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+ assertEquals("No warnings.",
+
+ lintProjectIncrementally(
+ "AndroidManifest.xml",
+ xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:fullBackupContent=\"true\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testFullBackupContentMissing() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+ assertEquals(""
+ + "AndroidManifest.xml:7: Warning: Missing <full-backup-content> resource [AllowBackup]\n"
+ + " android:fullBackupContent=\"@xml/backup\"\n"
+ + " ~~~~~~~~~~~\n"
+ + "0 errors, 1 warnings\n",
+
+ lintProjectIncrementally(
+ "AndroidManifest.xml",
+ xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:fullBackupContent=\"@xml/backup\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testFullBackupContentOk() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+ assertEquals("No warnings.",
+
+ lintProjectIncrementally(
+ "AndroidManifest.xml",
+ xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + "\n"
+ + " <application\n"
+ + " android:allowBackup=\"true\"\n"
+ + " android:fullBackupContent=\"@xml/backup\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n"),
+ xml("res/xml/backup.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<full-backup-content>\n"
+ + " <include domain=\"file\" path=\"dd\"/>\n"
+ + " <exclude domain=\"file\" path=\"dd/fo3o.txt\"/>\n"
+ + " <exclude domain=\"file\" path=\"dd/ss/foo.txt\"/>\n"
+ + "</full-backup-content>")));
+ }
+
+ public void testHasBackupSpecifiedInTarget23() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+ assertEquals("No warnings.",
+
+ lintProject(
+ xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + " <uses-sdk android:targetSdkVersion=\"23\" />"
+ + "\n"
+ + " <application\n"
+ + " android:fullBackupContent=\"no\"\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testMissingBackupInTarget23() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+ assertEquals(""
+ + "AndroidManifest.xml:5: Warning: Should explicitly set android:fullBackupContent to true or false to opt-in to or out of full app data back-up and restore, or alternatively to an @xml resource which specifies which files to backup [AllowBackup]\n"
+ + " <application\n"
+ + " ^\n"
+ + "0 errors, 1 warnings\n",
+
+ lintProject(
+ xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + " <uses-sdk android:targetSdkVersion=\"23\" />"
+ + "\n"
+ + " <application\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testMissingBackupWithoutGcmPreTarget23() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+ assertEquals("No warnings.",
+
+ lintProject(
+ xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + " <uses-sdk android:targetSdkVersion=\"21\" />"
+ + "\n"
+ + " <application\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ public void testMissingBackupWithGcmPreTarget23() throws Exception {
+ mEnabled = Collections.singleton(ManifestDetector.ALLOW_BACKUP);
+ assertEquals(""
+ + "AndroidManifest.xml:5: Warning: Should explicitly set android:fullBackupContent to avoid backing up the GCM device specific regId. [AllowBackup]\n"
+ + " <application\n"
+ + " ^\n"
+ + "0 errors, 1 warnings\n",
+
+ lintProject(
+ xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"com.example.helloworld\" >\n"
+ + " <uses-sdk android:targetSdkVersion=\"21\" />"
+ + "\n"
+ + " <application\n"
+ + " android:label=\"@string/app_name\"\n"
+ + " android:theme=\"@style/AppTheme\" >"
+ + " <receiver\n"
+ + " android:name=\".GcmBroadcastReceiver\"\n"
+ + " android:permission=\"com.google.android.c2dm.permission.SEND\" >\n"
+ + " <intent-filter>\n"
+ + " <action android:name=\"com.google.android.c2dm.intent.RECEIVE\" />\n"
+ + " <category android:name=\"com.example.gcm\" />\n"
+ + " </intent-filter>\n"
+ + " </receiver>\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>\n")));
+ }
+
+ // Custom project which locates all manifest files in the project rather than just
+ // being hardcoded to the root level
+
+ @Override
+ protected TestLintClient createClient() {
+ if ("testMipMapWithDensityFiltering".equals(getName())) {
+ // Set up a mock project model for the resource configuration test(s)
+ // where we provide a subset of densities to be included
+ return new TestLintClient() {
+ @NonNull
+ @Override
+ protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+ return new Project(this, dir, referenceDir) {
+ @Override
+ public boolean isGradleProject() {
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public AndroidProject getGradleProjectModel() {
+ /*
+ Simulate variant freeBetaDebug in this setup:
+ defaultConfig {
+ ...
+ resConfigs "cs"
+ }
+ flavorDimensions "pricing", "releaseType"
+ productFlavors {
+ beta {
+ flavorDimension "releaseType"
+ resConfig "en", "de"
+ resConfigs "nodpi", "hdpi"
+ }
+ normal { flavorDimension "releaseType" }
+ free { flavorDimension "pricing" }
+ paid { flavorDimension "pricing" }
+ }
+ */
+ ProductFlavor flavorFree = mock(ProductFlavor.class);
+ when(flavorFree.getName()).thenReturn("free");
+ when(flavorFree.getResourceConfigurations())
+ .thenReturn(Collections.<String>emptyList());
+
+ ProductFlavor flavorNormal = mock(ProductFlavor.class);
+ when(flavorNormal.getName()).thenReturn("normal");
+ when(flavorNormal.getResourceConfigurations())
+ .thenReturn(Collections.<String>emptyList());
+
+ ProductFlavor flavorPaid = mock(ProductFlavor.class);
+ when(flavorPaid.getName()).thenReturn("paid");
+ when(flavorPaid.getResourceConfigurations())
+ .thenReturn(Collections.<String>emptyList());
+
+ ProductFlavor flavorBeta = mock(ProductFlavor.class);
+ when(flavorBeta.getName()).thenReturn("beta");
+ List<String> resConfigs = Arrays.asList("hdpi", "en", "de", "nodpi");
+ when(flavorBeta.getResourceConfigurations()).thenReturn(resConfigs);
+
+ ProductFlavor defaultFlavor = mock(ProductFlavor.class);
+ when(defaultFlavor.getName()).thenReturn("main");
+ when(defaultFlavor.getResourceConfigurations()).thenReturn(
+ Collections.singleton("cs"));
+
+ ProductFlavorContainer containerBeta =
+ mock(ProductFlavorContainer.class);
+ when(containerBeta.getProductFlavor()).thenReturn(flavorBeta);
+
+ ProductFlavorContainer containerFree =
+ mock(ProductFlavorContainer.class);
+ when(containerFree.getProductFlavor()).thenReturn(flavorFree);
+
+ ProductFlavorContainer containerPaid =
+ mock(ProductFlavorContainer.class);
+ when(containerPaid.getProductFlavor()).thenReturn(flavorPaid);
+
+ ProductFlavorContainer containerNormal =
+ mock(ProductFlavorContainer.class);
+ when(containerNormal.getProductFlavor()).thenReturn(flavorNormal);
+
+ ProductFlavorContainer defaultContainer =
+ mock(ProductFlavorContainer.class);
+ when(defaultContainer.getProductFlavor()).thenReturn(defaultFlavor);
+
+ List<ProductFlavorContainer> containers = Arrays.asList(
+ containerPaid, containerFree, containerNormal, containerBeta
+ );
+
+ AndroidProject project = mock(AndroidProject.class);
+ when(project.getProductFlavors()).thenReturn(containers);
+ when(project.getDefaultConfig()).thenReturn(defaultContainer);
+ return project;
+ }
+
+ @Nullable
+ @Override
+ public Variant getCurrentVariant() {
+ List<String> productFlavorNames = Arrays.asList("free", "beta");
+ Variant mock = mock(Variant.class);
+ when(mock.getProductFlavors()).thenReturn(productFlavorNames);
+ return mock;
+ }
+ };
+ }
+ };
+ }
+ if (mEnabled.contains(ManifestDetector.MOCK_LOCATION)) {
+ return new TestLintClient() {
+ @NonNull
+ @Override
+ protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+ return new Project(this, dir, referenceDir) {
+ @NonNull
+ @Override
+ public List<File> getManifestFiles() {
+ if (mManifestFiles == null) {
+ mManifestFiles = Lists.newArrayList();
+ addManifestFiles(mDir);
+ }
+
+ return mManifestFiles;
+ }
+
+ private void addManifestFiles(File dir) {
+ if (dir.getName().equals(ANDROID_MANIFEST_XML)) {
+ mManifestFiles.add(dir);
+ } else if (dir.isDirectory()) {
+ File[] files = dir.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ addManifestFiles(file);
+ }
+ }
+ }
+ }
+
+ @NonNull SourceProvider createSourceProvider(File manifest) {
+ SourceProvider provider = mock(SourceProvider.class);
+ when(provider.getManifestFile()).thenReturn(manifest);
+ return provider;
+ }
+
+ @Nullable
+ @Override
+ public AndroidProject getGradleProjectModel() {
+ if (!isGradleProject()) {
+ return null;
+ }
+
+ File main = new File(mDir, ANDROID_MANIFEST_XML);
+ File debug = new File(mDir, "debug" + File.separator + ANDROID_MANIFEST_XML);
+ File test = new File(mDir, "test" + File.separator + ANDROID_MANIFEST_XML);
+
+ SourceProvider defaultSourceProvider = createSourceProvider(main);
+ SourceProvider debugSourceProvider = createSourceProvider(debug);
+ SourceProvider testSourceProvider = createSourceProvider(test);
+
+ ProductFlavorContainer defaultConfig = mock(ProductFlavorContainer.class);
+ when(defaultConfig.getSourceProvider()).thenReturn(defaultSourceProvider);
+
+ BuildType buildType = mock(BuildType.class);
+ when(buildType.isDebuggable()).thenReturn(true);
+
+ BuildTypeContainer buildTypeContainer = mock(BuildTypeContainer.class);
+ when(buildTypeContainer.getBuildType()).thenReturn(buildType);
+ when(buildTypeContainer.getSourceProvider()).thenReturn(debugSourceProvider);
+ List<BuildTypeContainer> buildTypes = Lists.newArrayList(buildTypeContainer);
+
+ SourceProviderContainer extraProvider = mock(SourceProviderContainer.class);
+ when(extraProvider.getArtifactName()).thenReturn(AndroidProject.ARTIFACT_ANDROID_TEST);
+ when(extraProvider.getSourceProvider()).thenReturn(testSourceProvider);
+ List<SourceProviderContainer> extraProviders = Lists.newArrayList(extraProvider);
+
+ ProductFlavorContainer productFlavorContainer = mock(ProductFlavorContainer.class);
+ when(productFlavorContainer.getExtraSourceProviders()).thenReturn(extraProviders);
+ List<ProductFlavorContainer> productFlavors = Lists.newArrayList(productFlavorContainer);
+
+ AndroidProject project = mock(AndroidProject.class);
+ when(project.getDefaultConfig()).thenReturn(defaultConfig);
+ when(project.getBuildTypes()).thenReturn(buildTypes);
+ when(project.getProductFlavors()).thenReturn(productFlavors);
+ return project;
+ }
+ };
+ }
+ };
+ } else if (mEnabled.contains(ManifestDetector.GRADLE_OVERRIDES)) {
+ return new TestLintClient() {
+ @NonNull
+ @Override
+ protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+ return new Project(this, dir, referenceDir) {
+ @Override
+ public boolean isGradleProject() {
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public Variant getCurrentVariant() {
+ ProductFlavor flavor = mock(ProductFlavor.class);
+ if (getName().equals("ManifestDetectorTest_testGradleOverridesOk") ||
+ getName().equals(
+ "ManifestDetectorTest_testManifestPackagePlaceholder")) {
+ when(flavor.getMinSdkVersion()).thenReturn(null);
+ when(flavor.getTargetSdkVersion()).thenReturn(null);
+ when(flavor.getVersionCode()).thenReturn(null);
+ when(flavor.getVersionName()).thenReturn(null);
+ } else {
+ assertEquals(getName(), "ManifestDetectorTest_testGradleOverrides");
+
+ ApiVersion apiMock = mock(ApiVersion.class);
+ when(apiMock.getApiLevel()).thenReturn(5);
+ when(apiMock.getApiString()).thenReturn("5");
+ when(flavor.getMinSdkVersion()).thenReturn(apiMock);
+
+ apiMock = mock(ApiVersion.class);
+ when(apiMock.getApiLevel()).thenReturn(16);
+ when(apiMock.getApiString()).thenReturn("16");
+ when(flavor.getTargetSdkVersion()).thenReturn(apiMock);
+
+ when(flavor.getVersionCode()).thenReturn(2);
+ when(flavor.getVersionName()).thenReturn("MyName");
+ }
+
+ Variant mock = mock(Variant.class);
+ when(mock.getMergedFlavor()).thenReturn(flavor);
+ return mock;
+ }
+ };
+ }
+ };
+
+ }
+ return super.createClient();
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ManifestTypoDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ManifestTypoDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ManifestTypoDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ManifestTypoDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/MathDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MathDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/MathDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MathDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MergeRootFrameLayoutDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/MissingIdDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MissingIdDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/MissingIdDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/MissingIdDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NamespaceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NamespaceDetectorTest.java
new file mode 100644
index 0000000..6759dbc
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NamespaceDetectorTest.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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("javadoc")
+public class NamespaceDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new NamespaceDetector();
+ }
+
+ public void testCustom() throws Exception {
+ assertEquals(
+ "res/layout/customview.xml:5: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" +
+ " xmlns:foo=\"http://schemas.android.com/apk/res/foo\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 0 warnings\n",
+
+ lintProject(
+ "multiproject/library-manifest.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>project.properties",
+ "res/layout/customview.xml"
+ ));
+ }
+
+ public void testGradle() throws Exception {
+ assertEquals(""
+ + "res/layout/customview.xml:5: Error: In Gradle projects, always use http://schemas.android.com/apk/res-auto for custom attributes [ResAuto]\n"
+ + " xmlns:foo=\"http://schemas.android.com/apk/res/foo\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+
+ lintProject(
+ "multiproject/library.properties=>build.gradle", // dummy; only name counts
+ "res/layout/customview.xml"
+ ));
+ }
+
+ public void testCustomOk() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "multiproject/library-manifest.xml=>AndroidManifest.xml",
+
+ // Use a standard project properties instead: no warning since it's
+ // not a library project:
+ //"multiproject/library.properties=>project.properties",
+
+ "res/layout/customview.xml"
+ ));
+ }
+
+ public void testCustomOk2() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "multiproject/library-manifest.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>project.properties",
+ // This project already uses the res-auto package
+ "res/layout/customview2.xml"
+ ));
+ }
+
+ public void testTypo() throws Exception {
+ assertEquals(
+ "res/layout/wrong_namespace.xml:2: Error: Unexpected namespace URI bound to the \"android\" prefix, was http://schemas.android.com/apk/res/andriod, expected http://schemas.android.com/apk/res/android [NamespaceTypo]\n" +
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/andriod\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 0 warnings\n",
+
+ lintProject("res/layout/wrong_namespace.xml"));
+ }
+
+ public void testTypo2() throws Exception {
+ assertEquals(
+ "res/layout/wrong_namespace2.xml:2: Error: URI is case sensitive: was \"http://schemas.android.com/apk/res/Android\", expected \"http://schemas.android.com/apk/res/android\" [NamespaceTypo]\n" +
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/Android\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 0 warnings\n",
+
+ lintProject("res/layout/wrong_namespace2.xml"));
+ }
+
+ public void testTypo3() throws Exception {
+ assertEquals(
+ "res/layout/wrong_namespace3.xml:2: Error: Unexpected namespace URI bound to the \"android\" prefix, was http://schemas.android.com/apk/res/androi, expected http://schemas.android.com/apk/res/android [NamespaceTypo]\n" +
+ "<LinearLayout xmlns:a=\"http://schemas.android.com/apk/res/androi\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 0 warnings\n",
+
+ lintProject("res/layout/wrong_namespace3.xml"));
+ }
+
+ public void testTypo4() throws Exception {
+ assertEquals(
+ "res/layout/wrong_namespace5.xml:2: Error: Suspicious namespace: should start with http:// [NamespaceTypo]\n" +
+ " xmlns:noturi=\"tp://schems.android.com/apk/res/com.my.package\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "res/layout/wrong_namespace5.xml:3: Error: Possible typo in URL: was \"http://schems.android.com/apk/res/com.my.package\", should probably be \"http://schemas.android.com/apk/res/com.my.package\" [NamespaceTypo]\n" +
+ " xmlns:typo1=\"http://schems.android.com/apk/res/com.my.package\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "res/layout/wrong_namespace5.xml:4: Error: Possible typo in URL: was \"http://schems.android.comm/apk/res/com.my.package\", should probably be \"http://schemas.android.com/apk/res/com.my.package\" [NamespaceTypo]\n" +
+ " xmlns:typo2=\"http://schems.android.comm/apk/res/com.my.package\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "3 errors, 0 warnings\n",
+
+ lintProject("res/layout/wrong_namespace5.xml"));
+ }
+
+ public void testMisleadingPrefix() throws Exception {
+ assertEquals(""
+ + "res/layout/layout.xml:3: Error: Suspicious namespace and prefix combination [NamespaceTypo]\n"
+ + " xmlns:app=\"http://schemas.android.com/tools\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "res/layout/layout.xml:4: Error: Suspicious namespace and prefix combination [NamespaceTypo]\n"
+ + " xmlns:tools=\"http://schemas.android.com/apk/res/android\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "2 errors, 0 warnings\n",
+
+ lintProject(
+ xml("res/layout/layout.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " xmlns:app=\"http://schemas.android.com/tools\"\n"
+ + " xmlns:tools=\"http://schemas.android.com/apk/res/android\"\n"
+ + " android:layout_width=\"match_parent\"\n"
+ + " android:layout_height=\"match_parent\"\n"
+ + " android:orientation=\"vertical\"\n"
+ + " app:foo=\"true\"\n"
+ + " tools:bar=\"true\" />\n")));
+ }
+
+ public void testTypoOk() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject("res/layout/wrong_namespace4.xml"));
+ }
+
+ public void testUnused() throws Exception {
+ assertEquals(
+ "res/layout/unused_namespace.xml:3: Warning: Unused namespace unused1 [UnusedNamespace]\n" +
+ " xmlns:unused1=\"http://schemas.android.com/apk/res/unused1\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "res/layout/unused_namespace.xml:4: Warning: Unused namespace unused2 [UnusedNamespace]\n" +
+ " xmlns:unused2=\"http://schemas.android.com/apk/res/unused1\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "0 errors, 2 warnings\n" +
+ "",
+
+ lintProject("res/layout/unused_namespace.xml"));
+ }
+
+ public void testUnusedOk() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject("res/layout/layout1.xml"));
+ }
+
+ public void testLayoutAttributesOk() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintFiles("res/layout/namespace3.xml"));
+ }
+
+ public void testLayoutAttributesOk2() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintFiles("res/layout/namespace4.xml"));
+ }
+
+ public void testLayoutAttributes() throws Exception {
+ assertEquals(
+ "res/layout/namespace3.xml:2: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" +
+ " xmlns:app=\"http://schemas.android.com/apk/res/com.example.apicalltest\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 0 warnings\n",
+
+ lintFiles("res/layout/namespace3.xml",
+ "multiproject/library-manifest.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>project.properties"));
+ }
+
+ public void testLayoutAttributes2() throws Exception {
+ assertEquals(
+ "res/layout/namespace4.xml:3: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" +
+ " xmlns:app=\"http://schemas.android.com/apk/res/com.example.apicalltest\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 0 warnings\n",
+
+ lintFiles("res/layout/namespace4.xml",
+ "multiproject/library-manifest.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>project.properties"));
+ }
+
+ public void testWrongResAutoUrl() throws Exception {
+ assertEquals(
+ "res/layout/namespace5.xml:3: Error: Suspicious namespace: Did you mean http://schemas.android.com/apk/res-auto? [ResAuto]\n" +
+ " xmlns:app=\"http://schemas.android.com/apk/auto-res/\"\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "1 errors, 0 warnings\n",
+
+ lintFiles("res/layout/namespace5.xml",
+ "multiproject/library-manifest.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>project.properties"));
+ }
+
+ public void testWrongResUrl() throws Exception {
+ assertEquals(""
+ + "AndroidManifest.xml:2: Error: Suspicious namespace: should start with http:// [NamespaceTypo]\n"
+ + "<manifest xmlns:android=\"https://schemas.android.com/apk/res/android\"\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+
+ lintFiles("https_namespace.xml=>AndroidManifest.xml"));
+ }
+}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/NegativeMarginDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NegativeMarginDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/NegativeMarginDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NegativeMarginDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/NestedScrollingWidgetDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NestedScrollingWidgetDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/NestedScrollingWidgetDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NestedScrollingWidgetDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/NfcTechListDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NfcTechListDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/NfcTechListDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NfcTechListDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/NonInternationalizedSmsDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NonInternationalizedSmsDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/NonInternationalizedSmsDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/NonInternationalizedSmsDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ObsoleteLayoutParamsDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ObsoleteLayoutParamsDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ObsoleteLayoutParamsDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ObsoleteLayoutParamsDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/OnClickDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OnClickDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/OnClickDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OnClickDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/OverdrawDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverdrawDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/OverdrawDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverdrawDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/OverrideConcreteDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverrideConcreteDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/OverrideConcreteDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverrideConcreteDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/OverrideDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverrideDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/OverrideDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/OverrideDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ParcelDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ParcelDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ParcelDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ParcelDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionHolderTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionHolderTest.java
new file mode 100644
index 0000000..6151455
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionHolderTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.sdklib.AndroidVersion;
+import com.android.tools.lint.checks.PermissionHolder.SetPermissionLookup;
+import com.google.common.collect.Sets;
+
+import junit.framework.TestCase;
+
+import java.util.Set;
+
+public class PermissionHolderTest extends TestCase {
+ public void test() {
+ assertTrue(new SetPermissionLookup(Sets.newHashSet("foo")).hasPermission("foo"));
+ assertTrue(new SetPermissionLookup(Sets.newHashSet("foo","bar")).hasPermission("foo"));
+ assertTrue(new SetPermissionLookup(Sets.newHashSet("foo","bar")).hasPermission("bar"));
+ assertFalse(new SetPermissionLookup(Sets.newHashSet("foo")).hasPermission("bar"));
+ assertFalse(new SetPermissionLookup(Sets.newHashSet("foo")).hasPermission(""));
+ assertFalse(new SetPermissionLookup(Sets.<String>newHashSet()).hasPermission(""));
+
+ Set<String> empty = Sets.newHashSet();
+ assertFalse(new SetPermissionLookup(Sets.newHashSet("foo"), empty).isRevocable("foo"));
+ assertTrue(new SetPermissionLookup(empty, Sets.newHashSet("foo")).isRevocable("foo"));
+ assertFalse(new SetPermissionLookup(empty, Sets.newHashSet("foo")).isRevocable("bar"));
+
+ SetPermissionLookup lookup1 = new SetPermissionLookup(Sets.newHashSet("foo", "bar"));
+ assertTrue(SetPermissionLookup.join(lookup1, Sets.newHashSet("baz")).hasPermission("foo"));
+ assertTrue(SetPermissionLookup.join(lookup1, Sets.newHashSet("baz")).hasPermission("bar"));
+ assertTrue(SetPermissionLookup.join(lookup1, Sets.newHashSet("baz")).hasPermission("baz"));
+ assertFalse(SetPermissionLookup.join(lookup1, Sets.newHashSet("baz")).hasPermission("a"));
+
+ AndroidVersion version = new AndroidVersion(5, null);
+ assertSame(version, new SetPermissionLookup(Sets.newHashSet("foo"), Sets.newHashSet("bar"),
+ version, AndroidVersion.DEFAULT).getMinSdkVersion());
+ assertSame(version, new SetPermissionLookup(Sets.newHashSet("foo"), Sets.newHashSet("bar"),
+ AndroidVersion.DEFAULT, version).getTargetSdkVersion());
+ }
+}
\ No newline at end of file
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionRequirementTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionRequirementTest.java
new file mode 100644
index 0000000..7992903
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PermissionRequirementTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import static com.android.SdkConstants.ANDROID_URI;
+import static com.android.SdkConstants.ATTR_NAME;
+import static com.android.SdkConstants.TAG_PERMISSION;
+import static com.android.tools.lint.checks.PermissionRequirement.REVOCABLE_PERMISSION_NAMES;
+import static com.android.tools.lint.checks.PermissionRequirement.isRevocableSystemPermission;
+import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.sdklib.AndroidVersion;
+import com.android.testutils.SdkTestCase;
+import com.android.tools.lint.checks.PermissionHolder.SetPermissionLookup;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
+import com.android.utils.XmlUtils;
+import com.google.common.base.Charsets;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+
+import junit.framework.TestCase;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import lombok.ast.BinaryOperator;
+
+public class PermissionRequirementTest extends TestCase {
+ private static ResolvedAnnotation createAnnotation(
+ @NonNull String name,
+ @NonNull ResolvedAnnotation.Value... values) {
+ ResolvedAnnotation annotation = mock(ResolvedAnnotation.class);
+ when(annotation.getName()).thenReturn(name);
+ when(annotation.getValues()).thenReturn(Arrays.asList(values));
+ for (ResolvedAnnotation.Value value : values) {
+ when(annotation.getValue(value.name)).thenReturn(value.value);
+ }
+ return annotation;
+ }
+
+ public void testSingle() {
+ ResolvedAnnotation.Value values = new ResolvedAnnotation.Value("value",
+ "android.permission.ACCESS_FINE_LOCATION");
+ Set<String> emptySet = Collections.emptySet();
+ Set<String> fineSet = Collections.singleton("android.permission.ACCESS_FINE_LOCATION");
+ ResolvedAnnotation annotation = createAnnotation(PERMISSION_ANNOTATION, values);
+ PermissionRequirement req = PermissionRequirement.create(null, annotation);
+ assertTrue(req.isRevocable(new SetPermissionLookup(emptySet)));
+
+ assertFalse(req.isSatisfied(new SetPermissionLookup(emptySet)));
+ assertFalse(req.isSatisfied(new SetPermissionLookup(Collections.singleton(""))));
+ assertTrue(req.isSatisfied(new SetPermissionLookup(fineSet)));
+ assertEquals("android.permission.ACCESS_FINE_LOCATION",
+ req.describeMissingPermissions(new SetPermissionLookup(emptySet)));
+ assertEquals(fineSet, req.getMissingPermissions(new SetPermissionLookup(emptySet)));
+ assertEquals(emptySet, req.getMissingPermissions(new SetPermissionLookup(fineSet)));
+ assertEquals(fineSet, req.getRevocablePermissions(new SetPermissionLookup(emptySet)));
+ assertNull(req.getOperator());
+ assertFalse(req.getChildren().iterator().hasNext());
+ }
+
+ public void testAny() {
+ ResolvedAnnotation.Value values = new ResolvedAnnotation.Value("anyOf",
+ new String[]{"android.permission.ACCESS_FINE_LOCATION",
+ "android.permission.ACCESS_COARSE_LOCATION"});
+ Set<String> emptySet = Collections.emptySet();
+ Set<String> fineSet = Collections.singleton("android.permission.ACCESS_FINE_LOCATION");
+ Set<String> coarseSet = Collections.singleton("android.permission.ACCESS_COARSE_LOCATION");
+ Set<String> bothSet = Sets.newHashSet(
+ "android.permission.ACCESS_FINE_LOCATION",
+ "android.permission.ACCESS_COARSE_LOCATION");
+
+ ResolvedAnnotation annotation = createAnnotation(PERMISSION_ANNOTATION, values);
+ PermissionRequirement req = PermissionRequirement.create(null, annotation);
+ assertTrue(req.isRevocable(new SetPermissionLookup(emptySet)));
+ assertFalse(req.isSatisfied(new SetPermissionLookup(emptySet)));
+ assertFalse(req.isSatisfied(new SetPermissionLookup(Collections.singleton(""))));
+ assertTrue(req.isSatisfied(new SetPermissionLookup(fineSet)));
+ assertTrue(req.isSatisfied(new SetPermissionLookup(coarseSet)));
+ assertEquals(
+ "android.permission.ACCESS_FINE_LOCATION or android.permission.ACCESS_COARSE_LOCATION",
+ req.describeMissingPermissions(new SetPermissionLookup(emptySet)));
+ assertEquals(bothSet, req.getMissingPermissions(new SetPermissionLookup(emptySet)));
+ assertEquals(bothSet, req.getRevocablePermissions(new SetPermissionLookup(emptySet)));
+ assertSame(BinaryOperator.LOGICAL_OR, req.getOperator());
+ }
+
+ public void testAll() {
+ ResolvedAnnotation.Value values = new ResolvedAnnotation.Value("allOf",
+ new String[]{"android.permission.ACCESS_FINE_LOCATION",
+ "android.permission.ACCESS_COARSE_LOCATION"});
+ Set<String> emptySet = Collections.emptySet();
+ Set<String> fineSet = Collections.singleton("android.permission.ACCESS_FINE_LOCATION");
+ Set<String> coarseSet = Collections.singleton("android.permission.ACCESS_COARSE_LOCATION");
+ Set<String> bothSet = Sets.newHashSet(
+ "android.permission.ACCESS_FINE_LOCATION",
+ "android.permission.ACCESS_COARSE_LOCATION");
+
+ ResolvedAnnotation annotation = createAnnotation(PERMISSION_ANNOTATION, values);
+ PermissionRequirement req = PermissionRequirement.create(null, annotation);
+ assertTrue(req.isRevocable(new SetPermissionLookup(emptySet)));
+ assertFalse(req.isSatisfied(new SetPermissionLookup(emptySet)));
+ assertFalse(req.isSatisfied(new SetPermissionLookup(Collections.singleton(""))));
+ assertFalse(req.isSatisfied(new SetPermissionLookup(fineSet)));
+ assertFalse(req.isSatisfied(new SetPermissionLookup(coarseSet)));
+ assertTrue(req.isSatisfied(new SetPermissionLookup(bothSet)));
+ assertEquals(
+ "android.permission.ACCESS_FINE_LOCATION and android.permission.ACCESS_COARSE_LOCATION",
+ req.describeMissingPermissions(new SetPermissionLookup(emptySet)));
+ assertEquals(bothSet, req.getMissingPermissions(new SetPermissionLookup(emptySet)));
+ assertEquals(
+ "android.permission.ACCESS_COARSE_LOCATION",
+ req.describeMissingPermissions(new SetPermissionLookup(fineSet)));
+ assertEquals(coarseSet, req.getMissingPermissions(new SetPermissionLookup(fineSet)));
+ assertEquals(
+ "android.permission.ACCESS_FINE_LOCATION",
+ req.describeMissingPermissions(new SetPermissionLookup(coarseSet)));
+ assertEquals(fineSet, req.getMissingPermissions(new SetPermissionLookup(coarseSet)));
+ assertEquals(bothSet, req.getRevocablePermissions(new SetPermissionLookup(emptySet)));
+ assertSame(BinaryOperator.LOGICAL_AND, req.getOperator());
+ }
+
+ public void testRevocable() {
+ assertTrue(isRevocableSystemPermission("android.permission.ACCESS_FINE_LOCATION"));
+ assertTrue(isRevocableSystemPermission("android.permission.ACCESS_COARSE_LOCATION"));
+ assertFalse(isRevocableSystemPermission("android.permission.UNKNOWN_PERMISSION_NAME"));
+ }
+
+ public void testRevocable2() {
+ assertTrue(new SetPermissionLookup(Collections.<String>emptySet(),
+ Sets.newHashSet("my.permission1", "my.permission2")).isRevocable("my.permission2"));
+ }
+
+ public void testAppliesTo() {
+ ResolvedAnnotation annotation;
+ PermissionRequirement req;
+
+ // No date range applies to permission
+ annotation = createAnnotation(PERMISSION_ANNOTATION,
+ new ResolvedAnnotation.Value("value", "android.permission.AUTHENTICATE_ACCOUNTS"));
+ req = PermissionRequirement.create(null, annotation);
+ assertTrue(req.appliesTo(getHolder(15, 1)));
+ assertTrue(req.appliesTo(getHolder(15, 19)));
+ assertTrue(req.appliesTo(getHolder(15, 23)));
+ assertTrue(req.appliesTo(getHolder(22, 23)));
+ assertTrue(req.appliesTo(getHolder(23, 23)));
+
+ // Permission discontinued in API 23:
+ annotation = createAnnotation(PERMISSION_ANNOTATION,
+ new ResolvedAnnotation.Value("value", "android.permission.AUTHENTICATE_ACCOUNTS"),
+ new ResolvedAnnotation.Value("apis", "..22"));
+ req = PermissionRequirement.create(null, annotation);
+ assertTrue(req.appliesTo(getHolder(15, 1)));
+ assertTrue(req.appliesTo(getHolder(15, 19)));
+ assertTrue(req.appliesTo(getHolder(15, 23)));
+ assertTrue(req.appliesTo(getHolder(22, 23)));
+ assertFalse(req.appliesTo(getHolder(23, 23)));
+
+ // Permission requirement started in API 23
+ annotation = createAnnotation(PERMISSION_ANNOTATION,
+ new ResolvedAnnotation.Value("value", "android.permission.AUTHENTICATE_ACCOUNTS"),
+ new ResolvedAnnotation.Value("apis", "23.."));
+ req = PermissionRequirement.create(null, annotation);
+ assertFalse(req.appliesTo(getHolder(15, 1)));
+ assertFalse(req.appliesTo(getHolder(1, 19)));
+ assertFalse(req.appliesTo(getHolder(15, 22)));
+ assertTrue(req.appliesTo(getHolder(22, 23)));
+ assertTrue(req.appliesTo(getHolder(23, 30)));
+
+ // Permission requirement applied from API 14 through API 18
+ annotation = createAnnotation(PERMISSION_ANNOTATION,
+ new ResolvedAnnotation.Value("value", "android.permission.AUTHENTICATE_ACCOUNTS"),
+ new ResolvedAnnotation.Value("apis", "14..18"));
+ req = PermissionRequirement.create(null, annotation);
+ assertFalse(req.appliesTo(getHolder(1, 5)));
+ assertTrue(req.appliesTo(getHolder(15, 19)));
+ }
+
+ private static PermissionHolder getHolder(int min, int target) {
+ return new PermissionHolder.SetPermissionLookup(Collections.<String>emptySet(),
+ Collections.<String>emptySet(), new AndroidVersion(min, null),
+ new AndroidVersion(target, null));
+ }
+
+ public static void testDbUpToDate() throws Exception {
+ List<String> expected = getDangerousPermissions();
+ if (expected == null) {
+ return;
+ }
+ List<String> actual = Arrays.asList(REVOCABLE_PERMISSION_NAMES);
+ if (!expected.equals(actual)) {
+ System.out.println("Correct list of exceptions:");
+ for (String name : expected) {
+ System.out.println(" \"" + name + "\",");
+ }
+ fail("List of revocable permissions has changed:\n" +
+ // Make the diff show what it take to bring the actual results into the
+ // expected results
+ SdkTestCase.getDiff(Joiner.on('\n').join(actual),
+ Joiner.on('\n').join(expected)));
+ }
+ }
+
+ @Nullable
+ private static List<String> getDangerousPermissions() throws IOException {
+ Pattern pattern = Pattern.compile("dangerous");
+ String top = System.getenv("ANDROID_BUILD_TOP"); //$NON-NLS-1$
+ if (top == null) {
+ top = "/Volumes/android/mnc-dev";
+ }
+
+ // TODO: We should ship this file with the SDK!
+ File file = new File(top, "frameworks/base/core/res/AndroidManifest.xml");
+ if (!file.exists()) {
+ System.out.println("Set $ANDROID_BUILD_TOP to point to the git repository to check permissions");
+ return null;
+ }
+ boolean passedRuntimeHeader = false;
+ boolean passedInstallHeader = false;
+ String xml = Files.toString(file, Charsets.UTF_8);
+ Document document = XmlUtils.parseDocumentSilently(xml, true);
+ Set<String> revocable = Sets.newHashSet();
+ if (document != null && document.getDocumentElement() != null) {
+ NodeList children = document.getDocumentElement().getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node child = children.item(i);
+ short nodeType = child.getNodeType();
+ if (nodeType == Node.COMMENT_NODE) {
+ String comment = child.getNodeValue();
+ if (comment.contains("RUNTIME PERMISSIONS")) {
+ passedRuntimeHeader = true;
+ } else if (comment.contains("INSTALLTIME PERMISSIONS"))
+ passedInstallHeader = true;
+ } else if (passedRuntimeHeader
+ && !passedInstallHeader
+ && nodeType == Node.ELEMENT_NODE
+ && child.getNodeName().equals(TAG_PERMISSION)) {
+ Element element = (Element) child;
+ String protectionLevel = element.getAttributeNS(ANDROID_URI, "protectionLevel");
+ String name = element.getAttributeNS(ANDROID_URI, ATTR_NAME);
+ if (!name.isEmpty() && pattern.matcher(protectionLevel).find()) {
+ revocable.add(name);
+ }
+ }
+ }
+ }
+
+ List<String> expected = Lists.newArrayList(revocable);
+ Collections.sort(expected);
+ return expected;
+ }
+}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/PluralsDatabaseTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PluralsDatabaseTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/PluralsDatabaseTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PluralsDatabaseTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/PluralsDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PluralsDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/PluralsDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PluralsDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/PreferenceActivityDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PreferenceActivityDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/PreferenceActivityDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PreferenceActivityDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/PrivateKeyDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PrivateKeyDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/PrivateKeyDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PrivateKeyDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PrivateResourceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PrivateResourceDetectorTest.java
new file mode 100644
index 0000000..4df2fd2
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PrivateResourceDetectorTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.FN_PUBLIC_TXT;
+import static com.android.SdkConstants.FN_RESOURCE_TEXT;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidArtifact;
+import com.android.builder.model.AndroidLibrary;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.Dependencies;
+import com.android.builder.model.Variant;
+import com.android.testutils.TestUtils;
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Project;
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
+import org.mockito.stubbing.OngoingStubbing;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+@SuppressWarnings("javadoc")
+public class PrivateResourceDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new PrivateResourceDetector();
+ }
+
+ public void testPrivateInXml() throws Exception {
+ assertEquals(""
+ + "res/layout/private.xml:11: Warning: The resource @string/my_private_string is marked as private in the library [PrivateResource]\n"
+ + " android:text=\"@string/my_private_string\" />\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 1 warnings\n",
+ lintProject(xml("res/layout/private.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " android:id=\"@+id/newlinear\"\n"
+ + " android:orientation=\"vertical\"\n"
+ + " android:layout_width=\"match_parent\"\n"
+ + " android:layout_height=\"match_parent\">\n"
+ + "\n"
+ + " <TextView\n"
+ + " android:layout_width=\"wrap_content\"\n"
+ + " android:layout_height=\"wrap_content\"\n"
+ + " android:text=\"@string/my_private_string\" />\n"
+ + "\n"
+ + " <TextView\n"
+ + " android:layout_width=\"wrap_content\"\n"
+ + " android:layout_height=\"wrap_content\"\n"
+ + " android:text=\"@string/my_public_string\" />\n"
+ + "</LinearLayout>\n")));
+ }
+
+ public void testPrivateInJava() throws Exception {
+ assertEquals(""
+ + ""
+ + "src/test/pkg/Private.java:3: Warning: The resource @string/my_private_string is marked as private in the library [PrivateResource]\n"
+ + " int x = R.string.my_private_string; // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 1 warnings\n",
+ lintProject(java("src/test/pkg/Private.java", ""
+ + "public class PrivateResourceDetectorTest {\n"
+ + " void test() {\n"
+ + " int x = R.string.my_private_string; // ERROR\n"
+ + " int y = R.string.my_public_string; // OK\n"
+ + " int y = android.R.string.my_private_string; // OK (not in project namespace)\n"
+ + " }\n"
+ + "}\n")));
+ }
+
+ public void testOverride() throws Exception {
+ assertEquals(""
+ + "res/layout/my_private_layout.xml: Warning: Overriding @layout/my_private_layout which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
+ + "res/values/strings.xml:5: Warning: Overriding @string/my_private_string which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
+ + " <string name=\"my_private_string\">String 1</string>\n"
+ + " ~~~~~~~~~~~~~~~~~\n"
+ + "res/values/strings.xml:9: Warning: Overriding @string/my_private_string which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
+ + " <item type=\"string\" name=\"my_private_string\">String 1</item>\n"
+ + " ~~~~~~~~~~~~~~~~~\n"
+ + "res/values/strings.xml:12: Warning: Overriding @string/my_private_string which is marked as private in the library. If deliberate, use tools:override=\"true\", otherwise pick a different name. [PrivateResource]\n"
+ + " <string tools:override=\"false\" name=\"my_private_string\">String 2</string>\n"
+ + " ~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 4 warnings\n",
+ lintProject(xml("res/values/strings.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n"
+ + "\n"
+ + " <string name=\"app_name\">LibraryProject</string>\n"
+ + " <string name=\"my_private_string\">String 1</string>\n"
+ + " <string name=\"my_public_string\">String 2</string>\n"
+ + " <string name=\"string3\"> @my_private_string </string>\n"
+ + " <string name=\"string4\"> @my_public_string </string>\n"
+ + " <item type=\"string\" name=\"my_private_string\">String 1</item>\n"
+ + " <dimen name=\"my_private_string\">String 1</dimen>\n" // unrelated
+ + " <string tools:ignore=\"PrivateResource\" name=\"my_private_string\">String 2</string>\n"
+ + " <string tools:override=\"false\" name=\"my_private_string\">String 2</string>\n"
+ + " <string tools:override=\"true\" name=\"my_private_string\">String 2</string>\n"
+ + "\n"
+ + "</resources>\n"),
+ xml("res/layout/my_private_layout.xml", "<LinearLayout/>"),
+ xml("res/layout/my_public_layout.xml", "<LinearLayout/>")));
+ }
+
+ @Override
+ protected TestLintClient createClient() {
+ return new TestLintClient() {
+ @NonNull
+ @Override
+ protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
+ return new Project(this, dir, referenceDir) {
+ @Override
+ public boolean isGradleProject() {
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public AndroidProject getGradleProjectModel() {
+ // First version which supported private resources; this does not
+ // need to track later versions we release
+ return createMockProject("1.3.0-alpha2", 3);
+ }
+
+ @Nullable
+ @Override
+ public Variant getCurrentVariant() {
+ try {
+ AndroidLibrary library = createMockLibrary(
+ ""
+ + "int string my_private_string 0x7f040000\n"
+ + "int string my_public_string 0x7f040001\n"
+ + "int layout my_private_layout 0x7f040002\n",
+ ""
+ + ""
+ + "string my_public_string\n",
+ Collections.<AndroidLibrary>emptyList()
+ );
+ AndroidArtifact artifact = createMockArtifact(
+ Collections.singletonList(library));
+
+ Variant variant = mock(Variant.class);
+ when(variant.getMainArtifact()).thenReturn(artifact);
+ return variant;
+ } catch (Exception e) {
+ fail(e.toString());
+ return null;
+ }
+ }
+ };
+ }
+ };
+ }
+
+ public static AndroidProject createMockProject(String modelVersion, int apiVersion) {
+ AndroidProject project = mock(AndroidProject.class);
+ when(project.getApiVersion()).thenReturn(apiVersion);
+ when(project.getModelVersion()).thenReturn(modelVersion);
+
+ return project;
+ }
+
+ public static AndroidArtifact createMockArtifact(List<AndroidLibrary> libraries) {
+ Dependencies dependencies = mock(Dependencies.class);
+ when(dependencies.getLibraries()).thenReturn(libraries);
+
+ AndroidArtifact artifact = mock(AndroidArtifact.class);
+ when(artifact.getDependencies()).thenReturn(dependencies);
+
+ return artifact;
+ }
+
+ public static AndroidLibrary createMockLibrary(String allResources, String publicResources,
+ List<AndroidLibrary> dependencies)
+ throws IOException {
+ final File tempDir = TestUtils.createTempDirDeletedOnExit();
+
+ Files.write(allResources, new File(tempDir, FN_RESOURCE_TEXT), Charsets.UTF_8);
+ File publicTxtFile = new File(tempDir, FN_PUBLIC_TXT);
+ if (publicResources != null) {
+ Files.write(publicResources, publicTxtFile, Charsets.UTF_8);
+ }
+ AndroidLibrary library = mock(AndroidLibrary.class);
+ when(library.getPublicResources()).thenReturn(publicTxtFile);
+
+ // Work around wildcard capture
+ //when(mock.getLibraryDependencies()).thenReturn(dependencies);
+ List libraryDependencies = library.getLibraryDependencies();
+ OngoingStubbing<List> setter = when(libraryDependencies);
+ setter.thenReturn(dependencies);
+ return library;
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ProguardDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ProguardDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ProguardDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ProguardDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/PropertyFileDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PropertyFileDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/PropertyFileDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PropertyFileDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/PxUsageDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PxUsageDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/PxUsageDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/PxUsageDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/RegistrationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RegistrationDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/RegistrationDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RegistrationDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/RelativeOverlapDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RelativeOverlapDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/RelativeOverlapDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RelativeOverlapDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RequiredAttributeDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RequiredAttributeDetectorTest.java
new file mode 100644
index 0000000..da36270
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RequiredAttributeDetectorTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import java.io.File;
+
+@SuppressWarnings("javadoc")
+public class RequiredAttributeDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new RequiredAttributeDetector();
+ }
+
+ public void test() throws Exception {
+ // Simple: Only consider missing attributes in the layout xml file
+ // (though skip warnings on <merge> tags and under <GridLayout>
+ assertEquals(
+ "res/layout/size.xml:13: Error: The required layout_height attribute is missing [RequiredSize]\n" +
+ " <RadioButton\n" +
+ " ^\n" +
+ "res/layout/size.xml:18: Error: The required layout_width attribute is missing [RequiredSize]\n" +
+ " <EditText\n" +
+ " ^\n" +
+ "res/layout/size.xml:23: Error: The required layout_width and layout_height attributes are missing [RequiredSize]\n" +
+ " <EditText\n" +
+ " ^\n" +
+ "3 errors, 0 warnings\n",
+
+ lintProject("res/layout/size.xml"));
+ }
+
+ public void test2() throws Exception {
+ // Consider styles (specifying sizes) and includes (providing sizes for the root tags)
+ assertEquals(
+ "res/layout/size2.xml:9: Error: The required layout_width and layout_height attributes are missing [RequiredSize]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "res/layout/size2.xml:18: Error: The required layout_height attribute is missing [RequiredSize]\n" +
+ " <Button\n" +
+ " ^\n" +
+ "2 errors, 0 warnings\n",
+
+ lintProject(
+ "res/layout/size2.xml",
+ "res/layout/sizeincluded.xml",
+ "res/values/sizestyles.xml"
+ ));
+ }
+
+ public void testInflaters() throws Exception {
+ // Consider java inflation
+ assertEquals(
+ "res/layout/size5.xml:2: Error: The required layout_width and layout_height attributes are missing [RequiredSize]\n" +
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ "^\n" +
+ "1 errors, 0 warnings\n",
+
+ lintProject(
+ "src/test/pkg/InflaterTest.java.txt=>src/test/pkg/InflaterTest.java",
+ "res/layout/sizeincluded.xml=>res/layout/size1.xml",
+ "res/layout/sizeincluded.xml=>res/layout/size2.xml",
+ "res/layout/sizeincluded.xml=>res/layout/size3.xml",
+ "res/layout/sizeincluded.xml=>res/layout/size4.xml",
+ "res/layout/sizeincluded.xml=>res/layout/size5.xml",
+ "res/layout/sizeincluded.xml=>res/layout/size6.xml",
+ "res/layout/sizeincluded.xml=>res/layout/size7.xml"
+ ));
+ }
+
+ public void testRequestFocus() throws Exception {
+ // See http://code.google.com/p/android/issues/detail?id=38700
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "res/layout/edit_type.xml"
+ ));
+ }
+
+ public void testFrameworkStyles() throws Exception {
+ // See http://code.google.com/p/android/issues/detail?id=38958
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "res/layout/listseparator.xml"
+ ));
+ }
+
+ public void testThemeStyles() throws Exception {
+ // Check that we don't complain about cases where the size is defined in a theme
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "res/layout/size.xml",
+ "res/values/themes.xml"
+ ));
+ }
+
+ public void testThemeStyles2() throws Exception {
+ // Check that we don't complain about cases where the size is defined in a theme
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "res/layout/size.xml",
+ "res/values/themes2.xml"
+ ));
+ }
+
+ public void testHasLayoutVariations() throws Exception {
+ File projectDir = getProjectDir(null,
+ copy("res/layout/size.xml"),
+ copy("res/layout/size.xml", "res/layout-land/size.xml"),
+ copy("res/layout/size.xml", "res/layout/size2.xml"));
+ assertTrue(RequiredAttributeDetector.hasLayoutVariations(
+ new File(projectDir, "res/layout/size.xml".replace('/', File.separatorChar))));
+ assertTrue(RequiredAttributeDetector.hasLayoutVariations(
+ new File(projectDir, "res/layout-land/size.xml".replace('/', File.separatorChar))));
+ assertFalse(RequiredAttributeDetector.hasLayoutVariations(
+ new File(projectDir, "res/layout/size2.xml".replace('/', File.separatorChar))));
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ResourceCycleDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ResourceCycleDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ResourceCycleDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ResourceCycleDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ResourcePrefixDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ResourcePrefixDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ResourcePrefixDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ResourcePrefixDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/RtlDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RtlDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/RtlDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RtlDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SQLiteDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SQLiteDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SQLiteDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SQLiteDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ScrollViewChildDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ScrollViewChildDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ScrollViewChildDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ScrollViewChildDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SdCardDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SdCardDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SdCardDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SdCardDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SecureRandomDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SecureRandomDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SecureRandomDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SecureRandomDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SecureRandomGeneratorDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SecureRandomGeneratorDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SecureRandomGeneratorDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SecureRandomGeneratorDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SecurityDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SecurityDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SecurityDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SecurityDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ServiceCastDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ServiceCastDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ServiceCastDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ServiceCastDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SetJavaScriptEnabledDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SharedPrefsDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SharedPrefsDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SharedPrefsDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SharedPrefsDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SignatureOrSystemDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SignatureOrSystemDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SignatureOrSystemDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SignatureOrSystemDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/StateListDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/StateListDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/StateListDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/StateListDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/StringFormatDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/StringFormatDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/StringFormatDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/StringFormatDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java
new file mode 100644
index 0000000..518566a
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SupportAnnotationDetectorTest.java
@@ -0,0 +1,1263 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import static com.android.tools.lint.checks.SupportAnnotationDetector.PERMISSION_ANNOTATION;
+
+import com.android.tools.lint.ExternalAnnotationRepository;
+import com.android.tools.lint.ExternalAnnotationRepositoryTest;
+import com.android.tools.lint.client.api.JavaParser.ResolvedAnnotation;
+import com.android.tools.lint.client.api.JavaParser.ResolvedMethod;
+import com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings("ClassNameDiffersFromFileName") // For embedded unit tests
+public class SupportAnnotationDetectorTest extends AbstractCheckTest {
+ private static final boolean SDK_ANNOTATIONS_AVAILABLE =
+ new SupportAnnotationDetectorTest().createClient().findResource(
+ ExternalAnnotationRepository.SDK_ANNOTATIONS_PATH) != null;
+
+ @Override
+ protected Detector getDetector() {
+ return new SupportAnnotationDetector();
+ }
+
+ public void testRange() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/RangeTest.java:32: Error: Expected length 5 (was 4) [Range]\n"
+ + " printExact(\"1234\"); // ERROR\n"
+ + " ~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:34: Error: Expected length 5 (was 6) [Range]\n"
+ + " printExact(\"123456\"); // ERROR\n"
+ + " ~~~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:36: Error: Expected length ≥ 5 (was 4) [Range]\n"
+ + " printMin(\"1234\"); // ERROR\n"
+ + " ~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:43: Error: Expected length ≤ 8 (was 9) [Range]\n"
+ + " printMax(\"123456789\"); // ERROR\n"
+ + " ~~~~~~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:45: Error: Expected length ≥ 4 (was 3) [Range]\n"
+ + " printRange(\"123\"); // ERROR\n"
+ + " ~~~~~\n"
+ + "src/test/pkg/RangeTest.java:49: Error: Expected length ≤ 6 (was 7) [Range]\n"
+ + " printRange(\"1234567\"); // ERROR\n"
+ + " ~~~~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:53: Error: Expected size 5 (was 4) [Range]\n"
+ + " printExact(new int[]{1, 2, 3, 4}); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:55: Error: Expected size 5 (was 6) [Range]\n"
+ + " printExact(new int[]{1, 2, 3, 4, 5, 6}); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:57: Error: Expected size ≥ 5 (was 4) [Range]\n"
+ + " printMin(new int[]{1, 2, 3, 4}); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:65: Error: Expected size ≤ 8 (was 9) [Range]\n"
+ + " printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:67: Error: Expected size ≥ 4 (was 3) [Range]\n"
+ + " printRange(new int[] {1,2,3}); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:71: Error: Expected size ≤ 6 (was 7) [Range]\n"
+ + " printRange(new int[] {1,2,3,4,5,6,7}); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:74: Error: Expected size to be a multiple of 3 (was 4 and should be either 3 or 6) [Range]\n"
+ + " printMultiple(new int[] {1,2,3,4}); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:75: Error: Expected size to be a multiple of 3 (was 5 and should be either 3 or 6) [Range]\n"
+ + " printMultiple(new int[] {1,2,3,4,5}); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:77: Error: Expected size to be a multiple of 3 (was 7 and should be either 6 or 9) [Range]\n"
+ + " printMultiple(new int[] {1,2,3,4,5,6,7}); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:80: Error: Expected size ≥ 4 (was 3) [Range]\n"
+ + " printMinMultiple(new int[]{1, 2, 3}); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:84: Error: Value must be ≥ 4 (was 3) [Range]\n"
+ + " printAtLeast(3); // ERROR\n"
+ + " ~\n"
+ + "src/test/pkg/RangeTest.java:91: Error: Value must be ≤ 7 (was 8) [Range]\n"
+ + " printAtMost(8); // ERROR\n"
+ + " ~\n"
+ + "src/test/pkg/RangeTest.java:93: Error: Value must be ≥ 4 (was 3) [Range]\n"
+ + " printBetween(3); // ERROR\n"
+ + " ~\n"
+ + "src/test/pkg/RangeTest.java:98: Error: Value must be ≤ 7 (was 8) [Range]\n"
+ + " printBetween(8); // ERROR\n"
+ + " ~\n"
+ + "src/test/pkg/RangeTest.java:102: Error: Value must be ≥ 2.5 (was 2.49) [Range]\n"
+ + " printAtLeastInclusive(2.49f); // ERROR\n"
+ + " ~~~~~\n"
+ + "src/test/pkg/RangeTest.java:106: Error: Value must be > 2.5 (was 2.49) [Range]\n"
+ + " printAtLeastExclusive(2.49f); // ERROR\n"
+ + " ~~~~~\n"
+ + "src/test/pkg/RangeTest.java:107: Error: Value must be > 2.5 (was 2.5) [Range]\n"
+ + " printAtLeastExclusive(2.5f); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/RangeTest.java:113: Error: Value must be ≤ 7.0 (was 7.1) [Range]\n"
+ + " printAtMostInclusive(7.1f); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/RangeTest.java:117: Error: Value must be < 7.0 (was 7.0) [Range]\n"
+ + " printAtMostExclusive(7.0f); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/RangeTest.java:118: Error: Value must be < 7.0 (was 7.1) [Range]\n"
+ + " printAtMostExclusive(7.1f); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/RangeTest.java:120: Error: Value must be ≥ 2.5 (was 2.4) [Range]\n"
+ + " printBetweenFromInclusiveToInclusive(2.4f); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/RangeTest.java:124: Error: Value must be ≤ 5.0 (was 5.1) [Range]\n"
+ + " printBetweenFromInclusiveToInclusive(5.1f); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/RangeTest.java:126: Error: Value must be > 2.5 (was 2.4) [Range]\n"
+ + " printBetweenFromExclusiveToInclusive(2.4f); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/RangeTest.java:127: Error: Value must be > 2.5 (was 2.5) [Range]\n"
+ + " printBetweenFromExclusiveToInclusive(2.5f); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/RangeTest.java:129: Error: Value must be ≤ 5.0 (was 5.1) [Range]\n"
+ + " printBetweenFromExclusiveToInclusive(5.1f); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/RangeTest.java:131: Error: Value must be ≥ 2.5 (was 2.4) [Range]\n"
+ + " printBetweenFromInclusiveToExclusive(2.4f); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/RangeTest.java:135: Error: Value must be < 5.0 (was 5.0) [Range]\n"
+ + " printBetweenFromInclusiveToExclusive(5.0f); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/RangeTest.java:137: Error: Value must be > 2.5 (was 2.4) [Range]\n"
+ + " printBetweenFromExclusiveToExclusive(2.4f); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/RangeTest.java:138: Error: Value must be > 2.5 (was 2.5) [Range]\n"
+ + " printBetweenFromExclusiveToExclusive(2.5f); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/RangeTest.java:141: Error: Value must be < 5.0 (was 5.0) [Range]\n"
+ + " printBetweenFromExclusiveToExclusive(5.0f); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/RangeTest.java:145: Error: Value must be ≥ 4 (was -7) [Range]\n"
+ + " printBetween(-7); // ERROR\n"
+ + " ~~\n"
+ + "src/test/pkg/RangeTest.java:146: Error: Value must be > 2.5 (was -10.0) [Range]\n"
+ + " printAtLeastExclusive(-10.0f); // ERROR\n"
+ + " ~~~~~~\n"
+ + "src/test/pkg/RangeTest.java:156: Error: Value must be ≥ -1 (was -2) [Range]\n"
+ + " printIndirect(-2); // ERROR\n"
+ + " ~~\n"
+ + "src/test/pkg/RangeTest.java:157: Error: Value must be ≤ 42 (was 43) [Range]\n"
+ + " printIndirect(43); // ERROR\n"
+ + " ~~\n"
+ + "src/test/pkg/RangeTest.java:158: Error: Expected length 5 (was 7) [Range]\n"
+ + " printIndirectSize(\"1234567\"); // ERROR\n"
+ + " ~~~~~~~~~\n"
+ + "41 errors, 0 warnings\n",
+
+ lintProject("src/test/pkg/RangeTest.java.txt=>src/test/pkg/RangeTest.java",
+ "src/android/support/annotation/Size.java.txt=>src/android/support/annotation/Size.java",
+ "src/android/support/annotation/IntRange.java.txt=>src/android/support/annotation/IntRange.java",
+ "src/android/support/annotation/FloatRange.java.txt=>src/android/support/annotation/FloatRange.java"
+ ));
+ }
+
+ public void testTypeDef() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/IntDefTest.java:31: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+ + " setStyle(0, 0); // ERROR\n"
+ + " ~\n"
+ + "src/test/pkg/IntDefTest.java:32: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+ + " setStyle(-1, 0); // ERROR\n"
+ + " ~~\n"
+ + "src/test/pkg/IntDefTest.java:33: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+ + " setStyle(UNRELATED, 0); // ERROR\n"
+ + " ~~~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:34: Error: Must be one of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+ + " setStyle(IntDefTest.UNRELATED, 0); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:35: Error: Flag not allowed here [WrongConstant]\n"
+ + " setStyle(IntDefTest.STYLE_NORMAL|STYLE_NO_FRAME, 0); // ERROR: Not a flag\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:36: Error: Flag not allowed here [WrongConstant]\n"
+ + " setStyle(~STYLE_NO_FRAME, 0); // ERROR: Not a flag\n"
+ + " ~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:55: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+ + " setFlags(\"\", UNRELATED); // ERROR\n"
+ + " ~~~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:56: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+ + " setFlags(\"\", UNRELATED|STYLE_NO_TITLE); // ERROR\n"
+ + " ~~~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:57: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+ + " setFlags(\"\", STYLE_NORMAL|STYLE_NO_TITLE|UNRELATED); // ERROR\n"
+ + " ~~~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:58: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+ + " setFlags(\"\", 1); // ERROR\n"
+ + " ~\n"
+ + "src/test/pkg/IntDefTest.java:59: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+ + " setFlags(\"\", arg < 0 ? STYLE_NORMAL : UNRELATED); // ERROR\n"
+ + " ~~~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:60: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+ + " setFlags(\"\", arg < 0 ? UNRELATED : STYLE_NORMAL); // ERROR\n"
+ + " ~~~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:79: Error: Must be one of: IntDefTest.TYPE_1, IntDefTest.TYPE_2 [WrongConstant]\n"
+ + " setTitle(\"\", UNRELATED_TYPE); // ERROR\n"
+ + " ~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:80: Error: Must be one of: IntDefTest.TYPE_1, IntDefTest.TYPE_2 [WrongConstant]\n"
+ + " setTitle(\"\", \"type2\"); // ERROR\n"
+ + " ~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:87: Error: Must be one of: IntDefTest.TYPE_1, IntDefTest.TYPE_2 [WrongConstant]\n"
+ + " setTitle(\"\", type); // ERROR\n"
+ + " ~~~~\n"
+ + "src/test/pkg/IntDefTest.java:92: Error: Must be one or more of: IntDefTest.STYLE_NORMAL, IntDefTest.STYLE_NO_TITLE, IntDefTest.STYLE_NO_FRAME, IntDefTest.STYLE_NO_INPUT [WrongConstant]\n"
+ + " setFlags(\"\", flag); // ERROR\n"
+ + " ~~~~\n"
+ + (SDK_ANNOTATIONS_AVAILABLE ?
+ "src/test/pkg/IntDefTest.java:99: Error: Must be one of: View.LAYOUT_DIRECTION_LTR, View.LAYOUT_DIRECTION_RTL, View.LAYOUT_DIRECTION_INHERIT, View.LAYOUT_DIRECTION_LOCALE [WrongConstant]\n"
+ + " view.setLayoutDirection(View.TEXT_DIRECTION_LTR); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:100: Error: Must be one of: View.LAYOUT_DIRECTION_LTR, View.LAYOUT_DIRECTION_RTL, View.LAYOUT_DIRECTION_INHERIT, View.LAYOUT_DIRECTION_LOCALE [WrongConstant]\n"
+ + " view.setLayoutDirection(0); // ERROR\n"
+ + " ~\n"
+ + "src/test/pkg/IntDefTest.java:101: Error: Flag not allowed here [WrongConstant]\n"
+ + " view.setLayoutDirection(View.LAYOUT_DIRECTION_LTR|View.LAYOUT_DIRECTION_RTL); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/IntDefTest.java:102: Error: Must be one of: Context.POWER_SERVICE, Context.WINDOW_SERVICE, Context.LAYOUT_INFLATER_SERVICE, Context.ACCOUNT_SERVICE, Context.ACTIVITY_SERVICE, Context.ALARM_SERVICE, Context.NOTIFICATION_SERVICE, Context.ACCESSIBILITY_SERVICE, Context.CAPTIONING_SERVICE, Context.KEYGUARD_SERVICE, Context.LOCATION_SERVICE, Context.SEARCH_SERVICE, Context.SENSOR_SERVICE, Context.STORAGE_SERVICE, Context.WALLPAPER_SERVICE, Context.VIBRATOR_SERVICE, Context.CONNECTIVITY_SERVICE, Context.NETWORK_STATS_SERVICE, Context.WIFI_SERVICE, Context.WIFI_P2P_SERVICE, Context.NSD_SERVICE, Context.AUDIO_SERVICE, Context.FINGERPRINT_SERVICE, Context.MEDIA_ROUTER_SERVICE, Context.TELEPHONY_SERVICE, Context.TELEPHONY_SUBSCRIPTION_SERVICE, Context.CARRIER_CONFIG_SERVICE, Context.TELECOM_SERVICE, Context.CLIPBOARD_SERVICE, Context.INPUT_METHOD_SERVICE, Context.TEXT_SERVICES_MANAGER_SERVICE, Context.APPWIDGET_SERVICE, Context.DROPBOX_SERVICE, Context.DEVICE_POLICY_SERVICE, Context.UI_MODE_SERVICE, Context.DOWNLOAD_SERVICE, Context.NFC_SERVICE, Context.BLUETOOTH_SERVICE, Context.USB_SERVICE, Context.LAUNCHER_APPS_SERVICE, Context.INPUT_SERVICE, Context.DISPLAY_SERVICE, Context.USER_SERVICE, Context.RESTRICTIONS_SERVICE, Context.APP_OPS_SERVICE, Context.CAMERA_SERVICE, Context.PRINT_SERVICE, Context.CONSUMER_IR_SERVICE, Context.TV_INPUT_SERVICE, Context.USAGE_STATS_SERVICE, Context.MEDIA_SESSION_SERVICE, Context.BATTERY_SERVICE, Context.JOB_SCHEDULER_SERVICE, Context.MEDIA_PROJECTION_SERVICE, Context.MIDI_SERVICE [WrongConstant]\n"
+ + " context.getSystemService(TYPE_1); // ERROR\n"
+ + " ~~~~~~\n"
+ + "20 errors, 0 warnings\n" :
+ "16 errors, 0 warnings\n"),
+
+ lintProject("src/test/pkg/IntDefTest.java.txt=>src/test/pkg/IntDefTest.java",
+ "src/android/support/annotation/IntDef.java.txt=>src/android/support/annotation/IntDef.java",
+ "src/android/support/annotation/StringDef.java.txt=>src/android/support/annotation/StringDef.java"
+ ));
+ }
+
+ public void testColorInt() throws Exception {
+ // Needs updated annotations!
+ assertEquals((SDK_ANNOTATIONS_AVAILABLE ? ""
+ + "src/test/pkg/WrongColor.java:9: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.blue) [ResourceAsColor]\n"
+ + " paint2.setColor(R.color.blue);\n"
+ + " ~~~~~~~~~~~~\n"
+ + "src/test/pkg/WrongColor.java:11: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.red) [ResourceAsColor]\n"
+ + " textView.setTextColor(R.color.red);\n"
+ + " ~~~~~~~~~~~\n"
+ + "src/test/pkg/WrongColor.java:12: Error: Should pass resolved color instead of resource id here: getResources().getColor(android.R.color.black) [ResourceAsColor]\n"
+ + " textView.setTextColor(android.R.color.black);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/WrongColor.java:13: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.blue) [ResourceAsColor]\n"
+ + " textView.setTextColor(foo > 0 ? R.color.green : R.color.blue);\n"
+ + " ~~~~~~~~~~~~\n"
+ + "src/test/pkg/WrongColor.java:13: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.green) [ResourceAsColor]\n"
+ + " textView.setTextColor(foo > 0 ? R.color.green : R.color.blue);\n"
+ + " ~~~~~~~~~~~~~\n" : "")
+ + "src/test/pkg/WrongColor.java:21: Error: Should pass resolved color instead of resource id here: getResources().getColor(R.color.blue) [ResourceAsColor]\n"
+ + " foo2(R.color.blue);\n"
+ + " ~~~~~~~~~~~~\n"
+ + "src/test/pkg/WrongColor.java:20: Error: Expected resource of type color [ResourceType]\n"
+ + " foo1(0xffff0000);\n"
+ + " ~~~~~~~~~~\n"
+ + (SDK_ANNOTATIONS_AVAILABLE ? "7 errors, 0 warnings\n" : "2 errors, 0 warnings\n"),
+
+ lintProject(
+ copy("src/test/pkg/WrongColor.java.txt", "src/test/pkg/WrongColor.java"),
+ copy("src/android/support/annotation/ColorInt.java.txt", "src/android/support/annotation/ColorInt.java"),
+ mColorResAnnotation
+ ));
+ }
+
+ public void testColorInt2() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/ColorTest.java:23: Error: Should pass resolved color instead of resource id here: getResources().getColor(actualColor) [ResourceAsColor]\n"
+ + " setColor2(actualColor); // ERROR\n"
+ + " ~~~~~~~~~~~\n"
+ + "src/test/pkg/ColorTest.java:24: Error: Should pass resolved color instead of resource id here: getResources().getColor(getColor2()) [ResourceAsColor]\n"
+ + " setColor2(getColor2()); // ERROR\n"
+ + " ~~~~~~~~~~~\n"
+ + "src/test/pkg/ColorTest.java:17: Error: Expected a color resource id (R.color.) but received an RGB integer [ResourceType]\n"
+ + " setColor1(actualColor); // ERROR\n"
+ + " ~~~~~~~~~~~\n"
+ + "src/test/pkg/ColorTest.java:18: Error: Expected a color resource id (R.color.) but received an RGB integer [ResourceType]\n"
+ + " setColor1(getColor1()); // ERROR\n"
+ + " ~~~~~~~~~~~\n"
+ + "4 errors, 0 warnings\n",
+
+ lintProject(
+ java("src/test/pkg/ColorTest.java", ""
+ + "package test.pkg;\n"
+ + "import android.content.Context;\n"
+ + "import android.content.res.Resources;\n"
+ + "import android.support.annotation.ColorInt;\n"
+ + "import android.support.annotation.ColorRes;\n"
+ + "\n"
+ + "public abstract class ColorTest {\n"
+ + " @ColorInt\n"
+ + " public abstract int getColor1();\n"
+ + " public abstract void setColor1(@ColorRes int color);\n"
+ + " @ColorRes\n"
+ + " public abstract int getColor2();\n"
+ + " public abstract void setColor2(@ColorInt int color);\n"
+ + "\n"
+ + " public void test1(Context context) {\n"
+ + " int actualColor = getColor1();\n"
+ + " setColor1(actualColor); // ERROR\n"
+ + " setColor1(getColor1()); // ERROR\n"
+ + " setColor1(getColor2()); // OK\n"
+ + " }\n"
+ + " public void test2(Context context) {\n"
+ + " int actualColor = getColor2();\n"
+ + " setColor2(actualColor); // ERROR\n"
+ + " setColor2(getColor2()); // ERROR\n"
+ + " setColor2(getColor1()); // OK\n"
+ + " }\n"
+ + "}\n"),
+ mColorResAnnotation,
+ mColorIntAnnotation
+ ));
+ }
+
+ public void testColorInt3() throws Exception {
+ // Regression test for https://code.google.com/p/android/issues/detail?id=176321
+
+ if (!SDK_ANNOTATIONS_AVAILABLE) {
+ return;
+ }
+ assertEquals(""
+ + "src/test/pkg/ColorTest.java:11: Error: Expected a color resource id (R.color.) but received an RGB integer [ResourceType]\n"
+ + " setColor(actualColor);\n"
+ + " ~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+
+ lintProject(
+ java("src/test/pkg/ColorTest.java", ""
+ + "package test.pkg;\n"
+ + "import android.content.Context;\n"
+ + "import android.content.res.Resources;\n"
+ + "import android.support.annotation.ColorRes;\n"
+ + "\n"
+ + "public abstract class ColorTest {\n"
+ + " public abstract void setColor(@ColorRes int color);\n"
+ + "\n"
+ + " public void test(Context context, @ColorRes int id) {\n"
+ + " int actualColor = context.getResources().getColor(id, null);\n"
+ + " setColor(actualColor);\n"
+ + " }\n"
+ + "}\n"),
+ mColorResAnnotation
+ ));
+ }
+ public void testResourceType() throws Exception {
+ assertEquals((SDK_ANNOTATIONS_AVAILABLE ? ""
+ + "src/p1/p2/Flow.java:13: Error: Expected resource of type drawable [ResourceType]\n"
+ + " resources.getDrawable(10); // ERROR\n"
+ + " ~~\n"
+ + "src/p1/p2/Flow.java:18: Error: Expected resource of type drawable [ResourceType]\n"
+ + " resources.getDrawable(R.string.my_string); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~\n" : "")
+ + "src/p1/p2/Flow.java:22: Error: Expected resource of type drawable [ResourceType]\n"
+ + " myMethod(R.string.my_string, null); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~\n"
+ + "src/p1/p2/Flow.java:26: Error: Expected resource of type drawable [ResourceType]\n"
+ + " resources.getDrawable(R.string.my_string); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~\n"
+ + "src/p1/p2/Flow.java:32: Error: Expected resource identifier (R.type.name) [ResourceType]\n"
+ + " myAnyResMethod(50); // ERROR\n"
+ + " ~~\n"
+ + (SDK_ANNOTATIONS_AVAILABLE ? "src/p1/p2/Flow.java:60: Error: Expected resource of type drawable [ResourceType]\n"
+ + " resources.getDrawable(MimeTypes.getAnnotatedString()); // Error\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" : "")
+ + "src/p1/p2/Flow.java:68: Error: Expected resource of type drawable [ResourceType]\n"
+ + " myMethod(z, null); // ERROR\n"
+ + " ~\n"
+ + (SDK_ANNOTATIONS_AVAILABLE ? "7 errors, 0 warnings\n" : "4 errors, 0 warnings\n"),
+
+ lintProject(
+ copy("src/p1/p2/Flow.java.txt", "src/p1/p2/Flow.java"),
+ copy("src/android/support/annotation/DrawableRes.java.txt", "src/android/support/annotation/DrawableRes.java"),
+ mStringResAnnotation,
+ mStyleResAnnotation,
+ mAnyResAnnotation
+ ));
+ }
+
+ public void testTypes2() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/ActivityType.java:5: Error: Expected resource of type drawable [ResourceType]\n"
+ + " SKI(1),\n"
+ + " ~\n"
+ + "src/test/pkg/ActivityType.java:6: Error: Expected resource of type drawable [ResourceType]\n"
+ + " SNOWBOARD(2);\n"
+ + " ~\n"
+ + "2 errors, 0 warnings\n",
+
+ lintProject(
+ java("src/test/pkg/ActivityType.java", ""
+ + "import android.support.annotation.DrawableRes;\n"
+ + "\n"
+ + "enum ActivityType {\n"
+ + "\n"
+ + " SKI(1),\n"
+ + " SNOWBOARD(2);\n"
+ + "\n"
+ + " private final int mIconResId;\n"
+ + "\n"
+ + " ActivityType(@DrawableRes int iconResId) {\n"
+ + " mIconResId = iconResId;\n"
+ + " }\n"
+ + "}"),
+ copy("src/android/support/annotation/DrawableRes.java.txt",
+ "src/android/support/annotation/DrawableRes.java")));
+ }
+
+ @SuppressWarnings({"MethodMayBeStatic", "ResultOfObjectAllocationIgnored"})
+ public void testConstructor() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/ConstructorTest.java:14: Error: Expected resource of type drawable [ResourceType]\n"
+ + " new ConstructorTest(1, 3);\n"
+ + " ~\n"
+ + "src/test/pkg/ConstructorTest.java:14: Error: Value must be ≥ 5 (was 3) [Range]\n"
+ + " new ConstructorTest(1, 3);\n"
+ + " ~\n"
+ + "src/test/pkg/ConstructorTest.java:19: Error: Method test.pkg.ConstructorTest must be called from the UI thread, currently inferred thread is worker thread [WrongThread]\n"
+ + " new ConstructorTest(res, range);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "3 errors, 0 warnings\n",
+
+ lintProject(
+ java("src/test/pkg/ConstructorTest.java", ""
+ + "package test.pkg;\n"
+ + "\n"
+ + "import android.support.annotation.DrawableRes;\n"
+ + "import android.support.annotation.IntRange;\n"
+ + "import android.support.annotation.UiThread;\n"
+ + "import android.support.annotation.WorkerThread;\n"
+ + "\n"
+ + "public class ConstructorTest {\n"
+ + " @UiThread\n"
+ + " ConstructorTest(@DrawableRes int iconResId, @IntRange(from = 5) int start) {\n"
+ + " }\n"
+ + "\n"
+ + " public void testParameters() {\n"
+ + " new ConstructorTest(1, 3);\n"
+ + " }\n"
+ + "\n"
+ + " @WorkerThread\n"
+ + " public void testMethod(int res, int range) {\n"
+ + " new ConstructorTest(res, range);\n"
+ + " }\n"
+ + "}\n"),
+ mWorkerThreadPermission,
+ mUiThreadPermission,
+ copy("src/android/support/annotation/DrawableRes.java.txt",
+ "src/android/support/annotation/DrawableRes.java"),
+ copy("src/android/support/annotation/IntRange.java.txt",
+ "src/android/support/annotation/IntRange.java")
+ ));
+ }
+
+ public void testColorAsDrawable() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject("src/p1/p2/ColorAsDrawable.java.txt=>src/p1/p2/ColorAsDrawable.java"));
+ }
+
+ public void testCheckResult() throws Exception {
+ if (!SDK_ANNOTATIONS_AVAILABLE) {
+ // Currently only tests @CheckResult on SDK annotations
+ return;
+ }
+ assertEquals(""
+ + "src/test/pkg/CheckPermissions.java:22: Warning: The result of extractAlpha is not used [CheckResult]\n"
+ + " bitmap.extractAlpha(); // WARNING\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/CheckPermissions.java:10: Warning: The result of checkCallingOrSelfPermission is not used; did you mean to call #enforceCallingOrSelfPermission(String,String)? [UseCheckPermission]\n"
+ + " context.checkCallingOrSelfPermission(Manifest.permission.INTERNET); // WRONG\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/CheckPermissions.java:11: Warning: The result of checkPermission is not used; did you mean to call #enforcePermission(String,int,int,String)? [UseCheckPermission]\n"
+ + " context.checkPermission(Manifest.permission.INTERNET, 1, 1);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "0 errors, 3 warnings\n",
+
+ lintProject("src/test/pkg/CheckPermissions.java.txt=>src/test/pkg/CheckPermissions.java"));
+ }
+
+ private final TestFile mPermissionTest = java("src/test/pkg/PermissionTest.java", ""
+ + "package test.pkg;\n"
+ + "\n"
+ + "import android.location.LocationManager;\n"
+ + "\n"
+ + "public class PermissionTest {\n"
+ + " public static void test(LocationManager locationManager, String provider) {\n"
+ + " LocationManager.Location location = locationManager.myMethod(provider);\n"
+ + " }\n"
+ + "}\n");
+
+ private final TestFile mLocationManagerStub = java("src/android/location/LocationManager.java", ""
+ + "package android.location;\n"
+ + "\n"
+ + "import android.support.annotation.RequiresPermission;\n"
+ + "\n"
+ + "import static android.Manifest.permission.ACCESS_COARSE_LOCATION;\n"
+ + "import static android.Manifest.permission.ACCESS_FINE_LOCATION;\n"
+ + "\n"
+ + "@SuppressWarnings(\"UnusedDeclaration\")\n"
+ + "public abstract class LocationManager {\n"
+ + " @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})\n"
+ + " public abstract Location myMethod(String provider);\n"
+ + " public static class Location {\n"
+ + " }\n"
+ + "}\n");
+
+ private final TestFile mComplexLocationManagerStub = java("src/android/location/LocationManager.java", ""
+ + "package android.location;\n"
+ + "\n"
+ + "import android.support.annotation.RequiresPermission;\n"
+ + "\n"
+ + "import static android.Manifest.permission.ACCESS_COARSE_LOCATION;\n"
+ + "import static android.Manifest.permission.ACCESS_FINE_LOCATION;\n"
+ + "import static android.Manifest.permission.BLUETOOTH;\n"
+ + "import static android.Manifest.permission.READ_SMS;\n"
+ + "\n"
+ + "@SuppressWarnings(\"UnusedDeclaration\")\n"
+ + "public abstract class LocationManager {\n"
+ + " @RequiresPermission(\"(\" + ACCESS_FINE_LOCATION + \"|| \" + ACCESS_COARSE_LOCATION + \") && (\" + BLUETOOTH + \" ^ \" + READ_SMS + \")\")\n"
+ + " public abstract Location myMethod(String provider);\n"
+ + " public static class Location {\n"
+ + " }\n"
+ + "}\n");
+
+ private final TestFile mRequirePermissionAnnotation = java("src/android/support/annotation/RequiresPermission.java", ""
+ + "/*\n"
+ + " * Copyright (C) 2015 The Android Open Source Project\n"
+ + " *\n"
+ + " * Licensed under the Apache License, Version 2.0 (the \"License\");\n"
+ + " * you may not use this file except in compliance with the License.\n"
+ + " * You may obtain a copy of the License at\n"
+ + " *\n"
+ + " * http://www.apache.org/licenses/LICENSE-2.0\n"
+ + " *\n"
+ + " * Unless required by applicable law or agreed to in writing, software\n"
+ + " * distributed under the License is distributed on an \"AS IS\" BASIS,\n"
+ + " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
+ + " * See the License for the specific language governing permissions and\n"
+ + " * limitations under the License.\n"
+ + " */\n"
+ + "package android.support.annotation;\n"
+ + "\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.Target;\n"
+ + "\n"
+ + "import static java.lang.annotation.ElementType.*;\n"
+ + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+ + "@Retention(CLASS)\n"
+ + "@Target({METHOD,CONSTRUCTOR,FIELD,PARAMETER,ANNOTATION_TYPE})\n"
+ + "public @interface RequiresPermission {\n"
+ + " String value() default \"\";\n"
+ + " String[] allOf() default {};\n"
+ + " String[] anyOf() default {};\n"
+ + " boolean conditional() default false;\n"
+ + " String notes() default \"\";\n"
+ + " @Target({FIELD,METHOD,PARAMETER})\n"
+ + " @interface Read {\n"
+ + " RequiresPermission value();\n"
+ + " }\n"
+ + " @Target({FIELD,METHOD,PARAMETER})\n"
+ + " @interface Write {\n"
+ + " RequiresPermission value();\n"
+ + " }\n"
+ + "}");
+
+ private final TestFile mUiThreadPermission = java("src/android/support/annotation/UiThread.java", ""
+ + "package android.support.annotation;\n"
+ + "\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.Target;\n"
+ + "\n"
+ + "import static java.lang.annotation.ElementType.CONSTRUCTOR;\n"
+ + "import static java.lang.annotation.ElementType.METHOD;\n"
+ + "import static java.lang.annotation.ElementType.TYPE;\n"
+ + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+ + "\n"
+ + "@Retention(CLASS)\n"
+ + "@Target({METHOD,CONSTRUCTOR,TYPE})\n"
+ + "public @interface UiThread {\n"
+ + "}\n");
+
+ private final TestFile mMainThreadPermission = java("src/android/support/annotation/MainThread.java", ""
+ + "package android.support.annotation;\n"
+ + "\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.Target;\n"
+ + "\n"
+ + "import static java.lang.annotation.ElementType.CONSTRUCTOR;\n"
+ + "import static java.lang.annotation.ElementType.METHOD;\n"
+ + "import static java.lang.annotation.ElementType.TYPE;\n"
+ + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+ + "\n"
+ + "@Retention(CLASS)\n"
+ + "@Target({METHOD,CONSTRUCTOR,TYPE})\n"
+ + "public @interface MainThread {\n"
+ + "}\n");
+
+ private final TestFile mWorkerThreadPermission = java("src/android/support/annotation/WorkerThread.java", ""
+ + "package android.support.annotation;\n"
+ + "\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.Target;\n"
+ + "\n"
+ + "import static java.lang.annotation.ElementType.CONSTRUCTOR;\n"
+ + "import static java.lang.annotation.ElementType.METHOD;\n"
+ + "import static java.lang.annotation.ElementType.TYPE;\n"
+ + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+ + "\n"
+ + "@Retention(CLASS)\n"
+ + "@Target({METHOD,CONSTRUCTOR,TYPE})\n"
+ + "public @interface WorkerThread {\n"
+ + "}\n");
+
+ private TestFile createResAnnotation(String prefix) {
+ return java("src/android/support/annotation/" + prefix + "Res.java", ""
+ + "package android.support.annotation;\n"
+ + "\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.Target;\n"
+ + "\n"
+ + "import static java.lang.annotation.ElementType.*;\n"
+ + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+ + "\n"
+ + "@Retention(CLASS)\n"
+ + "@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})\n"
+ + "public @interface " + prefix + "Res {\n"
+ + "}\n");
+ }
+
+ private final TestFile mColorResAnnotation = createResAnnotation("Color");
+ private final TestFile mStringResAnnotation = createResAnnotation("String");
+ private final TestFile mStyleResAnnotation = createResAnnotation("Style");
+ private final TestFile mAnyResAnnotation = createResAnnotation("Any");
+
+ private final TestFile mColorIntAnnotation = java("src/android/support/annotation/ColorInt.java", ""
+ + "package android.support.annotation;\n"
+ + "\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.Target;\n"
+ + "\n"
+ + "import static java.lang.annotation.ElementType.*;\n"
+ + "import static java.lang.annotation.RetentionPolicy.CLASS;\n"
+ + "\n"
+ + "@Retention(CLASS)\n"
+ + "@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})\n"
+ + "public @interface ColorInt {\n"
+ + "}\n");
+
+ private TestFile getManifestWithPermissions(int targetSdk, String... permissions) {
+ return getManifestWithPermissions(1, targetSdk, permissions);
+ }
+
+ private TestFile getManifestWithPermissions(int minSdk, int targetSdk, String... permissions) {
+ StringBuilder permissionBlock = new StringBuilder();
+ for (String permission : permissions) {
+ permissionBlock.append(" <uses-permission android:name=\"").append(permission)
+ .append("\" />\n");
+ }
+ return xml("AndroidManifest.xml", ""
+ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ + " package=\"foo.bar2\"\n"
+ + " android:versionCode=\"1\"\n"
+ + " android:versionName=\"1.0\" >\n"
+ + "\n"
+ + " <uses-sdk android:minSdkVersion=\"14\" android:targetSdkVersion=\""
+ + targetSdk + "\" />\n"
+ + "\n"
+ + permissionBlock.toString()
+ + "\n"
+ + " <application\n"
+ + " android:icon=\"@drawable/ic_launcher\"\n"
+ + " android:label=\"@string/app_name\" >\n"
+ + " </application>\n"
+ + "\n"
+ + "</manifest>");
+ }
+
+ private TestFile mRevokeTest = java("src/test/pkg/RevokeTest.java", ""
+ + "package test.pkg;\n"
+ + "\n"
+ + "import android.content.Context;\n"
+ + "import android.content.pm.PackageManager;\n"
+ + "import android.location.LocationManager;\n"
+ + "\n"
+ + "import java.security.AccessControlException;\n"
+ + "\n"
+ + "public class RevokeTest {\n"
+ + " public static void test1(LocationManager locationManager, String provider) {\n"
+ + " try {\n"
+ + " // Ok: Security exception caught in one of the branches\n"
+ + " locationManager.myMethod(provider); // OK\n"
+ + " } catch (IllegalArgumentException ignored) {\n"
+ + " } catch (SecurityException ignored) {\n"
+ + " }\n"
+ + "\n"
+ + " try {\n"
+ + " // Ok: Security exception super class caught in one of the branches\n"
+ + " locationManager.myMethod(provider); // OK\n"
+ + " } catch (RuntimeException e) { // includes Security Exception\n"
+ + " }\n"
+ + "\n"
+ + " try {\n"
+ + " // Ok: Caught in outer statement\n"
+ + " try {\n"
+ + " locationManager.myMethod(provider); // OK\n"
+ + " } catch (IllegalArgumentException e) {\n"
+ + " // inner\n"
+ + " }\n"
+ + " } catch (SecurityException ignored) {\n"
+ + " }\n"
+ + "\n"
+ + " try {\n"
+ + " // Ok: Security exception super class caught in one of the branches\n"
+ + " locationManager.myMethod(provider); // OK\n"
+ + " } catch (Exception e) { // includes Security Exception\n"
+ + " }\n"
+ + "\n"
+ + " // NOT OK: Catching security exception subclass (except for dedicated ones?)\n"
+ + "\n"
+ + " try {\n"
+ + " // Error: catching security exception, but not all of them\n"
+ + " locationManager.myMethod(provider); // ERROR\n"
+ + " } catch (AccessControlException e) { // security exception but specific one\n"
+ + " }\n"
+ + " }\n"
+ + "\n"
+ + " public static void test2(LocationManager locationManager, String provider) {\n"
+ + " locationManager.myMethod(provider); // ERROR: not caught\n"
+ + " }\n"
+ + "\n"
+ + " public static void test3(LocationManager locationManager, String provider)\n"
+ + " throws IllegalArgumentException {\n"
+ + " locationManager.myMethod(provider); // ERROR: not caught by right type\n"
+ + " }\n"
+ + "\n"
+ + " public static void test4(LocationManager locationManager, String provider)\n"
+ + " throws AccessControlException { // Security exception but specific one\n"
+ + " locationManager.myMethod(provider); // ERROR\n"
+ + " }\n"
+ + "\n"
+ + " public static void test5(LocationManager locationManager, String provider)\n"
+ + " throws SecurityException {\n"
+ + " locationManager.myMethod(provider); // OK\n"
+ + " }\n"
+ + "\n"
+ + " public static void test6(LocationManager locationManager, String provider)\n"
+ + " throws Exception { // includes Security Exception\n"
+ + " locationManager.myMethod(provider); // OK\n"
+ + " }\n"
+ + "\n"
+ + " public static void test7(LocationManager locationManager, String provider, Context context)\n"
+ + " throws IllegalArgumentException {\n"
+ + " if (context.getPackageManager().checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, context.getPackageName()) != PackageManager.PERMISSION_GRANTED) {\n"
+ + " return;\n"
+ + " }\n"
+ + " locationManager.myMethod(provider); // OK: permission checked\n"
+ + " }\n"
+ + "\n"
+ + "}\n");
+
+ public void testMissingPermissions() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/PermissionTest.java:7: Error: Missing permissions required by LocationManager.myMethod: android.permission.ACCESS_FINE_LOCATION or android.permission.ACCESS_COARSE_LOCATION [MissingPermission]\n"
+ + " LocationManager.Location location = locationManager.myMethod(provider);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+ lintProject(
+ getManifestWithPermissions(14),
+ mPermissionTest,
+ mLocationManagerStub,
+ mRequirePermissionAnnotation));
+ }
+
+ public void testHasPermission() throws Exception {
+ assertEquals("No warnings.",
+ lintProject(
+ getManifestWithPermissions(14, "android.permission.ACCESS_FINE_LOCATION"),
+ mPermissionTest,
+ mLocationManagerStub,
+ mRequirePermissionAnnotation));
+ }
+
+ public void testRevokePermissions() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/RevokeTest.java:44: Error: Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with checkPermission) or handle a potential SecurityException [MissingPermission]\n"
+ + " locationManager.myMethod(provider); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/RevokeTest.java:50: Error: Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with checkPermission) or handle a potential SecurityException [MissingPermission]\n"
+ + " locationManager.myMethod(provider); // ERROR: not caught\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/RevokeTest.java:55: Error: Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with checkPermission) or handle a potential SecurityException [MissingPermission]\n"
+ + " locationManager.myMethod(provider); // ERROR: not caught by right type\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/RevokeTest.java:60: Error: Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with checkPermission) or handle a potential SecurityException [MissingPermission]\n"
+ + " locationManager.myMethod(provider); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "4 errors, 0 warnings\n",
+ lintProject(
+ getManifestWithPermissions(23, "android.permission.ACCESS_FINE_LOCATION"),
+ mLocationManagerStub,
+ mRequirePermissionAnnotation,
+ mRevokeTest
+ ));
+ }
+
+ public void testImpliedPermissions() throws Exception {
+ // Regression test for
+ // https://code.google.com/p/android/issues/detail?id=177381
+ assertEquals(""
+ + "src/test/pkg/PermissionTest2.java:11: Error: Missing permissions required by PermissionTest2.method1: my.permission.PERM2 [MissingPermission]\n"
+ + " method1(); // ERROR\n"
+ + " ~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+ lintProject(
+ getManifestWithPermissions(14, 14, "android.permission.ACCESS_FINE_LOCATION"),
+ java("src/test/pkg/PermissionTest2.java", ""
+ + "package test.pkg;\n"
+ + "import android.support.annotation.RequiresPermission;\n"
+ + "\n"
+ + "public class PermissionTest2 {\n"
+ + " @RequiresPermission(allOf = {\"my.permission.PERM1\",\"my.permission.PERM2\"})\n"
+ + " public void method1() {\n"
+ + " }\n"
+ + "\n"
+ + " @RequiresPermission(\"my.permission.PERM1\")\n"
+ + " public void method2() {\n"
+ + " method1(); // ERROR\n"
+ + " }\n"
+ + "\n"
+ + " @RequiresPermission(allOf = {\"my.permission.PERM1\",\"my.permission.PERM2\"})\n"
+ + " public void method3() {\n"
+ + " // The above @RequiresPermission implies that we are holding these\n"
+ + " // permissions here, so the call to method1() should not be flagged as\n"
+ + " // missing a permission!\n"
+ + " method1(); // OK\n"
+ + " }\n"
+ + "}\n"),
+ mRequirePermissionAnnotation
+ ));
+ }
+
+ public void testRevokePermissionsPre23() throws Exception {
+ assertEquals("No warnings.",
+ lintProject(
+ getManifestWithPermissions(14, "android.permission.ACCESS_FINE_LOCATION"),
+ mLocationManagerStub,
+ mRequirePermissionAnnotation,
+ mRevokeTest
+ ));
+ }
+
+ public void testComplexPermission1() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/PermissionTest.java:7: Error: Missing permissions required by LocationManager.myMethod: android.permission.BLUETOOTH xor android.permission.READ_SMS [MissingPermission]\n"
+ + " LocationManager.Location location = locationManager.myMethod(provider);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+ lintProject(
+ getManifestWithPermissions(14,
+ "android.permission.ACCESS_FINE_LOCATION"),
+ mPermissionTest,
+ mComplexLocationManagerStub,
+ mRequirePermissionAnnotation));
+ }
+
+ public void testComplexPermission2() throws Exception {
+ assertEquals("No warnings.",
+ lintProject(
+ getManifestWithPermissions(14,
+ "android.permission.ACCESS_FINE_LOCATION",
+ "android.permission.BLUETOOTH"),
+ mPermissionTest,
+ mComplexLocationManagerStub,
+ mRequirePermissionAnnotation));
+ }
+
+ public void testComplexPermission3() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/PermissionTest.java:7: Error: Missing permissions required by LocationManager.myMethod: android.permission.BLUETOOTH xor android.permission.READ_SMS [MissingPermission]\n"
+ + " LocationManager.Location location = locationManager.myMethod(provider);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+ lintProject(
+ getManifestWithPermissions(14,
+ "android.permission.ACCESS_FINE_LOCATION",
+ "android.permission.BLUETOOTH",
+ "android.permission.READ_SMS"),
+ mPermissionTest,
+ mComplexLocationManagerStub,
+ mRequirePermissionAnnotation));
+ }
+
+ public void testPermissionAnnotation() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/LocationManager.java:24: Error: Missing permissions required by LocationManager.getLastKnownLocation: android.permission.ACCESS_FINE_LOCATION or android.permission.ACCESS_COARSE_LOCATION [MissingPermission]\n"
+ + " Location location = manager.getLastKnownLocation(\"provider\");\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
+ lintProject(
+ java("src/test/pkg/LocationManager.java", ""
+ + "package test.pkg;\n"
+ + "\n"
+ + "import android.support.annotation.RequiresPermission;\n"
+ + "\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.RetentionPolicy;\n"
+ + "\n"
+ + "import static android.Manifest.permission.ACCESS_COARSE_LOCATION;\n"
+ + "import static android.Manifest.permission.ACCESS_FINE_LOCATION;\n"
+ + "\n"
+ + "@SuppressWarnings(\"UnusedDeclaration\")\n"
+ + "public abstract class LocationManager {\n"
+ + " @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})\n"
+ + " @Retention(RetentionPolicy.SOURCE)\n"
+ + " @interface AnyLocationPermission {\n"
+ + " }\n"
+ + "\n"
+ + " @AnyLocationPermission\n"
+ + " public abstract Location getLastKnownLocation(String provider);\n"
+ + " public static class Location {\n"
+ + " }\n"
+ + " \n"
+ + " public static void test(LocationManager manager) {\n"
+ + " Location location = manager.getLastKnownLocation(\"provider\");\n"
+ + " }\n"
+ + "}\n"),
+ mRequirePermissionAnnotation));
+ }
+
+ public void testThreading() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/ThreadTest.java:15: Error: Method onPreExecute must be called from the main thread, currently inferred thread is worker thread [WrongThread]\n"
+ + " onPreExecute(); // ERROR\n"
+ + " ~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ThreadTest.java:16: Error: Method paint must be called from the UI thread, currently inferred thread is worker thread [WrongThread]\n"
+ + " view.paint(); // ERROR\n"
+ + " ~~~~~~~~~~~~\n"
+ + "src/test/pkg/ThreadTest.java:22: Error: Method publishProgress must be called from the worker thread, currently inferred thread is main thread [WrongThread]\n"
+ + " publishProgress(); // ERROR\n"
+ + " ~~~~~~~~~~~~~~~~~\n"
+ + "3 errors, 0 warnings\n",
+
+ lintProject(
+ java("src/test/pkg/ThreadTest.java", ""
+ + "package test.pkg;\n"
+ + "\n"
+ + "import android.support.annotation.MainThread;\n"
+ + "import android.support.annotation.UiThread;\n"
+ + "import android.support.annotation.WorkerThread;\n"
+ + "\n"
+ + "public class ThreadTest {\n"
+ + " public static AsyncTask testTask() {\n"
+ + "\n"
+ + " return new AsyncTask() {\n"
+ + " final CustomView view = new CustomView();\n"
+ + "\n"
+ + " @Override\n"
+ + " protected void doInBackground(Object... params) {\n"
+ + " onPreExecute(); // ERROR\n"
+ + " view.paint(); // ERROR\n"
+ + " publishProgress(); // OK\n"
+ + " }\n"
+ + "\n"
+ + " @Override\n"
+ + " protected void onPreExecute() {\n"
+ + " publishProgress(); // ERROR\n"
+ + " onProgressUpdate(); // OK\n"
+ + " }\n"
+ + " };\n"
+ + " }\n"
+ + "\n"
+ + " @UiThread\n"
+ + " public static class View {\n"
+ + " public void paint() {\n"
+ + " }\n"
+ + " }\n"
+ + "\n"
+ + " public static class CustomView extends View {\n"
+ + " @Override public void paint() {\n"
+ + " }\n"
+ + " }\n"
+ + "\n"
+ + " public abstract static class AsyncTask {\n"
+ + " @WorkerThread\n"
+ + " protected abstract void doInBackground(Object... params);\n"
+ + "\n"
+ + " @MainThread\n"
+ + " protected void onPreExecute() {\n"
+ + " }\n"
+ + "\n"
+ + " @MainThread\n"
+ + " protected void onProgressUpdate(Object... values) {\n"
+ + " }\n"
+ + "\n"
+ + " @WorkerThread\n"
+ + " protected final void publishProgress(Object... values) {\n"
+ + " }\n"
+ + " }\n"
+ + "}\n"),
+ mUiThreadPermission,
+ mMainThreadPermission,
+ mWorkerThreadPermission));
+ }
+
+ public void testIntentPermission() throws Exception {
+ if (SDK_ANNOTATIONS_AVAILABLE) {
+ TestLintClient client = createClient();
+ ExternalAnnotationRepository repository = ExternalAnnotationRepository.get(client);
+ ResolvedMethod method = ExternalAnnotationRepositoryTest.createMethod(
+ "android.content.Context", "void", "startActivity",
+ "android.content.Intent");
+ ResolvedAnnotation a = repository.getAnnotation(method, 0, PERMISSION_ANNOTATION);
+ if (a == null) {
+ // Running tests from outside the IDE (where it can't find the
+ // bundled up to date annotations in tools/adt/idea/android/annotations)
+ // and we have the annotations.zip file available in platform-tools,
+ // but its contents are old (it's from Android M Preview 1, not including
+ // the new intent-annotation data); skip this test for now.
+ return;
+ }
+ }
+
+ assertEquals(!SDK_ANNOTATIONS_AVAILABLE ? "" // Most of the intent/content provider checks are based on framework annotations
+ + "src/test/pkg/ActionTest.java:86: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+ + " myStartActivity(\"\", null, new Intent(ACTION_CALL));\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:87: Error: Missing permissions required to read ActionTest.BOOKMARKS_URI: com.android.browser.permission.READ_HISTORY_BOOKMARKS [MissingPermission]\n"
+ + " myReadResolverMethod(\"\", BOOKMARKS_URI);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:88: Error: Missing permissions required to read ActionTest.BOOKMARKS_URI: com.android.browser.permission.READ_HISTORY_BOOKMARKS [MissingPermission]\n"
+ + " myWriteResolverMethod(BOOKMARKS_URI);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "3 errors, 0 warnings\n" : ""
+
+ + "src/test/pkg/ActionTest.java:36: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+ + " activity.startActivity(intent);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:42: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+ + " activity.startActivity(intent);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:43: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+ + " activity.startActivity(intent, null);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:44: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+ + " activity.startActivityForResult(intent, 0);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:45: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+ + " activity.startActivityFromChild(activity, intent, 0);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:46: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+ + " activity.startActivityIfNeeded(intent, 0);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:47: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+ + " activity.startActivityFromFragment(null, intent, 0);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:48: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+ + " activity.startNextMatchingActivity(intent);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:54: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+ + " context.sendBroadcast(intent);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:55: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+ + " context.sendBroadcast(intent, \"\");\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:56: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+ + " context.sendBroadcastAsUser(intent, null);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:57: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+ + " context.sendStickyBroadcast(intent);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:62: Error: Missing permissions required to read ActionTest.BOOKMARKS_URI: com.android.browser.permission.READ_HISTORY_BOOKMARKS [MissingPermission]\n"
+ + " resolver.query(BOOKMARKS_URI, null, null, null, null);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:65: Error: Missing permissions required to write ActionTest.BOOKMARKS_URI: com.android.browser.permission.WRITE_HISTORY_BOOKMARKS [MissingPermission]\n"
+ + " resolver.insert(BOOKMARKS_URI, null);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:66: Error: Missing permissions required to write ActionTest.BOOKMARKS_URI: com.android.browser.permission.WRITE_HISTORY_BOOKMARKS [MissingPermission]\n"
+ + " resolver.delete(BOOKMARKS_URI, null, null);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:67: Error: Missing permissions required to write ActionTest.BOOKMARKS_URI: com.android.browser.permission.WRITE_HISTORY_BOOKMARKS [MissingPermission]\n"
+ + " resolver.update(BOOKMARKS_URI, null, null, null);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:86: Error: Missing permissions required by intent ActionTest.ACTION_CALL: android.permission.CALL_PHONE [MissingPermission]\n"
+ + " myStartActivity(\"\", null, new Intent(ACTION_CALL));\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:87: Error: Missing permissions required to read ActionTest.BOOKMARKS_URI: com.android.browser.permission.READ_HISTORY_BOOKMARKS [MissingPermission]\n"
+ + " myReadResolverMethod(\"\", BOOKMARKS_URI);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "src/test/pkg/ActionTest.java:88: Error: Missing permissions required to read ActionTest.BOOKMARKS_URI: com.android.browser.permission.READ_HISTORY_BOOKMARKS [MissingPermission]\n"
+ + " myWriteResolverMethod(BOOKMARKS_URI);\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "19 errors, 0 warnings\n",
+
+ lintProject(
+ getManifestWithPermissions(14, 23),
+ java("src/test/pkg/ActionTest.java", ""
+ + "package test.pkg;\n"
+ + "\n"
+ + "import android.Manifest;\n"
+ + "import android.app.Activity;\n"
+ + "import android.content.ContentResolver;\n"
+ + "import android.content.Context;\n"
+ + "import android.content.Intent;\n"
+ + "import android.database.Cursor;\n"
+ + "import android.net.Uri;\n"
+ + "import android.support.annotation.RequiresPermission;\n"
+ + "\n"
+ //+ "import static android.Manifest.permission.READ_HISTORY_BOOKMARKS;\n"
+ //+ "import static android.Manifest.permission.WRITE_HISTORY_BOOKMARKS;\n"
+ + "\n"
+ + "@SuppressWarnings({\"deprecation\", \"unused\"})\n"
+ + "public class ActionTest {\n"
+ + " public static final String READ_HISTORY_BOOKMARKS=\"com.android.browser.permission.READ_HISTORY_BOOKMARKS\";\n"
+ + " public static final String WRITE_HISTORY_BOOKMARKS=\"com.android.browser.permission.WRITE_HISTORY_BOOKMARKS\";\n"
+ + " @RequiresPermission(Manifest.permission.CALL_PHONE)\n"
+ + " public static final String ACTION_CALL = \"android.intent.action.CALL\";\n"
+ + "\n"
+ + " @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))\n"
+ + " @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))\n"
+ + " public static final Uri BOOKMARKS_URI = Uri.parse(\"content://browser/bookmarks\");\n"
+ + "\n"
+ + " public static final Uri COMBINED_URI = Uri.withAppendedPath(BOOKMARKS_URI, \"bookmarks\");\n"
+ + " \n"
+ + " public static void activities1(Activity activity) {\n"
+ + " Intent intent = new Intent(Intent.ACTION_CALL);\n"
+ + " intent.setData(Uri.parse(\"tel:1234567890\"));\n"
+ + " // This one will only be flagged if we have framework metadata on Intent.ACTION_CALL\n"
+ + " activity.startActivity(intent);\n"
+ + " }\n"
+ + "\n"
+ + " public static void activities2(Activity activity) {\n"
+ + " Intent intent = new Intent(ACTION_CALL);\n"
+ + " intent.setData(Uri.parse(\"tel:1234567890\"));\n"
+ + " activity.startActivity(intent);\n"
+ + " }\n"
+ + " public static void activities3(Activity activity) {\n"
+ + " Intent intent;\n"
+ + " intent = new Intent(ACTION_CALL);\n"
+ + " intent.setData(Uri.parse(\"tel:1234567890\"));\n"
+ + " activity.startActivity(intent);\n"
+ + " activity.startActivity(intent, null);\n"
+ + " activity.startActivityForResult(intent, 0);\n"
+ + " activity.startActivityFromChild(activity, intent, 0);\n"
+ + " activity.startActivityIfNeeded(intent, 0);\n"
+ + " activity.startActivityFromFragment(null, intent, 0);\n"
+ + " activity.startNextMatchingActivity(intent);\n"
+ + " }\n"
+ + "\n"
+ + " public static void broadcasts(Context context) {\n"
+ + " Intent intent;\n"
+ + " intent = new Intent(ACTION_CALL);\n"
+ + " context.sendBroadcast(intent);\n"
+ + " context.sendBroadcast(intent, \"\");\n"
+ + " context.sendBroadcastAsUser(intent, null);\n"
+ + " context.sendStickyBroadcast(intent);\n"
+ + " }\n"
+ + "\n"
+ + " public static void contentResolvers(Context context, ContentResolver resolver) {\n"
+ + " // read\n"
+ + " resolver.query(BOOKMARKS_URI, null, null, null, null);\n"
+ + "\n"
+ + " // write\n"
+ + " resolver.insert(BOOKMARKS_URI, null);\n"
+ + " resolver.delete(BOOKMARKS_URI, null, null);\n"
+ + " resolver.update(BOOKMARKS_URI, null, null, null);\n"
+ + "\n"
+ + " // Framework (external) annotation\n"
+ + "//REMOVED resolver.query(android.provider.Browser.BOOKMARKS_URI, null, null, null, null);\n"
+ + "\n"
+ + " // TODO: Look for more complex URI manipulations\n"
+ + " }\n"
+ + "\n"
+ + " public static void myStartActivity(String s1, String s2, \n"
+ + " @RequiresPermission Intent intent) {\n"
+ + " }\n"
+ + "\n"
+ + " public static void myReadResolverMethod(String s1, @RequiresPermission.Read(@RequiresPermission) Uri uri) {\n"
+ + " }\n"
+ + "\n"
+ + " public static void myWriteResolverMethod(@RequiresPermission.Read(@RequiresPermission) Uri uri) {\n"
+ + " }\n"
+ + " \n"
+ + " public static void testCustomMethods() {\n"
+ + " myStartActivity(\"\", null, new Intent(ACTION_CALL));\n"
+ + " myReadResolverMethod(\"\", BOOKMARKS_URI);\n"
+ + " myWriteResolverMethod(BOOKMARKS_URI);\n"
+ + " }\n"
+ + "}\n"),
+ mRequirePermissionAnnotation
+ ));
+ }
+
+ public void testCombinedIntDefAndIntRange() throws Exception {
+ assertEquals(""
+ + "src/test/pkg/X.java:27: Error: Must be one of: X.LENGTH_INDEFINITE, X.LENGTH_SHORT, X.LENGTH_LONG [WrongConstant]\n"
+ + " setDuration(UNRELATED); /// ERROR: Not right intdef, even if it's in the right number range\n"
+ + " ~~~~~~~~~\n"
+ + "src/test/pkg/X.java:28: Error: Must be one of: X.LENGTH_INDEFINITE, X.LENGTH_SHORT, X.LENGTH_LONG or value must be ≥ 10 (was -5) [WrongConstant]\n"
+ + " setDuration(-5); // ERROR (not right int def or value\n"
+ + " ~~\n"
+ + "src/test/pkg/X.java:29: Error: Must be one of: X.LENGTH_INDEFINITE, X.LENGTH_SHORT, X.LENGTH_LONG or value must be ≥ 10 (was 8) [WrongConstant]\n"
+ + " setDuration(8); // ERROR (not matching number range)\n"
+ + " ~\n"
+ + "3 errors, 0 warnings\n",
+ lintProject(
+ getManifestWithPermissions(14, 23),
+ java("src/test/pkg/X.java", ""
+ + "\n"
+ + "package test.pkg;\n"
+ + "\n"
+ + "import android.support.annotation.IntDef;\n"
+ + "import android.support.annotation.IntRange;\n"
+ + "\n"
+ + "import java.lang.annotation.Retention;\n"
+ + "import java.lang.annotation.RetentionPolicy;\n"
+ + "\n"
+ + "@SuppressWarnings({\"UnusedParameters\", \"unused\", \"SpellCheckingInspection\"})\n"
+ + "public class X {\n"
+ + "\n"
+ + " public static final int UNRELATED = 500;\n"
+ + "\n"
+ + " @IntDef({LENGTH_INDEFINITE, LENGTH_SHORT, LENGTH_LONG})\n"
+ + " @IntRange(from = 10)\n"
+ + " @Retention(RetentionPolicy.SOURCE)\n"
+ + " public @interface Duration {}\n"
+ + "\n"
+ + " public static final int LENGTH_INDEFINITE = -2;\n"
+ + " public static final int LENGTH_SHORT = -1;\n"
+ + " public static final int LENGTH_LONG = 0;\n"
+ + " public void setDuration(@Duration int duration) {\n"
+ + " }\n"
+ + "\n"
+ + " public void test() {\n"
+ + " setDuration(UNRELATED); /// ERROR: Not right intdef, even if it's in the right number range\n"
+ + " setDuration(-5); // ERROR (not right int def or value\n"
+ + " setDuration(8); // ERROR (not matching number range)\n"
+ + " setDuration(8000); // OK (@IntRange applies)\n"
+ + " setDuration(LENGTH_INDEFINITE); // OK (@IntDef)\n"
+ + " setDuration(LENGTH_LONG); // OK (@IntDef)\n"
+ + " setDuration(LENGTH_SHORT); // OK (@IntDef)\n"
+ + " }\n"
+ + "}\n"),
+ copy("src/android/support/annotation/IntDef.java.txt", "src/android/support/annotation/IntDef.java"),
+ copy("src/android/support/annotation/IntRange.java.txt", "src/android/support/annotation/IntRange.java")
+ ));
+ }
+
+
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/SystemPermissionsDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SystemPermissionsDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/SystemPermissionsDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/SystemPermissionsDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TextFieldDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TextFieldDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TextFieldDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TextFieldDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TextViewDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TextViewDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TextViewDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TextViewDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TitleDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TitleDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TitleDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TitleDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ToastDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ToastDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ToastDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ToastDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TooManyViewsDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TooManyViewsDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TooManyViewsDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TooManyViewsDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TranslationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TranslationDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TranslationDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TranslationDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TypoDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TypoDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TypoDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TypoDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TypoLookupTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TypoLookupTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TypoLookupTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TypoLookupTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TypographyDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TypographyDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/TypographyDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/TypographyDetectorTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UnusedResourceDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UnusedResourceDetectorTest.java
new file mode 100644
index 0000000..673685e
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UnusedResourceDetectorTest.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Issue;
+
+import java.io.File;
+import java.util.Arrays;
+
+@SuppressWarnings("javadoc")
+public class UnusedResourceDetectorTest extends AbstractCheckTest {
+ private boolean mEnableIds = false;
+
+ @Override
+ protected Detector getDetector() {
+ return new UnusedResourceDetector();
+ }
+
+ @Override
+ protected boolean isEnabled(Issue issue) {
+ if (issue == UnusedResourceDetector.ISSUE_IDS) {
+ return mEnableIds;
+ } else {
+ return true;
+ }
+ }
+
+ public void testUnused() throws Exception {
+ mEnableIds = false;
+ assertEquals(
+ "res/layout/accessibility.xml: Warning: The resource R.layout.accessibility appears to be unused [UnusedResources]\n" +
+ "res/layout/main.xml: Warning: The resource R.layout.main appears to be unused [UnusedResources]\n" +
+ "res/layout/other.xml: Warning: The resource R.layout.other appears to be unused [UnusedResources]\n" +
+ "res/values/strings2.xml:3: Warning: The resource R.string.hello appears to be unused [UnusedResources]\n" +
+ " <string name=\"hello\">Hello</string>\n" +
+ " ~~~~~~~~~~~~\n" +
+ "0 errors, 4 warnings\n" +
+ "",
+
+ lintProject(
+ "res/values/strings2.xml",
+ "res/layout/layout1.xml=>res/layout/main.xml",
+ "res/layout/layout1.xml=>res/layout/other.xml",
+
+ // Rename .txt files to .java
+ "src/my/pkg/Test.java.txt=>src/my/pkg/Test.java",
+ "gen/my/pkg/R.java.txt=>gen/my/pkg/R.java",
+ "AndroidManifest.xml",
+ "res/layout/accessibility.xml"));
+ }
+
+ public void testUnusedIds() throws Exception {
+ mEnableIds = true;
+
+ assertEquals(
+ "res/layout/accessibility.xml: Warning: The resource R.layout.accessibility appears to be unused [UnusedResources]\n" +
+ "Warning: The resource R.layout.main appears to be unused [UnusedResources]\n" +
+ "Warning: The resource R.layout.other appears to be unused [UnusedResources]\n" +
+ "Warning: The resource R.string.hello appears to be unused [UnusedResources]\n" +
+ "res/layout/accessibility.xml:2: Warning: The resource R.id.newlinear appears to be unused [UnusedIds]\n" +
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" android:id=\"@+id/newlinear\" android:orientation=\"vertical\" android:layout_width=\"match_parent\" android:layout_height=\"match_parent\">\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "res/layout/accessibility.xml:3: Warning: The resource R.id.button1 appears to be unused [UnusedIds]\n" +
+ " <Button android:text=\"Button\" android:id=\"@+id/button1\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\"></Button>\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "res/layout/accessibility.xml:4: Warning: The resource R.id.android_logo appears to be unused [UnusedIds]\n" +
+ " <ImageView android:id=\"@+id/android_logo\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:src=\"@drawable/android_button\" android:focusable=\"false\" android:clickable=\"false\" android:layout_weight=\"1.0\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "res/layout/accessibility.xml:5: Warning: The resource R.id.android_logo2 appears to be unused [UnusedIds]\n" +
+ " <ImageButton android:importantForAccessibility=\"yes\" android:id=\"@+id/android_logo2\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:src=\"@drawable/android_button\" android:focusable=\"false\" android:clickable=\"false\" android:layout_weight=\"1.0\" />\n" +
+ " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
+ "Warning: The resource R.id.imageView1 appears to be unused [UnusedIds]\n" +
+ "Warning: The resource R.id.include1 appears to be unused [UnusedIds]\n" +
+ "Warning: The resource R.id.linearLayout2 appears to be unused [UnusedIds]\n" +
+ "0 errors, 11 warnings\n",
+
+ lintProject(
+ // Rename .txt files to .java
+ "src/my/pkg/Test.java.txt=>src/my/pkg/Test.java",
+ "gen/my/pkg/R.java.txt=>gen/my/pkg/R.java",
+ "AndroidManifest.xml",
+ "res/layout/accessibility.xml"));
+ }
+
+ public void testArrayReference() throws Exception {
+ assertEquals(
+ "res/values/arrayusage.xml:3: Warning: The resource R.array.my_array appears to be unused [UnusedResources]\n" +
+ "<string-array name=\"my_array\">\n" +
+ " ~~~~~~~~~~~~~~~\n" +
+ "0 errors, 1 warnings\n" +
+ "",
+
+ lintProject(
+ "AndroidManifest.xml",
+ "res/values/arrayusage.xml"));
+ }
+
+ public void testAttrs() throws Exception {
+ assertEquals(
+ "res/layout/customattrlayout.xml: Warning: The resource R.layout.customattrlayout appears to be unused [UnusedResources]\n" +
+ "0 errors, 1 warnings\n" +
+ "",
+
+ lintProject(
+ "res/values/customattr.xml",
+ "res/layout/customattrlayout.xml",
+ "unusedR.java.txt=>gen/my/pkg/R.java",
+ "AndroidManifest.xml"));
+ }
+
+ public void testMultiProjectIgnoreLibraries() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ // Master project
+ "multiproject/main-manifest.xml=>AndroidManifest.xml",
+ "multiproject/main.properties=>project.properties",
+ "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java",
+
+ // Library project
+ "multiproject/library-manifest.xml=>../LibraryProject/AndroidManifest.xml",
+ "multiproject/library.properties=>../LibraryProject/project.properties",
+ "multiproject/LibraryCode.java.txt=>../LibraryProject/src/foo/library/LibraryCode.java",
+ "multiproject/strings.xml=>../LibraryProject/res/values/strings.xml"
+ ));
+ }
+
+ public void testMultiProject() throws Exception {
+ File master = getProjectDir("MasterProject",
+ // Master project
+ "multiproject/main-manifest.xml=>AndroidManifest.xml",
+ "multiproject/main.properties=>project.properties",
+ "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
+ );
+ File library = getProjectDir("LibraryProject",
+ // Library project
+ "multiproject/library-manifest.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>project.properties",
+ "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
+ "multiproject/strings.xml=>res/values/strings.xml"
+ );
+ assertEquals(
+ // string1 is defined and used in the library project
+ // string2 is defined in the library project and used in the master project
+ // string3 is defined in the library project and not used anywhere
+ "LibraryProject/res/values/strings.xml:7: Warning: The resource R.string.string3 appears to be unused [UnusedResources]\n" +
+ " <string name=\"string3\">String 3</string>\n" +
+ " ~~~~~~~~~~~~~~\n" +
+ "0 errors, 1 warnings\n",
+
+ checkLint(Arrays.asList(master, library)).replace("/TESTROOT/", ""));
+ }
+
+ public void testFqcnReference() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "res/layout/layout1.xml=>res/layout/main.xml",
+ "src/test/pkg/UnusedReference.java.txt=>src/test/pkg/UnusedReference.java",
+ "AndroidManifest.xml"));
+ }
+
+ public void testIgnoreXmlDrawable() throws Exception {
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "res/drawable/ic_menu_help.xml",
+ "gen/my/pkg/R2.java.txt=>gen/my/pkg/R.java"
+ ));
+ }
+
+ public void testPlurals() throws Exception {
+ assertEquals(
+ "res/values/plurals.xml:3: Warning: The resource R.plurals.my_plural appears to be unused [UnusedResources]\n" +
+ " <plurals name=\"my_plural\">\n" +
+ " ~~~~~~~~~~~~~~~~\n" +
+ "0 errors, 1 warnings\n" +
+ "",
+
+ lintProject(
+ "res/values/strings4.xml",
+ "res/values/plurals.xml",
+ "AndroidManifest.xml"));
+ }
+
+ public void testNoMerging() throws Exception {
+ // http://code.google.com/p/android/issues/detail?id=36952
+
+ File master = getProjectDir("MasterProject",
+ // Master project
+ "multiproject/main-manifest.xml=>AndroidManifest.xml",
+ "multiproject/main.properties=>project.properties",
+ "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
+ );
+ File library = getProjectDir("LibraryProject",
+ // Library project
+ "multiproject/library-manifest.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>project.properties",
+ "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
+ "multiproject/strings.xml=>res/values/strings.xml"
+ );
+ assertEquals(
+ // The strings are all referenced in the library project's manifest file
+ // which in this project is merged in
+ "LibraryProject/res/values/strings.xml:7: Warning: The resource R.string.string3 appears to be unused [UnusedResources]\n" +
+ " <string name=\"string3\">String 3</string>\n" +
+ " ~~~~~~~~~~~~~~\n" +
+ "0 errors, 1 warnings\n",
+
+ checkLint(Arrays.asList(master, library)).replace("/TESTROOT/", ""));
+ }
+
+ public void testLibraryMerging() throws Exception {
+ // http://code.google.com/p/android/issues/detail?id=36952
+ File master = getProjectDir("MasterProject",
+ // Master project
+ "multiproject/main-manifest.xml=>AndroidManifest.xml",
+ "multiproject/main-merge.properties=>project.properties",
+ "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
+ );
+ File library = getProjectDir("LibraryProject",
+ // Library project
+ "multiproject/library-manifest.xml=>AndroidManifest.xml",
+ "multiproject/library.properties=>project.properties",
+ "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
+ "multiproject/strings.xml=>res/values/strings.xml"
+ );
+ assertEquals(
+ // The strings are all referenced in the library project's manifest file
+ // which in this project is merged in
+ "No warnings.",
+
+ checkLint(Arrays.asList(master, library)));
+ }
+
+ public void testCornerCase() throws Exception {
+ // See http://code.google.com/p/projectlombok/issues/detail?id=415
+ mEnableIds = true;
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "src/test/pkg/Foo.java.txt=>src/test/pkg/Foo.java",
+ "AndroidManifest.xml"));
+ }
+
+ public void testAnalytics() throws Exception {
+ // See http://code.google.com/p/android/issues/detail?id=42565
+ mEnableIds = false;
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "res/values/analytics.xml"
+ ));
+ }
+
+ public void testIntegers() throws Exception {
+ // See https://code.google.com/p/android/issues/detail?id=53995
+ mEnableIds = true;
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "res/values/integers.xml",
+ "res/anim/slide_in_out.xml"
+ ));
+ }
+
+ public void testIntegerArrays() throws Exception {
+ // See http://code.google.com/p/android/issues/detail?id=59761
+ mEnableIds = false;
+ assertEquals(
+ "No warnings.",
+ lintProject("res/values/integer_arrays.xml=>res/values/integer_arrays.xml"));
+ }
+
+ public void testUnitTestReferences() throws Exception {
+ // Make sure that we pick up references in unit tests as well
+ // Regression test for
+ // https://code.google.com/p/android/issues/detail?id=79066
+ mEnableIds = false;
+ //noinspection ClassNameDiffersFromFileName
+ assertEquals("No warnings.",
+
+ lintProject(
+ copy("res/values/strings2.xml"),
+ copy("res/layout/layout1.xml", "res/layout/main.xml"),
+ copy("res/layout/layout1.xml", "res/layout/other.xml"),
+
+ copy("src/my/pkg/Test.java.txt", "src/my/pkg/Test.java"),
+ copy("gen/my/pkg/R.java.txt", "gen/my/pkg/R.java"),
+ copy("AndroidManifest.xml"),
+ copy("res/layout/accessibility.xml"),
+
+ // Add unit test source which references resources which would otherwise
+ // be marked as unused
+ java("test/my/pkg/MyTest.java", ""
+ + "package my.pkg;\n"
+ + "class MyTest {\n"
+ + " public void test() {\n"
+ + " System.out.println(R.layout.accessibility);\n"
+ + " System.out.println(R.layout.main);\n"
+ + " System.out.println(R.layout.other);\n"
+ + " System.out.println(R.string.hello);\n"
+ + " }\n"
+ + "}\n")
+ ));
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/UseCompoundDrawableDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UseCompoundDrawableDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/UseCompoundDrawableDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UseCompoundDrawableDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/UselessViewDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UselessViewDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/UselessViewDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/UselessViewDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/Utf8DetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/Utf8DetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/Utf8DetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/Utf8DetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ViewConstructorDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewConstructorDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ViewConstructorDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewConstructorDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ViewHolderDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewHolderDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ViewHolderDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewHolderDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ViewTagDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewTagDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ViewTagDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewTagDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/ViewTypeDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewTypeDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/ViewTypeDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/ViewTypeDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/WakelockDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WakelockDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/WakelockDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WakelockDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/WebViewDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WebViewDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/WebViewDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WebViewDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/WrongCallDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongCallDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/WrongCallDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongCallDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/WrongCaseDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongCaseDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/WrongCaseDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongCaseDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/WrongIdDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongIdDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/WrongIdDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongIdDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/WrongImportDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongImportDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/WrongImportDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongImportDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/WrongLocationDetectorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongLocationDetectorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/WrongLocationDetectorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/WrongLocationDetectorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/AbstractActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/AbstractActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/AbstractActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/AbstractActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/AndroidManifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/AndroidManifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/AndroidManifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/AndroidManifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/Dependencies.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/Dependencies.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/Dependencies.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/Dependencies.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/allowbackup.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/allowbackup.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/allowbackup.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/allowbackup.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/allowbackup_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/allowbackup_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/allowbackup_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/allowbackup_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/android/PreferenceActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/android/PreferenceActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/android/PreferenceActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/android/PreferenceActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest10.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyActivity.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyActivity.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyActivity.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyActivity.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyLinear.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyLinear.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyLinear.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11$MyLinear.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest11.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest12.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest13.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14$3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest14.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1$InnerInnerClass1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1$InnerInnerClass1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1$InnerInnerClass1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1$InnerInnerClass1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4$InnerClass2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest4.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest5.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest6.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest7.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest8.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiCallTest9.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiSourceCheck2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest$LocalClass.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest$LocalClass.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest$LocalClass.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest$LocalClass.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1$2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ApiTargetTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/CloseTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ConditionalApiTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Fragment.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/FragmentActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/GravityTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/GravityTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/GravityTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/GravityTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate$IntermediateCustomV.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate$IntermediateCustomV.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate$IntermediateCustomV.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate$IntermediateCustomV.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/Intermediate.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyActivityImpl.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R$color.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R$color.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R$color.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R$color.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment$R.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/MyFragment.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest$MyEngine2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuperCallTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/SuppressTest4.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TargetApiTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TestEnum.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TestLint.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TestLint.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/TestLint.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/TestLint.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional1b.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional2b.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/VersionConditional3b.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/android-support-v4.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/android-support-v4.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/android-support-v4.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/android-support-v4.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_selector.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_selector.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_selector.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_selector.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_vector.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_vector.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_vector.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/animated_vector.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/attribute3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/classpath b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/classpath
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/classpath
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/classpath
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/colors.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/colors.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/colors.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/colors.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/divider.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/divider.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/divider.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/divider.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/fragment_support.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/fragment_support.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/fragment_support.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/fragment_support.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/holomanifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/holomanifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/holomanifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/holomanifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/layout.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/layout.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/layout.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/layout.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/layout_targetapi.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/layout_targetapi.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/layout_targetapi.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/layout_targetapi.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/layoutattr.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/layoutattr.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/layoutattr.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/layoutattr.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minics.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minics.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minics.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minics.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk10.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk10.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk10.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk10.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk11.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk11.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk11.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk11.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk14.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk14.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk14.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk14.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk17.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk17.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk17.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk17.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk19.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk19.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk19.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk19.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk21.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk21.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk21.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk21.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/minsdk4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/padding_start.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/padding_start.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/padding_start.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/padding_start.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ripple.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ripple.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/ripple.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/ripple.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/themes.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/themes.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/themes.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/themes.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/unsupported.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/unsupported.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/unsupported.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/unsupported.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/vector.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/vector.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/vector.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/vector.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/view.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/view.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/apicheck/view.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/apicheck/view.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionBarActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionBarActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionBarActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionBarActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionMode.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionMode.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionMode.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/ActionMode.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatPrefTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatPrefTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatPrefTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatPrefTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/AppCompatTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/DialogFragment.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/DialogFragment.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/DialogFragment.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/DialogFragment.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/Fragment.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/Fragment.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/Fragment.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/Fragment.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentManager.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentManager.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentManager.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentManager.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentTransaction.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentTransaction.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentTransaction.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/FragmentTransaction.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/IntermediateActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/IntermediateActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/appcompat/IntermediateActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appcompat/IntermediateActivity.java.txt
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appindexing_manifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appindexing_manifest.xml
new file mode 100644
index 0000000..746c7c6
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/appindexing_manifest.xml
@@ -0,0 +1,28 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.helloworld">
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name=".FullscreenActivity"
+ android:configChanges="orientation|keyboardHidden|screenSize"
+ android:label="@string/title_activity_fullscreen"
+ android:theme="@style/FullscreenTheme" >
+ <intent-filter android:label="@string/title_activity_fullscreen">
+ <action android:name="android.intent.action.VIEW" />
+ <data android:scheme="http"
+ android:host="example.com"
+ android:pathPrefix="@string/path_prefix"
+ android:port="@string/port"/>
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ </intent-filter>
+ </activity>
+
+ <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
+ </application>
+
+</manifest>
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/broken-manifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/broken-manifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/broken-manifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/broken-manifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/broken-manifest2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/broken-manifest2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/broken-manifest2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/broken-manifest2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/.classpath b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/.classpath
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/.classpath
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/.classpath
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AbstractCustomView.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnNonWebView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnNonWebView.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnNonWebView.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnNonWebView.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebView.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebViewChild.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebViewChild.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebViewChild.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$CallAddJavascriptInterfaceOnWebViewChild.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$NonWebView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$NonWebView.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$NonWebView.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$NonWebView.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$WebViewChild.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$WebViewChild.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$WebViewChild.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest$WebViewChild.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AddJavascriptInterfaceTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestMinSdk17.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestMinSdk17.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestMinSdk17.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestMinSdk17.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestReg.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestReg.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestReg.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestReg.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestRegs.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestRegs.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestRegs.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestRegs.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestTarget17.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestTarget17.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestTarget17.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestTarget17.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestWrongRegs.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestWrongRegs.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestWrongRegs.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestWrongRegs.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/AnnotatedObject.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.class.data
rename to lint/libs/lint-tests/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/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CipherTest1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1$Class4.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1$Class4.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1$Class4.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1$Class4.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2$Class3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2$Class3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2$Class3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2$Class3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/Class2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousInvalidOnTouchListener.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$AnonymousValidOnTouchListener.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClick.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClick.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClick.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClick.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClickOnTouchListenerSetter.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClickOnTouchListenerSetter.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClickOnTouchListenerSetter.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$HasPerformClickOnTouchListenerSetter.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$InvalidOnTouchListener.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$InvalidOnTouchListener.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$InvalidOnTouchListener.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$InvalidOnTouchListener.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClick.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClick.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClick.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClick.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClickOnTouchListenerSetter.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClickOnTouchListenerSetter.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClickOnTouchListenerSetter.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NoPerformClickOnTouchListenerSetter.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAView.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAView.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAView.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAViewOnTouchListenerSetter.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAViewOnTouchListenerSetter.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAViewOnTouchListenerSetter.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$NotAViewOnTouchListenerSetter.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$PerformClickDoesNotCallSuper.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$PerformClickDoesNotCallSuper.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$PerformClickDoesNotCallSuper.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$PerformClickDoesNotCallSuper.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidOnTouchListener.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidOnTouchListener.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidOnTouchListener.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidOnTouchListener.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidView.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidView.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidView.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ValidView.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewDoesNotCallPerformClick.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewDoesNotCallPerformClick.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewDoesNotCallPerformClick.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewDoesNotCallPerformClick.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewOverridesOnTouchEventButNotPerformClick.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewOverridesOnTouchEventButNotPerformClick.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewOverridesOnTouchEventButNotPerformClick.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewOverridesOnTouchEventButNotPerformClick.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewSubclass.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewSubclass.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewSubclass.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewSubclass.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentOnTouchEvent.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentOnTouchEvent.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentOnTouchEvent.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentOnTouchEvent.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentPerformClick.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentPerformClick.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentPerformClick.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest$ViewWithDifferentPerformClick.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ClickableViewAccessibilityTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommentsActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2$MyDialogFragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2$MyDialogFragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2$MyDialogFragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2$MyDialogFragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyCompatDialogFragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyCompatDialogFragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyCompatDialogFragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyCompatDialogFragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyDialogFragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyDialogFragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyDialogFragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3$MyDialogFragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CommitTest3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomView3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomViewTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomViewTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomViewTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/CustomViewTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/DialogFragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/DialogFragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/DialogFragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/DialogFragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment4.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment4.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment4.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment4.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment5.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment5.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment5.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment5.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment6.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment6.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment6.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$Fragment6.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$NotAFragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$NotAFragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$NotAFragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$NotAFragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$ValidFragment1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$ValidFragment1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$ValidFragment1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest$ValidFragment1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/FragmentTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/GetterTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$Inner.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$Inner.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$Inner.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$Inner.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$StaticInner.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$StaticInner.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$StaticInner.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$StaticInner.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$WithArbitraryLooper.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$WithArbitraryLooper.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$WithArbitraryLooper.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest$WithArbitraryLooper.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/HandlerTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/InheritsFromAnnotated.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/JavaScriptTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LayoutTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/LocaleTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/META-INF/MANIFEST.MF b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/META-INF/MANIFEST.MF
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/META-INF/MANIFEST.MF
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/META-INF/MANIFEST.MF
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MathTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable4.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/MyParcelable5.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/NonAnnotatedObject.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/OnClickActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PowerManagerFlagTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.class.data
rename to lint/libs/lint-tests/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/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngCalls.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandom.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandom.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandom.class.data
rename to lint/libs/lint-tests/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/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandomProvider.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround$LinuxPRNGSecureRandomProvider.class.data
rename to lint/libs/lint-tests/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/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.class.data
rename to lint/libs/lint-tests/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/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/PrngWorkaround.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/RecycleTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/SecureRandomTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestFieldGetter.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestProvider2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver$1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver$1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver$1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver$1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestReceiver.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/TestService.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewAndUpdatePreferencesActivity$UserPreferenceFragment.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewAndUpdatePreferencesActivity$UserPreferenceFragment.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewAndUpdatePreferencesActivity$UserPreferenceFragment.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewAndUpdatePreferencesActivity$UserPreferenceFragment.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/ViewTagTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity4.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity5.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity6.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity7.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity8.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/WakelockActivity9.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/butterknife-2.0.1.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/butterknife-2.0.1.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/butterknife-2.0.1.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/butterknife-2.0.1.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/classes.jar b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/classes.jar
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/classes.jar
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/classes.jar
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-jar b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-jar
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-jar
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-jar
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-lib b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-lib
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-lib
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/classpath-lib
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/dagger-compiler-1.2.1-subset.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/dagger-compiler-1.2.1-subset.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/dagger-compiler-1.2.1-subset.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/dagger-compiler-1.2.1-subset.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/javax.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/javax.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/javax.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/javax.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/user_prefs_fragment.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/user_prefs_fragment.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/user_prefs_fragment.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/bytecode/user_prefs_fragment.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/debuggable.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/debuggable.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/debuggable.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/debuggable.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/deviceadmin.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/deviceadmin.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/deviceadmin.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/deviceadmin.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest-ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest-ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest-ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest-ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate-manifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_permissions3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature_ok.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature_ok.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature_ok.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/duplicate_uses_feature_ok.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/MacRoman.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/MacRoman.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/MacRoman.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/MacRoman.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom-implicit.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom-implicit.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom-implicit.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom-implicit.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-bom.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-nobom.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-nobom.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-nobom.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF-16-nobom.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-bom.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-bom.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-bom.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-bom.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-nobom.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-nobom.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-nobom.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32-nobom.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32LE-nobom.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32LE-nobom.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32LE-nobom.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/UTF_32LE-nobom.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/Windows-1252.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/Windows-1252.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/encoding/Windows-1252.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/encoding/Windows-1252.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_explicit.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_explicit.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_explicit.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_explicit.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_implicit.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_implicit.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_implicit.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_implicit.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_no_export.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_no_export.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_no_export.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_no_export.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_explicit.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_explicit.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_explicit.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_explicit.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_implicit.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_implicit.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_implicit.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_implicit.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_target_sdk_19.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_target_sdk_19.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_target_sdk_19.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_subclass_target_sdk_19.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_suppressed.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_suppressed.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_suppressed.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/export_preference_activity_suppressed.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity0.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity0.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity0.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity0.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportactivity4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportactivity4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportprovider1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportprovider1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportprovider1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportprovider1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportprovider2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportprovider2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportprovider2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportprovider2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver0.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver0.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver0.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver0.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver6.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver6.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver6.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver6.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver7.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver7.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver7.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver7.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver8.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver8.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportreceiver8.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportreceiver8.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/exportservice5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/exportservice5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gen/my/pkg/R2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/AccidentalOctal.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/AccidentalOctal.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/AccidentalOctal.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/AccidentalOctal.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/BadDependenciesInBuildscriptBlock.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/BadDependenciesInBuildscriptBlock.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/BadDependenciesInBuildscriptBlock.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/BadDependenciesInBuildscriptBlock.gradle
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Compatibility.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Compatibility.gradle
new file mode 100644
index 0000000..520216c
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Compatibility.gradle
@@ -0,0 +1,24 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion "19.0.0"
+
+ defaultConfig {
+ minSdkVersion 7
+ targetSdkVersion 19
+ versionCode 1
+ versionName "1.0"
+ }
+}
+
+dependencies {
+ compile 'com.android.support:support-v4:18.0.0'
+ compile 'com.android.support.test:espresso:0.2'
+ compile 'com.android.support:multidex:1.0.1'
+ compile 'com.android.support:multidex-instrumentation:1.0.1'
+
+ // Suppressed:
+ //noinspection GradleCompatible
+ compile 'com.android.support:support-v4:18.0.0'
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14_21.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14_21.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14_21.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Dependencies14_21.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesGson.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesGson.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesGson.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesGson.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesInAndroidBlock.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesInAndroidBlock.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesInAndroidBlock.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesInAndroidBlock.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesProps.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesProps.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesProps.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesProps.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesVariable.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesVariable.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesVariable.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DependenciesVariable.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DeprecatedPluginId.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DeprecatedPluginId.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/DeprecatedPluginId.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/DeprecatedPluginId.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/IdSuffix.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/IdSuffix.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/IdSuffix.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/IdSuffix.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/IgnoresGStringsInDependencies.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/IgnoresGStringsInDependencies.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/IgnoresGStringsInDependencies.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/IgnoresGStringsInDependencies.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/IncompatiblePlugin.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/IncompatiblePlugin.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/IncompatiblePlugin.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/IncompatiblePlugin.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/MinSdkAssignment.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/MinSdkAssignment.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/MinSdkAssignment.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/MinSdkAssignment.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/NoAndroidStatement.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/NoAndroidStatement.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/NoAndroidStatement.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/NoAndroidStatement.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/NoApplyPlugin.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/NoApplyPlugin.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/NoApplyPlugin.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/NoApplyPlugin.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Package.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Package.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Package.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Package.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Paths.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Paths.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Paths.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Paths.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/PlayServices.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/PlayServices.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/PlayServices.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/PlayServices.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Plus.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Plus.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Plus.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Plus.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/PreviewDependencies.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/PreviewDependencies.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/PreviewDependencies.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/PreviewDependencies.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions2.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions2.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions2.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/RemoteVersions2.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/RepositoriesInDependenciesBlock.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/RepositoriesInDependenciesBlock.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/RepositoriesInDependenciesBlock.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/RepositoriesInDependenciesBlock.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Setter.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Setter.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/Setter.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/Setter.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/StringInt.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/StringInt.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/StringInt.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/StringInt.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/SuppressLine2.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/SuppressLine2.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/SuppressLine2.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/SuppressLine2.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelDependenciesBlock.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelDependenciesBlock.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelDependenciesBlock.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelDependenciesBlock.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelRepositoriesBlock.gradle b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelRepositoriesBlock.gradle
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelRepositoriesBlock.gradle
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle/TopLevelRepositoriesBlock.gradle
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle_http.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle_http.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle_http.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle_http.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle_override.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle_override.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle_override.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle_override.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle_override_placeholder.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle_override_placeholder.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/gradle_override_placeholder.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/gradle_override_placeholder.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/grantpermission.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/grantpermission.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/grantpermission.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/grantpermission.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/https_namespace.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/https_namespace.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/https_namespace.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/https_namespace.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/ignoremissing.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/ignoremissing.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/ignoremissing.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/ignoremissing.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/illegal_version.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/illegal_version.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/illegal_version.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/illegal_version.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/local.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/local.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/local.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/local.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/local2.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/local2.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/local2.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/local2.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/.classpath b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/.classpath
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/.classpath
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/.classpath
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/AndroidManifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/AndroidManifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/AndroidManifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/AndroidManifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/project.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/project.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/project.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/project.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-de/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-de/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-de/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-de/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values-en-rGB/strings3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/strings5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/styles.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/styles.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/styles.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/locale33845/res/values/styles.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk14.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk14.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk14.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk14.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk9.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk9.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk9.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/minsdk5targetsdk9.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/mipmap.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/mipmap.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/mipmap.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/mipmap.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/missing_application_icon.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missing_application_icon.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/missing_application_icon.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missing_application_icon.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/missingmin.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingmin.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/missingmin.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingmin.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/missingprefix.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingprefix.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/missingprefix.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingprefix.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/missingtarget.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingtarget.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/missingtarget.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingtarget.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/missingusessdk.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingusessdk.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/missingusessdk.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/missingusessdk.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/mock_location.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/mock_location.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/mock_location.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/mock_location.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiplesdk.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiplesdk.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiplesdk.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiplesdk.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/LibraryCode.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/LibraryCode.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/LibraryCode.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/LibraryCode.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/MainCode.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/MainCode.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/MainCode.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/MainCode.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library-manifest2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library2.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library2.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/library2.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/library2.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/main-manifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/main-manifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/main-manifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/main-manifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/main-merge.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/main-merge.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/main-merge.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/main-merge.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/main.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/main.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/main.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/main.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/multiproject/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/multiproject/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/no_version.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/no_version.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/no_version.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/no_version.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/oldtarget.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/oldtarget.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/oldtarget.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/oldtarget.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/.classpath b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/.classpath
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/.classpath
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/.classpath
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/.project b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/.project
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/.project
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/.project
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/AndroidManifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/AndroidManifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/AndroidManifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/AndroidManifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/BuildConfig.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/BuildConfig.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/BuildConfig.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/BuildConfig.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/R.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/R.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/R.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/gen/test/pkg/R.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/project.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/project.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/project.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/project.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-hdpi/ic_launcher.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-hdpi/ic_launcher.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-ldpi/ic_launcher.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-ldpi/ic_launcher.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-ldpi/ic_launcher.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-mdpi/ic_launcher.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-mdpi/ic_launcher.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/drawable/custombg2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fifth.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fifth.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fifth.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fifth.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/fourth_context2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/main_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/nullbackground.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/nullbackground.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/nullbackground.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/nullbackground.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/second.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/second.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/second.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/second.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/sixth.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/sixth.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/sixth.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/sixth.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/third.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/third.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/third.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/third.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/tools_background.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/tools_background.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/tools_background.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/layout/tools_background.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/styles.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/styles.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/styles.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/res/values/styles.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/FourthActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/FourthActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/FourthActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/FourthActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/OverdrawActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/OverdrawActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/OverdrawActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/OverdrawActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/SecondActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/SecondActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/SecondActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/SecondActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/ThirdActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/ThirdActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/ThirdActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/overdraw/src/test/pkg/ThirdActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/proguard.cfg b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/proguard.cfg
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/proguard.cfg
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/proguard.cfg
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/proguard.pro b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/proguard.pro
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/proguard.pro
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/proguard.pro
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/proguard.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/proguard.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/proguard.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/proguard.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties1 b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties1
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties1
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties1
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties19 b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties19
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties19
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties19
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties2 b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties2
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties2
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties2
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties3 b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties3
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties3
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties3
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties4 b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties4
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/project.properties4
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/project.properties4
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/protectedpermissions.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protectedpermissions.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/protectedpermissions.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protectedpermissions.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/protectedpermissions2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protectedpermissions2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/protectedpermissions2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protectedpermissions2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/protection_level_fail.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protection_level_fail.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/protection_level_fail.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protection_level_fail.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/protection_level_ok.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protection_level_ok.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/protection_level_ok.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/protection_level_ok.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestInner.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestInner.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestInner.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestInner.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/AndroidManifestWrong2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Bar.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Bar.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Bar.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Bar.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Bar.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Bar.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Bar.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Bar.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Bar.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Bar.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Bar.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Bar.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Baz.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Baz.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Baz.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo$Baz.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/registration/Foo.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/registration/Foo.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/anim/slide_in_out.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/anim/slide_in_out.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/anim/slide_in_out.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/anim/slide_in_out.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/color/color1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/color/color1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/color/color1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/color/color1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg.9.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg.9.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg.9.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg.9.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg_focus.9.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg_focus.9.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg_focus.9.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/appwidget_bg_focus.9.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/filled.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/filled.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/filled.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/filled.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/ic_launcher.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/ic_launcher.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/other.9.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/other.9.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/other.9.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/other.9.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/unrelated.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/unrelated.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/unrelated.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-hdpi/unrelated.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/frame.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/frame.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/frame.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/frame.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/ic_menu_add_clip_normal.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/ic_menu_add_clip_normal.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/ic_menu_add_clip_normal.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/ic_menu_add_clip_normal.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.gif b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.gif
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.gif
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.gif
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.jpg b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.jpg
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.jpg
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/sample_icon.jpg
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/stat_notify_alarm.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/stat_notify_alarm.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/stat_notify_alarm.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-mdpi/stat_notify_alarm.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-nodpi/frame.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-nodpi/frame.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-nodpi/frame.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-nodpi/frame.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-xhdpi/ic_stat_notify.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-xhdpi/ic_stat_notify.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-xhdpi/ic_stat_notify.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-xhdpi/ic_stat_notify.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-xlarge-nodpi-v11/frame.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-xlarge-nodpi-v11/frame.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable-xlarge-nodpi-v11/frame.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable-xlarge-nodpi-v11/frame.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/drawable1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/drawable1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/drawable1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/drawable1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_launcher.png
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_menu_help.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_menu_help.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_menu_help.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/ic_menu_help.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/states.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/states.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/states.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/states.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/states2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/states2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/states2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/states2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/states3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/states3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/drawable/states3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/drawable/states3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/accessibility2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/activity_item_two_pane.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/activity_item_two_pane.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/activity_item_two_pane.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/activity_item_two_pane.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/baseline_weights3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/breadcrumbs_in_fragment.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/breadcrumbs_in_fragment.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/breadcrumbs_in_fragment.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/breadcrumbs_in_fragment.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/broken.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/broken.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/broken.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/broken.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar_suppressed.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar_suppressed.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar_suppressed.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/buttonbar_suppressed.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/case.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/case.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/case.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/case.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/casts4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/casts4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/compound.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/compound.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/compound.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/compound.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/compound2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/compound2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/compound2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/compound2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/compound3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/compound3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/compound3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/compound3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/crcrlf_ignore.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customattrlayout.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customattrlayout.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customattrlayout.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customattrlayout.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customview.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customview.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customview.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customview.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customview2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customview2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customview2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customview2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customview3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customview3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/customview3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/customview3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/default_item_badges.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/default_item_badges.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/default_item_badges.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/default_item_badges.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/deprecation.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/deprecation.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/deprecation.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/deprecation.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/detailed_item.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/detailed_item.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/detailed_item.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/detailed_item.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/duplicate.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/duplicate.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/duplicate.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/duplicate.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_textview.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_textview.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_textview.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_textview.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_type.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_type.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_type.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/edit_type.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/encoding2.xml
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/gridlayout4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/has_children2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/ignores2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/include_params.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/include_params.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/include_params.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/include_params.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/inefficient_weight3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/invalid_ids2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/labelfor_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout1_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4cycle.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4cycle.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4cycle.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layout4cycle.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layoutcycle1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layoutcycle1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/layoutcycle1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/layoutcycle1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/listseparator.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/listseparator.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/listseparator.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/listseparator.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/message_edit_detail.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/message_edit_detail.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/message_edit_detail.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/message_edit_detail.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/namespace5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/negative_margins.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/negative_margins.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/negative_margins.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/negative_margins.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/nested_weights2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/note_edit2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/now_playing_after.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/now_playing_after.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/now_playing_after.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/now_playing_after.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/onclick.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/onclick.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/onclick.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/onclick.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/orientation2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/relative_overlap.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/relative_overlap.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/relative_overlap.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/relative_overlap.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/scrolling.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/scrolling.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/scrolling.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/scrolling.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/siblings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/siblings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/siblings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/siblings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/simple.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/simple.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/simple.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/simple.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/simple_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/simple_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/simple_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/simple_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/simpleinclude.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/simpleinclude.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/simpleinclude.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/simpleinclude.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/size.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/size.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/size.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/size.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/size2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/size2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/size2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/size2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/sizeincluded.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/sizeincluded.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/sizeincluded.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/sizeincluded.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/tag.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/tag.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/tag.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/tag.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/textsize2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/textureview.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/textureview.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/textureview.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/textureview.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/too_deep.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/too_deep.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/too_deep.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/too_deep.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/too_many.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/too_many.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/too_many.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/too_many.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/useless4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/useless4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/webview.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/webview.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/webview.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/webview.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/webview2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/webview2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/webview2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/webview2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/webview3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/webview3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/webview3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/webview3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong0dp.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong0dp.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong0dp.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong0dp.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_dimension.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_dimension.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_dimension.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_dimension.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrong_namespace5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams6.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams6.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams6.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams6.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/wrongparams_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/yesno.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/yesno.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/yesno.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/layout/yesno.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu-land/actions2_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/menu.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/menu.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/menu.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/menu.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/showAction2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/titles.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/titles.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/menu/titles.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/menu/titles.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/private_key.pem b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/private_key.pem
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/private_key.pem
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/private_key.pem
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/arrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/arrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/arrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/arrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/plurals3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/plurals3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/plurals3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/plurals3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/translatedarrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/translatedarrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-cs/translatedarrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-cs/translatedarrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-de-rDE/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-de-rDE/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-de-rDE/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-de-rDE/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-de/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-de/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-de/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-de/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-de/typos.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-de/typos.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-de/typos.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-de/typos.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es-rUS/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es-rUS/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es-rUS/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es-rUS/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/donottranslate.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/donottranslate.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/donottranslate.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/donottranslate.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/formatstrings_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_locale.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_locale.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_locale.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-es/strings_locale.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-fr/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-fr/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-fr/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-fr/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-it/stringarrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-it/stringarrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-it/stringarrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-it/stringarrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-land/arrays_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-land/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-land/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-land/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-land/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos_locale.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos_locale.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos_locale.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nb/typos_locale.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/arrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/arrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/arrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/arrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-nl-rNL/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-pl/plurals2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-pl/plurals2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-pl/plurals2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-pl/plurals2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-ru/plurals6.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-ru/plurals6.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-ru/plurals6.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-ru/plurals6.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/bom.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/bom.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/bom.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/bom.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/plurals3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/plurals3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/plurals3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values-zh-rCN/plurals3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/aaptcrash.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/aaptcrash.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/aaptcrash.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/aaptcrash.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/aliases.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/aliases.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/aliases.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/aliases.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/aliases2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/aliases2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/aliases2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/aliases2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/analytics.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/analytics.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/analytics.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/analytics.xml
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/appindexing_strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/appindexing_strings.xml
new file mode 100644
index 0000000..677e98a
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/appindexing_strings.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <string name="path_prefix">/pathprefix</string>
+ <string name="port">8080</string>
+</resources>
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/appindexing_wrong_strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/appindexing_wrong_strings.xml
new file mode 100644
index 0000000..551c16c
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/appindexing_wrong_strings.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <string name="path_prefix">pathprefix</string>
+ <string name="port">gizmos</string>
+</resources>
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/arrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/arrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/arrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/arrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/arrayusage.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/arrayusage.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/arrayusage.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/arrayusage.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/buttonbar-values.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/buttonbar-values.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/buttonbar-values.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/buttonbar-values.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/colorcycle5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/customattr.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/customattr.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/customattr.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/customattr.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/dimens.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/dimens.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/dimens.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/dimens.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-items.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-items.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-items.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-items.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/duplicate-strings2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings-version2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings10.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings10.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings10.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings10.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings11.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings11.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings11.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings11.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings6.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings6.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings6.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings6.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings7.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings7.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings7.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings7.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings8.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings8.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings8.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings8.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings9.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings9.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings9.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings9.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/formatstrings_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/google_maps_api.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/google_maps_api.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/google_maps_api.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/google_maps_api.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/integer_arrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/integer_arrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/integer_arrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/integer_arrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/integers.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/integers.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/integers.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/integers.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/negative_margins.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/negative_margins.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/negative_margins.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/negative_margins.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/nontranslatable2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals5.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals5.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals5.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals5.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_candidates.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_candidates.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_candidates.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_candidates.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_typography.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_typography.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_typography.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/plurals_typography.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/pxsp.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/pxsp.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/pxsp.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/pxsp.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/refs.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/refs.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/refs.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/refs.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/refs2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/refs2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/refs2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/refs2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/repeated_words.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/repeated_words.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/repeated_words.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/repeated_words.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/shared_prefs_keys.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/shared_prefs_keys.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/shared_prefs_keys.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/shared_prefs_keys.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/sizestyles.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/sizestyles.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/sizestyles.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/sizestyles.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stringarrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stringarrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stringarrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stringarrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings4.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings4.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings4.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings4.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/strings_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/strings_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/stylecycle2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles-inherited-orientation.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles-inherited-orientation.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles-inherited-orientation.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles-inherited-orientation.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles-orientation.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles-orientation.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles-orientation.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles-orientation.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/styles2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/styles2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/themes.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/themes.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/themes.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/themes.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/themes2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/themes2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/themes2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/themes2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/themes3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/themes3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/themes3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/themes3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/translatedarrays.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/translatedarrays.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/translatedarrays.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/translatedarrays.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/typography.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/typography.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/typography.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/typography.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/typos.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/typos.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/typos.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/values/typos.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list_formatted.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list_formatted.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list_formatted.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/xml/nfc_tech_list_formatted.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/xml/prefs_headers.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/xml/prefs_headers.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/res/xml/prefs_headers.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/res/xml/prefs_headers.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/GravityTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/min17nortl.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/min17nortl.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/min17nortl.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/min17nortl.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/min17rtl.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/min17rtl.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/min17rtl.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/min17rtl.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/minsdk5targetsdk17.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/minsdk5targetsdk17.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/minsdk5targetsdk17.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/minsdk5targetsdk17.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/project-api14.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/project-api14.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/project-api14.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/project-api14.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/project-api17.properties b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/project-api17.properties
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/project-api17.properties
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/project-api17.properties
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/relative.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/relative.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/relative.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/relative.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/relativeCompat.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/relativeCompat.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/relativeCompat.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/relativeCompat.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/relativeOk.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/relativeOk.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/relativeOk.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/relativeOk.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/rtl.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/rtl.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/rtl.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/rtl.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/rtl_noprefix.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/rtl_noprefix.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/rtl_noprefix.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/rtl_noprefix.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/spinner.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/spinner.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/spinner.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/spinner.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/symmetry.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/symmetry.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rtl/symmetry.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rtl/symmetry.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rules/AppCompatTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/rules/appcompat.jar.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rules/appcompat.jar.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/rules/appcompat.jar.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/rules/appcompat.jar.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/AnyRes.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/AnyRes.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/AnyRes.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/AnyRes.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CallSuper.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CallSuper.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CallSuper.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CallSuper.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CheckResult.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CheckResult.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CheckResult.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/CheckResult.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/ColorInt.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/ColorInt.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/ColorInt.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/ColorInt.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/DrawableRes.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/DrawableRes.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/DrawableRes.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/DrawableRes.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/FloatRange.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/FloatRange.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/FloatRange.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/FloatRange.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntDef.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntDef.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntDef.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntDef.java.txt
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntRange.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntRange.java.txt
new file mode 100644
index 0000000..133efa8
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/IntRange.java.txt
@@ -0,0 +1,19 @@
+package android.support.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+@Retention(CLASS)
+@Target({CONSTRUCTOR,METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE})
+public @interface IntRange {
+ long from() default Long.MIN_VALUE;
+ long to() default Long.MAX_VALUE;
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/Size.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/Size.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/Size.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/Size.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/StringDef.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/StringDef.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/StringDef.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/android/support/annotation/StringDef.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/my/pkg/Test.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/my/pkg/Test.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/my/pkg/Test.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/my/pkg/Test.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/ColorAsDrawable.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/ColorAsDrawable.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/ColorAsDrawable.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/ColorAsDrawable.java.txt
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/Flow.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/Flow.java.txt
new file mode 100644
index 0000000..8d73186
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/p1/p2/Flow.java.txt
@@ -0,0 +1,125 @@
+import android.content.res.Resources;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.StringRes;
+import android.support.annotation.StyleRes;
+
+import java.util.Random;
+
+@SuppressWarnings("UnusedDeclaration")
+public class Flow {
+ public void testLiterals(Resources resources) {
+ resources.getDrawable(0); // OK
+ resources.getDrawable(-1); // OK
+ resources.getDrawable(10); // ERROR
+ }
+
+ public void testConstants(Resources resources) {
+ resources.getDrawable(R.drawable.my_drawable); // OK
+ resources.getDrawable(R.string.my_string); // ERROR
+ }
+
+ public void testLocalAnnotation() {
+ myMethod(R.string.my_string, null); // ERROR
+ }
+
+ private void myMethod(@DrawableRes int arg, Resources resources) {
+ resources.getDrawable(R.string.my_string); // ERROR
+ }
+
+ private void testAnyRes() {
+ myAnyResMethod(R.drawable.my_drawable); // OK
+ myAnyResMethod(R.string.my_string); // OK
+ myAnyResMethod(50); // ERROR
+ }
+
+ private void myAnyResMethod(@android.support.annotation.AnyRes int arg) {
+ }
+
+ public void testFields(String fileExt, Resources resources) {
+ int mimeIconId = MimeTypes.styleAndDrawable;
+ resources.getDrawable(mimeIconId); // OK
+
+ int s1 = MimeTypes.style;
+ resources.getDrawable(s1); // ERROR
+ int s2 = MimeTypes.styleAndDrawable;
+ resources.getDrawable(s2); // OK
+ int w3 = MimeTypes.drawable;
+ resources.getDrawable(w3); // OK
+
+ // Direct reference
+ resources.getDrawable(MimeTypes.style); // ERROR
+ resources.getDrawable(MimeTypes.styleAndDrawable); // OK
+ resources.getDrawable(MimeTypes.drawable); // OK
+ }
+
+ public void testCalls(String fileExt, Resources resources) {
+ int mimeIconId = MimeTypes.getIconForExt(fileExt);
+ resources.getDrawable(mimeIconId); // OK
+ resources.getDrawable(MimeTypes.getInferredString()); // OK (wrong but can't infer type)
+ resources.getDrawable(MimeTypes.getInferredDrawable()); // OK
+ resources.getDrawable(MimeTypes.getAnnotatedString()); // Error
+ resources.getDrawable(MimeTypes.getAnnotatedDrawable()); // OK
+ resources.getDrawable(MimeTypes.getUnknownType()); // OK (unknown/uncertain)
+ }
+
+ public void testFlow() {
+ int x = R.string.my_string;
+ int z = x;
+ myMethod(z, null); // ERROR
+
+ int w = MY_RESOURCE;
+ myMethod(w, null); // ERROR
+ }
+
+ private static final int MY_RESOURCE = R.string.my_string;
+
+ private static class MimeTypes {
+ @android.support.annotation.StyleRes
+ @android.support.annotation.DrawableRes
+ public static int styleAndDrawable;
+
+ @android.support.annotation.StyleRes
+ public static int style;
+
+ @android.support.annotation.DrawableRes
+ public static int drawable;
+
+ @android.support.annotation.DrawableRes
+ public static int getIconForExt(String ext) {
+ return R.drawable.my_drawable;
+ }
+
+ public static int getInferredString() {
+ // Implied string - can we handle this?
+ return R.string.my_string;
+ }
+
+ public static int getInferredDrawable() {
+ // Implied drawable - can we handle this?
+ return R.drawable.my_drawable;
+ }
+
+ @android.support.annotation.StringRes
+ public static int getAnnotatedString() {
+ return R.string.my_string;
+ }
+
+ @android.support.annotation.DrawableRes
+ public static int getAnnotatedDrawable() {
+ return R.drawable.my_drawable;
+ }
+
+ public static int getUnknownType() {
+ return new Random(1000).nextInt();
+ }
+ }
+
+ public static final class R {
+ public static final class drawable {
+ public static final int my_drawable =0x7f020057;
+ }
+ public static final class string {
+ public static final int my_string =0x7f0a000e;
+ }
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/pkg1/Class1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/pkg1/Class1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/pkg1/Class1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/pkg1/Class1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/pkg2/Class2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/pkg2/Class2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/pkg2/Class2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/pkg2/Class2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionBarTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionBarTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionBarTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionBarTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1_ignore.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1_ignore.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1_ignore.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest1_ignore.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ActionTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/AlarmTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/AlarmTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/AlarmTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/AlarmTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Assert.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Assert.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Assert.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Assert.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/BadImport.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/BadImport.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/BadImport.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/BadImport.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CallSuperTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CallSuperTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CallSuperTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CallSuperTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckPermissions.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckPermissions.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckPermissions.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckPermissions.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckResult.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckResult.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckResult.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CheckResult.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAES.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAES.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAES.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAES.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESCBC.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESCBC.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESCBC.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESCBC.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESECB.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESECB.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESECB.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceAESECB.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceDES.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceDES.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceDES.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceDES.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CipherGetInstanceTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ColorUsage.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ColorUsage.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ColorUsage.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ColorUsage.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ContentProviderClientTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ContentProviderClientTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ContentProviderClientTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ContentProviderClientTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CursorTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CursorTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CursorTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CursorTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomView1.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomView1.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomView1.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomView1.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomViewTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomViewTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomViewTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/CustomViewTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/DetachedFromWindow.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/DetachedFromWindow.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/DetachedFromWindow.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/DetachedFromWindow.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Foo.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Foo.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Foo.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Foo.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseAndTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseAndTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseAndTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseAndTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseOrTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseOrTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseOrTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseOrTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseXorTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseXorTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseXorTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesBitwiseXorTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesLocalVariableTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesLocalVariableTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesLocalVariableTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesLocalVariableTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNoFlagTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNoFlagTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNoFlagTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNoFlagTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNotPackageManagerTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNotPackageManagerTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNotPackageManagerTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesNotPackageManagerTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesSingleFlagTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesSingleFlagTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesSingleFlagTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesSingleFlagTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesStaticFieldTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesStaticFieldTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesStaticFieldTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/GetSignaturesStaticFieldTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Hidden.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Hidden.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Hidden.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Hidden.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ImportFrameActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ImportFrameActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ImportFrameActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ImportFrameActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/InflaterTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/InflaterTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/InflaterTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/InflaterTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/IntDefTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/IntDefTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/IntDefTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/IntDefTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.class.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.class.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.class.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.class.data
Binary files differ
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Java7API.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/JavaPerformanceTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest_ignored.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest_ignored.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest_ignored.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LayoutInflationTest_ignored.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Log.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Log.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Log.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Log.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LogTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LogTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LogTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LogTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LongSparseArray.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LongSparseArray.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LongSparseArray.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/LongSparseArray.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/MyTracksProvider.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/MyTracksProvider.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/MyTracksProvider.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/MyTracksProvider.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NonInternationalizedSmsDetectorTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NonInternationalizedSmsDetectorTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NonInternationalizedSmsDetectorTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NonInternationalizedSmsDetectorTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NotificationTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NotificationTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NotificationTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/NotificationTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/OverrideConcreteTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/OverrideConcreteTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/OverrideConcreteTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/OverrideConcreteTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PasteError.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PasteError.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PasteError.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PasteError.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclass.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclass.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclass.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclass.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclassOverridesIsValidFragment.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclassOverridesIsValidFragment.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclassOverridesIsValidFragment.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/PreferenceActivitySubclassOverridesIsValidFragment.java.txt
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/RangeTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/RangeTest.java.txt
new file mode 100644
index 0000000..40bf5df
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/RangeTest.java.txt
@@ -0,0 +1,160 @@
+package test.pkg;
+
+import android.support.annotation.FloatRange;
+import android.support.annotation.IntRange;
+import android.support.annotation.Size;
+
+@SuppressWarnings("UnusedDeclaration")
+public class RangeTest {
+ public void printExact(@Size(5) String arg) { System.out.println(arg); }
+ public void printMin(@Size(min=5) String arg) { }
+ public void printMax(@Size(max=8) String arg) { }
+ public void printRange(@Size(min=4,max=6) String arg) { }
+ public void printExact(@Size(5) int[] arg) { }
+ public void printMin(@Size(min=5) int[] arg) { }
+ public void printMax(@Size(max=8) int[] arg) { }
+ public void printRange(@Size(min=4,max=6) int[] arg) { }
+ public void printMultiple(@Size(multiple=3) int[] arg) { }
+ public void printMinMultiple(@Size(min=4,multiple=3) int[] arg) { }
+ public void printAtLeast(@IntRange(from=4) int arg) { }
+ public void printAtMost(@IntRange(to=7) int arg) { }
+ public void printBetween(@IntRange(from=4,to=7) int arg) { }
+ public void printAtLeastInclusive(@FloatRange(from=2.5) float arg) { }
+ public void printAtLeastExclusive(@FloatRange(from=2.5,fromInclusive=false) float arg) { }
+ public void printAtMostInclusive(@FloatRange(to=7) double arg) { }
+ public void printAtMostExclusive(@FloatRange(to=7,toInclusive=false) double arg) { }
+ public void printBetweenFromInclusiveToInclusive(@FloatRange(from=2.5,to=5.0) float arg) { }
+ public void printBetweenFromExclusiveToInclusive(@FloatRange(from=2.5,to=5.0,fromInclusive=false) float arg) { }
+ public void printBetweenFromInclusiveToExclusive(@FloatRange(from=2.5,to=5.0,toInclusive=false) float arg) { }
+ public void printBetweenFromExclusiveToExclusive(@FloatRange(from=2.5,to=5.0,fromInclusive=false,toInclusive=false) float arg) { }
+
+ public void testLength() {
+ printExact("1234"); // ERROR
+ printExact("12345"); // OK
+ printExact("123456"); // ERROR
+
+ printMin("1234"); // ERROR
+ printMin("12345"); // OK
+ printMin("123456"); // OK
+
+ printMax("123456"); // OK
+ printMax("1234567"); // OK
+ printMax("12345678"); // OK
+ printMax("123456789"); // ERROR
+
+ printRange("123"); // ERROR
+ printRange("1234"); // OK
+ printRange("12345"); // OK
+ printRange("123456"); // OK
+ printRange("1234567"); // ERROR
+ }
+
+ public void testSize() {
+ printExact(new int[]{1, 2, 3, 4}); // ERROR
+ printExact(new int[]{1, 2, 3, 4, 5}); // OK
+ printExact(new int[]{1, 2, 3, 4, 5, 6}); // ERROR
+
+ printMin(new int[]{1, 2, 3, 4}); // ERROR
+ printMin(new int[]{1, 2, 3, 4, 5}); // OK
+ printMin(new int[]{1, 2, 3, 4, 5, 6}); // OK
+
+ printMax(new int[]{1, 2, 3, 4, 5, 6}); // OK
+ printMax(new int[]{1, 2, 3, 4, 5, 6, 7}); // OK
+ printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8}); // OK
+ printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8}); // OK
+ printMax(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}); // ERROR
+
+ printRange(new int[] {1,2,3}); // ERROR
+ printRange(new int[] {1,2,3,4}); // OK
+ printRange(new int[] {1,2,3,4,5}); // OK
+ printRange(new int[] {1,2,3,4,5,6}); // OK
+ printRange(new int[] {1,2,3,4,5,6,7}); // ERROR
+
+ printMultiple(new int[] {1,2,3}); // OK
+ printMultiple(new int[] {1,2,3,4}); // ERROR
+ printMultiple(new int[] {1,2,3,4,5}); // ERROR
+ printMultiple(new int[] {1,2,3,4,5,6}); // OK
+ printMultiple(new int[] {1,2,3,4,5,6,7}); // ERROR
+
+ printMinMultiple(new int[] {1,2,3,4,5,6}); // OK
+ printMinMultiple(new int[]{1, 2, 3}); // ERROR
+ }
+
+ public void testIntRange() {
+ printAtLeast(3); // ERROR
+ printAtLeast(4); // OK
+ printAtLeast(5); // OK
+
+ printAtMost(5); // OK
+ printAtMost(6); // OK
+ printAtMost(7); // OK
+ printAtMost(8); // ERROR
+
+ printBetween(3); // ERROR
+ printBetween(4); // OK
+ printBetween(5); // OK
+ printBetween(6); // OK
+ printBetween(7); // OK
+ printBetween(8); // ERROR
+ }
+
+ public void testFloatRange() {
+ printAtLeastInclusive(2.49f); // ERROR
+ printAtLeastInclusive(2.5f); // OK
+ printAtLeastInclusive(2.6f); // OK
+
+ printAtLeastExclusive(2.49f); // ERROR
+ printAtLeastExclusive(2.5f); // ERROR
+ printAtLeastExclusive(2.501f); // OK
+
+ printAtMostInclusive(6.8f); // OK
+ printAtMostInclusive(6.9f); // OK
+ printAtMostInclusive(7.0f); // OK
+ printAtMostInclusive(7.1f); // ERROR
+
+ printAtMostExclusive(6.9f); // OK
+ printAtMostExclusive(6.99f); // OK
+ printAtMostExclusive(7.0f); // ERROR
+ printAtMostExclusive(7.1f); // ERROR
+
+ printBetweenFromInclusiveToInclusive(2.4f); // ERROR
+ printBetweenFromInclusiveToInclusive(2.5f); // OK
+ printBetweenFromInclusiveToInclusive(3f); // OK
+ printBetweenFromInclusiveToInclusive(5.0f); // OK
+ printBetweenFromInclusiveToInclusive(5.1f); // ERROR
+
+ printBetweenFromExclusiveToInclusive(2.4f); // ERROR
+ printBetweenFromExclusiveToInclusive(2.5f); // ERROR
+ printBetweenFromExclusiveToInclusive(5.0f); // OK
+ printBetweenFromExclusiveToInclusive(5.1f); // ERROR
+
+ printBetweenFromInclusiveToExclusive(2.4f); // ERROR
+ printBetweenFromInclusiveToExclusive(2.5f); // OK
+ printBetweenFromInclusiveToExclusive(3f); // OK
+ printBetweenFromInclusiveToExclusive(4.99f); // OK
+ printBetweenFromInclusiveToExclusive(5.0f); // ERROR
+
+ printBetweenFromExclusiveToExclusive(2.4f); // ERROR
+ printBetweenFromExclusiveToExclusive(2.5f); // ERROR
+ printBetweenFromExclusiveToExclusive(2.51f); // OK
+ printBetweenFromExclusiveToExclusive(4.99f); // OK
+ printBetweenFromExclusiveToExclusive(5.0f); // ERROR
+ }
+
+ public void testNegative() {
+ printBetween(-7); // ERROR
+ printAtLeastExclusive(-10.0f); // ERROR
+ }
+
+ public static final int MINIMUM = -1;
+ public static final int MAXIMUM = 42;
+ public void printIndirect(@IntRange(from = MINIMUM, to = MAXIMUM) int arg) { }
+ public static final int SIZE = 5;
+ public static void printIndirectSize(@Size(SIZE) String foo) { }
+
+ public void testIndirect() {
+ printIndirect(-2); // ERROR
+ printIndirect(43); // ERROR
+ printIndirectSize("1234567"); // ERROR
+ }
+}
\ No newline at end of file
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteDatabase.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteDatabase.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteDatabase.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteDatabase.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SQLiteTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SdCardTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SdCardTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SdCardTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SdCardTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SetJavaScriptEnabled.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SetJavaScriptEnabled.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SetJavaScriptEnabled.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SetJavaScriptEnabled.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest4.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest4.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest4.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest4.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest5.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest5.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest5.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest5.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest6.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest6.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest6.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest6.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest7.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest7.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest7.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest7.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest8.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest8.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest8.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SharedPrefsTest8.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SparseLongArray.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SparseLongArray.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SparseLongArray.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SparseLongArray.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat10.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat10.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat10.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat10.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat11.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat11.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat11.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat11.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat4.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat4.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat4.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat4.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat5.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat5.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat5.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat5.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat8.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat8.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat8.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat8.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat9.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat9.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat9.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormat9.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity_ignore.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity_ignore.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity_ignore.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/StringFormatActivity_ignore.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SuppressTest5.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SuppressTest5.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SuppressTest5.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SuppressTest5.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SurfaceTextureTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SurfaceTextureTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SurfaceTextureTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SurfaceTextureTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SystemServiceTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SystemServiceTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SystemServiceTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/SystemServiceTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ToastTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ToastTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ToastTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ToastTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TransactionTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TransactionTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TransactionTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TransactionTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryCatchHang.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryCatchHang.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryCatchHang.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryCatchHang.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryWithResources.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryWithResources.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryWithResources.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/TryWithResources.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/UnusedReference.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/UnusedReference.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/UnusedReference.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/UnusedReference.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Utf8BomTest.java.data b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Utf8BomTest.java.data
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Utf8BomTest.java.data
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/Utf8BomTest.java.data
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ViewHolderTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ViewHolderTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ViewHolderTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/ViewHolderTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WatchFaceTest.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WatchFaceTest.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WatchFaceTest.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WatchFaceTest.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WorldWriteableFile.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WorldWriteableFile.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WorldWriteableFile.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WorldWriteableFile.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongAnnotation.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongAnnotation.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongAnnotation.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongAnnotation.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity2.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity2.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity2.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity2.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity3.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity3.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity3.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongCastActivity3.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongColor.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongColor.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongColor.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/src/test/pkg/WrongColor.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/stubs/CanvasWatchFaceService.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/stubs/CanvasWatchFaceService.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/stubs/CanvasWatchFaceService.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/stubs/CanvasWatchFaceService.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/stubs/WatchFaceService.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/stubs/WatchFaceService.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/stubs/WatchFaceService.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/stubs/WatchFaceService.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_manifest.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_manifest.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_manifest.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_manifest.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_not_found.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_not_found.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_not_found.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_not_found.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_feature2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_library.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_library.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_library.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_library.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_library2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_library2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_library2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_library2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_permission2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/typo_uses_sdk2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/unusedR.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/unusedR.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/unusedR.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/unusedR.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/Foo.java.txt b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/Foo.java.txt
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/Foo.java.txt
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/Foo.java.txt
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/ids.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/ids.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/ids.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/ids.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/ignorelayout1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1_ignore.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1_ignore.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1_ignore.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout1_ignore.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout2.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout2.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout2.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout2.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout3.xml b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout3.xml
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/checks/data/wrongid/layout3.xml
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/data/wrongid/layout3.xml
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/CompositeIssueRegistryTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/CompositeIssueRegistryTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/CompositeIssueRegistryTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/CompositeIssueRegistryTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/CustomRuleTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/CustomRuleTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/CustomRuleTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/CustomRuleTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/DefaultConfigurationTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/DefaultConfigurationTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/DefaultConfigurationTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/DefaultConfigurationTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/DefaultSdkInfoTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/DefaultSdkInfoTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/DefaultSdkInfoTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/DefaultSdkInfoTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/JarFileIssueRegistryTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/JarFileIssueRegistryTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/JarFileIssueRegistryTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/JarFileIssueRegistryTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/LintClientTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/LintClientTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/LintClientTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/LintClientTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/LintDriverTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/LintDriverTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/LintDriverTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/LintDriverTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/client/api/ProjectTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/ProjectTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/client/api/ProjectTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/client/api/ProjectTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/ClassContextTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ClassContextTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/detector/api/ClassContextTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ClassContextTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ConstantEvaluatorTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/ImplementationTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ImplementationTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/detector/api/ImplementationTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ImplementationTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/IssueTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/IssueTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/detector/api/IssueTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/IssueTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.java
new file mode 100644
index 0000000..6956445
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LintUtilsTest.java
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.detector.api;
+
+import static com.android.tools.lint.detector.api.LintUtils.computeResourceName;
+import static com.android.tools.lint.detector.api.LintUtils.convertVersion;
+import static com.android.tools.lint.detector.api.LintUtils.escapePropertyValue;
+import static com.android.tools.lint.detector.api.LintUtils.findSubstring;
+import static com.android.tools.lint.detector.api.LintUtils.getFormattedParameters;
+import static com.android.tools.lint.detector.api.LintUtils.getLocaleAndRegion;
+import static com.android.tools.lint.detector.api.LintUtils.isImported;
+import static com.android.tools.lint.detector.api.LintUtils.splitPath;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.builder.model.AndroidProject;
+import com.android.builder.model.ApiVersion;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+import com.android.tools.lint.EcjParser;
+import com.android.tools.lint.LintCliClient;
+import com.android.tools.lint.checks.BuiltinIssueRegistry;
+import com.android.tools.lint.client.api.JavaParser;
+import com.android.tools.lint.client.api.LintDriver;
+import com.google.common.collect.Iterables;
+
+import junit.framework.TestCase;
+
+import org.intellij.lang.annotations.Language;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Locale;
+
+import lombok.ast.Node;
+
+@SuppressWarnings("javadoc")
+public class LintUtilsTest extends TestCase {
+ public void testPrintList() throws Exception {
+ assertEquals("foo, bar, baz",
+ LintUtils.formatList(Arrays.asList("foo", "bar", "baz"), 3));
+ assertEquals("foo, bar, baz",
+ LintUtils.formatList(Arrays.asList("foo", "bar", "baz"), 5));
+
+ assertEquals("foo, bar, baz... (3 more)",
+ LintUtils.formatList(
+ Arrays.asList("foo", "bar", "baz", "4", "5", "6"), 3));
+ assertEquals("foo... (5 more)",
+ LintUtils.formatList(
+ Arrays.asList("foo", "bar", "baz", "4", "5", "6"), 1));
+ assertEquals("foo, bar, baz",
+ LintUtils.formatList(Arrays.asList("foo", "bar", "baz"), 0));
+ }
+
+ public void testEndsWith() throws Exception {
+ assertTrue(LintUtils.endsWith("Foo", ""));
+ assertTrue(LintUtils.endsWith("Foo", "o"));
+ assertTrue(LintUtils.endsWith("Foo", "oo"));
+ assertTrue(LintUtils.endsWith("Foo", "Foo"));
+ assertTrue(LintUtils.endsWith("Foo", "FOO"));
+ assertTrue(LintUtils.endsWith("Foo", "fOO"));
+
+ assertFalse(LintUtils.endsWith("Foo", "f"));
+ }
+
+ public void testStartsWith() throws Exception {
+ assertTrue(LintUtils.startsWith("FooBar", "Bar", 3));
+ assertTrue(LintUtils.startsWith("FooBar", "BAR", 3));
+ assertTrue(LintUtils.startsWith("FooBar", "Foo", 0));
+ assertFalse(LintUtils.startsWith("FooBar", "Foo", 2));
+ }
+
+ public void testIsXmlFile() throws Exception {
+ assertTrue(LintUtils.isXmlFile(new File("foo.xml")));
+ assertTrue(LintUtils.isXmlFile(new File("foo.Xml")));
+ assertTrue(LintUtils.isXmlFile(new File("foo.XML")));
+
+ assertFalse(LintUtils.isXmlFile(new File("foo.png")));
+ assertFalse(LintUtils.isXmlFile(new File("xml")));
+ assertFalse(LintUtils.isXmlFile(new File("xml.png")));
+ }
+
+ public void testGetBasename() throws Exception {
+ assertEquals("foo", LintUtils.getBaseName("foo.png"));
+ assertEquals("foo", LintUtils.getBaseName("foo.9.png"));
+ assertEquals(".foo", LintUtils.getBaseName(".foo"));
+ }
+
+ public void testEditDistance() {
+ assertEquals(0, LintUtils.editDistance("kitten", "kitten"));
+
+ // editing kitten to sitting has edit distance 3:
+ // replace k with s
+ // replace e with i
+ // append g
+ assertEquals(3, LintUtils.editDistance("kitten", "sitting"));
+
+ assertEquals(3, LintUtils.editDistance("saturday", "sunday"));
+ assertEquals(1, LintUtils.editDistance("button", "bitton"));
+ assertEquals(6, LintUtils.editDistance("radiobutton", "bitton"));
+ }
+
+ public void testSplitPath() throws Exception {
+ assertTrue(Arrays.equals(new String[] { "/foo", "/bar", "/baz" },
+ Iterables.toArray(splitPath("/foo:/bar:/baz"), String.class)));
+
+ assertTrue(Arrays.equals(new String[] { "/foo", "/bar" },
+ Iterables.toArray(splitPath("/foo;/bar"), String.class)));
+
+ assertTrue(Arrays.equals(new String[] { "/foo", "/bar:baz" },
+ Iterables.toArray(splitPath("/foo;/bar:baz"), String.class)));
+
+ assertTrue(Arrays.equals(new String[] { "\\foo\\bar", "\\bar\\foo" },
+ Iterables.toArray(splitPath("\\foo\\bar;\\bar\\foo"), String.class)));
+
+ assertTrue(Arrays.equals(new String[] { "${sdk.dir}\\foo\\bar", "\\bar\\foo" },
+ Iterables.toArray(splitPath("${sdk.dir}\\foo\\bar;\\bar\\foo"),
+ String.class)));
+
+ assertTrue(Arrays.equals(new String[] { "${sdk.dir}/foo/bar", "/bar/foo" },
+ Iterables.toArray(splitPath("${sdk.dir}/foo/bar:/bar/foo"),
+ String.class)));
+
+ assertTrue(Arrays.equals(new String[] { "C:\\foo", "/bar" },
+ Iterables.toArray(splitPath("C:\\foo:/bar"), String.class)));
+ }
+
+ public void testCommonParen1() {
+ assertEquals(new File("/a"), (LintUtils.getCommonParent(
+ new File("/a/b/c/d/e"), new File("/a/c"))));
+ assertEquals(new File("/a"), (LintUtils.getCommonParent(
+ new File("/a/c"), new File("/a/b/c/d/e"))));
+
+ assertEquals(new File("/"), LintUtils.getCommonParent(
+ new File("/foo/bar"), new File("/bar/baz")));
+ assertEquals(new File("/"), LintUtils.getCommonParent(
+ new File("/foo/bar"), new File("/")));
+ assertNull(LintUtils.getCommonParent(
+ new File("C:\\Program Files"), new File("F:\\")));
+ assertNull(LintUtils.getCommonParent(
+ new File("C:/Program Files"), new File("F:/")));
+
+ assertEquals(new File("/foo/bar/baz"), LintUtils.getCommonParent(
+ new File("/foo/bar/baz"), new File("/foo/bar/baz")));
+ assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
+ new File("/foo/bar/baz"), new File("/foo/bar")));
+ assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
+ new File("/foo/bar/baz"), new File("/foo/bar/foo")));
+ assertEquals(new File("/foo"), LintUtils.getCommonParent(
+ new File("/foo/bar"), new File("/foo/baz")));
+ assertEquals(new File("/foo"), LintUtils.getCommonParent(
+ new File("/foo/bar"), new File("/foo/baz")));
+ assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
+ new File("/foo/bar"), new File("/foo/bar/baz")));
+ }
+
+ public void testCommonParent2() {
+ assertEquals(new File("/"), LintUtils.getCommonParent(
+ Arrays.asList(new File("/foo/bar"), new File("/bar/baz"))));
+ assertEquals(new File("/"), LintUtils.getCommonParent(
+ Arrays.asList(new File("/foo/bar"), new File("/"))));
+ assertNull(LintUtils.getCommonParent(
+ Arrays.asList(new File("C:\\Program Files"), new File("F:\\"))));
+ assertNull(LintUtils.getCommonParent(
+ Arrays.asList(new File("C:/Program Files"), new File("F:/"))));
+
+ assertEquals(new File("/foo"), LintUtils.getCommonParent(
+ Arrays.asList(new File("/foo/bar"), new File("/foo/baz"))));
+ assertEquals(new File("/foo"), LintUtils.getCommonParent(
+ Arrays.asList(new File("/foo/bar"), new File("/foo/baz"),
+ new File("/foo/baz/f"))));
+ assertEquals(new File("/foo/bar"), LintUtils.getCommonParent(
+ Arrays.asList(new File("/foo/bar"), new File("/foo/bar/baz"),
+ new File("/foo/bar/foo2/foo3"))));
+ }
+
+ public void testStripIdPrefix() throws Exception {
+ assertEquals("foo", LintUtils.stripIdPrefix("@+id/foo"));
+ assertEquals("foo", LintUtils.stripIdPrefix("@id/foo"));
+ assertEquals("foo", LintUtils.stripIdPrefix("foo"));
+ }
+
+ public void testIdReferencesMatch() throws Exception {
+ assertTrue(LintUtils.idReferencesMatch("@+id/foo", "@+id/foo"));
+ assertTrue(LintUtils.idReferencesMatch("@id/foo", "@id/foo"));
+ assertTrue(LintUtils.idReferencesMatch("@id/foo", "@+id/foo"));
+ assertTrue(LintUtils.idReferencesMatch("@+id/foo", "@id/foo"));
+
+ assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@+id/bar"));
+ assertFalse(LintUtils.idReferencesMatch("@id/foo", "@+id/bar"));
+ assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@id/bar"));
+ assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@+id/bar"));
+
+ assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@+id/foo1"));
+ assertFalse(LintUtils.idReferencesMatch("@id/foo", "@id/foo1"));
+ assertFalse(LintUtils.idReferencesMatch("@id/foo", "@+id/foo1"));
+ assertFalse(LintUtils.idReferencesMatch("@+id/foo", "@id/foo1"));
+
+ assertFalse(LintUtils.idReferencesMatch("@+id/foo1", "@+id/foo"));
+ assertFalse(LintUtils.idReferencesMatch("@id/foo1", "@id/foo"));
+ assertFalse(LintUtils.idReferencesMatch("@id/foo1", "@+id/foo"));
+ assertFalse(LintUtils.idReferencesMatch("@+id/foo1", "@id/foo"));
+ }
+
+ private static void checkEncoding(String encoding, boolean writeBom, String lineEnding)
+ throws Exception {
+ @SuppressWarnings("StringBufferReplaceableByString")
+ StringBuilder sb = new StringBuilder();
+
+ // Norwegian extra vowel characters such as "latin small letter a with ring above"
+ String value = "\u00e6\u00d8\u00e5";
+ String expected = "First line." + lineEnding + "Second line." + lineEnding
+ + "Third line." + lineEnding + value + lineEnding;
+ sb.append(expected);
+ File file = File.createTempFile("getEncodingTest" + encoding + writeBom, ".txt");
+ file.deleteOnExit();
+ BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(file));
+ OutputStreamWriter writer = new OutputStreamWriter(stream, encoding);
+
+ if (writeBom) {
+ String normalized = encoding.toLowerCase(Locale.US).replace("-", "_");
+ if (normalized.equals("utf_8")) {
+ stream.write(0xef);
+ stream.write(0xbb);
+ stream.write(0xbf);
+ } else if (normalized.equals("utf_16")) {
+ stream.write(0xfe);
+ stream.write(0xff);
+ } else if (normalized.equals("utf_16le")) {
+ stream.write(0xff);
+ stream.write(0xfe);
+ } else if (normalized.equals("utf_32")) {
+ stream.write(0x0);
+ stream.write(0x0);
+ stream.write(0xfe);
+ stream.write(0xff);
+ } else if (normalized.equals("utf_32le")) {
+ stream.write(0xff);
+ stream.write(0xfe);
+ stream.write(0x0);
+ stream.write(0x0);
+ } else {
+ fail("Can't write BOM for encoding " + encoding);
+ }
+ }
+ writer.write(sb.toString());
+ writer.close();
+
+ String s = LintUtils.getEncodedString(new LintCliClient(), file);
+ assertEquals(expected, s);
+ }
+
+ public void testGetEncodedString() throws Exception {
+ checkEncoding("utf-8", false /*bom*/, "\n");
+ checkEncoding("UTF-8", false /*bom*/, "\n");
+ checkEncoding("UTF_16", false /*bom*/, "\n");
+ checkEncoding("UTF-16", false /*bom*/, "\n");
+ checkEncoding("UTF_16LE", false /*bom*/, "\n");
+
+ // Try BOM's
+ checkEncoding("utf-8", true /*bom*/, "\n");
+ checkEncoding("UTF-8", true /*bom*/, "\n");
+ checkEncoding("UTF_16", true /*bom*/, "\n");
+ checkEncoding("UTF-16", true /*bom*/, "\n");
+ checkEncoding("UTF_16LE", true /*bom*/, "\n");
+ checkEncoding("UTF_32", true /*bom*/, "\n");
+ checkEncoding("UTF_32LE", true /*bom*/, "\n");
+
+ // Make sure this works for \r and \r\n as well
+ checkEncoding("UTF-16", false /*bom*/, "\r");
+ checkEncoding("UTF_16LE", false /*bom*/, "\r");
+ checkEncoding("UTF-16", false /*bom*/, "\r\n");
+ checkEncoding("UTF_16LE", false /*bom*/, "\r\n");
+ checkEncoding("UTF-16", true /*bom*/, "\r");
+ checkEncoding("UTF_16LE", true /*bom*/, "\r");
+ checkEncoding("UTF_32", true /*bom*/, "\r");
+ checkEncoding("UTF_32LE", true /*bom*/, "\r");
+ checkEncoding("UTF-16", true /*bom*/, "\r\n");
+ checkEncoding("UTF_16LE", true /*bom*/, "\r\n");
+ checkEncoding("UTF_32", true /*bom*/, "\r\n");
+ checkEncoding("UTF_32LE", true /*bom*/, "\r\n");
+ }
+
+ public void testGetLocaleAndRegion() throws Exception {
+ assertNull(getLocaleAndRegion(""));
+ assertNull(getLocaleAndRegion("values"));
+ assertNull(getLocaleAndRegion("values-xlarge-port"));
+ assertEquals("en", getLocaleAndRegion("values-en"));
+ assertEquals("pt-rPT", getLocaleAndRegion("values-pt-rPT-nokeys"));
+ assertEquals("b+pt+PT", getLocaleAndRegion("values-b+pt+PT-nokeys"));
+ assertEquals("zh-rCN", getLocaleAndRegion("values-zh-rCN-keyshidden"));
+ assertEquals("ms", getLocaleAndRegion("values-ms-keyshidden"));
+ }
+
+ public void testIsImported() throws Exception {
+ assertFalse(isImported(getCompilationUnit(
+ "package foo.bar;\n" +
+ "class Foo {\n" +
+ "}\n"),
+ "android.app.Activity"));
+
+ assertTrue(isImported(getCompilationUnit(
+ "package foo.bar;\n" +
+ "import foo.bar.*;\n" +
+ "import android.app.Activity;\n" +
+ "import foo.bar.Baz;\n" +
+ "class Foo {\n" +
+ "}\n"),
+ "android.app.Activity"));
+
+ assertTrue(isImported(getCompilationUnit(
+ "package foo.bar;\n" +
+ "import android.app.Activity;\n" +
+ "class Foo {\n" +
+ "}\n"),
+ "android.app.Activity"));
+
+ assertTrue(isImported(getCompilationUnit(
+ "package foo.bar;\n" +
+ "import android.app.*;\n" +
+ "class Foo {\n" +
+ "}\n"),
+ "android.app.Activity"));
+
+ assertFalse(isImported(getCompilationUnit(
+ "package foo.bar;\n" +
+ "import android.app.*;\n" +
+ "import foo.bar.Activity;\n" +
+ "class Foo {\n" +
+ "}\n"),
+ "android.app.Activity"));
+ }
+
+ public void testComputeResourceName() {
+ assertEquals("", computeResourceName("", ""));
+ assertEquals("foo", computeResourceName("", "foo"));
+ assertEquals("foo", computeResourceName("foo", ""));
+ assertEquals("prefix_name", computeResourceName("prefix_", "name"));
+ assertEquals("prefixName", computeResourceName("prefix", "name"));
+ }
+
+ public static Node getCompilationUnit(@Language("JAVA") String javaSource) {
+ return getCompilationUnit(javaSource, new File("test"));
+ }
+
+
+ public static Node getCompilationUnit(@Language("JAVA") String javaSource, File relativePath) {
+ JavaContext context = parse(javaSource, relativePath);
+ return context.getCompilationUnit();
+ }
+
+ public static JavaContext parse(@Language("JAVA") final String javaSource,
+ final File relativePath) {
+ File dir = new File("projectDir");
+ final File fullPath = new File(dir, relativePath.getPath());
+ LintCliClient client = new LintCliClient() {
+ @NonNull
+ @Override
+ public String readFile(@NonNull File file) {
+ if (file.getPath().equals(fullPath.getPath())) {
+ return javaSource;
+ }
+ return super.readFile(file);
+ }
+
+ @Nullable
+ @Override
+ public IAndroidTarget getCompileTarget(@NonNull Project project) {
+ IAndroidTarget[] targets = getTargets();
+ for (int i = targets.length - 1; i >= 0; i--) {
+ IAndroidTarget target = targets[i];
+ if (target.isPlatform()) {
+ return target;
+ }
+ }
+
+ return super.getCompileTarget(project);
+ }
+ };
+ Project project = client.getProject(dir, dir);
+
+ LintDriver driver = new LintDriver(new BuiltinIssueRegistry(),
+ new LintCliClient());
+ driver.setScope(Scope.JAVA_FILE_SCOPE);
+ TestContext context = new TestContext(driver, client, project, javaSource, fullPath);
+ JavaParser parser = new EcjParser(client, project);
+ parser.prepareJavaParse(Collections.<JavaContext>singletonList(context));
+ Node compilationUnit = parser.parseJava(context);
+ assertNotNull(javaSource, compilationUnit);
+ context.setCompilationUnit(compilationUnit);
+ return context;
+ }
+
+ public void testConvertVersion() {
+ assertEquals(new AndroidVersion(5, null), convertVersion(new DefaultApiVersion(5, null),
+ null));
+ assertEquals(new AndroidVersion(19, null), convertVersion(new DefaultApiVersion(19, null),
+ null));
+ //noinspection SpellCheckingInspection
+ assertEquals(new AndroidVersion(18, "KITKAT"), // a preview platform API level is not final
+ convertVersion(new DefaultApiVersion(0, "KITKAT"),
+ null));
+ }
+
+ public void testIsModelOlderThan() throws Exception {
+ AndroidProject project = mock(AndroidProject.class);
+ when(project.getModelVersion()).thenReturn("0.10.4");
+
+ assertTrue(LintUtils.isModelOlderThan(project, 0, 10, 5));
+ assertTrue(LintUtils.isModelOlderThan(project, 0, 11, 0));
+ assertTrue(LintUtils.isModelOlderThan(project, 0, 11, 4));
+ assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 0));
+
+ project = mock(AndroidProject.class);
+ when(project.getModelVersion()).thenReturn("0.11.0");
+
+ assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 0));
+ assertFalse(LintUtils.isModelOlderThan(project, 0, 11, 0));
+ assertFalse(LintUtils.isModelOlderThan(project, 0, 10, 4));
+
+ project = mock(AndroidProject.class);
+ when(project.getModelVersion()).thenReturn("0.11.5");
+
+ assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 0));
+ assertFalse(LintUtils.isModelOlderThan(project, 0, 11, 0));
+
+ project = mock(AndroidProject.class);
+ when(project.getModelVersion()).thenReturn("1.0.0");
+
+ assertTrue(LintUtils.isModelOlderThan(project, 1, 0, 1));
+ assertFalse(LintUtils.isModelOlderThan(project, 1, 0, 0));
+ assertFalse(LintUtils.isModelOlderThan(project, 0, 11, 0));
+ }
+
+ private static final class DefaultApiVersion implements ApiVersion {
+ private final int mApiLevel;
+ private final String mCodename;
+
+ public DefaultApiVersion(int apiLevel, @Nullable String codename) {
+ mApiLevel = apiLevel;
+ mCodename = codename;
+ }
+
+ @Override
+ public int getApiLevel() {
+ return mApiLevel;
+ }
+
+ @Nullable
+ @Override
+ public String getCodename() {
+ return mCodename;
+ }
+
+ @NonNull
+ @Override
+ public String getApiString() {
+ fail("Not needed in this test");
+ return "<invalid>";
+ }
+ }
+
+ public void testFindSubstring() {
+ assertEquals("foo", findSubstring("foo", null, null));
+ assertEquals("foo", findSubstring("foo ", null, " "));
+ assertEquals("foo", findSubstring(" foo", " ", null));
+ assertEquals("foo", findSubstring("[foo]", "[", "]"));
+ }
+
+ public void testGetFormattedParameters() {
+ assertEquals(Arrays.asList("foo","bar"),
+ getFormattedParameters("Prefix %1$s Divider %2$s Suffix",
+ "Prefix foo Divider bar Suffix"));
+ }
+
+ public void testEscapePropertyValue() throws Exception {
+ assertEquals("foo", escapePropertyValue("foo"));
+ assertEquals("\\ foo ", escapePropertyValue(" foo "));
+ assertEquals("c\\:/foo/bar", escapePropertyValue("c:/foo/bar"));
+ assertEquals("\\!\\#\\:\\\\a\\\\b\\\\c", escapePropertyValue("!#:\\a\\b\\c"));
+ assertEquals(
+ "foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo\\#foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo",
+ escapePropertyValue("foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo#foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo"));
+ }
+
+ private static class TestContext extends JavaContext {
+ private final String mJavaSource;
+ public TestContext(LintDriver driver, LintCliClient client, Project project,
+ String javaSource, File file) {
+ //noinspection ConstantConditions
+ super(driver, project,
+ null, file, client.getJavaParser(null));
+
+ mJavaSource = javaSource;
+ }
+
+ @Override
+ @Nullable
+ public String getContents() {
+ return mJavaSource;
+ }
+ }
+}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/LocationTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LocationTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/detector/api/LocationTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/LocationTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/ScopeTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ScopeTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/detector/api/ScopeTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/ScopeTest.java
diff --git a/lint/cli/src/test/java/com/android/tools/lint/detector/api/SeverityTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/SeverityTest.java
similarity index 100%
rename from lint/cli/src/test/java/com/android/tools/lint/detector/api/SeverityTest.java
rename to lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/SeverityTest.java
diff --git a/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/TextFormatTest.java b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/TextFormatTest.java
new file mode 100644
index 0000000..1984c0a
--- /dev/null
+++ b/lint/libs/lint-tests/src/test/java/com/android/tools/lint/detector/api/TextFormatTest.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.lint.detector.api;
+
+import static com.android.SdkConstants.AUTO_URI;
+import static com.android.tools.lint.detector.api.TextFormat.HTML;
+import static com.android.tools.lint.detector.api.TextFormat.RAW;
+import static com.android.tools.lint.detector.api.TextFormat.TEXT;
+
+import junit.framework.TestCase;
+
+public class TextFormatTest extends TestCase {
+ private static String convertMarkup(String raw, TextFormat to) {
+ return RAW.convertTo(raw, to);
+ }
+
+ public void testConvertMarkup() throws Exception {
+ assertEquals("", convertMarkup("", HTML));
+
+ // Normal escapes
+ assertEquals("foo bar", convertMarkup("foo bar", HTML));
+ assertEquals("foo<br/>\nbar", convertMarkup("foo\nbar", HTML));
+ assertEquals("foo<br/>\nbar", convertMarkup("foo\nbar", HTML));
+ assertEquals("<&>'\"", convertMarkup("<&>'\"", HTML));
+
+ // HTML Formatting
+ assertEquals("<code>@TargetApi(11)</code>, ", convertMarkup("`@TargetApi(11)`, ",
+ HTML));
+ assertEquals("with <code>getArguments()</code>.",
+ convertMarkup("with `getArguments()`.", HTML));
+ assertEquals("(<code>dip</code>)", convertMarkup("(`dip`)", HTML));
+ assertEquals(" <code>0dp</code> ", convertMarkup(" `0dp` ", HTML));
+ assertEquals(
+ "resources under <code>$ANDROID_SK/platforms/android-$VERSION/data/res/.</code>",
+ convertMarkup(
+ "resources under `$ANDROID_SK/platforms/android-$VERSION/data/res/.`",
+ HTML));
+ assertEquals("wrong format. Instead of <code>-keepclasseswithmembernames</code> use ",
+ convertMarkup("wrong format. Instead of `-keepclasseswithmembernames` use ",
+ HTML));
+ assertEquals("<code>exported=false</code>)", convertMarkup("`exported=false`)",
+ HTML));
+ assertEquals("by setting <code>inputType=\"text\"</code>.",
+ convertMarkup("by setting `inputType=\"text\"`.", HTML));
+ assertEquals("* <code>View(Context context)</code><br/>\n",
+ convertMarkup("* `View(Context context)`\n", HTML));
+ assertEquals("The <code>@+id/</code> syntax", convertMarkup("The `@+id/` syntax",
+ HTML));
+ assertEquals("", convertMarkup("", HTML));
+ assertEquals("", convertMarkup("", HTML));
+ assertEquals("This is <b>bold</b>", convertMarkup("This is *bold*", HTML));
+ assertEquals("Visit <a href=\"http://google.com\">http://google.com</a>.",
+ convertMarkup("Visit http://google.com.", HTML));
+ assertEquals("This is <code>monospace</code>!", convertMarkup("This is `monospace`!",
+ HTML));
+ assertEquals(
+ "See <a href=\"http://developer.android.com/reference/android/view/" +
+ "WindowManager.LayoutParams.html#FLAG_KEEP_SCREEN_ON\">http://developer." +
+ "android.com/reference/android/view/WindowManager.LayoutParams.html#" +
+ "FLAG_KEEP_SCREEN_ON</a>.",
+ convertMarkup(
+ "See http://developer.android.com/reference/android/view/WindowManager.Layout" +
+ "Params.html#FLAG_KEEP_SCREEN_ON.", HTML));
+
+ // Text formatting
+ assertEquals("@TargetApi(11), ", convertMarkup("`@TargetApi(11)`, ", TEXT));
+ assertEquals("with getArguments().", convertMarkup("with `getArguments()`.", TEXT));
+ assertEquals("bold", convertMarkup("*bold*", TEXT));
+ assertEquals("Visit http://google.com.", convertMarkup("Visit http://google.com.",
+ TEXT));
+
+ // Corners (match at the beginning and end)
+ assertEquals("<b>bold</b>", convertMarkup("*bold*", HTML));
+ assertEquals("<code>monospace</code>!", convertMarkup("`monospace`!", HTML));
+
+ // Not formatting
+ assertEquals("a*b", convertMarkup("a*b", HTML));
+ assertEquals("a* b*", convertMarkup("a* b*", HTML));
+ assertEquals("*a *b", convertMarkup("*a *b", HTML));
+ assertEquals("Prefix is http:// ", convertMarkup("Prefix is http:// ", HTML));
+ assertEquals("", convertMarkup("", HTML));
+ assertEquals("", convertMarkup("", HTML));
+ assertEquals("", convertMarkup("", HTML));
+ assertEquals("", convertMarkup("", HTML));
+ assertEquals("This is * not * bold", convertMarkup("This is * not * bold", HTML));
+ assertEquals("* List item 1<br/>\n* List Item 2",
+ convertMarkup("* List item 1\n* List Item 2", HTML));
+ assertEquals("myhttp://foo.bar", convertMarkup("myhttp://foo.bar", HTML));
+ }
+
+ public void testConvertMarkup2() throws Exception {
+ // http at the end:
+ // Explanation from ManifestDetector#TARGET_NEWER
+ String explanation =
+ "When your application runs on a version of Android that is more recent than your " +
+ "targetSdkVersion specifies that it has been tested with, various compatibility " +
+ "modes kick in. This ensures that your application continues to work, but it may " +
+ "look out of place. For example, if the targetSdkVersion is less than 14, your " +
+ "app may get an option button in the UI.\n" +
+ "\n" +
+ "To fix this issue, set the targetSdkVersion to the highest available value. Then " +
+ "test your app to make sure everything works correctly. You may want to consult " +
+ "the compatibility notes to see what changes apply to each version you are adding " +
+ "support for: " +
+ "http://developer.android.com/reference/android/os/Build.VERSION_CODES.html";
+
+ assertEquals(
+ "When your application runs on a version of Android that is more recent than your " +
+ "targetSdkVersion specifies that it has been tested with, various compatibility " +
+ "modes kick in. This ensures that your application continues to work, but it may " +
+ "look out of place. For example, if the targetSdkVersion is less than 14, your " +
+ "app may get an option button in the UI.<br/>\n" +
+ "<br/>\n" +
+ "To fix this issue, set the targetSdkVersion to the highest available value. Then " +
+ "test your app to make sure everything works correctly. You may want to consult " +
+ "the compatibility notes to see what changes apply to each version you are adding " +
+ "support for: " +
+ "<a href=\"http://developer.android.com/reference/android/os/Build.VERSION_CODES." +
+ "html\">http://developer.android.com/reference/android/os/Build.VERSION_CODES.html" +
+ "</a>",
+ convertMarkup(explanation, HTML));
+ }
+
+ public void testConvertMarkup3() throws Exception {
+ // embedded http markup test
+ // Explanation from NamespaceDetector#CUSTOMVIEW
+ String explanation =
+ "When using a custom view with custom attributes in a library project, the layout " +
+ "must use the special namespace " + AUTO_URI + " instead of a URI which includes " +
+ "the library project's own package. This will be used to automatically adjust the " +
+ "namespace of the attributes when the library resources are merged into the " +
+ "application project.";
+ assertEquals(
+ "When using a custom view with custom attributes in a library project, the layout " +
+ "must use the special namespace " +
+ "<a href=\"http://schemas.android.com/apk/res-auto\">" +
+ "http://schemas.android.com/apk/res-auto</a> " +
+ "instead of a URI which includes the library project's own package. " +
+ "This will be used to automatically adjust the namespace of the attributes when " +
+ "the library resources are merged into the application project.",
+ convertMarkup(explanation, HTML));
+ }
+
+ public void testConvertMarkup4() throws Exception {
+ // monospace test
+ String explanation =
+ "The manifest should contain a `<uses-sdk>` element which defines the " +
+ "minimum minimum API Level required for the application to run, " +
+ "as well as the target version (the highest API level you have tested " +
+ "the version for.)";
+
+ assertEquals(
+ "The manifest should contain a <code><uses-sdk></code> element which defines the " +
+ "minimum minimum API Level required for the application to run, " +
+ "as well as the target version (the highest API level you have tested " +
+ "the version for.)",
+ convertMarkup(explanation, HTML));
+ }
+
+ public void testConvertMarkup5() throws Exception {
+ // monospace and bold test
+ // From ManifestDetector#MULTIPLE_USES_SDK
+ String explanation =
+ "The `<uses-sdk>` element should appear just once; the tools will *not* merge the " +
+ "contents of all the elements so if you split up the attributes across multiple " +
+ "elements, only one of them will take effect. To fix this, just merge all the " +
+ "attributes from the various elements into a single <uses-sdk> element.";
+
+ assertEquals(
+ "The <code><uses-sdk></code> element should appear just once; the tools " +
+ "will <b>not</b> merge the " +
+ "contents of all the elements so if you split up the attributes across multiple " +
+ "elements, only one of them will take effect. To fix this, just merge all the " +
+ "attributes from the various elements into a single <uses-sdk> element.",
+ convertMarkup(explanation, HTML));
+ }
+
+ public void testConvertMarkup6() throws Exception {
+ // Embedded code next to attributes
+ // From AlwaysShowActionDetector#ISSUE
+ String explanation =
+ "Using `showAsAction=\"always\"` in menu XML, or `MenuItem.SHOW_AS_ACTION_ALWAYS` in "+
+ "Java code is usually a deviation from the user interface style guide." +
+ "Use `ifRoom` or the corresponding `MenuItem.SHOW_AS_ACTION_IF_ROOM` instead.\n" +
+ "\n" +
+ "If `always` is used sparingly there are usually no problems and behavior is " +
+ "roughly equivalent to `ifRoom` but with preference over other `ifRoom` " +
+ "items. Using it more than twice in the same menu is a bad idea.\n" +
+ "\n" +
+ "This check looks for menu XML files that contain more than two `always` " +
+ "actions, or some `always` actions and no `ifRoom` actions. In Java code, " +
+ "it looks for projects that contain references to `MenuItem.SHOW_AS_ACTION_ALWAYS` " +
+ "and no references to `MenuItem.SHOW_AS_ACTION_IF_ROOM`.";
+
+ assertEquals(
+ "Using <code>showAsAction=\"always\"</code> in menu XML, or " +
+ "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> in Java code is usually a deviation " +
+ "from the user interface style guide.Use <code>ifRoom</code> or the " +
+ "corresponding <code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code> instead.<br/>\n" +
+ "<br/>\n" +
+ "If <code>always</code> is used sparingly there are usually no problems and " +
+ "behavior is roughly equivalent to <code>ifRoom</code> but with preference over " +
+ "other <code>ifRoom</code> items. Using it more than twice in the same menu " +
+ "is a bad idea.<br/>\n" +
+ "<br/>\n" +
+ "This check looks for menu XML files that contain more than two <code>always</code> " +
+ "actions, or some <code>always</code> actions and no <code>ifRoom</code> actions. " +
+ "In Java code, it looks for projects that contain references to " +
+ "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> and no references to " +
+ "<code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code>.",
+ convertMarkup(explanation, HTML));
+ }
+
+ public void testConvertSelf() throws Exception {
+ // No changes
+ assertEquals("`foo`<b>", RAW.convertTo("`foo`<b>", RAW));
+ assertEquals("`foo`<b>", TEXT.convertTo("`foo`<b>", TEXT));
+ assertEquals("`foo`<b>", HTML.convertTo("`foo`<b>", HTML));
+ }
+
+ public void testConvertFromHtml() throws Exception {
+ assertEquals(""
+ + "Line 1\n"
+ + "Line 2 <div>\n",
+ HTML.convertTo("<html>Line 1<br>Line 2\n<!-- comment --><div></html>",
+ TEXT));
+ }
+
+ public void testConvertFromHtml2() throws Exception {
+ assertEquals(""
+ + "Using showAsAction=\"always\" in menu XML, or\n"
+ + "MenuItem.SHOW_AS_ACTION_ALWAYS in Java code is usually a\n"
+ + "deviation from the user interface style guide.Use ifRoom or\n"
+ + "the corresponding MenuItem.SHOW_AS_ACTION_IF_ROOM instead.\n"
+ + "If always is used sparingly there are usually no problems\n"
+ + "and behavior is roughly equivalent to ifRoom but with\n"
+ + "preference over other ifRoom items. Using it more than twice\n"
+ + "in the same menu is a bad idea. This check looks for menu\n"
+ + "XML files that contain more than two always actions, or some\n"
+ + "always actions and no ifRoom actions. In Java code, it looks\n"
+ + "for projects that contain references to\n"
+ + "MenuItem.SHOW_AS_ACTION_ALWAYS and no references to\n"
+ + "MenuItem.SHOW_AS_ACTION_IF_ROOM.\n",
+ HTML.convertTo(
+ "Using <code>showAsAction=\"always\"</code> in menu XML, or " +
+ "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> in Java code is usually a deviation " +
+ "from the user interface style guide.Use <code>ifRoom</code> or the " +
+ "corresponding <code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code> instead.<br/>\n" +
+ "<br/>\n" +
+ "If <code>always</code> is used sparingly there are usually no problems and " +
+ "behavior is roughly equivalent to <code>ifRoom</code> but with preference over " +
+ "other <code>ifRoom</code> items. Using it more than twice in the same menu " +
+ "is a bad idea.<br/>\n" +
+ "<br/>\n" +
+ "This check looks for menu XML files that contain more than two <code>always</code> " +
+ "actions, or some <code>always</code> actions and no <code>ifRoom</code> actions. " +
+ "In Java code, it looks for projects that contain references to " +
+ "<code>MenuItem.SHOW_AS_ACTION_ALWAYS</code> and no references to " +
+ "<code>MenuItem.SHOW_AS_ACTION_IF_ROOM</code>.",
+ TEXT));
+ }
+
+ public void testNbsp() throws Exception {
+ assertEquals(" text", RAW.convertTo("\u00a0\u00A0text", HTML));
+ }
+
+ public void test181820() throws Exception {
+ // Regression test for https://code.google.com/p/android/issues/detail?id=181820
+ // Make sure we handle formatting characters at the end
+ assertEquals("foo bar *", convertMarkup("foo bar *", HTML));
+ }
+}
\ No newline at end of file
diff --git a/misc/api-generator/.classpath b/misc/api-generator/.classpath
deleted file mode 100644
index 8c8b2ad..0000000
--- a/misc/api-generator/.classpath
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src/main/java"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/m2/repository/net/sf/kxml/kxml2/2.3.0/kxml2-2.3.0.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/m2/repository/net/sf/kxml/kxml2/2.3.0/kxml2-2.3.0-sources.jar"/>
- <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/m2/repository/org/ow2/asm/asm/5.0.3/asm-5.0.3.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/m2/repository/org/ow2/asm/asm/5.0.3/asm-5.0.3-sources.jar"/>
- <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/m2/repository/org/ow2/asm/asm-tree/5.0.3/asm-tree-5.0.3.jar" sourcepath="/ANDROID_SRC/prebuilts/tools/common/m2/repository/org/ow2/asm/asm-tree/5.0.3/asm-tree-5.0.3-sources.jar"/>
- <classpathentry combineaccessrules="false" kind="src" path="/common"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/misc/api-generator/.gitignore b/misc/api-generator/.gitignore
index c986b6b..89d1178 100644
--- a/misc/api-generator/.gitignore
+++ b/misc/api-generator/.gitignore
@@ -1,3 +1,8 @@
/bin
/build
-
+output.xml
+.gradle
+build
+.idea
+api-generator.iml
+run.sh
diff --git a/misc/api-generator/.project b/misc/api-generator/.project
deleted file mode 100644
index ef098b6..0000000
--- a/misc/api-generator/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>api-generator</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
diff --git a/misc/api-generator/.settings/org.eclipse.jdt.core.prefs b/misc/api-generator/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100755
index 7ce49c8..0000000
--- a/misc/api-generator/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,12 +0,0 @@
-#Wed Jun 04 15:07:10 PDT 2014
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.6
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate
-org.eclipse.jdt.core.compiler.debug.localVariable=generate
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.6
diff --git a/misc/api-generator/build.gradle b/misc/api-generator/build.gradle
index 9fe3ab2..754744b 100755
--- a/misc/api-generator/build.gradle
+++ b/misc/api-generator/build.gradle
@@ -1,15 +1,19 @@
+apply plugin: 'application'
apply plugin: 'java'
+mainClassName = "com.android.apigenerator.Main"
+applicationDefaultJvmArgs = ["-ea", "-Xms1048m", "-Xmx2048m"]
+
+repositories {
+ jcenter()
+}
+
+sourceCompatibility = 1.6
dependencies {
- compile project(':base:common')
+ compile 'com.android.tools:common:24.2.3'
compile 'net.sf.kxml:kxml2:2.3.0'
compile 'org.ow2.asm:asm:4.0'
compile 'org.ow2.asm:asm-tree:4.0'
}
-group = 'com.android.tools'
-archivesBaseName = 'api-generator'
-version = rootProject.ext.baseVersion
-
-// configure the manifest of the sdkJar task
-jar.manifest.attributes("Main-Class": "com.android.apigenerator.Main")
+defaultTasks 'installApp'
diff --git a/misc/api-generator/src/main/java/com/android/apigenerator/AndroidJarReader.java b/misc/api-generator/src/main/java/com/android/apigenerator/AndroidJarReader.java
index 8cdfe80..c06e1fa 100644
--- a/misc/api-generator/src/main/java/com/android/apigenerator/AndroidJarReader.java
+++ b/misc/api-generator/src/main/java/com/android/apigenerator/AndroidJarReader.java
@@ -17,7 +17,6 @@
package com.android.apigenerator;
import com.android.utils.Pair;
-
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
@@ -93,40 +92,41 @@
ClassNode classNode = new ClassNode();
reader.accept(classNode, 0 /*flags*/);
- if (classNode != null) {
- ApiClass theClass = addClass(map, classNode.name, apiLevel);
+ ApiClass theClass = addClass(map, classNode.name, apiLevel,
+ (classNode.access & Opcodes.ACC_DEPRECATED) != 0);
- // super class
- if (classNode.superName != null) {
- theClass.addSuperClass(classNode.superName, apiLevel);
+ // super class
+ if (classNode.superName != null) {
+ theClass.addSuperClass(classNode.superName, apiLevel);
+ }
+
+ // interfaces
+ for (Object interfaceName : classNode.interfaces) {
+ theClass.addInterface((String) interfaceName, apiLevel);
+ }
+
+ // fields
+ for (Object field : classNode.fields) {
+ FieldNode fieldNode = (FieldNode) field;
+ if ((fieldNode.access & Opcodes.ACC_PRIVATE) != 0) {
+ continue;
}
-
- // interfaces
- for (Object interfaceName : classNode.interfaces) {
- theClass.addInterface((String) interfaceName, apiLevel);
+ if (!fieldNode.name.startsWith("this$") &&
+ !fieldNode.name.equals("$VALUES")) {
+ boolean deprecated = (fieldNode.access & Opcodes.ACC_DEPRECATED) != 0;
+ theClass.addField(fieldNode.name, apiLevel, deprecated);
}
+ }
- // fields
- for (Object field : classNode.fields) {
- FieldNode fieldNode = (FieldNode) field;
- if ((fieldNode.access & Opcodes.ACC_PRIVATE) != 0) {
- continue;
- }
- if (fieldNode.name.startsWith("this$") == false &&
- fieldNode.name.equals("$VALUES") == false) {
- theClass.addField(fieldNode.name, apiLevel);
- }
+ // methods
+ for (Object method : classNode.methods) {
+ MethodNode methodNode = (MethodNode) method;
+ if ((methodNode.access & Opcodes.ACC_PRIVATE) != 0) {
+ continue;
}
-
- // methods
- for (Object method : classNode.methods) {
- MethodNode methodNode = (MethodNode) method;
- if ((methodNode.access & Opcodes.ACC_PRIVATE) != 0) {
- continue;
- }
- if (methodNode.name.equals("<clinit>") == false) {
- theClass.addMethod(methodNode.name + methodNode.desc, apiLevel);
- }
+ if (!methodNode.name.equals("<clinit>")) {
+ boolean deprecated = (methodNode.access & Opcodes.ACC_DEPRECATED) != 0;
+ theClass.addMethod(methodNode.name + methodNode.desc, apiLevel, deprecated);
}
}
}
@@ -171,7 +171,7 @@
String methodName = method.getKey();
int apiLevel = method.getValue();
- if (methodName.startsWith("<init>(") == false) {
+ if (!methodName.startsWith("<init>(")) {
for (Pair<String, Integer> parent : superClasses) {
// only check the parent if it was a parent class at the introduction
@@ -255,13 +255,17 @@
return false;
}
- private ApiClass addClass(HashMap<String, ApiClass> classes, String name, int apiLevel) {
+ private ApiClass addClass(HashMap<String, ApiClass> classes, String name, int apiLevel, boolean deprecated) {
ApiClass theClass = classes.get(name);
if (theClass == null) {
theClass = new ApiClass(name, apiLevel);
classes.put(name, theClass);
}
+ if (deprecated && apiLevel < theClass.deprecatedIn) {
+ theClass.deprecatedIn = apiLevel;
+ }
+
return theClass;
}
}
diff --git a/misc/api-generator/src/main/java/com/android/apigenerator/ApiClass.java b/misc/api-generator/src/main/java/com/android/apigenerator/ApiClass.java
index 50ee8c8..ad00cf4 100644
--- a/misc/api-generator/src/main/java/com/android/apigenerator/ApiClass.java
+++ b/misc/api-generator/src/main/java/com/android/apigenerator/ApiClass.java
@@ -33,6 +33,7 @@
* This is used to write the simplified XML file containing all the public API.
*
*/
+@SuppressWarnings({"UnnecessaryUnboxing", "UnnecessaryBoxing"})
public class ApiClass {
private final String mName;
@@ -45,12 +46,15 @@
private final Map<String, Integer> mFields = new HashMap<String, Integer>();
private final Map<String, Integer> mMethods = new HashMap<String, Integer>();
+ private final Map<String, Integer> mDeprecated = new HashMap<String, Integer>();
public ApiClass(String name, int since) {
mName = name;
mSince = since;
}
+ public int deprecatedIn = Integer.MAX_VALUE;
+
public String getName() {
return mName;
}
@@ -59,18 +63,31 @@
return mSince;
}
- public void addField(String name, int since) {
+ public void addField(String name, int since, boolean deprecated) {
Integer i = mFields.get(name);
if (i == null || i.intValue() > since) {
mFields.put(name, Integer.valueOf(since));
}
+
+ if (deprecated) {
+ i = mDeprecated.get(name);
+ if (i == null || i.intValue() > since) {
+ mDeprecated.put(name, Integer.valueOf(since));
+ }
+ }
}
- public void addMethod(String name, int since) {
+ public void addMethod(String name, int since, boolean deprecated) {
Integer i = mMethods.get(name);
if (i == null || i.intValue() > since) {
mMethods.put(name, Integer.valueOf(since));
}
+ if (deprecated) {
+ i = mDeprecated.get(name);
+ if (i == null || i.intValue() > since) {
+ mDeprecated.put(name, Integer.valueOf(since));
+ }
+ }
}
public Map<String, Integer> getMethods() {
@@ -114,6 +131,10 @@
stream.print(mName);
stream.print("\" since=\"");
stream.print(mSince);
+ if (deprecatedIn < Integer.MAX_VALUE) {
+ stream.print("\" deprecated=\"");
+ stream.print(deprecatedIn);
+ }
stream.println("\">");
print(mSuperClasses, "extends", stream);
@@ -134,11 +155,16 @@
});
for (Pair<String, Integer> pair : list) {
+ Integer deprecated = mDeprecated.get(pair.getFirst());
if (mSince == pair.getSecond()) {
stream.print("\t\t<");
stream.print(name);
stream.print(" name=\"");
stream.print(encodeAttribute(pair.getFirst()));
+ if (deprecated != null) {
+ stream.print("\" deprecated=\"");
+ stream.print(deprecated);
+ }
stream.println("\" />");
} else {
stream.print("\t\t<");
@@ -147,6 +173,10 @@
stream.print(encodeAttribute(pair.getFirst()));
stream.print("\" since=\"");
stream.print(pair.getSecond());
+ if (deprecated != null) {
+ stream.print("\" deprecated=\"");
+ stream.print(deprecated);
+ }
stream.println("\" />");
}
}
@@ -156,11 +186,16 @@
TreeMap<String, Integer> map2 = new TreeMap<String, Integer>(map);
for (Entry<String, Integer> entry : map2.entrySet()) {
+ Integer deprecated = mDeprecated.get(entry.getKey());
if (mSince == entry.getValue()) {
stream.print("\t\t<");
stream.print(name);
stream.print(" name=\"");
stream.print(encodeAttribute(entry.getKey()));
+ if (deprecated != null) {
+ stream.print("\" deprecated=\"");
+ stream.print(deprecated);
+ }
stream.println("\" />");
} else {
stream.print("\t\t<");
@@ -169,6 +204,10 @@
stream.print(encodeAttribute(entry.getKey()));
stream.print("\" since=\"");
stream.print(entry.getValue());
+ if (deprecated != null) {
+ stream.print("\" deprecated=\"");
+ stream.print(deprecated);
+ }
stream.println("\" />");
}
}
diff --git a/misc/api-generator/src/main/java/com/android/apigenerator/Main.java b/misc/api-generator/src/main/java/com/android/apigenerator/Main.java
index ef50c1d..2ca0c44 100644
--- a/misc/api-generator/src/main/java/com/android/apigenerator/Main.java
+++ b/misc/api-generator/src/main/java/com/android/apigenerator/Main.java
@@ -16,8 +16,6 @@
package com.android.apigenerator;
-
-
import java.io.File;
import java.io.PrintStream;
import java.util.ArrayList;
@@ -115,8 +113,8 @@
/**
* Creates the simplified diff-based API level.
- * @param outFolder the out folder.
- * @param classes
+ * @param outFile the output file
+ * @param classes the classes to write
*/
private static boolean createApiFile(File outFile, Map<String, ApiClass> classes) {
@@ -124,7 +122,7 @@
try {
ps = new PrintStream(outFile, "UTF-8");
ps.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
- ps.println("<api version=\"1\">");
+ ps.println("<api version=\"2\">");
TreeMap<String, ApiClass> map = new TreeMap<String, ApiClass>(classes);
for (ApiClass theClass : map.values()) {
(theClass).print(ps);
diff --git a/ninepatch/build.gradle b/ninepatch/build.gradle
index fdc032d..7699e1b 100644
--- a/ninepatch/build.gradle
+++ b/ninepatch/build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'java'
+apply plugin: 'jacoco'
apply plugin: 'sdk-java-lib'
group = 'com.android.tools'
diff --git a/ninepatch/src/main/java/com/android/ninepatch/NinePatchChunk.java b/ninepatch/src/main/java/com/android/ninepatch/NinePatchChunk.java
index 9dab870..6a684ea 100644
--- a/ninepatch/src/main/java/com/android/ninepatch/NinePatchChunk.java
+++ b/ninepatch/src/main/java/com/android/ninepatch/NinePatchChunk.java
@@ -119,7 +119,7 @@
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
try {
- if (mPatches.size() == 0) {
+ if (mPatches.isEmpty()) {
g.drawImage(image, x, y, scaledWidth, scaledHeight, null);
return;
}
@@ -272,7 +272,7 @@
data.mRemainderVertical = scaledHeight - remainderVertical;
data.mHorizontalPatchesSum = 0;
- if (mHorizontalPatches.size() > 0) {
+ if (!mHorizontalPatches.isEmpty()) {
int start = -1;
for (Rectangle rect : mHorizontalPatches) {
if (rect.x > start) {
@@ -291,7 +291,7 @@
}
data.mVerticalPatchesSum = 0;
- if (mVerticalPatches.size() > 0) {
+ if (!mVerticalPatches.isEmpty()) {
int start = -1;
for (Rectangle rect : mVerticalPatches) {
if (rect.y > start) {
@@ -341,14 +341,14 @@
mFixed = getRectangles(left.mFirst, top.mFirst);
mPatches = getRectangles(left.mSecond, top.mSecond);
- if (mFixed.size() > 0) {
+ if (!mFixed.isEmpty()) {
mHorizontalPatches = getRectangles(left.mFirst, top.mSecond);
mVerticalPatches = getRectangles(left.mSecond, top.mFirst);
} else {
- if (top.mFirst.size() > 0) {
+ if (!top.mFirst.isEmpty()) {
mHorizontalPatches = new ArrayList<Rectangle>(0);
mVerticalPatches = getVerticalRectangles(height, top.mFirst);
- } else if (left.mFirst.size() > 0) {
+ } else if (!left.mFirst.isEmpty()) {
mHorizontalPatches = getHorizontalRectangles(width, left.mFirst);
mVerticalPatches = new ArrayList<Rectangle>(0);
} else {
@@ -393,7 +393,7 @@
}
private Pair<Integer> getPadding(List<Pair<Integer>> pairs) {
- if (pairs.size() == 0) {
+ if (pairs.isEmpty()) {
return new Pair<Integer>(0, 0);
} else if (pairs.size() == 1) {
if (pairs.get(0).mFirst == 0) {
@@ -471,7 +471,7 @@
fixed.add(new Pair<Integer>(lastIndex, pixels.length));
}
- if (patches.size() == 0) {
+ if (patches.isEmpty()) {
patches.add(new Pair<Integer>(1, pixels.length));
startWithPatch[0] = true;
fixed.clear();
diff --git a/perflib/build.gradle b/perflib/build.gradle
index 5d58f9e..a923c0d 100644
--- a/perflib/build.gradle
+++ b/perflib/build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'java'
+apply plugin: 'jacoco'
group = 'com.android.tools.perflib'
archivesBaseName = 'perflib'
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/ArrayInstance.java b/perflib/src/main/java/com/android/tools/perflib/heap/ArrayInstance.java
index 2af5890..23774dc 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/ArrayInstance.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/ArrayInstance.java
@@ -17,6 +17,10 @@
package com.android.tools.perflib.heap;
import com.android.annotations.NonNull;
+import com.android.tools.perflib.heap.io.HprofBuffer;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
public class ArrayInstance extends Instance {
@@ -45,10 +49,30 @@
return values;
}
+ @NonNull
+ private byte[] asRawByteArray(int start, int elementCount) {
+ getBuffer().setPosition(mValuesOffset);
+ assert mType != Type.OBJECT;
+ assert start + elementCount <= mLength;
+ byte[] bytes = new byte[elementCount * mType.getSize()];
+ getBuffer().readSubSequence(bytes, start * mType.getSize(), elementCount * mType.getSize());
+ return bytes;
+ }
+
+ @NonNull
+ public char[] asCharArray(int offset, int length) {
+ assert mType == Type.CHAR;
+ // TODO: Make this copy less by supporting offset in asRawByteArray.
+ CharBuffer charBuffer = ByteBuffer.wrap(asRawByteArray(offset, length)).order(HprofBuffer.HPROF_BYTE_ORDER).asCharBuffer();
+ char[] result = new char[length];
+ charBuffer.get(result);
+ return result;
+ }
+
@Override
public final int getSize() {
// TODO: Take the rest of the fields into account: length, type, etc (~16 bytes).
- return mLength * mType.getSize();
+ return mLength * mHeap.mSnapshot.getTypeSize(mType);
}
@Override
@@ -57,9 +81,13 @@
if (mType == Type.OBJECT) {
for (Object value : getValues()) {
if (value instanceof Instance) {
- visitor.visitLater((Instance) value);
+ if (!mReferencesAdded) {
+ ((Instance)value).addReference(null, this);
+ }
+ visitor.visitLater(this, (Instance)value);
}
}
+ mReferencesAdded = true;
}
}
@@ -73,7 +101,15 @@
}
}
+ public Type getArrayType() {
+ return mType;
+ }
+
public final String toString() {
- return String.format("%s[%d]@0x%08x", mType.name(), mLength, mId);
+ String className = getClassObj().getClassName();
+ if (className.endsWith("[]")) {
+ className = className.substring(0, className.length() - 2);
+ }
+ return String.format("%s[%d]@%d (0x%x)", className, mLength, getUniqueId(), getUniqueId());
}
}
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/ClassInstance.java b/perflib/src/main/java/com/android/tools/perflib/heap/ClassInstance.java
index f7746d1..562ab48 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/ClassInstance.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/ClassInstance.java
@@ -17,10 +17,11 @@
package com.android.tools.perflib.heap;
import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
public class ClassInstance extends Instance {
@@ -32,19 +33,26 @@
}
@VisibleForTesting
- Object getField(Type type, String name) {
- return getValues().get(new Field(type, name));
+ @NonNull
+ List<FieldValue> getFields(String name) {
+ ArrayList<FieldValue> result = new ArrayList<FieldValue>();
+ for (FieldValue value : getValues()) {
+ if (value.getField().getName().equals(name)) {
+ result.add(value);
+ }
+ }
+ return result;
}
@NonNull
- public Map<Field, Object> getValues() {
- Map<Field, Object> result = new HashMap<Field, Object>();
+ public List<FieldValue> getValues() {
+ ArrayList<FieldValue> result = new ArrayList<FieldValue>();
ClassObj clazz = getClassObj();
getBuffer().setPosition(mValuesOffset);
while (clazz != null) {
for (Field field : clazz.getFields()) {
- result.put(field, readValue(field.getType()));
+ result.add(new FieldValue(field, readValue(field.getType())));
}
clazz = clazz.getSuperClassObj();
}
@@ -54,14 +62,41 @@
@Override
public final void accept(@NonNull Visitor visitor) {
visitor.visitClassInstance(this);
- for (Object value : getValues().values()) {
- if (value instanceof Instance) {
- visitor.visitLater((Instance) value);
+ for (FieldValue field : getValues()) {
+ if (field.getValue() instanceof Instance) {
+ if (!mReferencesAdded) {
+ ((Instance) field.getValue()).addReference(field.getField(), this);
+ }
+ visitor.visitLater(this, (Instance) field.getValue());
}
}
+ mReferencesAdded = true;
+ }
+
+ @Override
+ public boolean getIsSoftReference() {
+ return getClassObj().getIsSoftReference();
}
public final String toString() {
- return String.format("%s@0x%08x", getClassObj().getClassName(), mId);
+ return String.format("%s@%d (0x%x)", getClassObj().getClassName(), getUniqueId(), getUniqueId());
+ }
+
+ public static class FieldValue {
+ private Field mField;
+ private Object mValue;
+
+ public FieldValue(@NonNull Field field, @Nullable Object value) {
+ this.mField = field;
+ this.mValue = value;
+ }
+
+ public Field getField() {
+ return mField;
+ }
+
+ public Object getValue() {
+ return mValue;
+ }
}
}
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/ClassObj.java b/perflib/src/main/java/com/android/tools/perflib/heap/ClassObj.java
index 1da9af0..121c27c 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/ClassObj.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/ClassObj.java
@@ -19,15 +19,16 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
+import gnu.trove.TIntObjectHashMap;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
public class ClassObj extends Instance implements Comparable<ClassObj> {
+ public static class HeapData {
+ public int mShallowSize = 0;
+
+ public List<Instance> mInstances = new ArrayList<Instance>();
+ }
@NonNull
final String mClassName;
@@ -44,8 +45,10 @@
private int mInstanceSize;
+ private boolean mIsSoftReference = false;
+
@NonNull
- ArrayList<Instance> mInstances = new ArrayList<Instance>();
+ TIntObjectHashMap<HeapData> mHeapData = new TIntObjectHashMap<HeapData>();
@NonNull
Set<ClassObj> mSubclasses = new HashSet<ClassObj>();
@@ -77,8 +80,18 @@
return mClassName.replace('/', '.');
}
- public final void addInstance(@NonNull Instance instance) {
- mInstances.add(instance);
+ public final void addInstance(int heapId, @NonNull Instance instance) {
+ if (instance instanceof ClassInstance) {
+ instance.setSize(mInstanceSize);
+ }
+
+ HeapData heapData = mHeapData.get(heapId);
+ if (heapData == null) {
+ heapData = new HeapData();
+ mHeapData.put(heapId, heapData);
+ }
+ heapData.mInstances.add(instance);
+ heapData.mShallowSize += instance.getSize();
}
public final void setSuperClassId(long superClass) {
@@ -119,6 +132,20 @@
return mInstanceSize;
}
+ public int getShallowSize(int heapId) {
+ HeapData heapData = mHeapData.get(heapId);
+ return heapData == null ? 0 : mHeapData.get(heapId).mShallowSize;
+ }
+
+ public void setIsSoftReference() {
+ mIsSoftReference = true;
+ }
+
+ @Override
+ public boolean getIsSoftReference() {
+ return mIsSoftReference;
+ }
+
@NonNull
public Map<Field, Object> getStaticFieldValues() {
Map<Field, Object> result = new HashMap<Field, Object>();
@@ -164,16 +191,31 @@
@Override
public final void accept(@NonNull Visitor visitor) {
visitor.visitClassObj(this);
- for (Object value : getStaticFieldValues().values()) {
+ for (Map.Entry<Field, Object> entry : getStaticFieldValues().entrySet()) {
+ Object value = entry.getValue();
if (value instanceof Instance) {
- visitor.visitLater((Instance) value);
+ if (!mReferencesAdded) {
+ ((Instance)value).addReference(entry.getKey(), this);
+ }
+ visitor.visitLater(this, (Instance)value);
}
}
+ mReferencesAdded = true;
}
@Override
public final int compareTo(@NonNull ClassObj o) {
- return mClassName.compareTo(o.mClassName);
+ if (getId() == o.getId()) {
+ return 0;
+ }
+
+ int nameCompareResult = mClassName.compareTo(o.mClassName);
+ if (nameCompareResult != 0) {
+ return nameCompareResult;
+ }
+ else {
+ return getId() - o.getId() > 0 ? 1 : -1;
+ }
}
public final boolean equals(Object o) {
@@ -200,11 +242,65 @@
@Nullable
public Instance getClassLoader() {
- return mHeap.mSnapshot.findReference(mClassLoaderId);
+ return mHeap.mSnapshot.findInstance(mClassLoaderId);
+ }
+
+ public List<Instance> getInstancesList() {
+ int count = getInstanceCount();
+ ArrayList<Instance> resultList = new ArrayList<Instance>(count);
+ for (int heapId : mHeapData.keys()) {
+ resultList.addAll(getHeapInstances(heapId));
+ }
+ return resultList;
}
@NonNull
- public Collection<Instance> getInstances() {
- return mInstances;
+ public List<Instance> getHeapInstances(int heapId) {
+ HeapData result = mHeapData.get(heapId);
+ return result == null ? new ArrayList<Instance>(0) : result.mInstances;
+ }
+
+ public int getHeapInstancesCount(int heapId) {
+ HeapData result = mHeapData.get(heapId);
+ return result == null ? 0 : result.mInstances.size();
+ }
+
+ public int getInstanceCount() {
+ int count = 0;
+ for (Object heapStat : mHeapData.getValues()) {
+ count += ((HeapData)heapStat).mInstances.size();
+ }
+ return count;
+ }
+
+ public int getShallowSize() {
+ int size = 0;
+ for (Object heapStat : mHeapData.getValues()) {
+ size += ((HeapData)heapStat).mShallowSize;
+ }
+ return size;
+ }
+
+ @NonNull
+ public static String getReferenceClassName() {
+ return "java.lang.ref.Reference";
+ }
+
+ @NonNull
+ public List<ClassObj> getDescendantClasses() {
+ List<ClassObj> descendants = new ArrayList<ClassObj>();
+
+ Stack<ClassObj> searchStack = new Stack<ClassObj>();
+ searchStack.push(this);
+
+ while (!searchStack.isEmpty()) {
+ ClassObj classObj = searchStack.pop();
+ descendants.add(classObj);
+ for (ClassObj subClass : classObj.getSubclasses()) {
+ searchStack.push(subClass);
+ }
+ }
+
+ return descendants;
}
}
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/Heap.java b/perflib/src/main/java/com/android/tools/perflib/heap/Heap.java
index 4c4d601..3d617e6 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/Heap.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/Heap.java
@@ -20,8 +20,8 @@
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
+import com.google.common.collect.*;
import gnu.trove.TIntObjectHashMap;
import gnu.trove.TLongObjectHashMap;
import gnu.trove.TObjectProcedure;
@@ -53,8 +53,7 @@
@NonNull
TLongObjectHashMap<ClassObj> mClassesById = new TLongObjectHashMap<ClassObj>();
- @NonNull
- HashMap<String, ClassObj> mClassesByName = new HashMap<String, ClassObj>();
+ @NonNull Multimap<String, ClassObj> mClassesByName = ArrayListMultimap.create();
// List of instances of above class definitions
private final TLongObjectHashMap<Instance> mInstances = new TLongObjectHashMap<Instance>();
@@ -134,13 +133,21 @@
}
public final ClassObj getClass(String name) {
+ Collection<ClassObj> classes = mClassesByName.get(name);
+ if (classes.size() == 1) {
+ return classes.iterator().next();
+ }
+ return null;
+ }
+
+ public final Collection<ClassObj> getClasses(String name) {
return mClassesByName.get(name);
}
public final void dumpInstanceCounts() {
for (Object value : mClassesById.getValues()) {
ClassObj theClass = (ClassObj) value;
- int count = theClass.mInstances.size();
+ int count = theClass.getInstanceCount();
if (count > 0) {
System.out.println(theClass + ": " + count);
@@ -166,7 +173,7 @@
int size = 0;
- for (Instance instance : theClass.mInstances) {
+ for (Instance instance : theClass.getHeapInstances(getId())) {
size += instance.getCompositeSize();
}
@@ -194,4 +201,8 @@
});
return result;
}
+
+ public int getInstancesCount() {
+ return mInstances.size();
+ }
}
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/HprofParser.java b/perflib/src/main/java/com/android/tools/perflib/heap/HprofParser.java
index d420fd9..80271ab 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/HprofParser.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/HprofParser.java
@@ -150,7 +150,7 @@
readNullTerminatedString(); // Version, ignored for now.
mIdSize = mInput.readInt();
- Type.setIdSize(mIdSize);
+ mSnapshot.setIdSize(mIdSize);
mInput.readLong(); // Timestamp, ignored for now.
@@ -196,6 +196,7 @@
// this is fine
}
mSnapshot.resolveClasses();
+ mSnapshot.resolveReferences();
// TODO: enable this after the dominators computation is also optimized.
// mSnapshot.computeRetainedSizes();
} catch (Exception e) {
@@ -519,9 +520,9 @@
Type type = Type.getType(mInput.readByte());
staticFields[i] = new Field(type, name);
- skipFully(type.getSize());
+ skipFully(mSnapshot.getTypeSize(type));
- bytesRead += mIdSize + 1 + type.getSize();
+ bytesRead += mIdSize + 1 + mSnapshot.getTypeSize(type);
}
theClass.setStaticFields(staticFields);
@@ -587,7 +588,7 @@
StackTrace stack = mSnapshot.getStackTrace(stackId);
int numElements = mInput.readInt();
Type type = Type.getType(readUnsignedByte());
- int size = type.getSize();
+ int size = mSnapshot.getTypeSize(type);
ArrayInstance array = new ArrayInstance(id, stack, type, numElements, mInput.position());
mSnapshot.addInstance(id, array);
@@ -611,7 +612,7 @@
private int skipValue() throws IOException {
Type type = Type.getType(readUnsignedByte());
- int size = type.getSize();
+ int size = mSnapshot.getTypeSize(type);
skipFully(size);
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/Instance.java b/perflib/src/main/java/com/android/tools/perflib/heap/Instance.java
index e68ab8b..4aab914 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/Instance.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/Instance.java
@@ -46,6 +46,12 @@
// Another identifier for this Instance, that we computed during the analysis phase.
int mTopologicalOrder;
+ int mDistanceToGcRoot = Integer.MAX_VALUE;
+
+ boolean mReferencesAdded = false;
+
+ Instance mNextInstanceToGcRoot = null;
+
// The immediate dominator of this instance, or null if not reachable from any GC roots.
@Nullable
private Instance mImmediateDominator;
@@ -57,7 +63,11 @@
private long[] mRetainedSizes;
// List of all objects that hold a live reference to this object
- private final ArrayList<Instance> mReferences = new ArrayList<Instance>();
+ private final ArrayList<Instance> mHardReferences = new ArrayList<Instance>();
+
+ // List of all objects that hold a soft/weak/phantom reference to this object.
+ // Don't create an actual list until we need to.
+ private ArrayList<Instance> mSoftReferences = null;
Instance(long id, @NonNull StackTrace stackTrace) {
mId = id;
@@ -68,6 +78,10 @@
return mId;
}
+ public long getUniqueId() {
+ return getId() & mHeap.mSnapshot.getIdSizeMask();
+ }
+
public abstract void accept(Visitor visitor);
public void setClassId(long classId) {
@@ -118,6 +132,23 @@
mImmediateDominator = dominator;
}
+ public int getDistanceToGcRoot() {
+ return mDistanceToGcRoot;
+ }
+
+ public Instance getNextInstanceToGcRoot() {
+ return mNextInstanceToGcRoot;
+ }
+
+ public void setDistanceToGcRoot(int newDistance) {
+ assert(newDistance < mDistanceToGcRoot);
+ mDistanceToGcRoot = newDistance;
+ }
+
+ public void setNextInstanceToGcRoot(Instance instance) {
+ mNextInstanceToGcRoot = instance;
+ }
+
public void resetRetainedSize() {
List<Heap> allHeaps = mHeap.mSnapshot.mHeaps;
if (mRetainedSizes == null) {
@@ -138,7 +169,7 @@
public long getTotalRetainedSize() {
if (mRetainedSizes == null) {
- return 0;
+ return 0;
}
long totalSize = 0;
@@ -148,14 +179,42 @@
return totalSize;
}
- // Add to the list of objects that have a hard reference to this Instance
- public void addReference(Instance reference) {
- mReferences.add(reference);
+ /**
+ * Add to the list of objects that references this Instance.
+ *
+ * @param field the named variable in #reference pointing to this instance. If the name of the field is "referent", and #reference is a
+ * soft reference type, then reference is counted as a soft reference instead of the usual hard reference.
+ * @param reference another instance that references this instance
+ */
+ public void addReference(@Nullable Field field, @NonNull Instance reference) {
+ if (reference.getIsSoftReference() && field != null && field.getName().equals("referent")) {
+ if (mSoftReferences == null) {
+ mSoftReferences = new ArrayList<Instance>();
+ }
+ mSoftReferences.add(reference);
+ }
+ else {
+ mHardReferences.add(reference);
+ }
}
@NonNull
- public ArrayList<Instance> getReferences() {
- return mReferences;
+ public ArrayList<Instance> getHardReferences() {
+ return mHardReferences;
+ }
+
+ @Nullable
+ public ArrayList<Instance> getSoftReferences() {
+ return mSoftReferences;
+ }
+
+ /**
+ * There is an underlying assumption that a class that is a soft reference will only have one referent.
+ *
+ * @return true if the instance is a soft reference type, or false otherwise
+ */
+ public boolean getIsSoftReference() {
+ return false;
}
@Nullable
@@ -163,11 +222,7 @@
switch (type) {
case OBJECT:
long id = readId();
- Instance result = mHeap.mSnapshot.findReference(id);
- if (result != null) {
- result.addReference(this);
- }
- return result;
+ return mHeap.mSnapshot.findInstance(id);
case BOOLEAN:
return getBuffer().readByte() != 0;
case CHAR:
@@ -190,7 +245,7 @@
protected long readId() {
// As long as we don't interpret IDs, reading signed values here is fine.
- switch (Type.OBJECT.getSize()) {
+ switch (mHeap.mSnapshot.getTypeSize(Type.OBJECT)) {
case 1:
return getBuffer().readByte();
case 2:
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/NonRecursiveVisitor.java b/perflib/src/main/java/com/android/tools/perflib/heap/NonRecursiveVisitor.java
index 2269ab2..c481321 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/NonRecursiveVisitor.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/NonRecursiveVisitor.java
@@ -57,8 +57,8 @@
}
@Override
- public void visitLater(@NonNull Instance instance) {
- mStack.push(instance);
+ public void visitLater(Instance parent, @NonNull Instance child) {
+ mStack.push(child);
}
public void doVisit(Iterable<? extends Instance> startNodes) {
@@ -67,7 +67,7 @@
// RootObj nodes don't have their own id, they should be visited right away.
node.accept(this);
} else {
- visitLater(node);
+ visitLater(null, node);
}
}
while (!mStack.isEmpty()) {
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/Queries.java b/perflib/src/main/java/com/android/tools/perflib/heap/Queries.java
index 4b6b427..3868308 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/Queries.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/Queries.java
@@ -19,14 +19,7 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
+import java.util.*;
public class Queries {
/*
@@ -166,9 +159,8 @@
throw new IllegalArgumentException("Class not found: " + baseClassName);
}
- Instance[] instances = new Instance[theClass.mInstances.size()];
-
- return theClass.mInstances.toArray(instances);
+ List<Instance> instances = theClass.getInstancesList();
+ return instances.toArray(new Instance[instances.size()]);
}
/*
@@ -191,7 +183,7 @@
ArrayList<Instance> instanceList = new ArrayList<Instance>();
for (ClassObj someClass : classList) {
- instanceList.addAll(someClass.mInstances);
+ instanceList.addAll(someClass.getInstancesList());
}
Instance[] result = new Instance[instanceList.size()];
@@ -220,7 +212,7 @@
public static Instance findObject(@NonNull Snapshot snapshot, String id) {
long id2 = Long.parseLong(id, 16);
- return snapshot.findReference(id2);
+ return snapshot.findInstance(id2);
}
@NonNull
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/RootObj.java b/perflib/src/main/java/com/android/tools/perflib/heap/RootObj.java
index a37eed3..3a6fb86 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/RootObj.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/RootObj.java
@@ -48,7 +48,7 @@
if (mType == RootType.SYSTEM_CLASS) {
theClass = snapshot.findClass(mId);
} else {
- theClass = snapshot.findReference(mId).getClassObj();
+ theClass = snapshot.findInstance(mId).getClassObj();
}
if (theClass == null) {
@@ -63,7 +63,7 @@
visitor.visitRootObj(this);
Instance instance = getReferredInstance();
if (instance != null) {
- visitor.visitLater(instance);
+ visitor.visitLater(null, instance);
}
}
@@ -76,7 +76,7 @@
if (mType == RootType.SYSTEM_CLASS) {
return mHeap.mSnapshot.findClass(mId);
} else {
- return mHeap.mSnapshot.findReference(mId);
+ return mHeap.mSnapshot.findInstance(mId);
}
}
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/Snapshot.java b/perflib/src/main/java/com/android/tools/perflib/heap/Snapshot.java
index 04e0cff..0b074bf 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/Snapshot.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/Snapshot.java
@@ -19,13 +19,13 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.perflib.heap.analysis.Dominators;
+import com.android.tools.perflib.heap.analysis.ShortestDistanceVisitor;
import com.android.tools.perflib.heap.analysis.TopologicalSort;
import com.android.tools.perflib.heap.io.HprofBuffer;
import com.google.common.collect.ImmutableList;
+import gnu.trove.THashSet;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
/*
* A snapshot of all of the heaps, and related meta-data, for the runtime at a given instant.
@@ -56,6 +56,13 @@
private Dominators mDominators;
+ // The set of all classes that are (sub)class(es) of java.lang.ref.Reference.
+ private THashSet<ClassObj> mReferenceClasses = new THashSet<ClassObj>();
+
+ private int[] mTypeSizes;
+
+ private long mIdSizeMask = 0x00000000ffffffffl;
+
public Snapshot(@NonNull HprofBuffer buffer) {
mBuffer = buffer;
setToDefaultHeap();
@@ -134,8 +141,7 @@
return mCurrentHeap.getStackTrace(traceSerialNumber);
}
- public final StackTrace getStackTraceAtDepth(int traceSerialNumber,
- int depth) {
+ public final StackTrace getStackTraceAtDepth(int traceSerialNumber, int depth) {
return mCurrentHeap.getStackTraceAtDepth(traceSerialNumber, depth);
}
@@ -152,6 +158,30 @@
return mCurrentHeap.getThread(serialNumber);
}
+ public final void setIdSize(int size) {
+ int maxId = -1;
+ for (int i = 0; i < Type.values().length; ++i) {
+ maxId = Math.max(Type.values()[i].getTypeId(), maxId);
+ }
+ assert (maxId > 0) && (maxId <= Type.LONG.getTypeId()); // Update this if hprof format ever changes its supported types.
+ mTypeSizes = new int[maxId + 1];
+ Arrays.fill(mTypeSizes, -1);
+
+ for (int i = 0; i < Type.values().length; ++i) {
+ mTypeSizes[Type.values()[i].getTypeId()] = Type.values()[i].getSize();
+ }
+ mTypeSizes[Type.OBJECT.getTypeId()] = size;
+ mIdSizeMask = 0xffffffffffffffffl >>> ((8 - size) * 8);
+ }
+
+ public final int getTypeSize(Type type) {
+ return mTypeSizes[type.getTypeId()];
+ }
+
+ public final long getIdSizeMask() {
+ return mIdSizeMask;
+ }
+
public final void addInstance(long id, @NonNull Instance instance) {
mCurrentHeap.addInstance(id, instance);
instance.setHeap(mCurrentHeap);
@@ -163,7 +193,7 @@
}
@Nullable
- public final Instance findReference(long id) {
+ public final Instance findInstance(long id) {
//noinspection ForLoopReplaceableByForEach
for (int i = 0; i < mHeaps.size(); i++) {
Instance instance = mHeaps.get(i).getInstance(id);
@@ -191,6 +221,12 @@
return null;
}
+ /**
+ * Finds the first ClassObj with a class name that matches <code>name</code>.
+ *
+ * @param name of the class to find
+ * @return the found <code>ClassObj</code>, or null if not found
+ */
@Nullable
public final ClassObj findClass(String name) {
//noinspection ForLoopReplaceableByForEach
@@ -205,6 +241,24 @@
return null;
}
+ /**
+ * Finds all <code>ClassObj</code>s with class name that match the given <code>name</code>.
+ *
+ * @param name of the class to find
+ * @return a collection of the found <code>ClassObj</code>s, or empty collection if not found
+ */
+ @NonNull
+ public final Collection<ClassObj> findClasses(String name) {
+ ArrayList<ClassObj> classObjs = new ArrayList<ClassObj>();
+
+ //noinspection ForLoopReplaceableByForEach
+ for (int i = 0; i < mHeaps.size(); i++) {
+ classObjs.addAll(mHeaps.get(i).getClasses(name));
+ }
+
+ return classObjs;
+ }
+
public void resolveClasses() {
ClassObj clazz = findClass(JAVA_LANG_CLASS);
int javaLangClassSize = clazz != null ? clazz.getInstanceSize() : 0;
@@ -220,29 +274,46 @@
int classSize = javaLangClassSize;
for (Field f : classObj.mStaticFields) {
- classSize += f.getType().getSize();
+ classSize += getTypeSize(f.getType());
}
classObj.setSize(classSize);
}
for (Instance instance : heap.getInstances()) {
ClassObj classObj = instance.getClassObj();
if (classObj != null) {
- classObj.addInstance(instance);
- // Now is a good time to set the size of this instance
- if (instance instanceof ClassInstance) {
- instance.setSize(classObj.getInstanceSize());
- }
+ classObj.addInstance(heap.getId(), instance);
}
}
}
}
+ public void resolveReferences() {
+ List<ClassObj> referenceDescendants = findAllDescendantClasses(ClassObj.getReferenceClassName());
+ for (ClassObj classObj : referenceDescendants) {
+ classObj.setIsSoftReference();
+ mReferenceClasses.add(classObj);
+ }
+ }
+
+ @NonNull
+ public List<ClassObj> findAllDescendantClasses(@NonNull String className) {
+ Collection<ClassObj> ancestorClasses = findClasses(className);
+ List<ClassObj> descendants = new ArrayList<ClassObj>();
+ for (ClassObj ancestor : ancestorClasses) {
+ descendants.addAll(ancestor.getDescendantClasses());
+ }
+ return descendants;
+ }
+
// TODO: Break dominator computation into fixed chunks, because it can be unbounded/expensive.
public void computeDominators() {
if (mDominators == null) {
mTopSort = TopologicalSort.compute(getGCRoots());
mDominators = new Dominators(this, mTopSort);
mDominators.computeRetainedSizes();
+
+ ShortestDistanceVisitor shortestDistanceVisitor = new ShortestDistanceVisitor();
+ shortestDistanceVisitor.doVisit(getGCRoots());
}
}
@@ -257,6 +328,10 @@
return result;
}
+ public ImmutableList<Instance> getTopologicalOrdering() {
+ return mTopSort;
+ }
+
public final void dumpInstanceCounts() {
for (Heap heap : mHeaps) {
System.out.println(
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/Type.java b/perflib/src/main/java/com/android/tools/perflib/heap/Type.java
index 05ac6d7..d988869 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/Type.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/Type.java
@@ -21,7 +21,7 @@
import java.util.Map;
public enum Type {
- OBJECT(2, 0),
+ OBJECT(2, 0), // Pointer sizes are dependent on the hprof file, so set it to 0 for now.
BOOLEAN(4, 1),
CHAR(5, 2),
FLOAT(6, 4),
@@ -31,8 +31,6 @@
INT(10, 4),
LONG(11, 8);
- private static int sIdSize = 4;
-
private static Map<Integer, Type> sTypeMap = Maps.newHashMap();
private int mId;
@@ -50,16 +48,16 @@
mSize = size;
}
- public static final void setIdSize(int size) {
- sIdSize = size;
- }
-
public static Type getType(int id) {
return sTypeMap.get(id);
}
public int getSize() {
- return this == OBJECT ? sIdSize : mSize;
+ return mSize;
+ }
+
+ public int getTypeId() {
+ return mId;
}
public static String getClassNameOfPrimitiveArray(Type type) {
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/Value.java b/perflib/src/main/java/com/android/tools/perflib/heap/Value.java
index 98980ff..4e74ba6 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/Value.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/Value.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,7 +37,7 @@
mValue = value;
if (value instanceof Instance) {
- ((Instance) value).addReference(instance);
+ ((Instance) value).addReference(null, instance);
}
}
}
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/Visitor.java b/perflib/src/main/java/com/android/tools/perflib/heap/Visitor.java
index d6af10b..53b2e55 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/Visitor.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/Visitor.java
@@ -31,5 +31,5 @@
/**
* Marks an instance to be visited later, depending on the visitor's traversal strategy.
*/
- void visitLater(@NonNull Instance instance);
+ void visitLater(Instance parent, @NonNull Instance child);
}
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/analysis/Dominators.java b/perflib/src/main/java/com/android/tools/perflib/heap/analysis/Dominators.java
index cba7bb5..f7b04e8 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/analysis/Dominators.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/analysis/Dominators.java
@@ -17,10 +17,7 @@
package com.android.tools.perflib.heap.analysis;
import com.android.annotations.NonNull;
-import com.android.tools.perflib.heap.Heap;
-import com.android.tools.perflib.heap.Instance;
-import com.android.tools.perflib.heap.RootObj;
-import com.android.tools.perflib.heap.Snapshot;
+import com.android.tools.perflib.heap.*;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -73,8 +70,8 @@
if (node.getImmediateDominator() != Snapshot.SENTINEL_ROOT) {
Instance dominator = null;
- for (int j = 0; j < node.getReferences().size(); j++) {
- Instance predecessor = node.getReferences().get(j);
+ for (int j = 0; j < node.getHardReferences().size(); j++) {
+ Instance predecessor = node.getHardReferences().get(j);
if (predecessor.getImmediateDominator() == null) {
// If we don't have a dominator/approximation for predecessor, skip it
continue;
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/analysis/ShortestDistanceVisitor.java b/perflib/src/main/java/com/android/tools/perflib/heap/analysis/ShortestDistanceVisitor.java
new file mode 100644
index 0000000..5cb1a42
--- /dev/null
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/analysis/ShortestDistanceVisitor.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.perflib.heap.analysis;
+
+import com.android.annotations.NonNull;
+import com.android.tools.perflib.heap.Instance;
+import com.android.tools.perflib.heap.NonRecursiveVisitor;
+
+import java.util.Comparator;
+import java.util.PriorityQueue;
+
+public class ShortestDistanceVisitor extends NonRecursiveVisitor {
+ private PriorityQueue<Instance> mPriorityQueue = new PriorityQueue<Instance>(1024, new Comparator<Instance>() {
+ @Override
+ public int compare(Instance o1, Instance o2) {
+ return o1.getDistanceToGcRoot() - o2.getDistanceToGcRoot();
+ }
+ });
+ private Instance mPreviousInstance = null;
+ private int mVisitDistance = 0;
+
+ @Override
+ public void visitLater(Instance parent, @NonNull Instance child) {
+ if (mVisitDistance < child.getDistanceToGcRoot() &&
+ (parent == null ||
+ child.getSoftReferences() == null ||
+ !child.getSoftReferences().contains(parent) ||
+ child.getIsSoftReference())) {
+ child.setDistanceToGcRoot(mVisitDistance);
+ child.setNextInstanceToGcRoot(mPreviousInstance);
+ mPriorityQueue.add(child);
+ }
+ }
+
+ @Override
+ public void doVisit(Iterable<? extends Instance> startNodes) {
+ // root nodes are instances that share the same id as the node they point to.
+ // This means that we cannot mark them as visited here or they would be marking
+ // the actual root instance
+ // TODO RootObj should not be Instance objects
+ for (Instance node : startNodes) {
+ node.accept(this);
+ }
+
+ while (!mPriorityQueue.isEmpty()) {
+ Instance node = mPriorityQueue.poll();
+ mVisitDistance = node.getDistanceToGcRoot() + 1;
+ mPreviousInstance = node;
+ node.accept(this);
+ }
+ }
+}
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/analysis/TopologicalSort.java b/perflib/src/main/java/com/android/tools/perflib/heap/analysis/TopologicalSort.java
index 3ce8aa4..3bcc704 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/analysis/TopologicalSort.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/analysis/TopologicalSort.java
@@ -66,7 +66,19 @@
private final List<Instance> mPostorder = Lists.newArrayList();
@Override
+ public void visitLater(Instance parent, @NonNull Instance child) {
+ if (!mSeen.contains(child.getId())) {
+ mStack.push(child);
+ }
+ }
+
+ @Override
public void doVisit(Iterable<? extends Instance> startNodes) {
+ // root nodes are instances that share the same id as the node they point to.
+ // This means that we cannot mark them as visited here or they would be marking
+ // the actual root instance
+ // TODO RootObj should not be Instance objects
+
for (Instance node : startNodes) {
node.accept(this);
}
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/io/HprofBuffer.java b/perflib/src/main/java/com/android/tools/perflib/heap/io/HprofBuffer.java
index 09cb59a..15d7d6d 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/io/HprofBuffer.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/io/HprofBuffer.java
@@ -15,12 +15,17 @@
*/
package com.android.tools.perflib.heap.io;
+import java.nio.ByteOrder;
+
public interface HprofBuffer {
+ ByteOrder HPROF_BYTE_ORDER = ByteOrder.BIG_ENDIAN;
byte readByte();
void read(byte[] b);
+ void readSubSequence(byte[] b, int sourceStart, int sourceEnd);
+
char readChar();
short readShort();
diff --git a/perflib/src/main/java/com/android/tools/perflib/heap/io/MemoryMappedFileBuffer.java b/perflib/src/main/java/com/android/tools/perflib/heap/io/MemoryMappedFileBuffer.java
index b282663..7607f0f 100644
--- a/perflib/src/main/java/com/android/tools/perflib/heap/io/MemoryMappedFileBuffer.java
+++ b/perflib/src/main/java/com/android/tools/perflib/heap/io/MemoryMappedFileBuffer.java
@@ -23,7 +23,6 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import sun.nio.ch.DirectBuffer;
@@ -62,7 +61,7 @@
long size = Math.min(mLength - offset, mBufferSize + mPadding);
mByteBuffers[i] = inputStream.getChannel()
.map(FileChannel.MapMode.READ_ONLY, offset, size);
- mByteBuffers[i].order(ByteOrder.BIG_ENDIAN);
+ mByteBuffers[i].order(HPROF_BYTE_ORDER);
offset += mBufferSize;
}
mCurrentPosition = 0;
@@ -121,6 +120,35 @@
}
@Override
+ public void readSubSequence(@NonNull byte[] b, int sourceStart, int length) {
+ assert length < mLength;
+
+ mCurrentPosition += sourceStart;
+
+ int index = getIndex();
+ mByteBuffers[index].position(getOffset());
+ if (b.length <= mByteBuffers[index].remaining()) {
+ mByteBuffers[index].get(b, 0, b.length);
+ } else {
+ int split = mBufferSize - mByteBuffers[index].position();
+ mByteBuffers[index].get(b, 0, split);
+
+ int start = split;
+ int remainingMaxLength = Math.min(length - start, b.length - start);
+ int remainingShardCount = (remainingMaxLength + mBufferSize - 1) / mBufferSize;
+ for (int i = 0; i < remainingShardCount; ++i) {
+ int maxToRead = Math.min(remainingMaxLength, mBufferSize);
+ mByteBuffers[index + 1 + i].position(0);
+ mByteBuffers[index + 1 + i].get(b, start, maxToRead);
+ start += maxToRead;
+ remainingMaxLength -= maxToRead;
+ }
+ }
+
+ mCurrentPosition += Math.min(b.length, length);
+ }
+
+ @Override
public char readChar() {
char result = mByteBuffers[getIndex()].getChar(getOffset());
mCurrentPosition += 2;
diff --git a/perflib/src/main/java/com/android/tools/perflib/vmtrace/VmTraceData.java b/perflib/src/main/java/com/android/tools/perflib/vmtrace/VmTraceData.java
index ae14475..e12b5df 100644
--- a/perflib/src/main/java/com/android/tools/perflib/vmtrace/VmTraceData.java
+++ b/perflib/src/main/java/com/android/tools/perflib/vmtrace/VmTraceData.java
@@ -24,7 +24,6 @@
import com.google.common.collect.Maps;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
diff --git a/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/CallHierarchyRenderer.java b/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/CallHierarchyRenderer.java
index 6209153..17ce3ec 100644
--- a/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/CallHierarchyRenderer.java
+++ b/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/CallHierarchyRenderer.java
@@ -21,7 +21,6 @@
import com.android.tools.perflib.vmtrace.ClockType;
import com.android.tools.perflib.vmtrace.MethodInfo;
import com.android.tools.perflib.vmtrace.ThreadInfo;
-import com.android.tools.perflib.vmtrace.TimeSelector;
import com.android.tools.perflib.vmtrace.VmTraceData;
import com.android.utils.HtmlBuilder;
diff --git a/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/ZoomPanInteractor.java b/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/ZoomPanInteractor.java
index 90ed94e..b315459 100644
--- a/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/ZoomPanInteractor.java
+++ b/perflib/src/main/java/com/android/tools/perflib/vmtrace/viz/ZoomPanInteractor.java
@@ -19,7 +19,6 @@
import com.android.annotations.NonNull;
import com.android.annotations.VisibleForTesting;
-import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/ClassObjTest.java b/perflib/src/test/java/com/android/tools/perflib/heap/ClassObjTest.java
index 48563b3..9374e33 100644
--- a/perflib/src/test/java/com/android/tools/perflib/heap/ClassObjTest.java
+++ b/perflib/src/test/java/com/android/tools/perflib/heap/ClassObjTest.java
@@ -43,4 +43,28 @@
assertNotNull(dialer);
assertEquals(5, dialer.getAllFieldsCount());
}
+
+ public void testComparison() {
+ ClassObj a = new ClassObj(1, null, "TestClassA", 0);
+ ClassObj b = new ClassObj(1, null, "TestClassB", 0);
+ ClassObj c = new ClassObj(2, null, "TestClassC", 0);
+ ClassObj aAlt = new ClassObj(3, null, "TestClassA", 0);
+
+ assertEquals(0, a.compareTo(a));
+ assertEquals(0, a.compareTo(b)); // This is a weird test case, since IDs are supposed to be unique.
+ assertTrue(c.compareTo(a) > 0);
+ assertTrue(aAlt.compareTo(a) > 0);
+ }
+
+ public void testSubClassNameClash() {
+ ClassObj superClass = new ClassObj(1, null, "TestClassA", 0);
+ ClassObj subClass1 = new ClassObj(2, null, "SubClass", 0);
+ ClassObj subClass2 = new ClassObj(3, null, "SubClass", 0);
+ superClass.addSubclass(subClass1);
+ superClass.addSubclass(subClass2);
+
+ assertEquals(2, superClass.getSubclasses().size());
+ assertTrue(superClass.getSubclasses().contains(subClass1));
+ assertTrue(superClass.getSubclasses().contains(subClass2));
+ }
}
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/HprofParserTest.java b/perflib/src/test/java/com/android/tools/perflib/heap/HprofParserTest.java
index c089dda..9b674b2 100644
--- a/perflib/src/test/java/com/android/tools/perflib/heap/HprofParserTest.java
+++ b/perflib/src/test/java/com/android/tools/perflib/heap/HprofParserTest.java
@@ -22,6 +22,7 @@
import java.io.File;
import java.util.Collection;
+import java.util.List;
public class HprofParserTest extends TestCase {
@@ -70,23 +71,25 @@
public void testPrimitiveArrays() {
ClassObj byteArray = mSnapshot.findClass("byte[]");
- assertEquals(1406, byteArray.getInstances().size());
+ assertEquals(1406, byteArray.getInstancesList().size());
+ assertEquals(0, byteArray.getInstanceSize());
+ assertEquals(681489, byteArray.getShallowSize());
- ArrayInstance byteArrayInstance = (ArrayInstance) mSnapshot.findReference(0xB0D60401);
+ ArrayInstance byteArrayInstance = (ArrayInstance) mSnapshot.findInstance(0xB0D60401);
assertEquals(byteArray, byteArrayInstance.getClassObj());
assertEquals(43224, byteArrayInstance.getSize());
assertEquals(43224, byteArrayInstance.getCompositeSize());
ClassObj intArrayArray = mSnapshot.findClass("int[][]");
- assertEquals(37, intArrayArray.getInstances().size());
+ assertEquals(37, intArrayArray.getInstancesList().size());
- ArrayInstance intArrayInstance = (ArrayInstance) mSnapshot.findReference(0xB0F69F58);
+ ArrayInstance intArrayInstance = (ArrayInstance) mSnapshot.findInstance(0xB0F69F58);
assertEquals(intArrayArray, intArrayInstance.getClassObj());
assertEquals(40, intArrayInstance.getSize());
assertEquals(52, intArrayInstance.getCompositeSize());
ClassObj stringArray = mSnapshot.findClass("java.lang.String[]");
- assertEquals(1396, stringArray.getInstances().size());
+ assertEquals(1396, stringArray.getInstancesList().size());
}
/**
@@ -103,7 +106,7 @@
Object[] values = array.getValues();
assertEquals(6, values.length);
- Collection<Instance> instances = clazz.getInstances();
+ Collection<Instance> instances = clazz.getInstancesList();
for (Object value : values) {
assertTrue(value instanceof Instance);
assertTrue(instances.contains(value));
@@ -114,11 +117,18 @@
ClassInstance instance = (ClassInstance) enumValue;
assertSame(clazz, instance.getClassObj());
- Object name = instance.getField(Type.OBJECT, "name");
+ List<ClassInstance.FieldValue> fields = instance.getFields("name");
+ assertEquals(1, fields.size());
+ assertEquals(Type.OBJECT, fields.get(0).getField().getType());
+ Object name = fields.get(0).getValue();
+
assertTrue(name instanceof ClassInstance);
ClassInstance string = (ClassInstance) name;
assertEquals("java.lang.String", string.getClassObj().getClassName());
- Object value = string.getField(Type.OBJECT, "value");
+ fields = string.getFields("value");
+ assertEquals(1, fields.size());
+ assertEquals(Type.OBJECT, fields.get(0).getField().getType());
+ Object value = fields.get(0).getValue();
assertTrue(value instanceof ArrayInstance);
Object[] data = ((ArrayInstance) value).getValues();
assertEquals(3, data.length);
@@ -126,7 +136,45 @@
assertEquals('E', data[1]);
assertEquals('W', data[2]);
- Object ordinal = instance.getField(Type.INT, "ordinal");
- assertEquals(0, ordinal);
+ fields = instance.getFields("ordinal");
+ assertEquals(1, fields.size());
+ assertEquals(Type.INT, fields.get(0).getField().getType());
+ assertEquals(0, fields.get(0).getValue());
+ }
+
+ /**
+ * Tests getValues to make sure it's not adding duplicate entries to the back references.
+ */
+ public void testDuplicateEntries() {
+ mSnapshot = new SnapshotBuilder(2).addReferences(1, 2).addRoot(1).build();
+ mSnapshot.computeDominators();
+
+ assertEquals(2, mSnapshot.getReachableInstances().size());
+ ClassInstance parent = (ClassInstance)mSnapshot.findInstance(1);
+ List<ClassInstance.FieldValue> firstGet = parent.getValues();
+ List<ClassInstance.FieldValue> secondGet = parent.getValues();
+ assertEquals(1, firstGet.size());
+ assertEquals(firstGet.size(), secondGet.size());
+ Instance child = mSnapshot.findInstance(2);
+ assertEquals(1, child.getHardReferences().size());
+ }
+
+ public void testResolveReferences() {
+ mSnapshot = new SnapshotBuilder(1).addRoot(1).build();
+ ClassObj subSoftReferenceClass = new ClassObj(98, null, "SubSoftReference", 0);
+ subSoftReferenceClass.setSuperClassId(SnapshotBuilder.SOFT_REFERENCE_ID);
+ ClassObj subSubSoftReferenceClass = new ClassObj(97, null, "SubSubSoftReference", 0);
+ subSubSoftReferenceClass.setSuperClassId(98);
+
+ mSnapshot.findClass(SnapshotBuilder.SOFT_REFERENCE_ID).addSubclass(subSoftReferenceClass);
+ subSoftReferenceClass.addSubclass(subSubSoftReferenceClass);
+
+ mSnapshot.addClass(98, subSoftReferenceClass);
+ mSnapshot.addClass(97, subSubSoftReferenceClass);
+
+ mSnapshot.resolveReferences();
+
+ assertTrue(subSoftReferenceClass.getIsSoftReference());
+ assertTrue(subSubSoftReferenceClass.getIsSoftReference());
}
}
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/SnapshotBuilder.java b/perflib/src/test/java/com/android/tools/perflib/heap/SnapshotBuilder.java
new file mode 100644
index 0000000..35f06e9
--- /dev/null
+++ b/perflib/src/test/java/com/android/tools/perflib/heap/SnapshotBuilder.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.perflib.heap;
+
+import com.android.tools.perflib.heap.io.InMemoryBuffer;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Utility for creating Snapshot objects to be used in tests.
+ *
+ * As the main concern here is graph connectivity, we only initialize the app heap, creating
+ * ClassInstance objects with id in [1..numNodes], each instance pointing to a unique ClassObj.
+ * The class ids range in [101..100+numNodes] and their size is set to match the id of their object
+ * instance. The default heap holds the roots.
+ */
+public class SnapshotBuilder {
+ public static final int SOFT_REFERENCE_ID = 99;
+
+ public static final int SOFT_AND_HARD_REFERENCE_ID = 98;
+
+ private final Snapshot mSnapshot;
+
+ private final ClassInstance[] mNodes;
+
+ private final int[] mOffsets;
+
+ private final ByteBuffer mDirectBuffer;
+
+ private final int mMaxTotalNodes;
+
+ private short mNextAvailableSoftReferenceNodeId;
+
+ private short mNextAvailableSoftAndHardReferenceNodeId;
+
+ public SnapshotBuilder(int numNodes) {
+ this(numNodes, 0, 0);
+ }
+
+ public SnapshotBuilder(int numNodes, int numSoftNodes, int numSoftAndHardNodes) {
+ mMaxTotalNodes = numNodes + numSoftNodes + numSoftAndHardNodes;
+ InMemoryBuffer buffer = new InMemoryBuffer(2 * mMaxTotalNodes * mMaxTotalNodes);
+ mDirectBuffer = buffer.getDirectBuffer();
+ mOffsets = new int[mMaxTotalNodes + 1];
+
+ mSnapshot = new Snapshot(buffer);
+ mSnapshot.setHeapTo(13, "testHeap");
+ mSnapshot.setIdSize(2);
+
+ ClassObj softClazz = new ClassObj(SOFT_REFERENCE_ID, null, ClassObj.getReferenceClassName(), 0);
+ softClazz.setClassLoaderId(0);
+ softClazz.setFields(new Field[]{new Field(Type.OBJECT, "referent")});
+ softClazz.setIsSoftReference();
+ mSnapshot.addClass(SOFT_REFERENCE_ID, softClazz);
+
+ ClassObj softAndHardClazz = new ClassObj(SOFT_AND_HARD_REFERENCE_ID, null, "SoftAndHardReference", 0);
+ softAndHardClazz.setSuperClassId(SOFT_REFERENCE_ID);
+ softAndHardClazz.setClassLoaderId(0);
+ softAndHardClazz.setFields(new Field[]{new Field(Type.OBJECT, "referent"), new Field(Type.OBJECT, "hardReference")});
+ softAndHardClazz.setIsSoftReference();
+ mSnapshot.addClass(SOFT_AND_HARD_REFERENCE_ID, softAndHardClazz);
+
+ mNodes = new ClassInstance[mMaxTotalNodes + 1];
+ for (int i = 1; i <= numNodes; i++) {
+ // Use same name classes on different loaders to extend test coverage
+ ClassObj clazz = new ClassObj(100 + i, null, "Class" + (i / 2), 0);
+ clazz.setClassLoaderId(i % 2);
+ clazz.setFields(new Field[0]);
+ mSnapshot.addClass(100 + i, clazz);
+
+ mOffsets[i] = 2 * (i - 1) * mMaxTotalNodes;
+ mNodes[i] = new ClassInstance(i, null, mOffsets[i]);
+ mNodes[i].setClassId(100 + i);
+ mNodes[i].setSize(i);
+ mSnapshot.addInstance(i, mNodes[i]);
+ }
+
+ mNextAvailableSoftReferenceNodeId = (short)(numNodes + 1);
+ for (int i = mNextAvailableSoftReferenceNodeId; i <= mMaxTotalNodes; ++i) {
+ mOffsets[i] = 2 * (i - 1) * mMaxTotalNodes;
+ mNodes[i] = new ClassInstance(i, null, mOffsets[i]);
+ mNodes[i].setClassId(SOFT_REFERENCE_ID);
+ mNodes[i].setSize(i);
+ mSnapshot.addInstance(i, mNodes[i]);
+ }
+
+ mNextAvailableSoftAndHardReferenceNodeId = (short)(numNodes + numSoftNodes + 1);
+ for (int i = mNextAvailableSoftAndHardReferenceNodeId; i <= mMaxTotalNodes; ++i) {
+ mOffsets[i] = 2 * (i - 1) * mMaxTotalNodes;
+ mNodes[i] = new ClassInstance(i, null, mOffsets[i]);
+ mNodes[i].setClassId(SOFT_AND_HARD_REFERENCE_ID);
+ mNodes[i].setSize(i);
+ mSnapshot.addInstance(i, mNodes[i]);
+ }
+ }
+
+ public SnapshotBuilder addReferences(int nodeFrom, int... nodesTo) {
+ assertEquals(mNodes[nodeFrom].getClassObj().getFields().length, 0);
+
+ Field[] fields = new Field[nodesTo.length];
+ for (int i = 0; i < nodesTo.length; i++) {
+ insertHardReferenceIntoBuffer(nodeFrom, nodesTo[i], i);
+ // Fields should support duplicated field names due to inheritance of private fields
+ fields[i] = new Field(Type.OBJECT, "duplicated_name");
+ }
+
+ mNodes[nodeFrom].getClassObj().setFields(fields);
+ return this;
+ }
+
+ /**
+ * Inserts a soft reference instance between <code>nodeFrom</code> to <code>nodeTo</code>.
+ *
+ * @param nodeFrom the parent node
+ * @param nodeTo the child node
+ * @return this
+ */
+ public SnapshotBuilder insertSoftReference(int nodeFrom, int nodeToSoftReference) {
+ Field[] nodeFromFields = mNodes[nodeFrom].getClassObj().getFields();
+ Field[] newFields = Arrays.copyOf(nodeFromFields, nodeFromFields.length + 1);
+
+ short softReferenceId = mNextAvailableSoftReferenceNodeId++;
+ assert softReferenceId <= mMaxTotalNodes;
+ insertSoftReferenceIntoBuffer(nodeFrom, softReferenceId, nodeFromFields.length);
+
+ setupSoftReference(softReferenceId, nodeToSoftReference);
+ newFields[nodeFromFields.length] = new Field(Type.OBJECT, "soft" + nodeToSoftReference);
+ mNodes[nodeFrom].getClassObj().setFields(newFields);
+ return this;
+ }
+
+ public SnapshotBuilder insertSoftAndHardReference(int nodeFrom, int nodeToSoftReference, int nodeToHardReference) {
+ Field[] nodeFromFields = mNodes[nodeFrom].getClassObj().getFields();
+ Field[] newFields = Arrays.copyOf(nodeFromFields, nodeFromFields.length + 1);
+
+ short softReferenceId = mNextAvailableSoftAndHardReferenceNodeId++;
+ assert softReferenceId <= mMaxTotalNodes;
+ insertSoftReferenceIntoBuffer(nodeFrom, softReferenceId, nodeFromFields.length);
+
+ setupSoftAndHardReference(softReferenceId, nodeToSoftReference, nodeToHardReference);
+ newFields[nodeFromFields.length] = new Field(Type.OBJECT, "soft" + nodeToSoftReference + "hard" + nodeToHardReference);
+ mNodes[nodeFrom].getClassObj().setFields(newFields);
+ return this;
+ }
+
+ public SnapshotBuilder addRoot(int node) {
+ RootObj root = new RootObj(RootType.JAVA_LOCAL, node);
+ mSnapshot.setToDefaultHeap();
+ mSnapshot.addRoot(root);
+ return this;
+ }
+
+ public Snapshot build() {
+ return mSnapshot;
+ }
+
+ private void insertHardReferenceIntoBuffer(int nodeFrom, int nodeTo, int fieldIndex) {
+ mDirectBuffer.putShort(mOffsets[nodeFrom] + fieldIndex * 2, (short)nodeTo);
+ }
+
+ private void insertSoftReferenceIntoBuffer(int nodeFrom, int softReferenceId, int fieldIndex) {
+ mDirectBuffer.putShort(mOffsets[nodeFrom] + fieldIndex * 2, (short)softReferenceId);
+ }
+
+ private void setupSoftReference(int softReferenceId, int referent) {
+ mDirectBuffer.putShort(mOffsets[softReferenceId], (short)referent);
+ }
+
+ private void setupSoftAndHardReference(int softReferenceId, int referent, int hardReference) {
+ mDirectBuffer.putShort(mOffsets[softReferenceId], (short)referent);
+ mDirectBuffer.putShort(mOffsets[softReferenceId] + 2, (short)hardReference);
+ }
+}
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/analysis/DominatorsTest.java b/perflib/src/test/java/com/android/tools/perflib/heap/analysis/DominatorsTest.java
index 9e691ca..e398a2c 100644
--- a/perflib/src/test/java/com/android/tools/perflib/heap/analysis/DominatorsTest.java
+++ b/perflib/src/test/java/com/android/tools/perflib/heap/analysis/DominatorsTest.java
@@ -16,15 +16,14 @@
package com.android.tools.perflib.heap.analysis;
-import com.android.tools.perflib.heap.ClassObj;
-import com.android.tools.perflib.heap.HprofParser;
-import com.android.tools.perflib.heap.Instance;
-import com.android.tools.perflib.heap.Snapshot;
+import com.android.tools.perflib.heap.*;
import com.android.tools.perflib.heap.io.MemoryMappedFileBuffer;
import junit.framework.TestCase;
import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
public class DominatorsTest extends TestCase {
@@ -37,7 +36,7 @@
.addReferences(3, 4, 5)
.addReferences(4, 6)
.addRoot(1)
- .getSnapshot();
+ .build();
mSnapshot.computeDominators();
@@ -47,6 +46,12 @@
assertDominates(1, 4);
assertDominates(1, 6);
assertDominates(3, 5);
+
+ assertParentPathToGc(2, 1);
+ assertParentPathToGc(3, 1);
+ assertParentPathToGc(4, 2, 3);
+ assertParentPathToGc(5, 3);
+ assertParentPathToGc(6, 2);
}
public void testCyclicGraph() {
@@ -56,7 +61,7 @@
.addReferences(3, 4)
.addReferences(4, 2)
.addRoot(1)
- .getSnapshot();
+ .build();
mSnapshot.computeDominators();
@@ -64,6 +69,10 @@
assertDominates(1, 2);
assertDominates(1, 3);
assertDominates(1, 4);
+
+ assertParentPathToGc(2, 1);
+ assertParentPathToGc(3, 1);
+ assertParentPathToGc(4, 1);
}
public void testMultipleRoots() {
@@ -75,7 +84,7 @@
.addReferences(5, 6)
.addRoot(1)
.addRoot(2)
- .getSnapshot();
+ .build();
mSnapshot.computeDominators();
@@ -83,9 +92,13 @@
assertDominates(1, 3);
assertDominates(2, 4);
// Node 5 is reachable via both roots, neither of which can be the sole dominator.
- assertEquals(mSnapshot.SENTINEL_ROOT,
- mSnapshot.findReference(5).getImmediateDominator());
+ assertEquals(mSnapshot.SENTINEL_ROOT, mSnapshot.findInstance(5).getImmediateDominator());
assertDominates(5, 6);
+
+ assertParentPathToGc(3, 1);
+ assertParentPathToGc(4, 2);
+ assertParentPathToGc(5, 3, 4);
+ assertParentPathToGc(6, 5);
}
public void testDoublyLinkedList() {
@@ -101,14 +114,126 @@
.addReferences(8, 7, 9)
.addReferences(9, 2, 8)
.addRoot(1)
- .getSnapshot();
+ .build();
mSnapshot.computeDominators();
- assertEquals(45, mSnapshot.findReference(1).getRetainedSize(1));
- for (int i = 2; i <= 9; i++) {
- assertEquals(i, mSnapshot.findReference(i).getRetainedSize(1));
+ assertEquals(45, mSnapshot.findInstance(1).getRetainedSize(1));
+ assertEquals(44, mSnapshot.findInstance(2).getRetainedSize(1));
+ for (int i = 3; i <= 9; i++) {
+ assertEquals(i, mSnapshot.findInstance(i).getRetainedSize(1));
}
+
+ assertParentPathToGc(2, 1);
+ assertParentPathToGc(3, 2);
+ assertParentPathToGc(9, 2);
+ assertParentPathToGc(4, 3);
+ assertParentPathToGc(8, 9);
+ assertParentPathToGc(5, 4);
+ assertParentPathToGc(7, 8);
+ assertParentPathToGc(6, 5, 7);
+ }
+
+ public void testSameClassDifferentLoader() {
+ mSnapshot = new SnapshotBuilder(4)
+ .addReferences(1, 3, 2)
+ .addReferences(3, 2)
+ .addRoot(1)
+ .build();
+
+ assertNotNull(mSnapshot.getHeap(13).getClass(102));
+ assertNotNull(mSnapshot.getHeap(13).getClass(103));
+
+ mSnapshot.computeDominators();
+
+ assertEquals(0, mSnapshot.getHeap(13).getClass(102).getRetainedSize(1));
+ assertEquals(0, mSnapshot.getHeap(13).getClass(103).getRetainedSize(1));
+ }
+
+ public void testTopSort() {
+ mSnapshot = new SnapshotBuilder(4)
+ .addReferences(1, 3, 2)
+ .addReferences(3, 2)
+ .addRoot(1)
+ .build();
+
+ mSnapshot.computeDominators();
+
+ assertEquals(6, mSnapshot.findInstance(1).getRetainedSize(1));
+ assertEquals(2, mSnapshot.findInstance(2).getRetainedSize(1));
+ assertEquals(3, mSnapshot.findInstance(3).getRetainedSize(1));
+ }
+
+ public void testMultiplePaths() {
+ mSnapshot = new SnapshotBuilder(8)
+ .addReferences(1, 7, 8)
+ .addReferences(7, 2, 3)
+ .addReferences(8, 2)
+ .addReferences(2, 4)
+ .addReferences(3, 5)
+ .addReferences(5, 4)
+ .addReferences(4, 6)
+ .addRoot(1)
+ .build();
+
+ mSnapshot.computeDominators();
+
+ assertEquals(mSnapshot.findInstance(1), mSnapshot.findInstance(4).getImmediateDominator());
+ assertEquals(mSnapshot.findInstance(4), mSnapshot.findInstance(6).getImmediateDominator());
+ assertEquals(36, mSnapshot.findInstance(1).getRetainedSize(1));
+ assertEquals(2, mSnapshot.findInstance(2).getRetainedSize(1));
+ assertEquals(8, mSnapshot.findInstance(3).getRetainedSize(1));
+ }
+
+ public void testReachableInstances() {
+ mSnapshot = new SnapshotBuilder(11, 2, 1)
+ .addReferences(1, 2, 3)
+ .insertSoftReference(1, 11)
+ .addReferences(2, 4)
+ .addReferences(3, 5, 6)
+ .insertSoftReference(4, 9)
+ .addReferences(5, 7)
+ .addReferences(6, 7)
+ .addReferences(7, 8, 10)
+ .insertSoftAndHardReference(8, 10, 9)
+ .addRoot(1)
+ .build();
+
+ mSnapshot.computeDominators();
+ for (Heap heap : mSnapshot.getHeaps()) {
+ ClassObj softClass = heap.getClass(SnapshotBuilder.SOFT_REFERENCE_ID);
+ if (softClass != null) {
+ assertTrue(softClass.getIsSoftReference());
+ }
+
+ ClassObj softAndHardClass = heap.getClass(SnapshotBuilder.SOFT_AND_HARD_REFERENCE_ID);
+ if (softAndHardClass != null) {
+ assertTrue(softAndHardClass.getIsSoftReference());
+ }
+ }
+
+ Instance instance9 = mSnapshot.findInstance(9);
+ assertNotNull(instance9);
+ assertNotNull(instance9.getSoftReferences());
+ assertEquals(1, instance9.getHardReferences().size());
+ assertEquals(1, instance9.getSoftReferences().size());
+ assertEquals(6, instance9.getDistanceToGcRoot());
+
+ Instance instance10 = mSnapshot.findInstance(10);
+ assertNotNull(instance10);
+ assertNotNull(instance10.getSoftReferences());
+ assertEquals(1, instance10.getHardReferences().size());
+ assertEquals(1, instance10.getSoftReferences().size());
+ assertEquals(4, instance10.getDistanceToGcRoot());
+
+ Instance instance11 = mSnapshot.findInstance(11);
+ assertNotNull(instance11);
+ assertNotNull(instance11.getSoftReferences());
+ assertEquals(0, instance11.getHardReferences().size());
+ assertEquals(1, instance11.getSoftReferences().size());
+ assertEquals(Integer.MAX_VALUE, instance11.getDistanceToGcRoot());
+
+ assertEquals(13, mSnapshot.getReachableInstances().size());
}
public void testSampleHprof() throws Exception {
@@ -116,11 +241,20 @@
mSnapshot = (new HprofParser(new MemoryMappedFileBuffer(file))).parse();
mSnapshot.computeDominators();
- // TODO: investigate the unreachable objects: there are 43687 objects in total.
- assertEquals(42911, mSnapshot.getReachableInstances().size());
+ Set<Instance> topologicalSet = new HashSet<Instance>(mSnapshot.getTopologicalOrdering());
+ assertEquals(topologicalSet.size(), mSnapshot.getTopologicalOrdering().size());
+
+ long totalInstanceCount = 0;
+ for (Heap heap : mSnapshot.getHeaps()) {
+ totalInstanceCount += heap.getInstances().size();
+ totalInstanceCount += heap.getClasses().size();
+ }
+ assertEquals(43687, totalInstanceCount);
+
+ assertEquals(42839, mSnapshot.getReachableInstances().size());
// An object reachable via two GC roots, a JNI global and a Thread.
- Instance instance = mSnapshot.findReference(0xB0EDFFA0);
+ Instance instance = mSnapshot.findInstance(0xB0EDFFA0);
assertEquals(Snapshot.SENTINEL_ROOT, instance.getImmediateDominator());
int appIndex = mSnapshot.getHeapIndex(mSnapshot.getHeap("app"));
@@ -133,7 +267,7 @@
// One of the bigger objects in the app heap
ClassObj activityThread = mSnapshot.findClass("android.app.ActivityThread");
- assertEquals(813, activityThread.getRetainedSize(zygoteIndex));
+ assertEquals(853, activityThread.getRetainedSize(zygoteIndex));
assertEquals(576, activityThread.getRetainedSize(appIndex));
}
@@ -141,7 +275,20 @@
* Asserts that nodeA dominates nodeB in mHeap.
*/
private void assertDominates(int nodeA, int nodeB) {
- assertEquals(mSnapshot.findReference(nodeA),
- mSnapshot.findReference(nodeB).getImmediateDominator());
+ assertEquals(mSnapshot.findInstance(nodeA),
+ mSnapshot.findInstance(nodeB).getImmediateDominator());
+ }
+
+ /**
+ * Asserts that one of the parents is the direct parent to node in the shortest path to the GC root.
+ */
+ private void assertParentPathToGc(int node, int... parents) {
+ for (int parent : parents) {
+ Instance parentInstance = mSnapshot.findInstance(node).getNextInstanceToGcRoot();
+ if (parentInstance != null && parentInstance.getId() == parent) {
+ return;
+ }
+ }
+ fail();
}
}
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/analysis/SnapshotBuilder.java b/perflib/src/test/java/com/android/tools/perflib/heap/analysis/SnapshotBuilder.java
deleted file mode 100644
index 1358675..0000000
--- a/perflib/src/test/java/com/android/tools/perflib/heap/analysis/SnapshotBuilder.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.perflib.heap.analysis;
-
-import com.android.tools.perflib.heap.ClassInstance;
-import com.android.tools.perflib.heap.ClassObj;
-import com.android.tools.perflib.heap.Field;
-import com.android.tools.perflib.heap.RootObj;
-import com.android.tools.perflib.heap.RootType;
-import com.android.tools.perflib.heap.Snapshot;
-import com.android.tools.perflib.heap.Type;
-import com.android.tools.perflib.heap.io.InMemoryBuffer;
-
-import java.nio.ByteBuffer;
-
-/**
- * Utility for creating Snapshot objects to be used in tests.
- *
- * As the main concern here is graph connectivity, we only initialize the app heap, creating
- * ClassInstance objects with id in [1..numNodes], each instance pointing to a unique ClassObj.
- * The class ids range in [101..100+numNodes] and their size is set to match the id of their object
- * instance. The default heap holds the roots.
- */
-public class SnapshotBuilder {
-
- private final Snapshot mSnapshot;
-
- private final ClassInstance[] mNodes;
-
- private final int[] mOffsets;
-
- private final ByteBuffer mDirectBuffer;
-
- public SnapshotBuilder(int numNodes) {
- Type.setIdSize(2); // A good opportunity to test the handling of short IDs.
- InMemoryBuffer buffer = new InMemoryBuffer(2 * numNodes * numNodes);
- mDirectBuffer = buffer.getDirectBuffer();
- mOffsets = new int[numNodes + 1];
-
- mSnapshot = new Snapshot(buffer);
- mSnapshot.setHeapTo(13, "testHeap");
-
- mNodes = new ClassInstance[numNodes + 1];
- for (int i = 1; i <= numNodes; i++) {
- ClassObj clazz = new ClassObj(100 + i, null, "Class" + i, 0);
- clazz.setFields(new Field[0]);
- mSnapshot.addClass(100 + i, clazz);
-
- mOffsets[i] = 2 * (i - 1) * numNodes;
- mNodes[i] = new ClassInstance(i, null, mOffsets[i]);
- mNodes[i].setClassId(100 + i);
- mNodes[i].setSize(i);
- mSnapshot.addInstance(i, mNodes[i]);
- }
- }
-
- public SnapshotBuilder addReferences(int nodeFrom, int... nodesTo) {
- Field[] fields = new Field[nodesTo.length];
- for (int i = 0; i < nodesTo.length; i++) {
- mDirectBuffer.putShort(mOffsets[nodeFrom] + i * 2, (short) nodesTo[i]);
- fields[i] = new Field(Type.OBJECT, "f" + nodesTo[i]);
- }
-
- mNodes[nodeFrom].getClassObj().setFields(fields);
- return this;
- }
-
- public SnapshotBuilder addRoot(int node) {
- RootObj root = new RootObj(RootType.JAVA_LOCAL, node);
- mSnapshot.setToDefaultHeap();
- mSnapshot.addRoot(root);
- return this;
- }
-
- public Snapshot getSnapshot() {
- return mSnapshot;
- }
-}
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/analysis/VisitorsTest.java b/perflib/src/test/java/com/android/tools/perflib/heap/analysis/VisitorsTest.java
index 74de899..9b26671 100644
--- a/perflib/src/test/java/com/android/tools/perflib/heap/analysis/VisitorsTest.java
+++ b/perflib/src/test/java/com/android/tools/perflib/heap/analysis/VisitorsTest.java
@@ -17,15 +17,7 @@
package com.android.tools.perflib.heap.analysis;
import com.android.annotations.NonNull;
-import com.android.tools.perflib.heap.ArrayInstance;
-import com.android.tools.perflib.heap.ClassInstance;
-import com.android.tools.perflib.heap.ClassObj;
-import com.android.tools.perflib.heap.Field;
-import com.android.tools.perflib.heap.Instance;
-import com.android.tools.perflib.heap.RootObj;
-import com.android.tools.perflib.heap.RootType;
-import com.android.tools.perflib.heap.Snapshot;
-import com.android.tools.perflib.heap.Type;
+import com.android.tools.perflib.heap.*;
import com.android.tools.perflib.heap.io.InMemoryBuffer;
import com.google.common.collect.Maps;
@@ -54,7 +46,7 @@
}
public void testSimpleStaticFieldsGraph() {
- Type.setIdSize(4);
+ mSnapshot.setIdSize(4);
final ClassInstance object1 = new ClassInstance(1, null, 0);
object1.setClassId(42);
object1.setSize(20);
@@ -87,7 +79,7 @@
}
public void testSimpleArray() {
- Type.setIdSize(4);
+ mSnapshot.setIdSize(4);
final ClassInstance object = new ClassInstance(1, null, 0);
object.setClassId(42);
object.setSize(20);
@@ -116,12 +108,12 @@
.addReferences(2, 4)
.addReferences(3, 4)
.addRoot(1)
- .getSnapshot();
+ .build();
- assertEquals(10, snapshot.findReference(1).getCompositeSize());
- assertEquals(6, snapshot.findReference(2).getCompositeSize());
- assertEquals(7, snapshot.findReference(3).getCompositeSize());
- assertEquals(4, snapshot.findReference(4).getCompositeSize());
+ assertEquals(10, snapshot.findInstance(1).getCompositeSize());
+ assertEquals(6, snapshot.findInstance(2).getCompositeSize());
+ assertEquals(7, snapshot.findInstance(3).getCompositeSize());
+ assertEquals(4, snapshot.findInstance(4).getCompositeSize());
}
public void testBasicCycle() {
@@ -130,12 +122,12 @@
.addReferences(2, 3)
.addReferences(3, 1)
.addRoot(1)
- .getSnapshot();
+ .build();
// The composite size is a sum over all nodes participating in the cycle.
- assertEquals(6, snapshot.findReference(1).getCompositeSize());
- assertEquals(6, snapshot.findReference(2).getCompositeSize());
- assertEquals(6, snapshot.findReference(3).getCompositeSize());
+ assertEquals(6, snapshot.findInstance(1).getCompositeSize());
+ assertEquals(6, snapshot.findInstance(2).getCompositeSize());
+ assertEquals(6, snapshot.findInstance(3).getCompositeSize());
}
public void testTopSortSimpleGraph() {
@@ -145,26 +137,26 @@
.addReferences(3, 4, 5)
.addReferences(4, 6)
.addRoot(1)
- .getSnapshot();
+ .build();
List<Instance> topSort = TopologicalSort.compute(snapshot.getGCRoots());
assertEquals(6, topSort.size());
// Make sure finishing times are computed correctly. A visitor simply collecting nodes as
// they are expanded will not yield the correct order. The correct invariant for a DAG is:
// for each directed edge (u,v), topsort(u) < topsort(v).
- assertTrue(snapshot.findReference(1).getTopologicalOrder() <
- snapshot.findReference(2).getTopologicalOrder());
- assertTrue(snapshot.findReference(1).getTopologicalOrder() <
- snapshot.findReference(3).getTopologicalOrder());
- assertTrue(snapshot.findReference(2).getTopologicalOrder() <
- snapshot.findReference(4).getTopologicalOrder());
- assertTrue(snapshot.findReference(2).getTopologicalOrder() <
- snapshot.findReference(6).getTopologicalOrder());
- assertTrue(snapshot.findReference(3).getTopologicalOrder() <
- snapshot.findReference(4).getTopologicalOrder());
- assertTrue(snapshot.findReference(3).getTopologicalOrder() <
- snapshot.findReference(5).getTopologicalOrder());
- assertTrue(snapshot.findReference(4).getTopologicalOrder() <
- snapshot.findReference(6).getTopologicalOrder());
+ assertTrue(snapshot.findInstance(1).getTopologicalOrder() <
+ snapshot.findInstance(2).getTopologicalOrder());
+ assertTrue(snapshot.findInstance(1).getTopologicalOrder() <
+ snapshot.findInstance(3).getTopologicalOrder());
+ assertTrue(snapshot.findInstance(2).getTopologicalOrder() <
+ snapshot.findInstance(4).getTopologicalOrder());
+ assertTrue(snapshot.findInstance(2).getTopologicalOrder() <
+ snapshot.findInstance(6).getTopologicalOrder());
+ assertTrue(snapshot.findInstance(3).getTopologicalOrder() <
+ snapshot.findInstance(4).getTopologicalOrder());
+ assertTrue(snapshot.findInstance(3).getTopologicalOrder() <
+ snapshot.findInstance(5).getTopologicalOrder());
+ assertTrue(snapshot.findInstance(4).getTopologicalOrder() <
+ snapshot.findInstance(6).getTopologicalOrder());
}
}
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/io/HprofBufferTest.java b/perflib/src/test/java/com/android/tools/perflib/heap/io/HprofBufferTest.java
index 95bc9b9..ddf4210 100644
--- a/perflib/src/test/java/com/android/tools/perflib/heap/io/HprofBufferTest.java
+++ b/perflib/src/test/java/com/android/tools/perflib/heap/io/HprofBufferTest.java
@@ -20,9 +20,12 @@
import com.android.tools.perflib.heap.Snapshot;
import junit.framework.TestCase;
+import sun.misc.IOUtils;
import java.io.File;
+import java.io.FileInputStream;
import java.io.RandomAccessFile;
+import java.util.Arrays;
public class HprofBufferTest extends TestCase {
@@ -73,6 +76,52 @@
assertFalse(g.exists());
}
+ public void testSubsequenceReads() throws Exception {
+ byte[] fileContents = null;
+ FileInputStream fileInputStream = new FileInputStream(file);
+ try {
+ fileContents = IOUtils.readFully(fileInputStream, -1, true);
+ }
+ finally {
+ fileInputStream.close();
+ }
+
+ MemoryMappedFileBuffer mappedBuffer = new MemoryMappedFileBuffer(file, 8259, 8);
+
+ byte[] buffer = new byte[8190];
+ mappedBuffer.readSubSequence(buffer, 0, 8190);
+ assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(fileContents, 0, 8190)));
+ assertEquals(mappedBuffer.position(), 8190);
+
+ buffer = new byte[8190];
+ mappedBuffer.setPosition(0);
+ mappedBuffer.readSubSequence(buffer, 2000, 8190);
+ assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(fileContents, 2000, 2000 + 8190)));
+ assertEquals(mappedBuffer.position(), 2000 + 8190);
+
+ buffer = new byte[100000];
+ mappedBuffer.setPosition(0);
+ mappedBuffer.readSubSequence(buffer, 19242, 100000);
+ assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(fileContents, 19242, 19242 + 100000)));
+ assertEquals(mappedBuffer.position(), 19242 + 100000);
+
+ buffer = new byte[8259];
+ mappedBuffer.setPosition(0);
+ mappedBuffer.readSubSequence(buffer, 0, 8259);
+ assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(fileContents, 0, 8259)));
+ assertEquals(mappedBuffer.position(), 8259);
+
+ buffer = new byte[8259];
+ mappedBuffer.setPosition(0);
+ mappedBuffer.readSubSequence(buffer, 8259, 8259);
+ assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(fileContents, 8259, 8259 + 8259)));
+ assertEquals(mappedBuffer.position(), 8259 + 8259);
+
+ mappedBuffer.readSubSequence(buffer, 8259, 8259);
+ assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(fileContents, 8259 * 3, 8259 * 4)));
+ assertEquals(mappedBuffer.position(), 8259 * 4);
+ }
+
private void assertSnapshotCorrect(Snapshot snapshot) {
assertEquals(11182, snapshot.getGCRoots().size());
assertEquals(38, snapshot.getHeap(65).getClasses().size());
diff --git a/perflib/src/test/java/com/android/tools/perflib/heap/io/InMemoryBuffer.java b/perflib/src/test/java/com/android/tools/perflib/heap/io/InMemoryBuffer.java
index 3533250..055eb26 100644
--- a/perflib/src/test/java/com/android/tools/perflib/heap/io/InMemoryBuffer.java
+++ b/perflib/src/test/java/com/android/tools/perflib/heap/io/InMemoryBuffer.java
@@ -41,6 +41,11 @@
}
@Override
+ public void readSubSequence(byte[] b, int sourceStart, int sourceEnd) {
+ ((ByteBuffer)mBuffer.slice().position(sourceStart)).get(b);
+ }
+
+ @Override
public char readChar() {
return mBuffer.getChar();
}
diff --git a/perflib/src/test/java/com/android/tools/perflib/vmtrace/viz/TraceView.java b/perflib/src/test/java/com/android/tools/perflib/vmtrace/viz/TraceView.java
index ea4a460..2358a14 100644
--- a/perflib/src/test/java/com/android/tools/perflib/vmtrace/viz/TraceView.java
+++ b/perflib/src/test/java/com/android/tools/perflib/vmtrace/viz/TraceView.java
@@ -16,8 +16,8 @@
package com.android.tools.perflib.vmtrace.viz;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.fail;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
import com.android.tools.perflib.vmtrace.ClockType;
import com.android.tools.perflib.vmtrace.SearchResult;
diff --git a/rule-api/rule-api.iml b/rule-api/rule-api.iml
index f108533..c616ef9 100644
--- a/rule-api/rule-api.iml
+++ b/rule-api/rule-api.iml
@@ -3,7 +3,7 @@
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
- <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.settings" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
@@ -11,5 +11,4 @@
<orderEntry type="module" module-name="common" exported="" />
<orderEntry type="module" module-name="layoutlib-api-base" exported="" />
</component>
-</module>
-
+</module>
\ No newline at end of file
diff --git a/sdk-common/build.gradle b/sdk-common/build.gradle
index f2ac5ed..cc946c4 100644
--- a/sdk-common/build.gradle
+++ b/sdk-common/build.gradle
@@ -1,24 +1,17 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
+apply plugin: 'jacoco'
apply plugin: 'sdk-java-lib'
+apply plugin: 'application'
group = 'com.android.tools'
archivesBaseName = 'sdk-common'
version = rootProject.ext.baseVersion
+mainClassName = 'com.android.ide.common.vectordrawable.VdPreview'
dependencies {
compile project(':base:sdklib')
compile project(':base:builder-test-api')
compile project(':base:builder-model')
- compile 'com.google.code.gson:gson:2.2.4'
testCompile 'junit:junit:4.12'
testCompile project(':base:sdklib').sourceSets.test.output
diff --git a/sdk-common/generate-locale-data/src/main/java/com/android/ide/common/generate/locale/LocaleTableGenerator.java b/sdk-common/generate-locale-data/src/main/java/com/android/ide/common/generate/locale/LocaleTableGenerator.java
index bf37d7d..0bcecd2 100644
--- a/sdk-common/generate-locale-data/src/main/java/com/android/ide/common/generate/locale/LocaleTableGenerator.java
+++ b/sdk-common/generate-locale-data/src/main/java/com/android/ide/common/generate/locale/LocaleTableGenerator.java
@@ -127,6 +127,17 @@
continue;
}
assert iso3.length() == 3 : iso3;
+
+ // 3 languages in that spec have deprecated codes which will not work right;
+ // see LocaleFolderDetector#DEPRECATED_CODE for details
+ if (iso2.equals("he")) {
+ iso2 = "iw";
+ } else if (iso2.equals("id")) {
+ iso2 = "in";
+ } else if (iso2.equals("yi")) {
+ iso2 = "ji";
+ }
+
if (!iso2.isEmpty() && mLanguage2to3.containsKey(iso2)) {
// We already know about this one
matched++;
@@ -297,6 +308,11 @@
mLanguage2to3.put("in", "ind"); // proper 3-letter code, but NOT mapping back: should be id
mLanguage2to3.put("ji", "yid"); // proper 3-letter code, but NOT mapping back: should be yi
mLanguage2to3.put("iw", "heb"); // proper 3-letter code, but NOT mapping back: should be he
+ // Make sure the forward map (from 3 to 2) is also using the primary (new) code
+ mLanguage3to2.put("ind", "in");
+ mLanguage3to2.put("heb", "iw");
+ mLanguage3to2.put("yid", "ji");
+
mLanguage2Codes = sorted(mLanguage2to3.keySet());
mLanguage3Codes = sorted(mLanguageName.keySet());
@@ -1430,7 +1446,7 @@
}
private void generateZones(int level, Collection<TimeZone> zones) {
- assert zones.size() > 0;
+ assert !zones.isEmpty();
// See if they all map to the same region
boolean regionsDiffer = false;
diff --git a/sdk-common/sdk-common-base.iml b/sdk-common/sdk-common-base.iml
index 3296017..1c7c2dc 100644
--- a/sdk-common/sdk-common-base.iml
+++ b/sdk-common/sdk-common-base.iml
@@ -17,10 +17,9 @@
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
<orderEntry type="module" module-name="testutils" scope="TEST" />
<orderEntry type="module" module-name="builder-model" exported="" />
- <orderEntry type="library" name="mockito" level="project" />
- <orderEntry type="library" name="gson" level="project" />
- <orderEntry type="module" module-name="builder-test-api" />
+ <orderEntry type="library" scope="TEST" name="mockito" level="project" />
+ <orderEntry type="module" module-name="builder-test-api" exported="" />
<orderEntry type="module" module-name="sdklib-base" exported="" />
<orderEntry type="library" scope="TEST" name="easymock-tools" level="project" />
</component>
-</module>
+</module>
\ No newline at end of file
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/FilePosition.java b/sdk-common/src/main/java/com/android/ide/common/blame/FilePosition.java
deleted file mode 100644
index 76c3d94..0000000
--- a/sdk-common/src/main/java/com/android/ide/common/blame/FilePosition.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.common.blame;
-
-import com.android.annotations.NonNull;
-import com.google.common.base.Objects;
-
-import java.io.File;
-
-/**
- * An immutable position in a text file with a reference to that file.
- */
-public class FilePosition extends SourcePosition {
-
- private final File sourceFile;
-
- public FilePosition(@NonNull File sourceFile, @NonNull SourcePosition position) {
- super(position);
- this.sourceFile = sourceFile;
- }
-
- public static FilePosition wholeFile(@NonNull File sourceFile) {
- return new FilePosition(sourceFile, SourcePosition.UNKNOWN);
- }
-
- public File getSourceFile() {
- return sourceFile;
- }
-
- @Override
- public String toString() {
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append(sourceFile.getAbsoluteFile());
- if (getStartLine() != -1) {
- stringBuilder.append(':');
- stringBuilder.append(super.toString());
- }
- return stringBuilder.toString();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof FilePosition)) {
- return false;
- }
- FilePosition other = (FilePosition) obj;
-
- return other.getSourceFile().getAbsolutePath().equals(getSourceFile().getAbsolutePath()) &&
- super.equals((SourcePosition) other);
-
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(super.hashCode(), sourceFile.getAbsolutePath());
- }
-}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/MessageJsonSerializer.java b/sdk-common/src/main/java/com/android/ide/common/blame/MessageJsonSerializer.java
new file mode 100644
index 0000000..2bb4652
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/MessageJsonSerializer.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.blame;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.EnumHashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import com.google.gson.GsonBuilder;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Class to handle json serialization and deserialization of messages.
+ *
+ * Reads json objects of the form:
+ *
+ * <pre>
+ * {
+ * "kind":"ERROR",
+ * "text":"errorText",
+ * "original":"unparsed error text: Error in some intermediate file",
+ * "sources": [{
+ * "file":"/path/to/source.java",
+ * "position":{
+ * "startLine":1,
+ * "startColumn":2,
+ * "startOffset":3,
+ * "endLine":4,
+ * "endColumn":5,
+ * "endOffset":6
+ * }
+ * }]
+ * }</pre>
+ *
+ * All fields, other than text, may be omitted. They have the following defaults:
+ *
+ * <table>
+ * <tr><th>Property</th> <th>Default</th> <th>Notes</th></tr>
+ * <tr><td>kind (ERROR, WARNING,
+ * INFO, UNKNOWN)</td> <td>UNKNOWN</td> <td></td></tr>
+ * <tr><td>text</td> <td><i>Empty String</i></td> <td>Should not be omitted.</td></tr>
+ * <tr><td>file (Absolute)</td> <td>{} <i>[unknown]</i></td> <td>See {@link SourceFileJsonTypeAdapter}</td></tr>
+ * <tr><td>position</td> <td>UNKNOWN</td> <td></td></tr>
+ * <tr><td>startLine,
+ * startColumn,
+ * startOffset</td> <td>-1 <i>[unknown]</i></td> <td rowspan="2">0-based</td></tr>
+ * <tr><td>endLine,
+ * endColumn,
+ * endOffset</td> <td>startLine, startColumn,
+ * startOffset</td></tr>
+ * </table>
+ *
+ * <h4>Notes</h4>
+ * <ul>
+ * <li>Offset need not be included, if needed by the consumer of the message it can
+ * be derived from the file, line and column.</li>
+ * <li>If line is included and column is not the message will be considered to apply
+ * to the whole line.</li>
+ * <li>A message can have multiple sources.</li>
+ * </ul>
+ * It also can read legacy serialized objects of the form:
+ *
+ * <pre>{
+ * "kind":"ERROR",
+ * "text":"errorText",
+ * "sourcePath": "/path/to/source.java",
+ * "position":{
+ * "startLine":1,
+ * "startColumn":2,
+ * "startOffset":3,
+ * "endLine":4,
+ * "endColumn":5,
+ * "endOffset":6
+ * }
+ * }</pre>
+ *
+ * These serializers are implemented using the lower-level TypeAdapter gson API which gives much
+ * more control and allow changes to be made without breaking backward compatibility.
+ */
+public class MessageJsonSerializer extends TypeAdapter<Message> {
+
+ private static final String KIND = "kind";
+
+ private static final String TEXT = "text";
+
+ private static final String SOURCE_FILE_POSITIONS = "sources";
+
+ private static final String RAW_MESSAGE = "original";
+
+ private static final String LEGACY_SOURCE_PATH = "sourcePath";
+
+ private static final String LEGACY_POSITION = "position";
+
+ private static final BiMap<Message.Kind, String> KIND_STRING_ENUM_MAP;
+
+ static {
+ EnumHashBiMap<Message.Kind, String> map = EnumHashBiMap.create(Message.Kind.class);
+ map.put(Message.Kind.ERROR, "error");
+ map.put(Message.Kind.WARNING, "warning");
+ map.put(Message.Kind.INFO, "info");
+ map.put(Message.Kind.STATISTICS, "statistics");
+ map.put(Message.Kind.UNKNOWN, "unknown");
+ map.put(Message.Kind.SIMPLE, "simple");
+ KIND_STRING_ENUM_MAP = Maps.unmodifiableBiMap(map);
+ }
+
+ private final SourceFilePositionJsonSerializer mSourceFilePositionTypeAdapter;
+ private final SourcePositionJsonTypeAdapter mSourcePositionTypeAdapter;
+
+ public MessageJsonSerializer() {
+ mSourceFilePositionTypeAdapter = new SourceFilePositionJsonSerializer();
+ mSourcePositionTypeAdapter = mSourceFilePositionTypeAdapter.getSourcePositionTypeAdapter();
+ }
+
+ @Override
+ public void write(JsonWriter out, Message message) throws IOException {
+ out.beginObject()
+ .name(KIND).value(KIND_STRING_ENUM_MAP.get(message.getKind()))
+ .name(TEXT).value(message.getText())
+ .name(SOURCE_FILE_POSITIONS).beginArray();
+ for (SourceFilePosition position : message.getSourceFilePositions()) {
+ mSourceFilePositionTypeAdapter.write(out, position);
+ }
+ out.endArray();
+ if (!message.getRawMessage().equals(message.getText())) {
+ out.name(RAW_MESSAGE).value(message.getRawMessage());
+ }
+ out.endObject();
+ }
+
+ @Override
+ public Message read(JsonReader in) throws IOException {
+ in.beginObject();
+ Message.Kind kind = Message.Kind.UNKNOWN;
+ String text = "";
+ String rawMessage = null;
+ ImmutableList.Builder<SourceFilePosition> positions =
+ new ImmutableList.Builder<SourceFilePosition>();
+ SourceFile legacyFile = SourceFile.UNKNOWN;
+ SourcePosition legacyPosition = SourcePosition.UNKNOWN;
+ while (in.hasNext()) {
+ String name = in.nextName();
+ if (name.equals(KIND)) {
+ //noinspection StringToUpperCaseOrToLowerCaseWithoutLocale
+ Message.Kind theKind = KIND_STRING_ENUM_MAP.inverse()
+ .get(in.nextString().toLowerCase());
+ kind = (theKind != null) ? theKind : Message.Kind.UNKNOWN;
+ } else if (name.equals(TEXT)) {
+ text = in.nextString();
+ } else if (name.equals(RAW_MESSAGE)) {
+ rawMessage = in.nextString();
+ } else if (name.equals(SOURCE_FILE_POSITIONS)) {
+ switch (in.peek()) {
+ case BEGIN_ARRAY:
+ in.beginArray();
+ while(in.hasNext()) {
+ positions.add(mSourceFilePositionTypeAdapter.read(in));
+ }
+ in.endArray();
+ break;
+ case BEGIN_OBJECT:
+ positions.add(mSourceFilePositionTypeAdapter.read(in));
+ break;
+ default:
+ in.skipValue();
+ break;
+ }
+ } else if (name.equals(LEGACY_SOURCE_PATH)) {
+ legacyFile = new SourceFile(new File(in.nextString()));
+ } else if (name.equals(LEGACY_POSITION)) {
+ legacyPosition = mSourcePositionTypeAdapter.read(in);
+ } else {
+ in.skipValue();
+ }
+ }
+ in.endObject();
+
+ if (legacyFile != SourceFile.UNKNOWN || legacyPosition != SourcePosition.UNKNOWN) {
+ positions.add(new SourceFilePosition(legacyFile, legacyPosition));
+ }
+ if (rawMessage == null) {
+ rawMessage = text;
+ }
+ ImmutableList<SourceFilePosition> sourceFilePositions = positions.build();
+ if (!sourceFilePositions.isEmpty()) {
+ return new Message(kind, text, rawMessage, sourceFilePositions);
+ } else {
+ return new Message(kind, text, rawMessage, ImmutableList.of(SourceFilePosition.UNKNOWN));
+ }
+ }
+
+ public static void registerTypeAdapters(GsonBuilder builder) {
+ builder.registerTypeAdapter(SourceFile.class, new SourceFileJsonTypeAdapter());
+ builder.registerTypeAdapter(SourcePosition.class, new SourcePositionJsonTypeAdapter());
+ builder.registerTypeAdapter(SourceFilePosition.class,
+ new SourceFilePositionJsonSerializer());
+ builder.registerTypeAdapter(Message.class, new MessageJsonSerializer());
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/MessageReceiver.java b/sdk-common/src/main/java/com/android/ide/common/blame/MessageReceiver.java
new file mode 100644
index 0000000..1b6ab98
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/MessageReceiver.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.blame;
+
+import com.android.annotations.NonNull;
+
+public interface MessageReceiver {
+ void receiveMessage(@NonNull Message m);
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/ParsingProcessOutputHandler.java b/sdk-common/src/main/java/com/android/ide/common/blame/ParsingProcessOutputHandler.java
new file mode 100644
index 0000000..c7fd993
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/ParsingProcessOutputHandler.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.blame;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.blame.parser.ToolOutputParser;
+import com.android.ide.common.process.BaseProcessOutputHandler;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessOutput;
+import com.android.utils.ILogger;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * A ProcessOutputHandler that runs ToolOutputParsers over the process output and emits Messages.
+ */
+public class ParsingProcessOutputHandler extends BaseProcessOutputHandler {
+
+ @NonNull
+ private final ToolOutputParser mErrorToolOutputParser;
+
+ @NonNull
+ private final ToolOutputParser mStdoutToolOutputParser;
+
+ @NonNull
+ private final List<MessageReceiver> mMessageReceivers;
+
+ /**
+ * Create a ParsingProcessOutputHandler.
+ *
+ * @param errorToolOutputParser the {@link ToolOutputParser} to use for process output sent
+ * to stderr,
+ * @param stdoutToolOutputParser the ToolOutputParser to use for process output to sent to
+ * stdout.
+ * @param messageReceivers the message receivers to notify for each message,
+ */
+ public ParsingProcessOutputHandler(
+ @NonNull ToolOutputParser errorToolOutputParser,
+ @NonNull ToolOutputParser stdoutToolOutputParser,
+ @NonNull MessageReceiver... messageReceivers) {
+ mErrorToolOutputParser = errorToolOutputParser;
+ mStdoutToolOutputParser = stdoutToolOutputParser;
+ mMessageReceivers = ImmutableList.copyOf(messageReceivers);
+ }
+
+ /**
+ * Create a ParsingProcessOutputHandler.
+ *
+ * @param toolOutputParser the {@link ToolOutputParser} to use for process output sent to
+ * stderr and stdout,
+ * @param messageReceivers the message receivers to notify for each message,
+ */
+ public ParsingProcessOutputHandler(
+ @NonNull ToolOutputParser toolOutputParser,
+ @NonNull MessageReceiver... messageReceivers) {
+ this(toolOutputParser, toolOutputParser, messageReceivers);
+ }
+
+ @Override
+ public void handleOutput(@NonNull ProcessOutput processOutput) throws ProcessException {
+ if (!(processOutput instanceof BaseProcessOutput)) {
+ throw new IllegalArgumentException("processOutput was not created by this handler.");
+ }
+ BaseProcessOutput impl = (BaseProcessOutput) processOutput;
+ String stdout = impl.getStandardOutputAsString();
+ if (!stdout.isEmpty()) {
+ outputMessages(mStdoutToolOutputParser.parseToolOutput(stdout));
+ }
+ String stderr = impl.getErrorOutputAsString();
+ if (!stderr.isEmpty()) {
+ outputMessages(mErrorToolOutputParser.parseToolOutput(stderr));
+ }
+ }
+
+ private void outputMessages(List<Message> messages) {
+ for (Message message: messages) {
+ for (MessageReceiver messageReceiver: mMessageReceivers) {
+ messageReceiver.receiveMessage(message);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/SourceFileJsonTypeAdapter.java b/sdk-common/src/main/java/com/android/ide/common/blame/SourceFileJsonTypeAdapter.java
new file mode 100644
index 0000000..3794c57
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/SourceFileJsonTypeAdapter.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.blame;
+
+import com.google.common.base.Strings;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * JsonSerializer and Deserializer for {@link SourceFile}.
+ *
+ * The JsonDeserialiser accepts either a string of the file path or a json object of the form
+ * <pre>{
+ * "path":"/path/to/file.java",
+ * "description": "short human-readable description"
+ * }</pre> where both
+ * properties are optionally present, so unknown is represented by the empty object.
+ */
+public class SourceFileJsonTypeAdapter extends TypeAdapter<SourceFile> {
+
+ private static final String PATH = "path";
+
+ private static final String DESCRIPTION = "description";
+
+ @Override
+ public void write(JsonWriter out, SourceFile src) throws IOException {
+ File file = src.getSourceFile();
+ String description = src.getDescription();
+
+ if (description == null && file != null) {
+ out.value(file.getAbsolutePath());
+ return;
+ }
+
+ out.beginObject();
+ if (description != null) {
+ out.name(DESCRIPTION).value(description);
+ }
+ if (file != null) {
+ out.name(PATH).value(file.getAbsolutePath());
+ }
+ out.endObject();
+ }
+
+ @Override
+ public SourceFile read(JsonReader in) throws IOException {
+ switch (in.peek()) {
+ case BEGIN_OBJECT:
+ in.beginObject();
+ String filePath = null;
+ String description = null;
+ while (in.hasNext()) {
+ String name = in.nextName();
+ if (name.equals(PATH)) {
+ filePath = in.nextString();
+ } else if (DESCRIPTION.equals(name)) {
+ description = in.nextString();
+ } else {
+ in.skipValue();
+ }
+ }
+ in.endObject();
+ if (!Strings.isNullOrEmpty(filePath)) {
+ File file = new File(filePath);
+ if (!Strings.isNullOrEmpty(description)) {
+ return new SourceFile(file, description);
+ } else {
+ return new SourceFile(file);
+ }
+ } else {
+ if (!Strings.isNullOrEmpty(description)) {
+ return new SourceFile(description);
+ } else {
+ return SourceFile.UNKNOWN;
+ }
+ }
+ case STRING:
+ String fileName = in.nextString();
+ if (Strings.isNullOrEmpty(fileName)) {
+ return SourceFile.UNKNOWN;
+ }
+ return new SourceFile(new File(fileName));
+ default:
+ return SourceFile.UNKNOWN;
+ }
+
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/SourceFilePositionJsonSerializer.java b/sdk-common/src/main/java/com/android/ide/common/blame/SourceFilePositionJsonSerializer.java
new file mode 100644
index 0000000..58e55f1
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/SourceFilePositionJsonSerializer.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.blame;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+public class SourceFilePositionJsonSerializer extends TypeAdapter<SourceFilePosition> {
+
+ private static final String POSITION = "position";
+
+ private static final String FILE = "file";
+
+ private final SourceFileJsonTypeAdapter mSourceFileJsonTypeAdapter;
+ private final SourcePositionJsonTypeAdapter mSourcePositionJsonTypeAdapter;
+
+ public SourceFilePositionJsonSerializer() {
+ mSourcePositionJsonTypeAdapter = new SourcePositionJsonTypeAdapter();
+ mSourceFileJsonTypeAdapter = new SourceFileJsonTypeAdapter();
+ }
+
+ @Override
+ public SourceFilePosition read(JsonReader in) throws IOException {
+ in.beginObject();
+ SourceFile file = SourceFile.UNKNOWN;
+ SourcePosition position = SourcePosition.UNKNOWN;
+ while(in.hasNext()) {
+ String name = in.nextName();
+ if (name.equals(FILE)) {
+ file = mSourceFileJsonTypeAdapter.read(in);
+ } else if (name.equals(POSITION)) {
+ position = mSourcePositionJsonTypeAdapter.read(in);
+ } else {
+ in.skipValue();
+ }
+ }
+ in.endObject();
+ return new SourceFilePosition(file, position);
+ }
+
+ @Override
+ public void write(JsonWriter out, SourceFilePosition src) throws IOException {
+ out.beginObject();
+ SourceFile sourceFile = src.getFile();
+ if (!sourceFile.equals(SourceFile.UNKNOWN)) {
+ out.name(FILE);
+ mSourceFileJsonTypeAdapter.write(out, sourceFile);
+ }
+ SourcePosition position = src.getPosition();
+ if (!position.equals(SourcePosition.UNKNOWN)) {
+ out.name(POSITION);
+ mSourcePositionJsonTypeAdapter.write(out, position);
+ }
+ out.endObject();
+ }
+
+ /* package */ SourcePositionJsonTypeAdapter getSourcePositionTypeAdapter() {
+ return mSourcePositionJsonTypeAdapter;
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/output/BlameAwareLoggedProcessOutputHandler.java b/sdk-common/src/main/java/com/android/ide/common/blame/output/BlameAwareLoggedProcessOutputHandler.java
deleted file mode 100644
index ebad245..0000000
--- a/sdk-common/src/main/java/com/android/ide/common/blame/output/BlameAwareLoggedProcessOutputHandler.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.common.blame.output;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.process.LoggedProcessOutputHandler;
-import com.android.utils.ILogger;
-
-public class BlameAwareLoggedProcessOutputHandler extends LoggedProcessOutputHandler {
- public BlameAwareLoggedProcessOutputHandler(@NonNull ILogger logger, @NonNull GradleMessageRewriter.ErrorFormatMode errorFormatMode) {
- super(new BlameRewritingLogger(logger, errorFormatMode));
- }
-}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/output/BlameRewritingLogger.java b/sdk-common/src/main/java/com/android/ide/common/blame/output/BlameRewritingLogger.java
deleted file mode 100644
index caba3ce..0000000
--- a/sdk-common/src/main/java/com/android/ide/common/blame/output/BlameRewritingLogger.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.common.blame.output;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.blame.parser.ToolOutputParser;
-import com.android.ide.common.blame.parser.aapt.AaptOutputParser;
-import com.android.utils.ILogger;
-
-public class BlameRewritingLogger implements ILogger {
-
- private final ILogger mLogger;
- private final GradleMessageRewriter mGradleMessageRewriter;
-
- public BlameRewritingLogger(@NonNull ILogger logger, @NonNull GradleMessageRewriter.ErrorFormatMode errorFormatMode) {
- this.mLogger = logger;
- ToolOutputParser parser = new ToolOutputParser(new AaptOutputParser(), logger);
- mGradleMessageRewriter = new GradleMessageRewriter(parser, errorFormatMode);
- }
-
- @Override
- public void error(@Nullable Throwable t, @Nullable String msgFormat, Object... args) {
- mLogger.error(t, mGradleMessageRewriter.rewriteMessages(msgFormat), args);
- }
-
- @Override
- public void warning(@NonNull String msgFormat, Object... args) {
- mLogger.warning(mGradleMessageRewriter.rewriteMessages(msgFormat), args);
- }
-
- @Override
- public void info(@NonNull String msgFormat, Object... args) {
- mLogger.info(msgFormat, args);
- }
-
- @Override
- public void verbose(@NonNull String msgFormat, Object... args) {
- mLogger.verbose(msgFormat, args);
- }
-}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/output/GradleMessage.java b/sdk-common/src/main/java/com/android/ide/common/blame/output/GradleMessage.java
deleted file mode 100644
index e0d03d3..0000000
--- a/sdk-common/src/main/java/com/android/ide/common/blame/output/GradleMessage.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.common.blame.output;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.blame.SourcePosition;
-import com.google.common.base.Objects;
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Message produced by tools invoked when building an Android project.
- */
-public class GradleMessage {
-
- @SerializedName("kind")
- @NonNull
- private final Kind mKind;
-
- @SerializedName("text")
- @NonNull
- private final String mText;
-
- @SerializedName("sourcePath")
- @Nullable
- private final String mSourcePath;
-
- @SerializedName("position")
- @NonNull
- private final SourcePosition mPosition;
-
- @SerializedName("original")
- @NonNull
- private final String mOriginal;
-
- public GradleMessage(@NonNull Kind kind, @NonNull String text) {
- this(kind, text, null /* sourcePath */, SourcePosition.UNKNOWN, text);
- }
-
-
- public GradleMessage(@NonNull Kind kind,
- @NonNull String text,
- @Nullable String sourcePath,
- @NonNull SourcePosition position,
- @NonNull String original) {
- mKind = kind;
- mText = text;
- mSourcePath = sourcePath;
- mPosition = position;
- mOriginal = original;
- }
-
- public GradleMessage(@NonNull Kind kind, @NonNull String text, @Nullable String sourcePath, int line, int column) {
- this(kind, text, sourcePath, new SourcePosition(line, column, -1), text);
- }
-
- @NonNull
- public Kind getKind() {
- return mKind;
- }
-
- @NonNull
- public String getText() {
- return mText;
- }
-
- @Nullable
- public String getSourcePath() {
- return mSourcePath;
- }
-
- public int getLineNumber() {
- return mPosition.getStartLine();
- }
-
- public int getColumn() {
- return mPosition.getStartColumn();
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- GradleMessage that = (GradleMessage) o;
-
- return Objects.equal(mPosition, that.mPosition) &&
- mKind == that.mKind &&
- Objects.equal(mSourcePath, that.mSourcePath) &&
- Objects.equal(mText, that.mText);
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(mPosition, mKind, mSourcePath, mText);
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "[" +
- "kind=" + mKind +
- ", text=\"" + mText + '\"' +
- ", sourcePath=" + mSourcePath +
- ", position=" + mPosition.toString() +
- ']';
- }
-
- public SourcePosition getPosition() {
- return mPosition;
- }
-
- public String getOriginal() {
- return mOriginal;
- }
-
- public enum Kind {
- ERROR, WARNING, INFO, STATISTICS, SIMPLE;
-
- @Nullable
- public static Kind findIgnoringCase(@NonNull String s) {
- for (Kind kind : values()) {
- if (kind.toString().equalsIgnoreCase(s)) {
- return kind;
- }
- }
- return null;
- }
- }
-}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/output/GradleMessageRewriter.java b/sdk-common/src/main/java/com/android/ide/common/blame/output/GradleMessageRewriter.java
deleted file mode 100644
index 38a7f18..0000000
--- a/sdk-common/src/main/java/com/android/ide/common/blame/output/GradleMessageRewriter.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.ide.common.blame.output;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.blame.SourcePosition;
-import com.android.ide.common.blame.SourcePositionJsonTypeAdapter;
-import com.android.ide.common.blame.parser.ToolOutputParser;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-
-import java.util.List;
-
-public class GradleMessageRewriter {
-
- public static final String STDOUT_ERROR_TAG = "AGPBI: ";
-
- public enum ErrorFormatMode {
- MACHINE_PARSABLE, HUMAN_READABLE
- }
-
- private final ToolOutputParser mParser;
- private final Gson mGson;
- private final ErrorFormatMode mErrorFormatMode;
-
- public GradleMessageRewriter(ToolOutputParser parser, ErrorFormatMode errorFormatMode) {
- mParser = parser;
- mErrorFormatMode = errorFormatMode;
- mGson = createGson();
- }
-
- public String rewriteMessages(@NonNull String originalMessage) {
- List<GradleMessage> messages = mParser.parseToolOutput(originalMessage);
-
- if (messages.isEmpty()) {
- return originalMessage;
- }
-
- StringBuilder errorStringBuilder = new StringBuilder();
- for (GradleMessage message: messages) {
- if (mErrorFormatMode == ErrorFormatMode.HUMAN_READABLE) {
- if (message.getPosition() != null && message.getPosition().getStartLine() != -1) {
- errorStringBuilder.append(" Position ");
- errorStringBuilder.append(message.getPosition().toString());
- errorStringBuilder.append(" : ");
-
- }
- errorStringBuilder.append(message.getText())
- .append("\n");
-
- } else {
- errorStringBuilder.append(STDOUT_ERROR_TAG)
- .append(mGson.toJson(message)).append("\n");
- }
- }
- return errorStringBuilder.toString();
- }
-
- private static Gson createGson() {
- GsonBuilder gsonBuilder = new GsonBuilder();
- gsonBuilder.registerTypeAdapter(SourcePosition.class,
- new SourcePositionJsonTypeAdapter());
- return gsonBuilder.create();
- }
-}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/JsonEncodedGradleMessageParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/JsonEncodedGradleMessageParser.java
index 977eae8..391757d 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/JsonEncodedGradleMessageParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/JsonEncodedGradleMessageParser.java
@@ -16,10 +16,8 @@
package com.android.ide.common.blame.parser;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.SourcePosition;
-import com.android.ide.common.blame.SourcePositionJsonTypeAdapter;
-import com.android.ide.common.blame.output.GradleMessage;
-import com.android.ide.common.blame.output.GradleMessageRewriter;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.MessageJsonSerializer;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.utils.ILogger;
import com.google.gson.Gson;
@@ -31,21 +29,25 @@
import java.util.regex.Pattern;
/**
- * Reconstruct GradleMessages that were parsed by the gradle plugin.
+ * Reconstruct Messages that were parsed by the gradle plugin.
*/
public class JsonEncodedGradleMessageParser implements PatternAwareOutputParser {
+ // Android gradle parsed build issue. A prefix that is very unlikely to occur elsewhere to
+ // signify the rest of the line should be parsed as a json encoded {@link Message},
+ public static final String STDOUT_ERROR_TAG = "AGPBI: ";
+
/**
* The errors are of the form:
* <pre>AGPBI: {"kind":"ERROR","text":"Nothing"...}</pre>
*/
private static final Pattern MSG_PATTERN = Pattern.compile("^" + Pattern.quote(
- GradleMessageRewriter.STDOUT_ERROR_TAG) + "(.*)$");
+ STDOUT_ERROR_TAG) + "(.*)$");
@Override
public boolean parse(@NonNull String line,
@NonNull OutputLineReader reader,
- @NonNull List<GradleMessage> messages,
+ @NonNull List<Message> messages,
@NonNull ILogger logger) throws ParsingFailedException {
Matcher m = MSG_PATTERN.matcher(line);
if (!m.matches()) {
@@ -57,11 +59,10 @@
}
GsonBuilder gsonBuilder = new GsonBuilder();
- gsonBuilder.registerTypeAdapter(SourcePosition.class,
- new SourcePositionJsonTypeAdapter());
+ MessageJsonSerializer.registerTypeAdapters(gsonBuilder);
Gson gson = gsonBuilder.create();
try {
- GradleMessage msg = gson.fromJson(json, GradleMessage.class);
+ Message msg = gson.fromJson(json, Message.class);
messages.add(msg);
return true;
} catch (JsonParseException e) {
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/LegacyNdkOutputParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/LegacyNdkOutputParser.java
new file mode 100644
index 0000000..36f802a
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/LegacyNdkOutputParser.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.common.blame.parser;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
+import com.android.ide.common.blame.parser.util.OutputLineReader;
+import com.android.utils.ILogger;
+import com.android.utils.SdkUtils;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+
+import java.io.File;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Parses output from the legacy NDK support.
+ */
+public class LegacyNdkOutputParser implements PatternAwareOutputParser {
+
+ private static final String FROM = "from";
+ private static final String UNKNOWN_MSG_PREFIX1 = "In file included " + FROM;
+ private static final String UNKNOWN_MSG_PREFIX2 = " " + FROM;
+
+ private static final char COLON = ':';
+
+ @Override
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader,
+ @NonNull List<Message> messages, @NonNull ILogger logger)
+ throws ParsingFailedException {
+ // Parses unknown message
+ if (line.startsWith(UNKNOWN_MSG_PREFIX1) || line.startsWith(UNKNOWN_MSG_PREFIX2)) {
+ int fromIndex = line.indexOf(FROM);
+ String unknownMsgCause = line.substring(0, fromIndex).trim();
+ unknownMsgCause = "(Unknown) " + unknownMsgCause;
+ String coordinates = line.substring(fromIndex + FROM.length()).trim();
+ if (!coordinates.isEmpty()) {
+ int colonIndex1 = line.indexOf(COLON);
+ if (colonIndex1 == 1) { // drive letter (Windows)
+ coordinates = coordinates.substring(colonIndex1 + 1);
+ }
+ if (coordinates.endsWith(",") || coordinates.endsWith(":")) {
+ coordinates = coordinates.substring(0, coordinates.length() - 1);
+ }
+
+ List<String> segments = Splitter.on(COLON).splitToList(coordinates);
+ if (segments.size() == 3) {
+ String pathname = segments.get(0);
+ File file = new File(pathname);
+ int lineNumber = 0;
+ try {
+ lineNumber = Integer.parseInt(segments.get(1));
+ }
+ catch (NumberFormatException ignore) {
+ }
+ int column = 0;
+ try {
+ column = Integer.parseInt(segments.get(2));
+ }
+ catch (NumberFormatException ignore) {
+ }
+ SourceFilePosition position = new SourceFilePosition(file,
+ new SourcePosition(lineNumber - 1, column - 1, -1));
+ Message message = new Message(Message.Kind.INFO, unknownMsgCause.trim(), position);
+ if (!messages.contains(message)) {
+ // There may be a few duplicate "unknown" messages
+ addMessage(message, messages);
+ }
+ }
+ }
+ return true;
+ }
+
+ // Parses unresolved include.
+ int colonIndex1 = line.indexOf(COLON);
+ if (colonIndex1 == 1) { // drive letter (Windows)
+ colonIndex1 = line.indexOf(COLON, colonIndex1 + 1);
+ }
+ if (colonIndex1 >= 0) { // looks like found something like a file path.
+ String part1 = line.substring(0, colonIndex1).trim();
+
+ int colonIndex2 = line.indexOf(COLON, colonIndex1 + 1);
+ if (colonIndex2 >= 0) {
+ File file = new File(part1);
+ if (!file.isFile()) {
+ // the part one is not a file path.
+ return false;
+ }
+ try {
+ int lineNumber = Integer.parseInt(
+ line.substring(colonIndex1 + 1, colonIndex2).trim()); // 1-based.
+
+ int colonIndex3 = line.indexOf(COLON, colonIndex2 + 1);
+ if (colonIndex1 >= 0) {
+ int column = Integer.parseInt(
+ line.substring(colonIndex2 + 1, colonIndex3).trim());
+
+ int colonIndex4 = line.indexOf(COLON, colonIndex3 + 1);
+ if (colonIndex4 >= 0) {
+ Message.Kind kind = Message.Kind.INFO;
+
+ String severity =
+ line.substring(colonIndex3 + 1,
+ colonIndex4).toLowerCase(Locale.getDefault()).trim();
+ if (severity.endsWith("error")) {
+ kind = Message.Kind.ERROR;
+ } else if (severity.endsWith("warning")) {
+ kind = Message.Kind.WARNING;
+ }
+ String text = line.substring(colonIndex4 + 1).trim();
+ List<String> messageList = Lists.newArrayList();
+ messageList.add(text);
+ String prevLine = null;
+ do {
+ String nextLine = reader.readLine();
+ if (nextLine == null) {
+ return false;
+ }
+ if (nextLine.trim().equals("^")) {
+ String messageEnd = reader.readLine();
+
+ while (isMessageEnd(messageEnd)) {
+ messageList.add(messageEnd.trim());
+ messageEnd = reader.readLine();
+ }
+
+ if (messageEnd != null) {
+ reader.pushBack(messageEnd);
+ }
+ break;
+ }
+ if (prevLine != null) {
+ messageList.add(prevLine);
+ }
+ prevLine = nextLine;
+ } while (true);
+
+ if (column >= 0) {
+ messageList = convertMessages(messageList);
+ StringBuilder buf = new StringBuilder();
+ for (String m : messageList) {
+ if (buf.length() > 0) {
+ buf.append(SdkUtils.getLineSeparator());
+ }
+ buf.append(m);
+ }
+ Message msg = new Message(kind, buf.toString(),
+ new SourceFilePosition(file,
+ new SourcePosition(lineNumber - 1, column - 1, -1)));
+ if (!messages.contains(msg)) {
+ addMessage(msg, messages);
+ }
+ return true;
+ }
+ }
+
+ }
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ }
+ return false;
+ }
+
+ private static void addMessage(@NonNull Message message, @NonNull List<Message> messages) {
+ boolean duplicatesPrevious = false;
+ int messageCount = messages.size();
+ if (messageCount > 0) {
+ Message lastMessage = messages.get(messageCount - 1);
+ duplicatesPrevious = lastMessage.equals(message);
+ }
+ if (!duplicatesPrevious) {
+ messages.add(message);
+ }
+ }
+
+ private static boolean isMessageEnd(@Nullable String line) {
+ return line != null && !line.isEmpty() && Character.isWhitespace(line.charAt(0));
+ }
+
+ @NonNull
+ private static List<String> convertMessages(@NonNull List<String> messages) {
+ if (messages.size() <= 1) {
+ return messages;
+ }
+ final String line0 = messages.get(0);
+ final String line1 = messages.get(1);
+ final int colonIndex = line1.indexOf(':');
+ if (colonIndex > 0) {
+ String part1 = line1.substring(0, colonIndex).trim();
+ // jikes
+ if ("symbol".equals(part1)) {
+ String symbol = line1.substring(colonIndex + 1).trim();
+ messages.remove(1);
+ if (messages.size() >= 2) {
+ messages.remove(1);
+ }
+ messages.set(0, line0 + " " + symbol);
+ }
+ }
+ return messages;
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/PatternAwareOutputParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/PatternAwareOutputParser.java
index b6f0a9f..70b6c57 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/PatternAwareOutputParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/PatternAwareOutputParser.java
@@ -16,7 +16,7 @@
package com.android.ide.common.blame.parser;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.utils.ILogger;
@@ -32,13 +32,13 @@
*
* @param line the line to parse.
* @param reader passed in case this parser needs to parse more lines in order to create a
- * {@code GradleMessage}.
+ * {@code Message}.
* @param messages stores the messages created during parsing, if any.
* @return {@code true} if this parser was able to parser the given line, {@code false}
* otherwise.
* @throws ParsingFailedException if something goes wrong (e.g. malformed output.)
*/
boolean parse(@NonNull String line, @NonNull OutputLineReader reader,
- @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+ @NonNull List<Message> messages, @NonNull ILogger logger)
throws ParsingFailedException;
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/ToolOutputParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/ToolOutputParser.java
index 69a2c5e..4cc42d2 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/ToolOutputParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/ToolOutputParser.java
@@ -17,10 +17,10 @@
package com.android.ide.common.blame.parser;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.SourceFilePosition;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.utils.ILogger;
-import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -50,14 +50,14 @@
mLogger = logger;
}
- public List<GradleMessage> parseToolOutput(@NonNull String output) {
+ public List<Message> parseToolOutput(@NonNull String output) {
OutputLineReader outputReader = new OutputLineReader(output);
if (outputReader.getLineCount() == 0) {
return Collections.emptyList();
}
- List<GradleMessage> messages = Lists.newArrayList();
+ List<Message> messages = Lists.newArrayList();
String line;
while ((line = outputReader.readLine()) != null) {
if (line.isEmpty()) {
@@ -78,7 +78,7 @@
if (handled) {
int messageCount = messages.size();
if (messageCount > 0) {
- GradleMessage last = messages.get(messageCount - 1);
+ Message last = messages.get(messageCount - 1);
if (last.getText().contains("Build cancelled")) {
// Build was cancelled, just quit. Extra messages are just confusing noise.
break;
@@ -86,11 +86,11 @@
}
}
else {
- // If none of the standard parsers recognize the input, include it as info such
+ // If none of the standard parsers recogni ze the input, include it as info such
// that users don't miss potentially vital output such as gradle plugin exceptions.
// If there is predictable useless input we don't want to appear here, add a custom
// parser to digest it.
- messages.add(new GradleMessage(GradleMessage.Kind.SIMPLE, line));
+ messages.add(new Message(Message.Kind.SIMPLE, line, SourceFilePosition.UNKNOWN));
}
}
return messages;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AaptOutputParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AaptOutputParser.java
index 0e5bf94..dae8779 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AaptOutputParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AaptOutputParser.java
@@ -16,7 +16,7 @@
package com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.ide.common.blame.parser.PatternAwareOutputParser;
@@ -47,7 +47,7 @@
};
@Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger) {
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger) {
for (AbstractAaptOutputParser parser : PARSERS) {
try {
if (parser.parse(line, reader, messages, logger)) {
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AbstractAaptOutputParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AbstractAaptOutputParser.java
index 60d801b..e30eae1 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AbstractAaptOutputParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/AbstractAaptOutputParser.java
@@ -24,13 +24,13 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
-import com.android.ide.common.blame.FilePosition;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
import com.android.ide.common.blame.SourcePosition;
-import com.android.ide.common.blame.output.GradleMessage;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.ide.common.blame.parser.PatternAwareOutputParser;
-import com.android.ide.common.res2.MergedResourceWriter;
import com.android.resources.ResourceFolderType;
import com.android.utils.ILogger;
import com.android.utils.SdkUtils;
@@ -190,8 +190,8 @@
int endResultOffset = resultOffset + (second != null ? second.length() : first.length());
int endLineNumber = document.lineNumber(endResultOffset);
int endLineOffset = document.lineOffset((endLineNumber));
- return new SourcePosition(startLineNumber, resultOffset - startLineOffset + 1, resultOffset,
- endLineNumber, endResultOffset - endLineOffset + 1, endResultOffset);
+ return new SourcePosition(startLineNumber, resultOffset - startLineOffset, resultOffset,
+ endLineNumber, endResultOffset - endLineOffset, endResultOffset);
}
@Nullable
@@ -231,8 +231,8 @@
break;
}
}
- return new SourcePosition(locationLine, resultOffset - lineOffset + 1, resultOffset,
- locationLine, endResultOffset - lineOffset + 1, endResultOffset);
+ return new SourcePosition(locationLine, resultOffset - lineOffset, resultOffset,
+ locationLine, endResultOffset - lineOffset, endResultOffset);
}
@Nullable
@@ -279,11 +279,11 @@
* "string/group2_string" it will locate an element {@code <string name="group2_string">} or
* {@code <item type="string" name="group2_string"}
*/
- public static int findResourceLine(@NonNull File file, @NonNull String key, @NonNull ILogger logger) {
+ public static SourcePosition findResourceLine(@NonNull File file, @NonNull String key, @NonNull ILogger logger) {
int slash = key.indexOf('/');
if (slash == -1) {
assert false : slash; // invalid key format
- return -1;
+ return SourcePosition.UNKNOWN;
}
final String type = key.substring(0, slash);
@@ -296,26 +296,26 @@
* Locates a resource value declaration in a given file and returns the corresponding line
* number, or -1 if not found.
*/
- public static int findValueDeclaration(@NonNull File file, @NonNull final String type,
+ public static SourcePosition findValueDeclaration(@NonNull File file, @NonNull final String type,
@NonNull final String name, @NonNull ILogger logger) {
if (!file.exists()) {
- return -1;
+ return SourcePosition.UNKNOWN;
}
final ReadOnlyDocument document = getDocument(file, logger);
if (document == null) {
- return -1;
+ return SourcePosition.UNKNOWN;
}
// First just do something simple: scan for the string. If it only occurs once, it's easy!
int index = document.findText(name, 0);
if (index == -1) {
- return -1;
+ return SourcePosition.UNKNOWN;
}
// See if there are any more occurrences; if not, we're done
if (document.findText(name, index + name.length()) == -1) {
- return document.lineNumber(index);
+ return document.sourcePosition(index);
}
// Try looking for name="$name"
@@ -323,21 +323,21 @@
if (nameIndex != -1) {
// TODO: Disambiguate by type, so if values.xml contains both R.string.foo and R.dimen.foo we
// pick the right one!
- return document.lineNumber(nameIndex);
+ return document.sourcePosition(nameIndex);
}
- int lineNumber = findValueDeclarationViaParse(type, name, document);
- if (lineNumber != -1) {
+ SourcePosition lineNumber = findValueDeclarationViaParse(type, name, document);
+ if (!SourcePosition.UNKNOWN.equals(lineNumber)) {
return lineNumber;
}
// Just fall back to the first occurrence of the string
//noinspection ConstantConditions
assert index != -1;
- return document.lineNumber(index);
+ return document.sourcePosition(index);
}
- private static int findValueDeclarationViaParse(final String type, final String name,
+ private static SourcePosition findValueDeclarationViaParse(final String type, final String name,
ReadOnlyDocument document) {
// Finally do a full SAX parse to identify the position
final int[] certain = new int[]{-1, 0}; // line,column for exact match
@@ -361,8 +361,8 @@
myDepth++;
if (myDepth == 2) {
if (name.equals(attributes.getValue(ATTR_NAME))) {
- int lineNumber = myLocator.getLineNumber();
- int column = myLocator.getColumnNumber();
+ int lineNumber = myLocator.getLineNumber() - 1;
+ int column = myLocator.getColumnNumber() - 1;
if (qName.equals(type) || TAG_ITEM.equals(qName) && type
.equals(attributes.getValue(ATTR_TYPE))) {
line.set(lineNumber);
@@ -393,31 +393,33 @@
// Ignore parser errors; we might have found the error position earlier than the parse error position
}
- int lineNumber;
- int column;
+ int endLineNumber;
+ int endColumn;
if (certain[0] != -1) {
- lineNumber = certain[0];
- column = certain[1];
+ endLineNumber = certain[0];
+ endColumn = certain[1];
} else {
- lineNumber = possible[0];
- column = possible[1];
+ endLineNumber = possible[0];
+ endColumn = possible[1];
}
- if (lineNumber != -1) {
+ if (endLineNumber != -1) {
// SAX' locator will point to the END of the opening declaration, meaning that if it spans multiple lines, we are pointing
// to the last line:
// <item
// type="dimen"
// name="attribute"
// > <--- this is where the locator points, so we need to search backwards
- int offset = document.lineOffset(lineNumber) + column;
- offset = document.findTextBackwards(name, offset);
+ int endOffset = document.lineOffset(endLineNumber) + endColumn;
+ int offset = document.findTextBackwards(name, endOffset);
if (offset != -1) {
- lineNumber = document.lineNumber(offset);
+ SourcePosition start = document.sourcePosition(offset);
+ return new SourcePosition(start.getStartLine(), start.getStartColumn(), start.getStartOffset(),
+ endLineNumber, endColumn, endOffset);
}
- return lineNumber;
+ return new SourcePosition(endLineNumber, endColumn, endOffset);
}
- return -1;
+ return SourcePosition.UNKNOWN;
}
@Nullable
@@ -433,7 +435,7 @@
}
@NonNull
- GradleMessage createMessage(@NonNull GradleMessage.Kind kind,
+ Message createMessage(@NonNull Message.Kind kind,
@NonNull String text,
@Nullable String sourcePath,
@Nullable String lineNumberAsText,
@@ -449,12 +451,12 @@
SourcePosition errorPosition = parseLineNumber(lineNumberAsText);
if (sourcePath != null) {
- FilePosition source = findSourcePosition(file, errorPosition.getStartLine(), text, logger);
+ SourceFilePosition source = findSourcePosition(file, errorPosition.getStartLine(), text, logger);
if (source != null) {
- file = source.getSourceFile();
+ file = source.getFile().getSourceFile();
sourcePath = file.getPath();
- if (source.getStartLine() != -1) {
- errorPosition = source;
+ if (source.getPosition().getStartLine() != -1) {
+ errorPosition = source.getPosition();
}
}
}
@@ -465,7 +467,7 @@
if (file != null && errorPosition.getStartLine() != -1) {
errorPosition = findMessagePositionInFile(file, text, errorPosition.getStartLine(), logger);
}
- return new GradleMessage(kind, text, sourcePath, errorPosition, original);
+ return new Message(kind, text, original, new SourceFilePosition(file, errorPosition));
}
private SourcePosition parseLineNumber(String lineNumberAsText) throws ParsingFailedException {
@@ -478,11 +480,19 @@
}
}
- return new SourcePosition(lineNumber, -1, -1);
+ return new SourcePosition(lineNumber - 1, -1, -1);
}
+ /**
+ *
+ * @param file
+ * @param locationLine
+ * @param message
+ * @param logger
+ * @return null if could not be found, new SourceFilePosition(new SourceFile file,
+ */
@Nullable
- protected static FilePosition findSourcePosition(@NonNull File file, int locationLine,
+ protected static SourceFilePosition findSourcePosition(@NonNull File file, int locationLine,
String message, ILogger logger) {
if (!file.getPath().endsWith(DOT_XML)) {
return null;
@@ -552,9 +562,9 @@
// Look up the line number
SourcePosition position = findMessagePositionInFile(sourceFile, message,
1, logger); // Search from the beginning
- return new FilePosition(sourceFile, position);
+ return new SourceFilePosition(new SourceFile(sourceFile), position);
}
- return new FilePosition(sourceFile, SourcePosition.UNKNOWN);
+ return new SourceFilePosition(new SourceFile(sourceFile), SourcePosition.UNKNOWN);
}
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/BadXmlBlockParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/BadXmlBlockParser.java
index 46e6a98..52b46b7 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/BadXmlBlockParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/BadXmlBlockParser.java
@@ -16,7 +16,7 @@
package com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.utils.ILogger;
@@ -31,7 +31,7 @@
.compile("W/ResourceType\\(.*\\): Bad XML block: no root element node found");
@Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
throws ParsingFailedException {
Matcher m = MSG_PATTERN.matcher(line);
if (!m.matches()) {
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error1Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error1Parser.java
index 774c820..d06e902 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error1Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error1Parser.java
@@ -16,7 +16,7 @@
package com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.utils.ILogger;
@@ -41,7 +41,7 @@
);
@Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
throws ParsingFailedException {
Matcher m = MSG_PATTERNS.get(0).matcher(line);
if (!m.matches()) {
@@ -56,7 +56,7 @@
}
String sourcePath = m.group(1);
- GradleMessage msg = createMessage(GradleMessage.Kind.ERROR, msgText, sourcePath,
+ Message msg = createMessage(Message.Kind.ERROR, msgText, sourcePath,
lineNumber, "", logger);
messages.add(msg);
return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error2Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error2Parser.java
index 5ff8864..de0db3f 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error2Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error2Parser.java
@@ -16,7 +16,7 @@
package com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.utils.ILogger;
@@ -40,7 +40,7 @@
Pattern.compile("Defined\\s+at\\s+file\\s+(.+)\\s+line\\s+(\\d+)"));
@Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
throws ParsingFailedException {
Matcher m = MSG_PATTERNS.get(0).matcher(line);
if (!m.matches()) {
@@ -55,7 +55,7 @@
String sourcePath = m.group(1);
String lineNumber = m.group(2);
- GradleMessage msg = createMessage(GradleMessage.Kind.ERROR, msgText, sourcePath,
+ Message msg = createMessage(Message.Kind.ERROR, msgText, sourcePath,
lineNumber, "", logger);
messages.add(msg);
return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error3Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error3Parser.java
index c3d3e0e..f496cfd 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error3Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error3Parser.java
@@ -16,7 +16,7 @@
package com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.utils.ILogger;
@@ -36,7 +36,7 @@
private static final Pattern MSG_PATTERN = Pattern.compile("^(.+)\\sline\\s(\\d+):\\s(.+)$");
@Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
throws ParsingFailedException {
Matcher m = MSG_PATTERN.matcher(line);
if (!m.matches()) {
@@ -46,7 +46,7 @@
String lineNumber = m.group(2);
String msgText = m.group(3);
- GradleMessage msg = createMessage(GradleMessage.Kind.ERROR, msgText, sourcePath,
+ Message msg = createMessage(Message.Kind.ERROR, msgText, sourcePath,
lineNumber, "", logger);
messages.add(msg);
return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error4Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error4Parser.java
index ddff220..17ee45b 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error4Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error4Parser.java
@@ -16,7 +16,7 @@
package com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.utils.ILogger;
@@ -40,7 +40,7 @@
Pattern.compile("^(.+)\\s+at\\s+line\\s+(\\d+)$"));
@Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
throws ParsingFailedException {
Matcher m = MSG_PATTERNS.get(0).matcher(line);
if (!m.matches()) {
@@ -55,7 +55,7 @@
String msgText = m.group(1);
String lineNumber = m.group(2);
- GradleMessage msg = createMessage(GradleMessage.Kind.ERROR, msgText, sourcePath,
+ Message msg = createMessage(Message.Kind.ERROR, msgText, sourcePath,
lineNumber, "", logger);
messages.add(msg);
return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error5Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error5Parser.java
index b13fd2a..449edd8 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error5Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error5Parser.java
@@ -17,7 +17,7 @@
import com.android.SdkConstants;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.utils.ILogger;
@@ -44,7 +44,7 @@
);
@Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
throws ParsingFailedException {
for (Pattern pattern : MSG_PATTERNS) {
Matcher m = pattern.matcher(line);
@@ -52,15 +52,15 @@
String sourcePath = m.group(1);
String lineNumber = m.group(2);
String msgText = m.group(3);
- GradleMessage.Kind kind = GradleMessage.Kind.ERROR;
+ Message.Kind kind = Message.Kind.ERROR;
if (msgText.startsWith("warning: ")) {
// NDK warning also matches this regexp
- kind = GradleMessage.Kind.WARNING;
+ kind = Message.Kind.WARNING;
}
if (sourcePath.endsWith(SdkConstants.DOT_JAVA)) {
return false;
}
- GradleMessage msg = createMessage(kind, msgText, sourcePath, lineNumber, "", logger);
+ Message msg = createMessage(kind, msgText, sourcePath, lineNumber, "", logger);
messages.add(msg);
return true;
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error6Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error6Parser.java
index fe8f1de..fb464af 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error6Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error6Parser.java
@@ -16,7 +16,7 @@
package com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.utils.ILogger;
@@ -42,7 +42,7 @@
.compile("^ERROR:\\s+9-patch\\s+image\\s+(.+)\\s+malformed\\.$");
@Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
throws ParsingFailedException {
Matcher m = MSG_PATTERN.matcher(line);
if (!m.matches()) {
@@ -58,7 +58,7 @@
reader.skipNextLine();
}
}
- GradleMessage msg = createMessage(GradleMessage.Kind.ERROR, msgText, sourcePath,
+ Message msg = createMessage(Message.Kind.ERROR, msgText, sourcePath,
null, "", logger);
messages.add(msg);
return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error7Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error7Parser.java
index 31644c4..27fafaa 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error7Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error7Parser.java
@@ -16,7 +16,7 @@
package com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.utils.ILogger;
@@ -31,7 +31,7 @@
.compile("^(invalid resource directory name): (.*)$");
@Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
throws ParsingFailedException {
Matcher m = MSG_PATTERN.matcher(line);
if (!m.matches()) {
@@ -39,7 +39,7 @@
}
String sourcePath = m.group(2);
String text = m.group(1);
- GradleMessage msg = createMessage(GradleMessage.Kind.ERROR, text, sourcePath, null, "", logger);
+ Message msg = createMessage(Message.Kind.ERROR, text, sourcePath, null, "", logger);
messages.add(msg);
return true;
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error8Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error8Parser.java
index d09b485..a611704 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error8Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Error8Parser.java
@@ -16,7 +16,7 @@
package com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.utils.ILogger;
@@ -38,7 +38,7 @@
private static final Pattern MSG_PATTERN = Pattern.compile("^Invalid configuration: (.+)$");
@Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
throws ParsingFailedException {
Matcher m = MSG_PATTERN.matcher(line);
if (!m.matches()) {
@@ -49,7 +49,7 @@
// skip the next line
reader.skipNextLine();
- GradleMessage msg = createMessage(GradleMessage.Kind.ERROR, msgText, null, null, "", logger);
+ Message msg = createMessage(Message.Kind.ERROR, msgText, null, null, "", logger);
messages.add(msg);
return true;
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/ReadOnlyDocument.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/ReadOnlyDocument.java
index 7fb92b2..3c2366b 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/ReadOnlyDocument.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/ReadOnlyDocument.java
@@ -16,6 +16,7 @@
package com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
+import com.android.ide.common.blame.SourcePosition;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
@@ -23,7 +24,6 @@
import java.io.File;
import java.io.IOException;
-import java.util.Arrays;
import java.util.List;
/**
@@ -58,7 +58,6 @@
myFile = file;
myLastModified = file.lastModified();
myOffsets = Lists.newArrayListWithExpectedSize(mFileContents.length() / 30);
- myOffsets.add(0);
for (int i = 0; i < mFileContents.length(); i++) {
char c = mFileContents.charAt(i);
if (c == '\n') {
@@ -76,7 +75,8 @@
}
/**
- * Returns the offset of the given line number, relative to the beginning of the document.
+ * Returns the [0-based] offset of the given [0-based] line number,
+ * relative to the beginning of the document.
*
* @param lineNumber the given line number.
* @return the offset of the given line. -1 is returned if the document is empty, or if the
@@ -91,7 +91,7 @@
}
/**
- * Returns the line number of the given offset.
+ * Returns the [0-based] line number of the given [0-based] offset.
*
* @param offset the given offset.
* @return the line number of the given offset. -1 is returned if the document is empty or if
@@ -99,14 +99,23 @@
*/
int lineNumber(int offset) {
for (int i = 0; i < myOffsets.size(); i++) {
- int savedOffset = myOffsets.get(i);
- if (offset <= savedOffset) {
+ if (offset < myOffsets.get(i)) {
return i;
}
}
return -1;
}
+ SourcePosition sourcePosition(int offset) {
+ for (int i = 0; i < myOffsets.size(); i++) {
+ if (offset < myOffsets.get(i)) {
+ int lineStartOffset = i==0 ? 0 : myOffsets.get(i-1);
+ return new SourcePosition(i, offset - lineStartOffset, offset);
+ }
+ }
+ return SourcePosition.UNKNOWN;
+ }
+
/**
* Finds the given text in the document, starting from the given offset.
*
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingHiddenFileParser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingHiddenFileParser.java
index 9400b50..93e6511 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingHiddenFileParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingHiddenFileParser.java
@@ -16,7 +16,7 @@
package com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.utils.ILogger;
@@ -37,7 +37,7 @@
.compile("^\\s+\\(skipping hidden file\\s'(.*)'\\)$");
@Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
throws ParsingFailedException {
Matcher m = MSG_PATTERN.matcher(line);
return m.matches();
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning1Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning1Parser.java
index 067b975..0441583 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning1Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning1Parser.java
@@ -16,7 +16,7 @@
package com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.utils.ILogger;
@@ -35,7 +35,7 @@
private static final Pattern MSG_PATTERN = Pattern.compile(" \\(skipping (.+) .+ '(.*)'\\)");
@Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
throws ParsingFailedException {
Matcher m = MSG_PATTERN.matcher(line);
if (!m.matches()) {
@@ -50,7 +50,7 @@
|| type.equals("index")) { // thumbs.db, etc
return true;
}
- GradleMessage msg = createMessage(GradleMessage.Kind.WARNING, line, sourcePath,
+ Message msg = createMessage(Message.Kind.WARNING, line, sourcePath,
null, "", logger);
messages.add(msg);
return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning2Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning2Parser.java
index 7919475..12b434d 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning2Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/SkippingWarning2Parser.java
@@ -16,7 +16,7 @@
package com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.utils.ILogger;
@@ -36,7 +36,7 @@
.compile(" \\(skipping .+ '(.+)' due to ANDROID_AAPT_IGNORE pattern '.+'\\)");
@Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
throws ParsingFailedException {
Matcher m = MSG_PATTERN.matcher(line);
if (!m.matches()) {
@@ -46,7 +46,7 @@
if (sourcePath != null && (sourcePath.startsWith(".") || sourcePath.endsWith("~"))) {
return true;
}
- GradleMessage msg = createMessage(GradleMessage.Kind.WARNING, line, sourcePath,
+ Message msg = createMessage(Message.Kind.WARNING, line, sourcePath,
null, "", logger);
messages.add(msg);
return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Warning1Parser.java b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Warning1Parser.java
index 4adf8d1..4041208 100644
--- a/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Warning1Parser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/blame/parser/aapt/Warning1Parser.java
@@ -16,7 +16,7 @@
package com.android.ide.common.blame.parser.aapt;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.blame.parser.util.OutputLineReader;
import com.android.ide.common.blame.parser.ParsingFailedException;
import com.android.utils.ILogger;
@@ -33,7 +33,7 @@
private static final Pattern MSG_PATTERN = Pattern.compile("^(.+?):(\\d+):\\s+WARNING:(.+)$");
@Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader, @NonNull List<Message> messages, @NonNull ILogger logger)
throws ParsingFailedException {
Matcher m = MSG_PATTERN.matcher(line);
if (!m.matches()) {
@@ -44,7 +44,7 @@
String lineNumber = m.group(2);
String msgText = m.group(3);
- GradleMessage msg = createMessage(GradleMessage.Kind.WARNING, msgText, sourcePath,
+ Message msg = createMessage(Message.Kind.WARNING, msgText, sourcePath,
lineNumber, "", logger);
messages.add(msg);
return true;
diff --git a/sdk-common/src/main/java/com/android/ide/common/build/SplitOutputMatcher.java b/sdk-common/src/main/java/com/android/ide/common/build/SplitOutputMatcher.java
index 3850917..9fa3276 100644
--- a/sdk-common/src/main/java/com/android/ide/common/build/SplitOutputMatcher.java
+++ b/sdk-common/src/main/java/com/android/ide/common/build/SplitOutputMatcher.java
@@ -53,7 +53,7 @@
* @param outputs the tested variant outpts.
* @param variantAbiFilters a list of abi filters applied to the variant. This is used in place
* of the outputs, if there is a single output with no abi filters.
- * If the list is null, then the variant does not restrict ABI
+ * If the list is null or empty, then the variant does not restrict ABI
* packaging.
* @return the list of APK files to install.
* @throws ProcessException
@@ -226,7 +226,9 @@
Collection<String> variantAbiFilters,
Collection<String> deviceAbis) {
// so far, we are not dealing with the pure split files...
- if (getFilter(mainOutputFile, OutputFile.ABI) == null && variantAbiFilters != null) {
+ if (getFilter(mainOutputFile, OutputFile.ABI) == null
+ && variantAbiFilters != null
+ && !variantAbiFilters.isEmpty()) {
// if we have a match that has no abi filter, and we have variant-level filters, then
// we need to make sure that the variant filters are compatible with the device abis.
for (String abi : deviceAbis) {
diff --git a/sdk-common/src/main/java/com/android/ide/common/caching/CreatingCache.java b/sdk-common/src/main/java/com/android/ide/common/caching/CreatingCache.java
index b0f25de..37f9363 100644
--- a/sdk-common/src/main/java/com/android/ide/common/caching/CreatingCache.java
+++ b/sdk-common/src/main/java/com/android/ide/common/caching/CreatingCache.java
@@ -22,7 +22,6 @@
import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
import com.android.annotations.concurrency.GuardedBy;
-import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import java.util.Map;
@@ -166,7 +165,8 @@
* State of values.
*/
@VisibleForTesting
- enum State { EXISTING_VALUE, NEW_VALUE, PROCESSED_VALUE; }
+ enum State { EXISTING_VALUE, NEW_VALUE, PROCESSED_VALUE
+ }
/**
* A Value State. This contains the Type as {@link State}, and a optional value {@link V}
diff --git a/sdk-common/src/main/java/com/android/ide/common/internal/ExecutorSingleton.java b/sdk-common/src/main/java/com/android/ide/common/internal/ExecutorSingleton.java
index 05fd1db..bd56920 100644
--- a/sdk-common/src/main/java/com/android/ide/common/internal/ExecutorSingleton.java
+++ b/sdk-common/src/main/java/com/android/ide/common/internal/ExecutorSingleton.java
@@ -26,10 +26,11 @@
private static ExecutorService sExecutorService;
+ private static int sThreadPoolSize = Runtime.getRuntime().availableProcessors();
+
public static synchronized ExecutorService getExecutor() {
if (sExecutorService == null) {
- sExecutorService = Executors.newFixedThreadPool(
- Runtime.getRuntime().availableProcessors());
+ sExecutorService = Executors.newFixedThreadPool(sThreadPoolSize);
}
return sExecutorService;
@@ -41,4 +42,16 @@
sExecutorService = null;
}
}
+
+ /**
+ * Changes the thread pool size for the singleton ExecutorService.
+ *
+ * <b>Caution</b>: This will have no effect if getExecutor() has already been called until the
+ * executor is shutdown and reinitialized.
+ *
+ * @param threadPoolSize the number of threads to use.
+ */
+ public static void setThreadPoolSize(int threadPoolSize) {
+ sThreadPoolSize = threadPoolSize;
+ }
}
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 91fc182..19e4a19 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
@@ -16,6 +16,8 @@
package com.android.ide.common.packaging;
+import com.google.common.collect.ImmutableList;
+
/**
* Utility class for packaging.
*/
@@ -59,20 +61,55 @@
public static boolean checkFileForPackaging(String fileName, String extension) {
// ignore hidden files and backup files
return !(fileName.charAt(0) == '.' || fileName.charAt(fileName.length() - 1) == '~') &&
- !"aidl".equalsIgnoreCase(extension) && // Aidl files
- !"rs".equalsIgnoreCase(extension) && // RenderScript files
- !"fs".equalsIgnoreCase(extension) && // FilterScript files
- !"rsh".equalsIgnoreCase(extension) && // RenderScript header files
- !"d".equalsIgnoreCase(extension) && // Dependency files
- !"java".equalsIgnoreCase(extension) && // Java files
- !"scala".equalsIgnoreCase(extension) && // Scala files
- !"class".equalsIgnoreCase(extension) && // Java class files
- !"scc".equalsIgnoreCase(extension) && // VisualSourceSafe
- !"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
+ !isOfNonResourcesExtensions(extension) &&
+ !isNotAResourceFile(fileName);
}
+
+ private static boolean isOfNonResourcesExtensions(String extension) {
+ for (String ext : NON_RESOURCES_EXTENSIONS) {
+ if (ext.equalsIgnoreCase(extension)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isNotAResourceFile(String fileName) {
+ for (String name : NON_RESOURCES_FILENAMES) {
+ if (name.equalsIgnoreCase(fileName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the list of file extensions that represents non resources files.
+ */
+ public static final ImmutableList<String> NON_RESOURCES_EXTENSIONS =
+ ImmutableList.<String>builder()
+ .add("aidl") // Aidl files
+ .add("rs") // RenderScript files
+ .add("fs") // FilterScript files
+ .add("rsh") // RenderScript header files
+ .add("d") // Dependency files
+ .add("java") // Java files
+ .add("scala") // Scala files
+ .add("class") // Java class files
+ .add("so") // native .so libraries
+ .add("scc") // VisualSourceSafe
+ .add("swp") // vi swap file
+ .build();
+
+ /**
+ * Return file names that are not resource files.
+ */
+ public static final ImmutableList<String> NON_RESOURCES_FILENAMES =
+ ImmutableList.<String>builder()
+ .add("thumbs.db") // image index file
+ .add("picasa.ini") // image index file
+ .add("about.html") // Javadoc
+ .add("package.html") // Javadoc
+ .add("overview.html") // Javadoc
+ .build();
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/process/ProcessEnvBuilder.java b/sdk-common/src/main/java/com/android/ide/common/process/ProcessEnvBuilder.java
index be30551..a6b21e5 100644
--- a/sdk-common/src/main/java/com/android/ide/common/process/ProcessEnvBuilder.java
+++ b/sdk-common/src/main/java/com/android/ide/common/process/ProcessEnvBuilder.java
@@ -34,7 +34,7 @@
* @return this
*/
@NonNull
- public T addEnvironments(@NonNull Map<String, ? extends Object> map) {
+ public T addEnvironments(@NonNull Map<String, ?> map) {
mEnvironment.putAll(map);
return thisAsT();
}
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 55fba98..b6a32dd 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
@@ -20,6 +20,7 @@
import com.android.annotations.Nullable;
import com.android.ide.common.rendering.api.HardwareConfig;
import com.android.resources.ScreenOrientation;
+import com.android.resources.ScreenRound;
import com.android.sdklib.devices.ButtonType;
import com.android.sdklib.devices.Device;
import com.android.sdklib.devices.Screen;
@@ -164,6 +165,7 @@
(float) screen.getYdpi(),
screen.getSize(),
mScreenOrientation,
+ mDevice.getDefaultHardware().getScreen().getScreenRound(),
mDevice.getDefaultHardware().getButtonType() == ButtonType.SOFT);
}
@@ -199,7 +201,7 @@
* Returns a user-displayable description of the given generic device
* @param device the device to check
* @return the label
- * @see #isGeneric(com.android.sdklib.devices.Device)
+ * @see #isGeneric(Device)
*/
@NonNull
public static String getGenericLabel(@NonNull Device device) {
@@ -268,13 +270,6 @@
}
/**
- * Whether the given device has a round screen
- */
- public static boolean isRound(@Nullable Device device) {
- return device != null && ID_PREFIX_WEAR_ROUND.equals(device.getId());
- }
-
- /**
* Whether the given device is a TV device
*/
public static boolean isTv(@Nullable Device device) {
diff --git a/sdk-common/src/main/java/com/android/ide/common/repository/GradleCoordinate.java b/sdk-common/src/main/java/com/android/ide/common/repository/GradleCoordinate.java
index b534f48..627100a 100644
--- a/sdk-common/src/main/java/com/android/ide/common/repository/GradleCoordinate.java
+++ b/sdk-common/src/main/java/com/android/ide/common/repository/GradleCoordinate.java
@@ -18,8 +18,6 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.google.common.base.Joiner;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/sdk-common/src/main/java/com/android/ide/common/repository/SdkMavenRepository.java b/sdk-common/src/main/java/com/android/ide/common/repository/SdkMavenRepository.java
index b05aa17..22c8f1e 100644
--- a/sdk-common/src/main/java/com/android/ide/common/repository/SdkMavenRepository.java
+++ b/sdk-common/src/main/java/com/android/ide/common/repository/SdkMavenRepository.java
@@ -110,7 +110,7 @@
}
/**
- * Find the best matching {@link com.android.ide.common.repository.GradleCoordinate}
+ * Find the best matching {@link GradleCoordinate}
*
* @param sdkHome the SDK installation
* @param groupId the artifact group id
@@ -136,7 +136,7 @@
}
/**
- * Find the best matching {@link com.android.ide.common.repository.GradleCoordinate}
+ * Find the best matching {@link GradleCoordinate}
*
* @param groupId the artifact group id
* @param artifactId the artifact id
@@ -188,6 +188,20 @@
return null;
}
+ @Nullable
+ public static SdkMavenRepository getByGroupId(@NonNull String groupId) {
+ if ("com.android.support".equals(groupId) || "com.android.support.test".equals(groupId)) {
+ return ANDROID;
+ }
+ if (groupId.startsWith("com.google.android.")) {
+ // com.google.android.gms, com.google.android.support.wearable,
+ // com.google.android.wearable, ... possibly more in the future
+ return GOOGLE;
+ }
+
+ return null;
+ }
+
/** The directory name of the repository inside the extras folder */
@NonNull
public String getDirName() {
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/AssetFile.java b/sdk-common/src/main/java/com/android/ide/common/res2/AssetFile.java
index a7e35a4..7104ea5 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/AssetFile.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/AssetFile.java
@@ -34,7 +34,7 @@
* @param item the resource item
*/
AssetFile(@NonNull File file, @NonNull AssetItem item) {
- super(file, FileType.SINGLE);
+ super(file, FileType.SINGLE_FILE);
init(item);
}
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/AssetSet.java b/sdk-common/src/main/java/com/android/ide/common/res2/AssetSet.java
index 44ad1cb..77bff3d 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/AssetSet.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/AssetSet.java
@@ -17,7 +17,6 @@
package com.android.ide.common.res2;
import com.android.annotations.NonNull;
-import com.android.ide.common.packaging.PackagingUtils;
import com.android.utils.ILogger;
import org.w3c.dom.Attr;
@@ -37,7 +36,7 @@
* @param configName the name of the config this set is associated with.
*/
public AssetSet(String configName) {
- super(configName);
+ super(configName, true /*validateEnabled*/);
}
@Override
@@ -54,7 +53,7 @@
}
@Override
- protected AssetFile createFileAndItems(@NonNull File file, @NonNull Node fileNode) {
+ protected AssetFile createFileAndItemsFromXml(@NonNull File file, @NonNull Node fileNode) {
Attr nameAttr = (Attr) fileNode.getAttributes().getNamedItem(ATTR_NAME);
if (nameAttr == null) {
return null;
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/DataBindingResourceItem.java b/sdk-common/src/main/java/com/android/ide/common/res2/DataBindingResourceItem.java
new file mode 100644
index 0000000..679d815
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/DataBindingResourceItem.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.common.res2;
+
+import com.android.annotations.NonNull;
+
+public abstract class DataBindingResourceItem extends DataItem<ResourceFile> {
+
+ private DataBindingResourceType mType;
+
+ /**
+ * Constructs the object with a name, type and optional value. <p/> Note that the object is not
+ * fully usable as-is. It must be added to a DataFile first.
+ *
+ * @param name the name of the item
+ */
+ public DataBindingResourceItem(@NonNull String name, @NonNull DataBindingResourceType type) {
+ super(name);
+ mType = type;
+ }
+
+ @NonNull
+ public DataBindingResourceType getType() {
+ return mType;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ DataBindingResourceItem that = (DataBindingResourceItem) o;
+ if (mType != that.mType) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + mType.hashCode();
+ return result;
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/DataBindingResourceType.java b/sdk-common/src/main/java/com/android/ide/common/res2/DataBindingResourceType.java
new file mode 100644
index 0000000..17f0b99
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/DataBindingResourceType.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.common.res2;
+
+import com.android.SdkConstants;
+
+public enum DataBindingResourceType {
+ VARIABLE(SdkConstants.ATTR_NAME, SdkConstants.ATTR_TYPE),
+ IMPORT(SdkConstants.ATTR_ALIAS, SdkConstants.ATTR_TYPE);
+
+ public final String[] attributes;
+
+ DataBindingResourceType(String... attributes) {
+ this.attributes = attributes;
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/DataFile.java b/sdk-common/src/main/java/com/android/ide/common/res2/DataFile.java
index 3f5437d..4e650fc 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/DataFile.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/DataFile.java
@@ -35,7 +35,9 @@
public abstract class DataFile<I extends DataItem> {
enum FileType {
- SINGLE, MULTI
+ SINGLE_FILE,
+ GENERATED_FILES,
+ XML_VALUES
}
private final FileType mType;
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/DataItem.java b/sdk-common/src/main/java/com/android/ide/common/res2/DataItem.java
index 228428a..ff0ce90 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/DataItem.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/DataItem.java
@@ -18,9 +18,12 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+
import org.w3c.dom.Document;
import org.w3c.dom.Node;
+import java.io.File;
+
/**
* Base item.
*
@@ -79,6 +82,10 @@
mSource = sourceFile;
}
+ public File getFile() {
+ return getSource().getFile();
+ }
+
/**
* Resets the state of the item be nothing.
* @return this
@@ -187,7 +194,12 @@
// nothing
}
- Node getAdoptedNode(Document document) {
+ /**
+ * Returns a node that describes additional properties of this {@link DataItem}. If not null, it
+ * will be persisted in the merger XML blob and can be used used to restore the exact state of
+ * this item.
+ */
+ Node getDetailsXml(Document document) {
return null;
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/DataMerger.java b/sdk-common/src/main/java/com/android/ide/common/res2/DataMerger.java
index fef86c1..c25926b 100755
--- a/sdk-common/src/main/java/com/android/ide/common/res2/DataMerger.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/DataMerger.java
@@ -57,7 +57,7 @@
static final String FN_MERGER_XML = "merger.xml";
static final String NODE_MERGER = "merger";
static final String NODE_DATA_SET = "dataSet";
- static final String NODE_MERGED_ITEMS = "mergedItems";
+
static final String NODE_CONFIGURATION = "configuration";
static final String ATTR_VERSION = "version";
@@ -332,23 +332,23 @@
}
// write merged items
- writeMergedItems(document, rootNode);
+ writeAdditionalData(document, rootNode);
- String content = XmlUtils.toXml(document, true /*preserveWhitespace*/);
+ String content = XmlUtils.toXml(document);
try {
createDir(blobRootFolder);
} catch (IOException ioe) {
- throw new MergingException(ioe).addFile(blobRootFolder);
+ throw MergingException.wrapException(ioe).withFile(blobRootFolder).build();
}
File file = new File(blobRootFolder, FN_MERGER_XML);
try {
Files.write(content, file, Charsets.UTF_8);
} catch (IOException ioe) {
- throw new MergingException(ioe).addFile(file);
+ throw MergingException.wrapException(ioe).withFile(file).build();
}
} catch (ParserConfigurationException e) {
- throw new MergingException(e);
+ throw MergingException.wrapException(e).build();
}
}
@@ -410,13 +410,11 @@
if (NODE_DATA_SET.equals(node.getLocalName())) {
S dataSet = createFromXml(node);
if (dataSet != null) {
- mDataSets.add(dataSet);
+ addDataSet(dataSet);
}
- } else if (incrementalState && NODE_MERGED_ITEMS.equals(node.getLocalName())) {
- // only load the merged item in incremental state.
- // In non incremental state, they will be recreated by the touched
- // items anyway.
- loadMergedItems(node);
+ } else if (incrementalState
+ && getAdditionalDataTagName().equals(node.getLocalName())) {
+ loadAdditionalData(node, incrementalState);
}
}
@@ -428,21 +426,29 @@
return true;
} catch (SAXParseException e) {
- throw new MergingException(e).addFilePosition(file, e);
+ throw MergingException.wrapException(e).withFile(file).build();
} catch (IOException e) {
- throw new MergingException(e).addFile(file);
+ throw MergingException.wrapException(e).withFile(file).build();
} catch (ParserConfigurationException e) {
- throw new MergingException(e).addFile(file);
+ throw MergingException.wrapException(e).withFile(file).build();
} catch (SAXException e) {
- throw new MergingException(e).addFile(file);
+ throw MergingException.wrapException(e).withFile(file).build();
}
}
- protected void loadMergedItems(@NonNull Node mergedItemsNode) throws MergingException {
+ @NonNull
+ protected String getAdditionalDataTagName() {
+ // No tag can have an empty name, so mergers that store additional data, have to provide
+ // this.
+ return "";
+ }
+
+ protected void loadAdditionalData(@NonNull Node additionalDataNode, boolean incrementalState)
+ throws MergingException {
// do nothing by default.
}
- protected void writeMergedItems(Document document, Node rootNode) throws MergingException {
+ protected void writeAdditionalData(Document document, Node rootNode) throws MergingException {
// do nothing by default.
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/DataSet.java b/sdk-common/src/main/java/com/android/ide/common/res2/DataSet.java
index 870e852..8d662a0 100755
--- a/sdk-common/src/main/java/com/android/ide/common/res2/DataSet.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/DataSet.java
@@ -18,7 +18,9 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.ide.common.blame.Message;
import com.android.utils.ILogger;
+import com.google.common.base.Objects;
import com.google.common.base.Splitter;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
@@ -63,6 +65,8 @@
private final String mConfigName;
+ private final boolean mValidateEnabled;
+
/**
* List of source files. The may not have been loaded yet.
*/
@@ -92,8 +96,9 @@
*
* @param configName the name of the config this set is associated with.
*/
- public DataSet(String configName) {
+ public DataSet(String configName, boolean validateEnabled) {
mConfigName = configName;
+ mValidateEnabled = validateEnabled;
}
protected abstract DataSet<I, F> createSet(String name);
@@ -106,7 +111,7 @@
* @param fileNode the XML node.
* @return a DataFile
*/
- protected abstract F createFileAndItems(@NonNull File file, @NonNull Node fileNode)
+ protected abstract F createFileAndItemsFromXml(@NonNull File file, @NonNull Node fileNode)
throws MergingException;
/**
@@ -229,14 +234,20 @@
* @throws MergingException if something goes wrong
*/
public void loadFromFiles(ILogger logger) throws MergingException {
+ List<Message> errors = Lists.newArrayList();
for (File file : mSourceFiles) {
if (file.isDirectory()) {
- readSourceFolder(file, logger);
+ try {
+ readSourceFolder(file, logger);
+ } catch (MergingException e) {
+ errors.addAll(e.getMessages());
+ }
} else if (file.isFile()) {
// TODO support resource bundle
}
}
+ MergingException.throwIfNonEmpty(errors);
checkItems();
}
@@ -276,22 +287,30 @@
dataFile.getFile().getAbsolutePath());
dataFile.addExtraAttributes(document, fileNode, null);
- if (dataFile.getType() == DataFile.FileType.MULTI) {
- for (I item : dataFile.getItems()) {
- if (item.isRemoved()|| consumer.ignoreItemInMerge(item)) {
- continue;
+ switch (dataFile.getType()) {
+ case GENERATED_FILES:
+ // Fall through. getDetailsXml() will return the XML which describes the
+ // generated files.
+ case XML_VALUES:
+ for (I item : dataFile.getItems()) {
+ if (item.isRemoved()|| consumer.ignoreItemInMerge(item)) {
+ continue;
+ }
+ Node adoptedNode = item.getDetailsXml(document);
+ if (adoptedNode != null) {
+ fileNode.appendChild(adoptedNode);
+ }
}
- Node adoptedNode = item.getAdoptedNode(document);
- if (adoptedNode != null) {
- fileNode.appendChild(adoptedNode);
- }
- }
- } else {
- // no need to check for isRemoved here since it's checked
- // at the file level and there's only one item.
- I dataItem = dataFile.getItem();
- NodeUtils.addAttribute(document, fileNode, null, ATTR_NAME, dataItem.getName());
- dataItem.addExtraAttributes(document, fileNode, null);
+ break;
+ case SINGLE_FILE:
+ // no need to check for isRemoved here since it's checked
+ // at the file level and there's only one item.
+ I dataItem = dataFile.getItem();
+ NodeUtils.addAttribute(document, fileNode, null, ATTR_NAME, dataItem.getName());
+ dataItem.addExtraAttributes(document, fileNode, null);
+ break;
+ default:
+ throw new IllegalStateException();
}
}
}
@@ -350,7 +369,7 @@
continue;
}
- F dataFile = createFileAndItems(new File(pathAttr.getValue()), fileNode);
+ F dataFile = createFileAndItemsFromXml(new File(pathAttr.getValue()), fileNode);
if (dataFile != null) {
dataSet.processNewDataFile(sourceFolder, dataFile, false /*setTouched*/);
@@ -367,6 +386,10 @@
* @throws DuplicateDataException if a duplicated item is found.
*/
protected void checkItems() throws DuplicateDataException {
+ if (!mValidateEnabled) {
+ return;
+ }
+ Collection<Collection<I>> duplicateCollections = Lists.newArrayList();
// check a list for duplicate, ignoring removed items.
for (Map.Entry<String, Collection<I>> entry : mItems.asMap().entrySet()) {
Collection<I> items = entry.getValue();
@@ -378,11 +401,16 @@
if (lastItem == null) {
lastItem = item;
} else {
- throw new DuplicateDataException(item, lastItem);
+ // We have duplicates, store them and throw the exception later, so
+ // the user gets all the error messages at once.
+ duplicateCollections.add(items);
}
}
}
}
+ if (!duplicateCollections.isEmpty()) {
+ throw new DuplicateDataException(DuplicateDataException.createMessages(duplicateCollections));
+ }
}
/**
@@ -401,24 +429,28 @@
case NEW:
return handleNewFile(sourceFolder, changedFile, logger);
case CHANGED:
- return handleChangedFile(sourceFolder, changedFile);
+ return handleChangedFile(sourceFolder, changedFile, logger);
case REMOVED:
- F dataFile = mDataFileMap.get(changedFile);
-
- if (dataFile == null) {
- return false;
- }
-
- // flag all resource items are removed
- for (I dataItem : dataFile.getItems()) {
- dataItem.setRemoved();
- }
- return true;
+ return handleRemovedFile(changedFile);
}
return false;
}
+ protected boolean handleRemovedFile(File removedFile) {
+ F dataFile = getDataFile(removedFile);
+
+ if (dataFile == null) {
+ return false;
+ }
+
+ // flag all resource items are removed
+ for (I dataItem : dataFile.getItems()) {
+ dataItem.setRemoved();
+ }
+ return true;
+ }
+
protected boolean isValidSourceFile(@NonNull File sourceFolder, @NonNull File file) {
return checkFileForAndroidRes(file);
}
@@ -447,10 +479,14 @@
}
}
- protected boolean handleChangedFile(@NonNull File sourceFolder,
- @NonNull File changedFile) throws MergingException {
+ protected boolean handleChangedFile(
+ @NonNull File sourceFolder,
+ @NonNull File changedFile,
+ @NonNull ILogger logger) throws MergingException {
F dataFile = mDataFileMap.get(changedFile);
- dataFile.getItem().setTouched();
+ for (I item : dataFile.getItems()) {
+ item.setTouched();
+ }
return true;
}
@@ -479,7 +515,10 @@
@Override
public String toString() {
- return Arrays.toString(mSourceFiles.toArray());
+ return Objects.toStringHelper(getClass())
+ .addValue(mConfigName)
+ .add("sources", Arrays.toString(mSourceFiles.toArray()))
+ .toString();
}
/**
@@ -487,7 +526,7 @@
* @param file the file to check
* @return true if it is a valid file, false if it should be ignored.
*/
- protected boolean checkFileForAndroidRes(@NonNull File file) {
+ protected static boolean checkFileForAndroidRes(@NonNull File file) {
return !isIgnored(file);
}
@@ -588,4 +627,8 @@
return ignore;
}
+
+ protected boolean getValidateEnabled() {
+ return mValidateEnabled;
+ }
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/DuplicateDataException.java b/sdk-common/src/main/java/com/android/ide/common/res2/DuplicateDataException.java
index f7ece6e..f7316fd 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/DuplicateDataException.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/DuplicateDataException.java
@@ -17,34 +17,56 @@
package com.android.ide.common.res2;
import com.android.annotations.NonNull;
-import com.android.ide.common.blame.FilePosition;
-import com.android.utils.PositionXmlParser;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
-import org.w3c.dom.Node;
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+
/**
* Exception when a {@link DataItem} is declared more than once in a {@link DataSet}
*/
public class DuplicateDataException extends MergingException {
- private DataItem mOne;
- private DataItem mTwo;
+ private static final String DUPLICATE_RESOURCES = "Duplicate resources";
- DuplicateDataException(@NonNull DataItem one, @NonNull DataItem two) {
- super(String.format("Duplicate resources: %1s:%2s, %3s:%4s",
- one.getSource().getFile().getAbsolutePath(), one.getKey(),
- two.getSource().getFile().getAbsolutePath(), two.getKey()));
- mOne = one;
- mTwo = two;
- addFile(one.getSource().getFile());
- addFile(two.getSource().getFile());
+ DuplicateDataException(Message[] messages) {
+ super(null, messages);
}
- public DataItem getOne() {
- return mOne;
+ static <I extends DataItem> Message[] createMessages(
+ @NonNull Collection<Collection<I>> duplicateDataItemSets) {
+ List<Message> messages = Lists.newArrayListWithCapacity(duplicateDataItemSets.size());
+ for (Collection<I> duplicateItems : duplicateDataItemSets) {
+ ImmutableList.Builder<SourceFilePosition> positions = ImmutableList.builder();
+ for (I item : duplicateItems) {
+ if (!item.isRemoved()) {
+ positions.add(getPosition(item));
+ }
+ }
+ messages.add(new Message(
+ Message.Kind.ERROR,
+ DUPLICATE_RESOURCES,
+ DUPLICATE_RESOURCES,
+ positions.build()));
+ }
+ return Iterables.toArray(messages, Message.class);
}
- public DataItem getTwo() {
- return mTwo;
+ private static SourceFilePosition getPosition(DataItem item) {
+ DataFile dataFile = item.getSource();
+ if (dataFile == null) {
+ return new SourceFilePosition(new SourceFile(item.getKey()), SourcePosition.UNKNOWN);
+ }
+ File f = dataFile.getFile();
+ SourcePosition sourcePosition = SourcePosition.UNKNOWN; // TODO: find position in file.
+ return new SourceFilePosition(new SourceFile(f, item.getKey()), sourcePosition);
}
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/FileResourceNameValidator.java b/sdk-common/src/main/java/com/android/ide/common/res2/FileResourceNameValidator.java
index 244d6bc..e94fc9f 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/FileResourceNameValidator.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/FileResourceNameValidator.java
@@ -18,6 +18,7 @@
import static com.android.SdkConstants.DOT_9PNG;
import static com.android.SdkConstants.DOT_XML;
+import static com.android.SdkConstants.DOT_XSD;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -45,7 +46,7 @@
throws MergingException {
String error = getErrorTextForFileResource(file.getName(), resourceType);
if (error != null) {
- throw new MergingException(error).setFile(file);
+ throw MergingException.withMessage(error).withFile(file).build();
}
}
@@ -119,6 +120,23 @@
}
}
}
+ } else if (resourceType == ResourceType.XML) {
+ // Also allow xsd as they are xml files.
+ if (SdkUtils.endsWithIgnoreCase(fileNameWithExt, DOT_XML) ||
+ SdkUtils.endsWithIgnoreCase(fileNameWithExt, DOT_XSD)) {
+ fileName = removeSingleExtension(fileNameWithExt);
+ } else {
+ if (!allowPartialOrMissingExtension) {
+ return "The file name must end with .xml";
+ } else {
+ fileName = removeSingleExtension(fileNameWithExt);
+ String ext = fileNameWithExt.substring(fileName.length());
+ if (!SdkUtils.startsWithIgnoreCase(DOT_XML, ext) &&
+ !SdkUtils.startsWithIgnoreCase(DOT_XSD, ext)) {
+ return "The file name must end with .xml";
+ }
+ }
+ }
} else {
// Require xml extension
if (SdkUtils.endsWithIgnoreCase(fileNameWithExt, DOT_XML)) {
@@ -136,7 +154,7 @@
}
}
}
- return getErrorTextForFileResourceNoExt(fileName);
+ return getErrorTextForNameWithoutExtension(fileName);
}
/**
@@ -146,11 +164,11 @@
* @return null if no error, otherwise a string describing the error.
*/
@Nullable
- private static String getErrorTextForFileResourceNoExt(
+ public static String getErrorTextForNameWithoutExtension(
@NonNull final String fileNameWithoutExt) {
char first = fileNameWithoutExt.charAt(0);
- if (!(first >= 'a' && first <= 'z')) {
- return "File-based resource names must start with a lowercase letter";
+ if (!Character.isJavaIdentifierStart(first)) {
+ return "The resource name must start with a letter";
}
// AAPT only allows lowercase+digits+_:
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/FileValidity.java b/sdk-common/src/main/java/com/android/ide/common/res2/FileValidity.java
index 36f6478..f2a6226 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/FileValidity.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/FileValidity.java
@@ -41,4 +41,10 @@
public File getSourceFile() {
return sourceFile;
}
+
+ public void clear() {
+ dataSet = null;
+ sourceFile = null;
+ status = FileStatus.UNKNOWN_FILE;
+ }
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/GeneratedResourceItem.java b/sdk-common/src/main/java/com/android/ide/common/res2/GeneratedResourceItem.java
new file mode 100644
index 0000000..de129de
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/GeneratedResourceItem.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.res2;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.resources.ResourceType;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.io.File;
+
+/**
+ * A {@link ResourceItem} that is generated, it knows its generate file path, which is not the same
+ * as as the owner ResourceFile.
+ */
+public class GeneratedResourceItem extends ResourceItem {
+
+ private final File mGeneratedFile;
+
+ private final String mQualifiers;
+
+ public GeneratedResourceItem(
+ @NonNull String name,
+ @NonNull File generatedFile,
+ @NonNull ResourceType type,
+ @NonNull String qualifiers) {
+ super(name, type, null);
+ mGeneratedFile = generatedFile;
+ mQualifiers = qualifiers;
+ }
+
+ @NonNull
+ @Override
+ public String getQualifiers() {
+ return mQualifiers;
+ }
+
+ @Override
+ public File getFile() {
+ return mGeneratedFile;
+ }
+
+ @Override
+ Node getDetailsXml(Document document) {
+ Element element = document.createElement("generated-file");
+ element.setAttribute(SdkConstants.ATTR_PATH, mGeneratedFile.getAbsolutePath());
+ element.setAttribute(SdkConstants.ATTR_TYPE, getType().getName());
+ element.setAttribute(ResourceFile.ATTR_QUALIFIER, mQualifiers);
+ return element;
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/GeneratedResourceSet.java b/sdk-common/src/main/java/com/android/ide/common/res2/GeneratedResourceSet.java
new file mode 100644
index 0000000..587d347
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/GeneratedResourceSet.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.res2;
+
+import com.android.annotations.NonNull;
+import com.android.utils.ILogger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import java.io.File;
+
+/**
+ * A {@link ResourceSet} that contains only generated files (e.g. PNGs generated from a vector
+ * drawable XML). It is always a mirror of a normal {@link ResourceSet} which delegates to this
+ * set when it encounters a file that needs to be replaced by generated files.
+ */
+public class GeneratedResourceSet extends ResourceSet {
+
+ public GeneratedResourceSet(ResourceSet originalSet) {
+ super(originalSet.getConfigName() + "$Generated", originalSet.getValidateEnabled());
+ for (File source : originalSet.getSourceFiles()) {
+ addSource(source);
+ }
+ }
+
+ public GeneratedResourceSet(String name) {
+ super(name);
+ }
+
+ @Override
+ protected DataSet<ResourceItem, ResourceFile> createSet(String name) {
+ return new GeneratedResourceSet(name);
+ }
+
+ @Override
+ void appendToXml(
+ @NonNull Node setNode,
+ @NonNull Document document,
+ @NonNull MergeConsumer<ResourceItem> consumer) {
+ NodeUtils.addAttribute(document, setNode, null, "generated", "true");
+ super.appendToXml(setNode, document, consumer);
+ }
+
+ @Override
+ public void loadFromFiles(ILogger logger) throws MergingException {
+ // Do nothing, the original set will hand us the generated files.
+ }
+
+ @Override
+ public File findMatchingSourceFile(File file) {
+ // Do nothing, the original set will hand us the generated files.
+ return null;
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/MergeConsumer.java b/sdk-common/src/main/java/com/android/ide/common/res2/MergeConsumer.java
index 68203a6..3f4881e 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/MergeConsumer.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/MergeConsumer.java
@@ -18,6 +18,14 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
+import com.google.common.base.Objects;
+import com.google.common.base.Throwables;
+
+import java.io.File;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -27,24 +35,37 @@
public interface MergeConsumer<I extends DataItem> {
/**
- * An exception thrown during by the consumer. It always contains the original exception
- * as its cause.
+ * An exception thrown during by the consumer. It always contains the original exception as its
+ * cause.
*/
class ConsumerException extends MergingException {
- public ConsumerException(Throwable cause) {
- super(cause);
+
+ public ConsumerException(@NonNull Throwable cause) {
+ this(cause, SourceFile.UNKNOWN);
+ }
+
+ public ConsumerException(@NonNull Throwable cause, @NonNull File file) {
+ this(cause, new SourceFile(file));
+ }
+
+ private ConsumerException(@NonNull Throwable cause, @NonNull SourceFile file) {
+ super(cause, new Message(
+ Message.Kind.ERROR,
+ Objects.firstNonNull(
+ cause.getLocalizedMessage(),
+ cause.getClass().getCanonicalName()),
+ Throwables.getStackTraceAsString(cause),
+ new SourceFilePosition(file, SourcePosition.UNKNOWN)));
}
}
/**
* Called before the merge starts.
- * @throws ConsumerException
*/
void start(@NonNull DocumentBuilderFactory factory) throws ConsumerException;
/**
* Called after the merge ends.
- * @throws ConsumerException
*/
void end() throws ConsumerException;
@@ -53,22 +74,19 @@
* indicate whether the item actually changed.
*
* @param item the new item.
- *
- * @throws ConsumerException
*/
void addItem(@NonNull I item) throws ConsumerException;
/**
- * Removes an item. Optionally pass the item that will replace this one.
- * This methods does not do the replacement. The replaced item is just there
- * in case the removal can be optimized when it's a replacement vs. a removal.
+ * Removes an item. Optionally pass the item that will replace this one. This methods does not
+ * do the replacement. The replaced item is just there in case the removal can be optimized when
+ * it's a replacement vs. a removal.
*
* @param removedItem the removed item.
- * @param replacedBy the optional item that replaces the removed item.
- *
- * @throws ConsumerException
+ * @param replacedBy the optional item that replaces the removed item.
*/
void removeItem(@NonNull I removedItem, @Nullable I replacedBy) throws ConsumerException;
boolean ignoreItemInMerge(I item);
+
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/MergedResourceWriter.java b/sdk-common/src/main/java/com/android/ide/common/res2/MergedResourceWriter.java
index d127317..ef56149 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/MergedResourceWriter.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/MergedResourceWriter.java
@@ -25,6 +25,7 @@
import static com.android.SdkConstants.TAG_EAT_COMMENT;
import static com.android.SdkConstants.TAG_RESOURCES;
import static com.android.utils.SdkUtils.createPathComment;
+import static com.google.common.base.Preconditions.checkState;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
@@ -63,7 +64,12 @@
@NonNull
private final PngCruncher mCruncher;
- /** If non-null, points to a File that we should write public.txt to */
+ @NonNull
+ private final ResourcePreprocessor mPreprocessor;
+
+ /**
+ * If non-null, points to a File that we should write public.txt to
+ */
private final File mPublicFile;
private DocumentBuilderFactory mFactory;
@@ -82,9 +88,9 @@
private ListMultimap<String, ResourceItem> mValuesResMap;
/**
- * Set of qualifier that had a previously written resource now gone.
- * This is to keep a list of values files that must be written out even with no
- * touched or updated resources, in case one or more resources were removed.
+ * Set of qualifier that had a previously written resource now gone. This is to keep a list of
+ * values files that must be written out even with no touched or updated resources, in case one
+ * or more resources were removed.
*/
private Set<String> mQualifierWithDeletedValues;
@@ -92,13 +98,15 @@
@NonNull PngCruncher pngRunner,
boolean crunchPng,
boolean process9Patch,
- @Nullable File publicFile) {
+ @Nullable File publicFile,
+ @NonNull ResourcePreprocessor preprocessor) {
super(rootFolder);
mCruncher = pngRunner;
mCruncherKey = mCruncher.start();
mCrunchPng = crunchPng;
mProcess9Patch = process9Patch;
mPublicFile = publicFile;
+ mPreprocessor = preprocessor;
}
/**
@@ -107,7 +115,7 @@
* @param insertSourceMarkers if true, insert source markers
*/
public void setInsertSourceMarkers(boolean insertSourceMarkers) {
- mInsertSourceMarkers = insertSourceMarkers;
+ mInsertSourceMarkers = insertSourceMarkers;
}
/**
@@ -116,7 +124,7 @@
* @return whether this manifest merger will insert source markers into the merged source
*/
public boolean isInsertSourceMarkers() {
- return mInsertSourceMarkers;
+ return mInsertSourceMarkers;
}
@Override
@@ -129,8 +137,10 @@
@Override
public void end() throws ConsumerException {
+ // Make sure all PNGs are generated first.
super.end();
try {
+ // Wait for all PNGs to be crunched.
mCruncher.end(mCruncherKey);
} catch (InterruptedException e) {
throw new ConsumerException(e);
@@ -148,23 +158,23 @@
@Override
public void addItem(@NonNull final ResourceItem item) throws ConsumerException {
- ResourceFile.FileType type = item.getSourceType();
+ final ResourceFile.FileType type = item.getSourceType();
- if (type == ResourceFile.FileType.MULTI) {
+ if (type == ResourceFile.FileType.XML_VALUES) {
// this is a resource for the values files
// just add the node to write to the map based on the qualifier.
// We'll figure out later if the files needs to be written or (not)
mValuesResMap.put(item.getQualifiers(), item);
} else {
- // This is a single value file.
- // Only write it if the state is TOUCHED.
+ checkState(item.getSource() != null);
+ // This is a single value file or a set of generated files. Only write it if the state
+ // is TOUCHED.
if (item.isTouched()) {
getExecutor().execute(new Callable<Void>() {
@Override
public Void call() throws Exception {
- ResourceFile resourceFile = item.getSource();
- File file = resourceFile.getFile();
+ File file = item.getFile();
String filename = file.getName();
String folderName = getFolderName(item);
@@ -172,11 +182,15 @@
try {
createDir(typeFolder);
} catch (IOException ioe) {
- throw new MergingException(ioe).setFile(typeFolder);
+ throw MergingException.wrapException(ioe).withFile(typeFolder).build();
}
File outFile = new File(typeFolder, filename);
+ if (type == DataFile.FileType.GENERATED_FILES) {
+ mPreprocessor.generateFile(file, item.getSource().getFile());
+ }
+
try {
if (item.getType() == ResourceType.RAW) {
// Don't crunch, don't insert source comments, etc - leave alone.
@@ -199,9 +213,9 @@
Files.copy(file, outFile);
}
} catch (PngException e) {
- throw new MergingException(e).setFile(file);
+ throw MergingException.wrapException(e).withFile(file).build();
} catch (IOException ioe) {
- throw new MergingException(ioe).setFile(file);
+ throw MergingException.wrapException(ioe).withFile(file).build();
}
return null;
}
@@ -214,29 +228,26 @@
public void removeItem(@NonNull ResourceItem removedItem, @Nullable ResourceItem replacedBy)
throws ConsumerException {
ResourceFile.FileType removedType = removedItem.getSourceType();
- ResourceFile.FileType replacedType = replacedBy != null ?
- replacedBy.getSourceType() : null;
+ ResourceFile.FileType replacedType = replacedBy != null
+ ? replacedBy.getSourceType()
+ : null;
- if (removedType == replacedType) {
- // if the type is multi, then we make sure to flag the qualifier as deleted.
- if (removedType == ResourceFile.FileType.MULTI) {
- mQualifierWithDeletedValues.add(
- removedItem.getQualifiers());
- } else {
- // both are single type resources, so we actually don't delete the previous
- // file as the new one will replace it instead.
- }
- } else if (removedType == ResourceFile.FileType.SINGLE) {
- // removed type is single.
- // The case of both single type is above, so here either, there is no replacement
- // or the replacement is multi. We always need to remove the old file.
- // if replacedType is non-null, then it was values, if not,
- removeOutFile(removedItem);
- } else {
- // removed type is multi.
- // whether the new type is single or doesn't exist, we always need to mark the qualifier
- // for rewrite.
- mQualifierWithDeletedValues.add(removedItem.getQualifiers());
+ switch (removedType) {
+ case SINGLE_FILE: // Fall through.
+ case GENERATED_FILES:
+ if (replacedType == DataFile.FileType.SINGLE_FILE
+ || replacedType == DataFile.FileType.GENERATED_FILES) {
+ // Save one IO operation and don't delete a file that will be overwritten
+ // anyway.
+ break;
+ }
+ removeOutFile(removedItem);
+ break;
+ case XML_VALUES:
+ mQualifierWithDeletedValues.add(removedItem.getQualifiers());
+ break;
+ default:
+ throw new IllegalStateException();
}
}
@@ -324,7 +335,7 @@
currentFile = null;
- String content = XmlUtils.toXml(document, true /*preserveWhitespace*/);
+ String content = XmlUtils.toXml(document);
Files.write(content, outFile, Charsets.UTF_8);
if (publicNodes != null && mPublicFile != null) {
@@ -352,8 +363,8 @@
Files.write(text, mPublicFile, Charsets.UTF_8);
}
} catch (Throwable t) {
- ConsumerException exception = new ConsumerException(t);
- exception.setFile(currentFile != null ? currentFile.getFile() : outFile);
+ ConsumerException exception = new ConsumerException(t,
+ currentFile != null ? currentFile.getFile() : outFile);
throw exception;
}
}
@@ -376,23 +387,14 @@
* @return true if success.
*/
private boolean removeOutFile(ResourceItem resourceItem) {
- ResourceFile resourceFile = resourceItem.getSource();
- if (resourceFile.getType() == ResourceFile.FileType.MULTI) {
- throw new IllegalArgumentException("SourceFile cannot be a FileType.MULTI");
- }
-
- File file = resourceFile.getFile();
- String fileName = file.getName();
- String folderName = getFolderName(resourceItem);
-
- return removeOutFile(folderName, fileName);
+ return removeOutFile(getFolderName(resourceItem), resourceItem.getFile().getName());
}
/**
* Removes a file from a folder based on a sub folder name and a filename
*
* @param folderName the sub folder name
- * @param fileName the file name.
+ * @param fileName the file name.
* @return true if success.
*/
private boolean removeOutFile(String folderName, String fileName) {
@@ -409,6 +411,7 @@
/**
* Calculates the right folder name give a resource item.
+ *
* @param resourceItem the resource item to calculate the folder name from.
* @return a relative folder name
*/
@@ -416,7 +419,7 @@
private static String getFolderName(ResourceItem resourceItem) {
ResourceType itemType = resourceItem.getType();
String folderName = itemType.getName();
- String qualifiers = resourceItem.getSource().getQualifiers();
+ String qualifiers = resourceItem.getQualifiers();
if (!qualifiers.isEmpty()) {
folderName = folderName + RES_QUALIFIER_SEP + qualifiers;
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/MergingException.java b/sdk-common/src/main/java/com/android/ide/common/res2/MergingException.java
index 1da85ea..e0c7b68 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/MergingException.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/MergingException.java
@@ -18,123 +18,200 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.ide.common.blame.FilePosition;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.Message.Kind;
+import com.android.ide.common.blame.SourceFile;
+import com.android.ide.common.blame.SourceFilePosition;
import com.android.ide.common.blame.SourcePosition;
-import com.google.common.base.Function;
import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.xml.sax.SAXParseException;
import java.io.File;
+import java.util.Collection;
import java.util.List;
-/** Exception for errors during merging */
+/**
+ * Exception for errors during merging.
+ */
public class MergingException extends Exception {
- private String mMessage; // Keeping our own copy since parent prepends exception class name
- private List<FilePosition> mFilePositions = Lists.newArrayList();
- public MergingException(@NonNull String message, @Nullable Throwable cause) {
- super(message, cause);
- mMessage = message;
+ public static final String MULTIPLE_ERRORS = "Multiple errors:";
+
+ @NonNull
+ private final List<Message> mMessages;
+
+ /**
+ * For internal use. Creates a new MergingException
+ *
+ * @param cause the original exception. May be null.
+ * @param messages the messaged. Must contain at least one item.
+ */
+ protected MergingException(@Nullable Throwable cause, @NonNull Message... messages) {
+ super(messages.length == 1 ? messages[0].getText() : MULTIPLE_ERRORS, cause);
+ mMessages = ImmutableList.copyOf(messages);
}
- public MergingException(@NonNull String message) {
- this(message, null);
+ public static class Builder {
+
+ @Nullable
+ private Throwable mCause = null;
+
+ @Nullable
+ private String mMessageText = null;
+
+ @Nullable
+ private String mOriginalMessageText = null;
+
+ @NonNull
+ private SourceFile mFile = SourceFile.UNKNOWN;
+
+ @NonNull
+ private SourcePosition mPosition = SourcePosition.UNKNOWN;
+
+ private Builder() {
+ }
+
+ public Builder wrapException(@NonNull Throwable cause) {
+ mCause = cause;
+ mOriginalMessageText = Throwables.getStackTraceAsString(cause);
+ return this;
+ }
+
+ public Builder withFile(@NonNull File file) {
+ mFile = new SourceFile(file);
+ return this;
+ }
+
+ public Builder withFile(@NonNull SourceFile file) {
+ mFile = file;
+ return this;
+ }
+
+ public Builder withPosition(@NonNull SourcePosition position) {
+ mPosition = position;
+ return this;
+ }
+
+ public Builder withMessage(@NonNull String messageText, Object... args) {
+ mMessageText = args.length == 0 ? messageText : String.format(messageText, args);
+ return this;
+ }
+
+ public MergingException build() {
+ if (mCause != null) {
+ if (mMessageText == null) {
+ mMessageText = Objects.firstNonNull(
+ mCause.getLocalizedMessage(), mCause.getClass().getCanonicalName());
+ }
+ if (mPosition == SourcePosition.UNKNOWN && mCause instanceof SAXParseException) {
+ SAXParseException exception = (SAXParseException) mCause;
+ int lineNumber = exception.getLineNumber();
+ if (lineNumber != -1) {
+ // Convert positions to be 0-based for SourceFilePosition.
+ mPosition = new SourcePosition(lineNumber - 1,
+ exception.getColumnNumber() - 1, -1);
+ }
+ }
+ }
+
+ if (mMessageText == null) {
+ mMessageText = "Unknown error.";
+ }
+
+ return new MergingException(
+ mCause,
+ new Message(
+ Kind.ERROR,
+ mMessageText,
+ Objects.firstNonNull(mOriginalMessageText, mMessageText),
+ new SourceFilePosition(mFile, mPosition)));
+ }
+
}
- public MergingException(@NonNull Throwable cause) {
- this(cause.getLocalizedMessage(), cause);
+ public static Builder wrapException(@NonNull Throwable cause) {
+ return new Builder().wrapException(cause);
+ }
+
+ public static Builder withMessage(@NonNull String message, Object... args) {
+ return new Builder().withMessage(message, args);
+ }
+
+
+ public static void throwIfNonEmpty(Collection<Message> messages) throws MergingException {
+ if (!messages.isEmpty()) {
+ throw new MergingException(null, Iterables.toArray(messages, Message.class));
+ }
+ }
+
+ @NonNull
+ public List<Message> getMessages() {
+ return mMessages;
}
/**
- * Add the file if it hasn't already been included in the list of file positions.
+ * Computes the error message to display for this error
*/
- public MergingException setFile(@NonNull File file) {
- for (FilePosition filePosition : mFilePositions) {
- if (filePosition.getSourceFile().equals(file)) {
- return this;
- }
- }
- addFile(file);
- return this;
- }
-
- public MergingException addFile(@NonNull File file) {
- mFilePositions.add(new FilePosition(file, SourcePosition.UNKNOWN));
- return this;
- }
-
- public MergingException addFileIfNonNull(@Nullable File file) {
- return (file != null) ? addFile(file) : this;
- }
-
- public MergingException addFilePosition(@NonNull FilePosition filePosition) {
- mFilePositions.add(filePosition);
- return this;
- }
-
- public MergingException addFilePosition(@NonNull File file, @NonNull SAXParseException exception) {
- int lineNumber = exception.getLineNumber();
- if (lineNumber != -1) {
- addFilePosition(new FilePosition(file, new SourcePosition(
- exception.getLineNumber() - 1, exception.getColumnNumber() - 1, -1)));
- } else {
- addFile(file);
- }
- return this;
- }
-
- public MergingException setCause(@NonNull Throwable cause) {
- initCause(cause);
- return this;
- }
-
- /** Computes the error message to display for this error */
+ @NonNull
@Override
public String getMessage() {
- StringBuilder sb = new StringBuilder();
- sb.append(Joiner.on('\t').join(mFilePositions));
+ List<String> messages = Lists.newArrayListWithCapacity(mMessages.size());
+ for (Message message : mMessages) {
+ StringBuilder sb = new StringBuilder();
+ List<SourceFilePosition> sourceFilePositions = message.getSourceFilePositions();
+ if (sourceFilePositions.size() > 1 || !sourceFilePositions.get(0)
+ .equals(SourceFilePosition.UNKNOWN)) {
+ sb.append(Joiner.on('\t').join(sourceFilePositions));
+ }
- if (sb.length() > 0) {
- sb.append(':').append(' ');
+ String text = message.getText();
+ if (sb.length() > 0) {
+ sb.append(':').append(' ');
- // ALWAYS insert the string "Error:" between the path and the message.
- // This is done to make the error messages more simple to detect
- // (since a generic path: message pattern can match a lot of output, basically
- // any labeled output, and we don't want to do file existence checks on any random
- // string to the left of a colon.)
- if (!mMessage.startsWith("Error: ")) {
+ // ALWAYS insert the string "Error:" between the path and the message.
+ // This is done to make the error messages more simple to detect
+ // (since a generic path: message pattern can match a lot of output, basically
+ // any labeled output, and we don't want to do file existence checks on any random
+ // string to the left of a colon.)
+ if (!text.startsWith("Error: ")) {
+ sb.append("Error: ");
+ }
+ } else if (!text.contains("Error: ")) {
sb.append("Error: ");
}
- } else if (!mMessage.contains("Error: ")) {
- sb.append("Error: ");
- }
- String message = mMessage;
-
- // If the error message already starts with the path, strip it out.
- // This avoids redundant looking error messages you can end up with
- // like for example for permission denied errors where the error message
- // string itself contains the path as a prefix:
- // /my/full/path: /my/full/path (Permission denied)
- if (mFilePositions.size() == 1) {
- String path = mFilePositions.get(0).getSourceFile().getAbsolutePath();
- if (path != null && message.startsWith(path)) {
- int stripStart = path.length();
- if (message.length() > stripStart && message.charAt(stripStart) == ':') {
- stripStart++;
+ // If the error message already starts with the path, strip it out.
+ // This avoids redundant looking error messages you can end up with
+ // like for example for permission denied errors where the error message
+ // string itself contains the path as a prefix:
+ // /my/full/path: /my/full/path (Permission denied)
+ if (sourceFilePositions.size() == 1) {
+ File file = sourceFilePositions.get(0).getFile().getSourceFile();
+ if (file != null) {
+ String path = file.getAbsolutePath();
+ if (text.startsWith(path)) {
+ int stripStart = path.length();
+ if (text.length() > stripStart && text.charAt(stripStart) == ':') {
+ stripStart++;
+ }
+ if (text.length() > stripStart && text.charAt(stripStart) == ' ') {
+ stripStart++;
+ }
+ text = text.substring(stripStart);
+ }
}
- if (message.length() > stripStart && message.charAt(stripStart) == ' ') {
- stripStart++;
- }
- message = message.substring(stripStart);
}
- }
- sb.append(message);
- return sb.toString();
+ sb.append(text);
+ messages.add(sb.toString());
+ }
+ return Joiner.on('\n').join(messages);
}
@Override
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 615b6f9..f124398 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
@@ -18,6 +18,7 @@
import com.android.SdkConstants;
import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
@@ -344,4 +345,13 @@
return true;
}
+
+ @Nullable
+ static String getAttribute(@NonNull Node node, @NonNull String attrName) {
+ Attr attr = (Attr) node.getAttributes().getNamedItem(attrName);
+ if (attr != null) {
+ return attr.getValue();
+ }
+ return null;
+ }
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceFile.java b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceFile.java
index e9243b8..3274494 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceFile.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceFile.java
@@ -16,7 +16,9 @@
package com.android.ide.common.res2;
+import com.android.SdkConstants;
import com.android.annotations.NonNull;
+import com.google.common.base.Objects;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
@@ -44,7 +46,7 @@
*
* The source file is set on the item with {@link ResourceItem#setSource(DataFile)}
*
- * The type of the ResourceFile will be {@link FileType#SINGLE}.
+ * The type of the ResourceFile will be {@link FileType#SINGLE_FILE}.
*
* @param file the File
* @param item the resource item
@@ -52,7 +54,7 @@
*/
public ResourceFile(@NonNull File file, @NonNull ResourceItem item,
@NonNull String qualifiers) {
- super(file, FileType.SINGLE);
+ super(file, FileType.SINGLE_FILE);
mQualifiers = qualifiers;
init(item);
}
@@ -62,7 +64,7 @@
*
* The source file is set on the items with {@link ResourceItem#setSource(DataFile)}
*
- * The type of the ResourceFile will be {@link FileType#MULTI}.
+ * The type of the ResourceFile will be {@link FileType#XML_VALUES}.
*
* @param file the File
* @param items the resource items
@@ -70,11 +72,25 @@
*/
public ResourceFile(@NonNull File file, @NonNull List<ResourceItem> items,
@NonNull String qualifiers) {
- super(file, FileType.MULTI);
+ this(file, items, qualifiers, FileType.XML_VALUES);
+ }
+
+ private ResourceFile(@NonNull File file, @NonNull List<ResourceItem> items,
+ @NonNull String qualifiers, @NonNull FileType fileType) {
+ super(file, fileType);
mQualifiers = qualifiers;
init(items);
}
+ public static ResourceFile generatedFiles(
+ @NonNull File file,
+ @NonNull List<ResourceItem> items,
+ @NonNull String qualifiers) {
+ // TODO: Replace other constructors with named methods.
+ return new ResourceFile(file, items, qualifiers, FileType.GENERATED_FILES);
+ }
+
+
@NonNull
public String getQualifiers() {
return mQualifiers;
@@ -89,13 +105,17 @@
void addExtraAttributes(Document document, Node node, String namespaceUri) {
NodeUtils.addAttribute(document, node, namespaceUri, ATTR_QUALIFIER,
getQualifiers());
+
+ if (getType() == FileType.GENERATED_FILES) {
+ NodeUtils.addAttribute(document, node, namespaceUri, SdkConstants.ATTR_PREPROCESSING, "true");
+ }
}
@Override
public String toString() {
- return "ResourceFile{" +
- "mFile='" + getFile() + '\'' +
- ", mQualifiers='" + mQualifiers + '\'' +
- '}';
+ return Objects.toStringHelper(getClass())
+ .add("mFile", mFile)
+ .add("mQualifiers", mQualifiers)
+ .toString();
}
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceItem.java b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceItem.java
index 3248524..0af7176 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceItem.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceItem.java
@@ -68,10 +68,13 @@
public class ResourceItem extends DataItem<ResourceFile>
implements Configurable, Comparable<ResourceItem> {
+ @NonNull
private final ResourceType mType;
+ @Nullable
private Node mValue;
+ @Nullable
protected ResourceValue mResourceValue;
/**
@@ -148,7 +151,7 @@
*
* @param from the resource to copy the value from.
*/
- void setValue(ResourceItem from) {
+ void setValue(@NonNull ResourceItem from) {
mValue = from.mValue;
setTouched();
}
@@ -180,11 +183,20 @@
"ResourceItem.getKey called on object with no ResourceFile: " + this);
}
String qualifiers = getQualifiers();
- if (!qualifiers.isEmpty()) {
- return mType.getName() + "-" + qualifiers + "/" + getName();
+
+ String typeName = mType.getName();
+ if (mType == ResourceType.PUBLIC && mValue != null) {
+ String typeAttribute = ((Element) mValue).getAttribute(ATTR_TYPE);
+ if (typeAttribute != null) {
+ typeName += "_" + typeAttribute;
+ }
}
- return mType.getName() + "/" + getName();
+ if (!qualifiers.isEmpty()) {
+ return typeName + "-" + qualifiers + "/" + getName();
+ }
+
+ return typeName + "/" + getName();
}
@Override
@@ -660,9 +672,7 @@
}
@Override
- Node getAdoptedNode(Document document) {
+ Node getDetailsXml(Document document) {
return NodeUtils.adoptNode(document, mValue);
}
-
-
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceMerger.java b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceMerger.java
index d0563ad..2cfba18 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceMerger.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceMerger.java
@@ -46,6 +46,7 @@
* {@link ResourceFile}.
*/
public class ResourceMerger extends DataMerger<ResourceItem, ResourceFile, ResourceSet> {
+ private static final String NODE_MERGED_ITEMS = "mergedItems";
/**
* Override of the normal ResourceItem to handle merged item cases.
@@ -86,7 +87,7 @@
@Override
@NonNull
public FileType getSourceType() {
- return FileType.MULTI;
+ return FileType.XML_VALUES;
}
}
@@ -98,10 +99,36 @@
protected final Map<String, Map<String, ResourceItem>> mMergedItems = Maps.newHashMap();
+ /**
+ * Reads the {@link ResourceSet} from the blob XML. {@link ResourceMerger} deals with two kinds
+ * of sets - {@link GeneratedResourceSet} and "plain" {@link ResourceSet} . Instances of the
+ * former are marked with {@code generated="true"} attribute. Instances of the latter have a
+ * {@code generated-set} attribute that references the corresponding generated set by name.
+ * For any variant, the generated set has a lower priority, so it comes in the XML first. This
+ * means we will find it by name at this stage.
+ */
@Override
protected ResourceSet createFromXml(Node node) throws MergingException {
- ResourceSet set = new ResourceSet("");
- return (ResourceSet) set.createFromXml(node);
+ String generated = NodeUtils.getAttribute(node, "generated");
+ ResourceSet set;
+ if ("true".equals(generated)) {
+ set = new GeneratedResourceSet("");
+ } else {
+ set = new ResourceSet("");
+ }
+ ResourceSet newResourceSet = (ResourceSet) set.createFromXml(node);
+
+ String generatedSetName = NodeUtils.getAttribute(node, "generated-set");
+ if (generatedSetName != null) {
+ for (ResourceSet resourceSet : getDataSets()) {
+ if (resourceSet.getConfigName().equals(generatedSetName)) {
+ newResourceSet.setGeneratedSet(resourceSet);
+ break;
+ }
+ }
+ }
+
+ return newResourceSet;
}
@Override
@@ -214,7 +241,7 @@
}
}
} catch (ParserConfigurationException e) {
- throw new MergingException(e);
+ throw MergingException.wrapException(e).build();
}
}
@@ -228,8 +255,21 @@
return null;
}
+ @NonNull
@Override
- protected void loadMergedItems(@NonNull Node mergedItemsNode) throws MergingException {
+ protected String getAdditionalDataTagName() {
+ return NODE_MERGED_ITEMS;
+ }
+
+ @Override
+ protected void loadAdditionalData(@NonNull Node mergedItemsNode, boolean incrementalState) throws MergingException {
+ // only load the merged item in incremental state.
+ // In non incremental state, they will be recreated by the touched
+ // items anyway.
+ if (!incrementalState) {
+ return;
+ }
+
// loop on the qualifiers.
NodeList configurationList = mergedItemsNode.getChildNodes();
@@ -269,8 +309,8 @@
}
@Override
- protected void writeMergedItems(Document document, Node rootNode) {
- Node mergedItemsNode = document.createElement(NODE_MERGED_ITEMS);
+ protected void writeAdditionalData(Document document, Node rootNode) {
+ Node mergedItemsNode = document.createElement(getAdditionalDataTagName());
rootNode.appendChild(mergedItemsNode);
for (String qualifier : mMergedItems.keySet()) {
@@ -283,7 +323,7 @@
mergedItemsNode.appendChild(qualifierNode);
for (ResourceItem item : itemMap.values()) {
- Node adoptedNode = item.getAdoptedNode(document);
+ Node adoptedNode = item.getDetailsXml(document);
if (adoptedNode != null) {
qualifierNode.appendChild(adoptedNode);
}
@@ -317,4 +357,10 @@
return null;
}
+
+ @Override
+ public void addDataSet(ResourceSet resourceSet) {
+
+ super.addDataSet(resourceSet);
+ }
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/ResourcePreprocessor.java b/sdk-common/src/main/java/com/android/ide/common/res2/ResourcePreprocessor.java
new file mode 100644
index 0000000..71f20b3
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/ResourcePreprocessor.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.res2;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+/**
+ * Provides functionality the resource merger needs for preprocessing resources during merge.
+ */
+public interface ResourcePreprocessor {
+ /** Checks if the given file should be replaced by N generated files. */
+ boolean needsPreprocessing(File file);
+
+ /** Returns the paths that should be generated for the given file. */
+ Collection<File> getFilesToBeGenerated(File original);
+
+ /** Actually generate the file based on the original file. */
+ void generateFile(File toBeGenerated, File original) throws IOException;
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceSet.java b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceSet.java
index 88763b6..09795bf 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/ResourceSet.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/ResourceSet.java
@@ -17,10 +17,13 @@
package com.android.ide.common.res2;
import static com.android.ide.common.res2.ResourceFile.ATTR_QUALIFIER;
+import static com.google.common.base.Objects.firstNonNull;
+import static com.google.common.base.Preconditions.checkState;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.ide.common.blame.Message;
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.resources.FolderTypeRelationship;
import com.android.resources.ResourceConstants;
@@ -30,11 +33,12 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.File;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -47,15 +51,29 @@
public class ResourceSet extends DataSet<ResourceItem, ResourceFile> {
private boolean mNormalizeResources = false;
+ private ResourceSet mGeneratedSet;
+ private ResourcePreprocessor mPreprocessor;
public ResourceSet(String name) {
- super(name);
+ this(name, true /*validateEnabled*/);
+ }
+
+ public ResourceSet(String name, boolean validateEnabled) {
+ super(name, validateEnabled);
}
public void setNormalizeResources(boolean normalizeResources) {
mNormalizeResources = normalizeResources;
}
+ public void setGeneratedSet(ResourceSet generatedSet) {
+ mGeneratedSet = generatedSet;
+ }
+
+ public void setPreprocessor(ResourcePreprocessor preprocessor) {
+ mPreprocessor = preprocessor;
+ }
+
@Override
protected DataSet<ResourceItem, ResourceFile> createSet(String name) {
return new ResourceSet(name);
@@ -75,14 +93,52 @@
}
@Override
- protected ResourceFile createFileAndItems(@NonNull File file, @NonNull Node fileNode)
+ protected ResourceFile createFileAndItemsFromXml(@NonNull File file, @NonNull Node fileNode)
throws MergingException {
- Attr qualifierAttr = (Attr) fileNode.getAttributes().getNamedItem(ATTR_QUALIFIER);
- String qualifier = qualifierAttr != null ? qualifierAttr.getValue() : "";
+ String qualifier = firstNonNull(NodeUtils.getAttribute(fileNode, ATTR_QUALIFIER), "");
+ String typeAttr = NodeUtils.getAttribute(fileNode, SdkConstants.ATTR_TYPE);
- Attr typeAttr = (Attr) fileNode.getAttributes().getNamedItem(SdkConstants.ATTR_TYPE);
- if (typeAttr == null) {
- // multi res file
+ if (NodeUtils.getAttribute(fileNode, SdkConstants.ATTR_PREPROCESSING) != null) {
+ // FileType.GENERATED_FILES
+ NodeList childNodes = fileNode.getChildNodes();
+ int childCount = childNodes.getLength();
+
+ List<ResourceItem> resourceItems =
+ Lists.newArrayListWithCapacity(childCount);
+
+ for (int i = 0; i < childCount; i++) {
+ Node childNode = childNodes.item(i);
+
+ String path = NodeUtils.getAttribute(childNode, SdkConstants.ATTR_PATH);
+ if (path == null) {
+ continue;
+ }
+
+ File generatedFile = new File(path);
+ String resourceType = NodeUtils.getAttribute(childNode, SdkConstants.ATTR_TYPE);
+ if (resourceType == null) {
+ continue;
+ }
+ String qualifers = NodeUtils.getAttribute(childNode, ATTR_QUALIFIER);
+ if (qualifers == null) {
+ continue;
+ }
+
+ resourceItems.add(
+ new GeneratedResourceItem(
+ getNameForFile(generatedFile),
+ generatedFile,
+ FolderTypeRelationship
+ .getRelatedResourceTypes(
+ ResourceFolderType.getTypeByName(resourceType))
+ .get(0),
+ qualifers));
+ }
+
+ return ResourceFile.generatedFiles(file, resourceItems, qualifier);
+ }
+ else if (typeAttr == null) {
+ // FileType.XML_VALUES
List<ResourceItem> resourceList = Lists.newArrayList();
// loop on each node that represent a resource
@@ -113,18 +169,20 @@
} else {
// single res file
- ResourceType type = ResourceType.getEnum(typeAttr.getValue());
+ ResourceType type = ResourceType.getEnum(typeAttr);
if (type == null) {
return null;
}
- Attr nameAttr = (Attr) fileNode.getAttributes().getNamedItem(ATTR_NAME);
+ String nameAttr = NodeUtils.getAttribute(fileNode, ATTR_NAME);
if (nameAttr == null) {
return null;
}
- FileResourceNameValidator.validate(file, type);
- ResourceItem item = new ResourceItem(nameAttr.getValue(), type, null);
+ if (getValidateEnabled()) {
+ FileResourceNameValidator.validate(file, type);
+ }
+ ResourceItem item = new ResourceItem(nameAttr, type, null);
return new ResourceFile(file, item, qualifier);
}
}
@@ -132,17 +190,23 @@
@Override
protected void readSourceFolder(File sourceFolder, ILogger logger)
throws MergingException {
+ List<Message> errors = Lists.newArrayList();
File[] folders = sourceFolder.listFiles();
if (folders != null) {
for (File folder : folders) {
if (folder.isDirectory() && !isIgnored(folder)) {
FolderData folderData = getFolderData(folder);
if (folderData != null) {
- parseFolder(sourceFolder, folder, folderData, logger);
+ try {
+ parseFolder(sourceFolder, folder, folderData, logger);
+ } catch (MergingException e) {
+ errors.addAll(e.getMessages());
+ }
}
}
}
}
+ MergingException.throwIfNonEmpty(errors);
}
@Override
@@ -159,79 +223,144 @@
}
@Override
- protected boolean handleChangedFile(@NonNull File sourceFolder, @NonNull File changedFile)
+ protected boolean handleNewFile(File sourceFolder, File file, ILogger logger)
throws MergingException {
+ ResourceFile resourceFile = createFileAndItems(sourceFolder, file, logger);
+ processNewResourceFile(sourceFolder, resourceFile);
+ return true;
+ }
+ @Override
+ protected boolean handleRemovedFile(File removedFile) {
+ if (mGeneratedSet != null && mGeneratedSet.getDataFile(removedFile) != null) {
+ return mGeneratedSet.handleRemovedFile(removedFile);
+ } else {
+ return super.handleRemovedFile(removedFile);
+ }
+ }
+
+ @Override
+ protected boolean handleChangedFile(
+ @NonNull File sourceFolder,
+ @NonNull File changedFile,
+ @NonNull ILogger logger) throws MergingException {
FolderData folderData = getFolderData(changedFile.getParentFile());
if (folderData == null) {
return true;
}
ResourceFile resourceFile = getDataFile(changedFile);
-
- if (resourceFile == null) {
- String message = String.format(
- "In DataSet '%s', no data file for changedFile '%s'. "
- + "This is an internal error in the incremental builds code; "
- + "to work around it, try doing a full clean build.",
- getConfigName(), changedFile.getAbsolutePath());
- throw new MergingException(message).addFile(changedFile);
+ if (mGeneratedSet == null) {
+ // This is a generated set.
+ doHandleChangedFile(changedFile, resourceFile);
+ return true;
}
- //noinspection VariableNotUsedInsideIf
- if (folderData.type != null) {
- // single res file
- resourceFile.getItem().setTouched();
+ ResourceFile generatedSetResourceFile = mGeneratedSet.getDataFile(changedFile);
+ boolean needsPreprocessing = needsPreprocessing(changedFile);
+
+ if (resourceFile != null && generatedSetResourceFile == null && needsPreprocessing) {
+ // It didn't use to need preprocessing, but it does now.
+ handleRemovedFile(changedFile);
+ mGeneratedSet.handleNewFile(sourceFolder, changedFile, logger);
+ } else if (resourceFile == null
+ && generatedSetResourceFile != null
+ && !needsPreprocessing) {
+ // It used to need preprocessing, but not anymore.
+ mGeneratedSet.handleRemovedFile(changedFile);
+ handleNewFile(sourceFolder, changedFile, logger);
+ } else if (resourceFile == null
+ && generatedSetResourceFile != null
+ && needsPreprocessing) {
+ // Delegate to the generated set.
+ mGeneratedSet.handleChangedFile(sourceFolder, changedFile, logger);
+ } else if (resourceFile != null
+ && !needsPreprocessing
+ && generatedSetResourceFile == null) {
+ // The "normal" case, handle it here.
+ doHandleChangedFile(changedFile, resourceFile);
} else {
- // multi res. Need to parse the file and compare the items one by one.
- ValueResourceParser2 parser = new ValueResourceParser2(changedFile);
+ // Something strange happened.
+ throw MergingException.withMessage("In DataSet '%s', no data file for changedFile. "
+ + "This is an internal error in the incremental builds code; "
+ + "to work around it, try doing a full clean build.",
+ getConfigName()).withFile(changedFile).build();
+ }
- List<ResourceItem> parsedItems = parser.parseFile();
- Map<String, ResourceItem> oldItems = Maps.newHashMap(resourceFile.getItemMap());
- Map<String, ResourceItem> newItems = Maps.newHashMap();
+ return true;
+ }
- // create a fake ResourceFile to be able to call resource.getKey();
- // It's ok because we never use this instance anyway.
- ResourceFile fakeResourceFile = new ResourceFile(changedFile, parsedItems,
- resourceFile.getQualifiers());
+ private void doHandleChangedFile(@NonNull File changedFile, ResourceFile resourceFile)
+ throws MergingException {
+ switch (resourceFile.getType()) {
+ case SINGLE_FILE:
+ // single res file
+ resourceFile.getItem().setTouched();
+ break;
+ case GENERATED_FILES:
+ handleChangedItems(resourceFile,
+ getResourceItemsForGeneratedFiles(changedFile));
+ break;
+ case XML_VALUES:
+ // multi res. Need to parse the file and compare the items one by one.
+ ValueResourceParser2 parser = new ValueResourceParser2(changedFile);
- for (ResourceItem newItem : parsedItems) {
- String newKey = newItem.getKey();
- ResourceItem oldItem = oldItems.get(newKey);
+ List<ResourceItem> parsedItems = parser.parseFile();
+ handleChangedItems(resourceFile, parsedItems);
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+ }
- if (oldItem == null) {
- // this is a new item
- newItem.setTouched();
- newItems.put(newKey, newItem);
- } else {
- // remove it from the list of oldItems (this is to detect deletion)
- //noinspection SuspiciousMethodCalls
- oldItems.remove(oldItem.getKey());
+ private void handleChangedItems(
+ ResourceFile resourceFile,
+ List<ResourceItem> currentItems) throws MergingException {
+ Map<String, ResourceItem> oldItems = Maps.newHashMap(resourceFile.getItemMap());
+ Map<String, ResourceItem> addedItems = Maps.newHashMap();
- // now compare the items
+ // Set the source of newly determined items, so we can call getKey() on them.
+ for (ResourceItem currentItem : currentItems) {
+ currentItem.setSource(resourceFile);
+ }
+
+ for (ResourceItem newItem : currentItems) {
+ String newKey = newItem.getKey();
+ ResourceItem oldItem = oldItems.get(newKey);
+
+ if (oldItem == null) {
+ // this is a new item
+ newItem.setTouched();
+ addedItems.put(newKey, newItem);
+ } else {
+ // remove it from the list of oldItems (this is to detect deletion)
+ //noinspection SuspiciousMethodCalls
+ oldItems.remove(oldItem.getKey());
+
+ if (oldItem.getSource().getType() == DataFile.FileType.XML_VALUES) {
if (!oldItem.compareValueWith(newItem)) {
- // if the values are different, take the values from the newItems
+ // if the values are different, take the values from the newItem
// and update the old item status.
oldItem.setValue(newItem);
}
+ } else {
+ oldItem.setTouched();
}
}
-
- // at this point oldItems is left with the deleted items.
- // just update their status to removed.
- for (ResourceItem deletedItem : oldItems.values()) {
- deletedItem.setRemoved();
- }
-
- // Now we need to add the new items to the resource file and the main map
- resourceFile.addItems(newItems.values());
- for (Map.Entry<String, ResourceItem> entry : newItems.entrySet()) {
- addItem(entry.getValue(), entry.getKey());
- }
}
- return true;
+ // at this point oldItems is left with the deleted items.
+ // just update their status to removed.
+ for (ResourceItem deletedItem : oldItems.values()) {
+ deletedItem.setRemoved();
+ }
+
+ // Now we need to add the new items to the resource file and the main map
+ resourceFile.addItems(addedItems.values());
+ for (Map.Entry<String, ResourceItem> entry : addedItems.entrySet()) {
+ addItem(entry.getValue(), entry.getKey());
+ }
}
/**
@@ -256,27 +385,40 @@
}
ResourceFile resourceFile = createResourceFile(file, folderData, logger);
- if (resourceFile != null) {
- processNewDataFile(sourceFolder, resourceFile, true /*setTouched*/);
- }
+ processNewResourceFile(sourceFolder, resourceFile);
}
}
}
- private static ResourceFile createResourceFile(@NonNull File file,
+ private void processNewResourceFile(File sourceFolder, ResourceFile resourceFile)
+ throws MergingException {
+ if (resourceFile != null) {
+ if (resourceFile.getType() == DataFile.FileType.GENERATED_FILES
+ && mGeneratedSet != null) {
+ mGeneratedSet.processNewDataFile(sourceFolder, resourceFile, true);
+ } else {
+ processNewDataFile(sourceFolder, resourceFile, true /*setTouched*/);
+ }
+ }
+ }
+
+ private ResourceFile createResourceFile(@NonNull File file,
@NonNull FolderData folderData, @NonNull ILogger logger) throws MergingException {
if (folderData.type != null) {
FileResourceNameValidator.validate(file, folderData.type);
- String name = file.getName();
- int pos = name.indexOf('.'); // get the resource name based on the filename
- if (pos >= 0) {
- name = name.substring(0, pos);
- }
+ String name = getNameForFile(file);
- return new ResourceFile(
- file,
- new ResourceItem(name, folderData.type, null),
- folderData.qualifiers);
+ if (needsPreprocessing(file)) {
+ return ResourceFile.generatedFiles(
+ file,
+ getResourceItemsForGeneratedFiles(file),
+ folderData.qualifiers);
+ } else {
+ return new ResourceFile(
+ file,
+ new ResourceItem(name, folderData.type, null),
+ folderData.qualifiers);
+ }
} else {
try {
ValueResourceParser2 parser = new ValueResourceParser2(file);
@@ -284,13 +426,51 @@
return new ResourceFile(file, items, folderData.qualifiers);
} catch (MergingException e) {
- e.setFile(file);
logger.error(e, "Failed to parse %s", file.getAbsolutePath());
throw e;
}
}
}
+ private boolean needsPreprocessing(@NonNull File file) {
+ return mPreprocessor != null && mPreprocessor.needsPreprocessing(file);
+ }
+
+ @NonNull
+ private List<ResourceItem> getResourceItemsForGeneratedFiles(
+ @NonNull File file)
+ throws MergingException {
+ List<ResourceItem> resourceItems = new ArrayList<ResourceItem>();
+
+ for (File generatedFile : mPreprocessor.getFilesToBeGenerated(file)) {
+ FolderData generatedFileFolderData =
+ getFolderData(generatedFile.getParentFile());
+
+ checkState(
+ generatedFileFolderData != null,
+ "Can't determine folder type for %s",
+ generatedFile.getPath());
+
+ resourceItems.add(
+ new GeneratedResourceItem(
+ getNameForFile(generatedFile),
+ generatedFile,
+ generatedFileFolderData.type,
+ generatedFileFolderData.qualifiers));
+ }
+ return resourceItems;
+ }
+
+ @NonNull
+ private static String getNameForFile(@NonNull File file) {
+ String name = file.getName();
+ int pos = name.indexOf('.'); // get the resource name based on the filename
+ if (pos >= 0) {
+ name = name.substring(0, pos);
+ }
+ return name;
+ }
+
/**
* temp structure containing a qualifier string and a {@link com.android.resources.ResourceType}.
*/
@@ -301,9 +481,11 @@
}
/**
- * Returns a FolderData for the given folder
+ * Returns a FolderData for the given folder.
+ *
* @param folder the folder.
- * @return the FolderData object.
+ * @return the FolderData object, or null if we can't determine the {#link ResourceFolderType}
+ * of the folder.
*/
@Nullable
private FolderData getFolderData(File folder) throws MergingException {
@@ -319,7 +501,8 @@
FolderConfiguration folderConfiguration = FolderConfiguration.getConfigForFolder(folderName);
if (folderConfiguration == null) {
- throw new MergingException("Invalid resource directory name").addFile(folder);
+ throw MergingException.withMessage("Invalid resource directory name")
+ .withFile(folder).build();
}
if (mNormalizeResources) {
@@ -341,4 +524,18 @@
return fd;
}
+
+ @Override
+ void appendToXml(@NonNull Node setNode, @NonNull Document document,
+ @NonNull MergeConsumer<ResourceItem> consumer) {
+ if (mGeneratedSet != null) {
+ NodeUtils.addAttribute(
+ document,
+ setNode,
+ null,
+ "generated-set",
+ mGeneratedSet.getConfigName());
+ }
+ super.appendToXml(setNode, document, consumer);
+ }
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceNameValidator.java b/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceNameValidator.java
index e8cb0c3..0639052 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceNameValidator.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceNameValidator.java
@@ -43,11 +43,7 @@
String error = getErrorText(resourceName, resourceType);
if (error != null) {
// TODO find location in file.
- if (file != null) {
- throw new MergingException(error).setFile(file);
- } else {
- throw new MergingException(error);
- }
+ throw MergingException.withMessage( error).withFile(file).build();
}
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceParser2.java b/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceParser2.java
index 58e8145..fdf46fd 100644
--- a/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceParser2.java
+++ b/sdk-common/src/main/java/com/android/ide/common/res2/ValueResourceParser2.java
@@ -27,6 +27,7 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.resources.ResourceType;
+import com.android.utils.PositionXmlParser;
import com.android.utils.XmlUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -37,9 +38,10 @@
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
+import java.io.BufferedInputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
@@ -99,18 +101,13 @@
ResourceItem resource = getResource(node, mFile);
if (resource != null) {
// check this is not a dup
- checkDuplicate(resource, map);
+ checkDuplicate(resource, map, mFile);
resources.add(resource);
if (resource.getType() == ResourceType.DECLARE_STYLEABLE) {
// Need to also create ATTR items for its children
- try {
- addStyleableItems(node, resources, map, mFile);
- } catch (MergingException e) {
- e.setFile(mFile);
- throw e;
- }
+ addStyleableItems(node, resources, map, mFile);
}
}
}
@@ -167,12 +164,11 @@
if (type != null) {
return type;
}
- throw new MergingException(String.format("Unsupported type '%s'", typeString))
- .addFileIfNonNull(from);
+ throw MergingException.withMessage("Unsupported type '%s'", typeString).withFile(from)
+ .build();
}
- throw new MergingException(String.format("Unsupported node '%s'", nodeName))
- .addFileIfNonNull(from);
+ throw MergingException.withMessage("Unsupported node '%s'", nodeName).withFile(from).build();
}
/**
@@ -199,15 +195,13 @@
@NonNull
static Document parseDocument(@NonNull File file) throws MergingException {
try {
- return XmlUtils.parseUtfXmlFile(file, true /*namespaceAware*/);
- } catch (SAXParseException e) {
- throw new MergingException(e).addFilePosition(file, e);
- } catch (ParserConfigurationException e) {
- throw new MergingException(e).addFile(file);
+ return PositionXmlParser.parse(new BufferedInputStream(new FileInputStream(file)));
} catch (SAXException e) {
- throw new MergingException(e).addFile(file);
+ throw MergingException.wrapException(e).withFile(file).build();
+ } catch (ParserConfigurationException e) {
+ throw MergingException.wrapException(e).withFile(file).build();
} catch (IOException e) {
- throw new MergingException(e).addFile(file);
+ throw MergingException.wrapException(e).withFile(file).build();
}
}
@@ -241,7 +235,7 @@
// is the attribute in the android namespace?
if (!resource.getName().startsWith(ANDROID_NS_NAME_PREFIX)) {
if (hasFormatAttribute(node) || XmlUtils.hasElementChildren(node)) {
- checkDuplicate(resource, map);
+ checkDuplicate(resource, map, from);
resource.setIgnoredFromDiskMerge(true);
list.add(resource);
}
@@ -251,7 +245,8 @@
}
private static void checkDuplicate(@NonNull ResourceItem resource,
- @Nullable Map<ResourceType, Set<String>> map)
+ @Nullable Map<ResourceType, Set<String>> map,
+ @Nullable File from)
throws MergingException {
if (map == null) {
return;
@@ -263,10 +258,10 @@
set = Sets.newHashSet(name);
map.put(resource.getType(), set);
} else {
- if (set.contains(name)) {
- throw new MergingException(String.format(
+ if (set.contains(name) && resource.getType() != ResourceType.PUBLIC) {
+ throw MergingException.withMessage(
"Found item %s/%s more than one time",
- resource.getType().getDisplayName(), name));
+ resource.getType().getDisplayName(), name).withFile(from).build();
}
set.add(name);
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/LocaleManager.java b/sdk-common/src/main/java/com/android/ide/common/resources/LocaleManager.java
index c895c3a..416ae39 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/LocaleManager.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/LocaleManager.java
@@ -1345,7 +1345,7 @@
"Haitian; Haitian Creole", // Code hat/ht
"Hausa", // Code hau/ha
"Hawaiian", // Code haw
- "Hebrew", // Code heb/he
+ "Hebrew", // Code heb/iw
"Herero", // Code her/hz
"Hiligaynon", // Code hil
"Himachali languages; Western Pahari languages",// Code him
@@ -1368,7 +1368,7 @@
"Iloko", // Code ilo
"Interlingua (International Auxiliary Language Association)",// Code ina/ia
"Indic languages", // Code inc
- "Indonesian", // Code ind/id
+ "Indonesian", // Code ind/in
"Indo-European languages", // Code ine
"Ingush", // Code inh
"Inupiaq", // Code ipk/ik
@@ -1684,7 +1684,7 @@
"Yao", // Code yao
"Yapese", // Code yap
"Yangben", // Code yav
- "Yiddish", // Code yid/yi
+ "Yiddish", // Code yid/ji
"Yoruba", // Code yor/yo
"Yupik languages", // Code ypk
"Zapotec", // Code zap
@@ -1762,9 +1762,9 @@
-1, -1, 47, -1, -1, -1, -1, 48, 43, -1, -1,
-1, -1, -1, -1, -1, 50, 49, 51, 54, -1, -1,
-1, -1, -1, -1, -1, 52, -1, 53, -1, -1, -1,
- 60, 55, -1, 56, 63, -1, -1, 57, -1, -1, 58,
+ 60, 55, -1, 75, 63, -1, -1, 57, -1, -1, 58,
59, -1, 61, -1, 62, -1, 67, 71, 68, -1, 74,
- 66, -1, 64, -1, 65, -1, -1, 69, -1, -1, 72,
+ 66, -1, 64, -1, 70, -1, -1, 69, -1, -1, 72,
73, 78, -1, -1, -1, 76, -1, -1, -1, -1, -1,
84, -1, 86, -1, 89, 79, 88, -1, 83, -1, -1,
-1, -1, -1, 85, -1, -1, 81, 139, 93, -1, -1,
@@ -1793,7 +1793,7 @@
-1, -1, 170, -1, -1, -1, -1, -1, 172, 173, -1,
-1, 174, 175, -1, 176, 177, 178, -1, -1, -1, -1,
-1, -1, -1, -1, 179, 180, -1, 181, -1, -1, -1,
- -1, 182, 183, -1, -1, -1, -1, -1, 184, 185, -1,
+ -1, 77, 183, -1, -1, -1, -1, -1, 184, 185, -1,
186, -1, -1
};
private static final String[] ISO_3166_2_CODES = new String[] {
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/MultiResourceFile.java b/sdk-common/src/main/java/com/android/ide/common/resources/MultiResourceFile.java
index 8531f3e..a1f053f 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/MultiResourceFile.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/MultiResourceFile.java
@@ -132,7 +132,7 @@
@Override
public boolean hasResources(ResourceType type) {
Map<String, ResourceValue> list = mResourceItems.get(type);
- return (list != null && list.size() > 0);
+ return (list != null && !list.isEmpty());
}
private void updateResourceItems(ScanningContext context) {
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItem.java b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItem.java
index bed1a5a..3059cb8 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItem.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItem.java
@@ -197,7 +197,7 @@
}
// We only want to return false if there's no default and more than 0 items.
- return (mFiles.size() == 0);
+ return (mFiles.isEmpty());
}
/**
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItemResolver.java b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItemResolver.java
index b3106a4..f218abe 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItemResolver.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceItemResolver.java
@@ -26,7 +26,6 @@
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.ide.common.res2.AbstractResourceRepository;
-import com.android.ide.common.res2.MergingException;
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.resources.ResourceType;
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceRepository.java b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceRepository.java
index d308751..237bdbf 100755
--- a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceRepository.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceRepository.java
@@ -515,7 +515,7 @@
ensureInitialized();
Map<String, ResourceItem> items = mResourceMap.get(type);
- return (items != null && items.size() > 0);
+ return (items != null && !items.isEmpty());
}
/**
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 a0749c8..23f471b 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
@@ -758,7 +758,7 @@
if (value instanceof StyleResourceValue) {
StyleResourceValue srv = (StyleResourceValue) value;
String name = srv.getName();
- if (name.startsWith(THEME_NAME_DOT) || name.equals(THEME_NAME)) {
+ if (srv.isFramework() && (name.equals(THEME_NAME) || name.startsWith(THEME_NAME_DOT))) {
if (cache != null) {
cache.put(value, true);
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceUrl.java b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceUrl.java
index 076d9f3..c1b92ce 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/ResourceUrl.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/ResourceUrl.java
@@ -47,14 +47,15 @@
public final boolean create;
/** Whether this is a theme resource reference */
- public boolean theme;
+ public final boolean theme;
private ResourceUrl(@NonNull ResourceType type, @NonNull String name,
- boolean framework, boolean create) {
+ boolean framework, boolean create, boolean theme) {
this.type = type;
this.name = name;
this.framework = framework;
this.create = create;
+ this.theme = theme;
}
/**
@@ -67,7 +68,7 @@
*/
public static ResourceUrl create(@NonNull ResourceType type, @NonNull String name,
boolean framework, boolean create) {
- return new ResourceUrl(type, name, framework, create);
+ return new ResourceUrl(type, name, framework, create, false);
}
public static ResourceUrl create(@NonNull ResourceValue value) {
@@ -82,33 +83,44 @@
*/
@Nullable
public static ResourceUrl parse(@NonNull String url) {
+ return parse(url, false);
+ }
+
+ /**
+ * Return the resource type of the given url, and the resource name.
+ *
+ * @param url the resource url to be parsed
+ * @param forceFramework force the returned value to be a framework resource.
+ * @return a pair of the resource type and the resource name
+ */
+ @Nullable
+ public static ResourceUrl parse(@NonNull String url, boolean forceFramework) {
+ boolean isTheme = false;
// Handle theme references
if (url.startsWith(PREFIX_THEME_REF)) {
+ isTheme = true;
String remainder = url.substring(PREFIX_THEME_REF.length());
if (url.startsWith(ATTR_REF_PREFIX)) {
url = PREFIX_RESOURCE_REF + url.substring(PREFIX_THEME_REF.length());
- return setTheme(parse(url));
- }
- int colon = url.indexOf(':');
- if (colon != -1) {
- // Convert from ?android:progressBarStyleBig to ?android:attr/progressBarStyleBig
- if (remainder.indexOf('/', colon) == -1) {
- remainder = remainder.substring(0, colon) + RESOURCE_CLZ_ATTR + '/'
- + remainder.substring(colon);
- }
- url = PREFIX_RESOURCE_REF + remainder;
- return setTheme(parse(url));
} else {
- int slash = url.indexOf('/');
- if (slash == -1) {
- url = PREFIX_RESOURCE_REF + RESOURCE_CLZ_ATTR + '/' + remainder;
- return setTheme(parse(url));
+ int colon = url.indexOf(':');
+ if (colon != -1) {
+ // Convert from ?android:progressBarStyleBig to ?android:attr/progressBarStyleBig
+ if (remainder.indexOf('/', colon) == -1) {
+ remainder = remainder.substring(0, colon) + RESOURCE_CLZ_ATTR + '/'
+ + remainder.substring(colon);
+ }
+ url = PREFIX_RESOURCE_REF + remainder;
+ } else {
+ int slash = url.indexOf('/');
+ if (slash == -1) {
+ url = PREFIX_RESOURCE_REF + RESOURCE_CLZ_ATTR + '/' + remainder;
+ }
}
}
}
- if (!url.startsWith(PREFIX_RESOURCE_REF) || url.equals(REFERENCE_NULL)
- || url.equals(REFERENCE_EMPTY) || url.equals(REFERENCE_UNDEFINED)) {
+ if (!url.startsWith(PREFIX_RESOURCE_REF) || isNullOrEmpty(url)) {
return null;
}
@@ -123,7 +135,7 @@
int typeBegin = create ? 2 : 1;
int colon = url.lastIndexOf(':', typeEnd);
- boolean framework = false;
+ boolean framework = forceFramework;
if (colon != -1) {
if (url.startsWith(ANDROID_NS_NAME, typeBegin)) {
framework = true;
@@ -136,16 +148,13 @@
return null;
}
String name = url.substring(nameBegin);
- return new ResourceUrl(type, name, framework, create);
+ return new ResourceUrl(type, name, framework, create, isTheme);
}
- /** Marks the given url, if any, as corresponding to a theme attribute */
- @Nullable
- private static ResourceUrl setTheme(@Nullable ResourceUrl url) {
- if (url != null) {
- url.theme = true;
- }
- return url;
+ /** Returns if the resource url is @null, @empty or @undefined. */
+ public static boolean isNullOrEmpty(@NonNull String url) {
+ return url.equals(REFERENCE_NULL) || url.equals(REFERENCE_EMPTY) ||
+ url.equals(REFERENCE_UNDEFINED);
}
/**
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/ValueResourceParser.java b/sdk-common/src/main/java/com/android/ide/common/resources/ValueResourceParser.java
index 936808d..19cc5fc 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/ValueResourceParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/ValueResourceParser.java
@@ -16,6 +16,16 @@
package com.android.ide.common.resources;
+import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX;
+import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX_LEN;
+import static com.android.SdkConstants.ATTR_NAME;
+import static com.android.SdkConstants.ATTR_PARENT;
+import static com.android.SdkConstants.ATTR_TYPE;
+import static com.android.SdkConstants.ATTR_VALUE;
+import static com.android.SdkConstants.TAG_ITEM;
+import static com.android.SdkConstants.TAG_RESOURCES;
+
+import com.android.ide.common.rendering.api.ArrayResourceValue;
import com.android.ide.common.rendering.api.AttrResourceValue;
import com.android.ide.common.rendering.api.DeclareStyleableResourceValue;
import com.android.ide.common.rendering.api.ItemResourceValue;
@@ -33,27 +43,17 @@
*/
public final class ValueResourceParser extends DefaultHandler {
- // TODO: reuse definitions from somewhere else.
- private static final String NODE_RESOURCES = "resources";
- private static final String NODE_ITEM = "item";
- private static final String ATTR_NAME = "name";
- private static final String ATTR_TYPE = "type";
- private static final String ATTR_PARENT = "parent";
- private static final String ATTR_VALUE = "value";
-
- private static final String DEFAULT_NS_PREFIX = "android:";
- private static final int DEFAULT_NS_PREFIX_LEN = DEFAULT_NS_PREFIX.length();
-
public interface IValueResourceRepository {
void addResourceValue(ResourceValue value);
boolean hasResourceValue(ResourceType type, String name);
}
- private boolean inResources = false;
- private int mDepth = 0;
- private ResourceValue mCurrentValue = null;
- private StyleResourceValue mCurrentStyle = null;
- private DeclareStyleableResourceValue mCurrentDeclareStyleable = null;
+ private boolean inResources;
+ private int mDepth;
+ private ResourceValue mCurrentValue;
+ private ArrayResourceValue mArrayResourceValue;
+ private StyleResourceValue mCurrentStyle;
+ private DeclareStyleableResourceValue mCurrentDeclareStyleable;
private AttrResourceValue mCurrentAttr;
private IValueResourceRepository mRepository;
private final boolean mIsFramework;
@@ -71,15 +71,20 @@
mCurrentValue.setValue(value);
}
- if (inResources && qName.equals(NODE_RESOURCES)) {
+ if (inResources && qName.equals(TAG_RESOURCES)) {
inResources = false;
} else if (mDepth == 2) {
mCurrentValue = null;
mCurrentStyle = null;
mCurrentDeclareStyleable = null;
mCurrentAttr = null;
+ mArrayResourceValue = null;
} else if (mDepth == 3) {
+ if (mArrayResourceValue != null && mCurrentValue != null) {
+ mArrayResourceValue.addElement(mCurrentValue.getValue());
+ }
mCurrentValue = null;
+ //noinspection VariableNotUsedInsideIf
if (mCurrentDeclareStyleable != null) {
mCurrentAttr = null;
}
@@ -94,11 +99,11 @@
throws SAXException {
try {
mDepth++;
- if (inResources == false && mDepth == 1) {
- if (qName.equals(NODE_RESOURCES)) {
+ if (!inResources && mDepth == 1) {
+ if (qName.equals(TAG_RESOURCES)) {
inResources = true;
}
- } else if (mDepth == 2 && inResources == true) {
+ } else if (mDepth == 2 && inResources) {
ResourceType type = getType(qName, attributes);
if (type != null) {
@@ -121,6 +126,10 @@
mCurrentAttr = new AttrResourceValue(type, name, mIsFramework);
mRepository.addResourceValue(mCurrentAttr);
break;
+ case ARRAY:
+ mArrayResourceValue = new ArrayResourceValue(name, mIsFramework);
+ mRepository.addResourceValue(mArrayResourceValue);
+ break;
default:
mCurrentValue = new ResourceValue(type, name, mIsFramework);
mRepository.addResourceValue(mCurrentValue);
@@ -136,8 +145,8 @@
if (mCurrentStyle != null) {
// is the attribute in the android namespace?
boolean isFrameworkAttr = mIsFramework;
- if (name.startsWith(DEFAULT_NS_PREFIX)) {
- name = name.substring(DEFAULT_NS_PREFIX_LEN);
+ if (name.startsWith(ANDROID_NS_NAME_PREFIX)) {
+ name = name.substring(ANDROID_NS_NAME_PREFIX_LEN);
isFrameworkAttr = true;
}
@@ -146,8 +155,8 @@
} else if (mCurrentDeclareStyleable != null) {
// is the attribute in the android namespace?
boolean isFramework = mIsFramework;
- if (name.startsWith(DEFAULT_NS_PREFIX)) {
- name = name.substring(DEFAULT_NS_PREFIX_LEN);
+ if (name.startsWith(ANDROID_NS_NAME_PREFIX)) {
+ name = name.substring(ANDROID_NS_NAME_PREFIX_LEN);
isFramework = true;
}
@@ -168,9 +177,13 @@
} catch (NumberFormatException e) {
// pass, we'll just ignore this value
}
-
}
- }
+ } else //noinspection VariableNotUsedInsideIf
+ if (mArrayResourceValue != null) {
+ // Create a temporary resource value to hold the item's value. The value is
+ // not added to the repository, since it's just a holder.
+ mCurrentValue = new ResourceValue(null, null, mIsFramework);
+ }
} else if (mDepth == 4 && mCurrentAttr != null) {
// get the enum/flag name
String name = attributes.getValue(ATTR_NAME);
@@ -189,22 +202,20 @@
}
}
- private ResourceType getType(String qName, Attributes attributes) {
+ private static ResourceType getType(String qName, Attributes attributes) {
String typeValue;
// if the node is <item>, we get the type from the attribute "type"
- if (NODE_ITEM.equals(qName)) {
+ if (TAG_ITEM.equals(qName)) {
typeValue = attributes.getValue(ATTR_TYPE);
} else {
// the type is the name of the node.
typeValue = qName;
}
- ResourceType type = ResourceType.getEnum(typeValue);
- return type;
+ return ResourceType.getEnum(typeValue);
}
-
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (mCurrentValue != null) {
@@ -216,6 +227,4 @@
}
}
}
-
-
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/DeviceConfigHelper.java b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/DeviceConfigHelper.java
index f3ba97d..217a1b8 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/DeviceConfigHelper.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/DeviceConfigHelper.java
@@ -18,6 +18,7 @@
import com.android.annotations.Nullable;
import com.android.resources.NightMode;
+import com.android.resources.ScreenRound;
import com.android.resources.UiMode;
import com.android.sdklib.devices.Device;
import com.android.sdklib.devices.Hardware;
@@ -58,6 +59,12 @@
config.setScreenSizeQualifier(new ScreenSizeQualifier(screen.getSize()));
config.setTextInputMethodQualifier(new TextInputMethodQualifier(hw.getKeyboard()));
config.setTouchTypeQualifier(new TouchScreenQualifier(screen.getMechanism()));
+ ScreenRound screenRound = screen.getScreenRound();
+ if (screenRound == null) {
+ // The default is not round.
+ screenRound = ScreenRound.NOTROUND;
+ }
+ config.setScreenRoundQualifier(new ScreenRoundQualifier(screenRound));
config.setKeyboardStateQualifier(new KeyboardStateQualifier(state.getKeyState()));
config.setNavigationStateQualifier(new NavigationStateQualifier(state.getNavState()));
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 1adad89..ccb66d0 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
@@ -53,7 +53,7 @@
public static final Splitter QUALIFIER_SPLITTER = Splitter.on('-');
- private final ResourceQualifier[] mQualifiers = new ResourceQualifier[INDEX_COUNT];
+ private final ResourceQualifier[] mQualifiers;
private static final int INDEX_COUNTRY_CODE = 0;
private static final int INDEX_NETWORK_CODE = 1;
@@ -64,19 +64,28 @@
private static final int INDEX_SCREEN_HEIGHT = 6;
private static final int INDEX_SCREEN_LAYOUT_SIZE = 7;
private static final int INDEX_SCREEN_RATIO = 8;
- private static final int INDEX_SCREEN_ORIENTATION = 9;
- private static final int INDEX_UI_MODE = 10;
- private static final int INDEX_NIGHT_MODE = 11;
- private static final int INDEX_PIXEL_DENSITY = 12;
- private static final int INDEX_TOUCH_TYPE = 13;
- private static final int INDEX_KEYBOARD_STATE = 14;
- private static final int INDEX_TEXT_INPUT_METHOD = 15;
- private static final int INDEX_NAVIGATION_STATE = 16;
- private static final int INDEX_NAVIGATION_METHOD = 17;
- private static final int INDEX_SCREEN_DIMENSION = 18;
- private static final int INDEX_VERSION = 19;
- private static final int INDEX_COUNT = 20;
+ private static final int INDEX_SCREEN_ROUND = 9;
+ private static final int INDEX_SCREEN_ORIENTATION = 10;
+ private static final int INDEX_UI_MODE = 11;
+ private static final int INDEX_NIGHT_MODE = 12;
+ private static final int INDEX_PIXEL_DENSITY = 13;
+ private static final int INDEX_TOUCH_TYPE = 14;
+ private static final int INDEX_KEYBOARD_STATE = 15;
+ private static final int INDEX_TEXT_INPUT_METHOD = 16;
+ private static final int INDEX_NAVIGATION_STATE = 17;
+ private static final int INDEX_NAVIGATION_METHOD = 18;
+ private static final int INDEX_SCREEN_DIMENSION = 19;
+ private static final int INDEX_VERSION = 20;
+ private static final int INDEX_COUNT = 21;
+ public FolderConfiguration() {
+ mQualifiers = new ResourceQualifier[INDEX_COUNT];
+ }
+
+ private FolderConfiguration(ResourceQualifier[] qualifiers) {
+ this();
+ System.arraycopy(qualifiers, 0, mQualifiers, 0, INDEX_COUNT);
+ }
/**
* Creates a {@link FolderConfiguration} matching the folder segments.
* @param folderSegments The segments of the folder name. The first segments should contain
@@ -302,6 +311,15 @@
}
/**
+ * Creates a copy of the given {@link FolderConfiguration}, that can be modified without
+ * affecting the original.
+ */
+ @NonNull
+ public static FolderConfiguration copyOf(@NonNull FolderConfiguration original) {
+ return new FolderConfiguration(original.mQualifiers);
+ }
+
+ /**
* Creates a {@link FolderConfiguration} matching the given qualifier string
* (just the qualifiers; e.g. for a folder like "values-en-rUS" this would be "en-rUS").
*
@@ -310,7 +328,11 @@
*/
@Nullable
public static FolderConfiguration getConfigForQualifierString(@NonNull String qualifierString) {
- return getConfigFromQualifiers(QUALIFIER_SPLITTER.split(qualifierString));
+ if (qualifierString.isEmpty()) {
+ return new FolderConfiguration();
+ } else {
+ return getConfigFromQualifiers(QUALIFIER_SPLITTER.split(qualifierString));
+ }
}
/**
@@ -432,6 +454,9 @@
} else if (qualifier instanceof ScreenRatioQualifier) {
mQualifiers[INDEX_SCREEN_RATIO] = qualifier;
+ } else if (qualifier instanceof ScreenRoundQualifier) {
+ mQualifiers[INDEX_SCREEN_ROUND] = qualifier;
+
} else if (qualifier instanceof ScreenOrientationQualifier) {
mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier;
@@ -573,6 +598,15 @@
return (ScreenRatioQualifier)mQualifiers[INDEX_SCREEN_RATIO];
}
+ public void setScreenRoundQualifier(ScreenRoundQualifier qualifier) {
+ mQualifiers[INDEX_SCREEN_ROUND] = qualifier;
+ }
+
+ @Nullable
+ public ScreenRoundQualifier getScreenRoundQualifier() {
+ return (ScreenRoundQualifier)mQualifiers[INDEX_SCREEN_ROUND];
+ }
+
public void setScreenOrientationQualifier(ScreenOrientationQualifier qualifier) {
mQualifiers[INDEX_SCREEN_ORIENTATION] = qualifier;
}
@@ -1124,6 +1158,7 @@
mQualifiers[INDEX_SCREEN_HEIGHT] = new ScreenHeightQualifier();
mQualifiers[INDEX_SCREEN_LAYOUT_SIZE] = new ScreenSizeQualifier();
mQualifiers[INDEX_SCREEN_RATIO] = new ScreenRatioQualifier();
+ mQualifiers[INDEX_SCREEN_ROUND] = new ScreenRoundQualifier();
mQualifiers[INDEX_SCREEN_ORIENTATION] = new ScreenOrientationQualifier();
mQualifiers[INDEX_UI_MODE] = new UiModeQualifier();
mQualifiers[INDEX_NIGHT_MODE] = new NightModeQualifier();
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/LocaleQualifier.java b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/LocaleQualifier.java
index 454a956..26fddd0 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/LocaleQualifier.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/LocaleQualifier.java
@@ -387,7 +387,11 @@
return false;
}
- public void setRegionSegment(@NonNull String segment) {
+ /**
+ * Used only when constructing the qualifier, don't use after it's been assigned to a
+ * {@link FolderConfiguration}.
+ */
+ void setRegionSegment(@NonNull String segment) {
assert segment.length() == 3 : segment;
mRegion = new String(new char[] {
Character.toUpperCase(segment.charAt(1)),
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/ResourceQualifier.java b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/ResourceQualifier.java
index fb43de7..c4ab88f 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/ResourceQualifier.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/ResourceQualifier.java
@@ -95,7 +95,7 @@
public boolean isBetterMatchThan(ResourceQualifier compareTo, ResourceQualifier reference) {
// the default is to always return false. This gives less overhead than always returning
// true, as it would only compare same values anyway.
- return false;
+ return compareTo == null;
}
@Override
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/ScreenRoundQualifier.java b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/ScreenRoundQualifier.java
new file mode 100644
index 0000000..936f835
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/ScreenRoundQualifier.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.resources.configuration;
+
+import com.android.annotations.Nullable;
+import com.android.resources.ResourceEnum;
+import com.android.resources.ScreenRound;
+
+public class ScreenRoundQualifier extends EnumBasedResourceQualifier {
+
+ public static final String NAME = "Screen Roundness";
+
+ @Nullable
+ private ScreenRound mValue = null;
+
+ public ScreenRoundQualifier() {
+ }
+
+ public ScreenRoundQualifier(@Nullable ScreenRound value) {
+ mValue = value;
+ }
+
+ public ScreenRound getValue() {
+ return mValue;
+ }
+
+ @Override
+ ResourceEnum getEnumValue() {
+ return mValue;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public String getShortName() {
+ return "Roundness";
+ }
+
+ @Override
+ public int since() {
+ return 23;
+ }
+
+ @Override
+ public boolean checkAndSet(String value, FolderConfiguration config) {
+ ScreenRound roundness = ScreenRound.getEnum(value);
+ if (roundness != null) {
+ ScreenRoundQualifier qualifier = new ScreenRoundQualifier(roundness);
+ config.setScreenRoundQualifier(qualifier);
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/VersionQualifier.java b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/VersionQualifier.java
index 1626b23..87d23a1 100644
--- a/sdk-common/src/main/java/com/android/ide/common/resources/configuration/VersionQualifier.java
+++ b/sdk-common/src/main/java/com/android/ide/common/resources/configuration/VersionQualifier.java
@@ -23,7 +23,7 @@
* Resource Qualifier for Platform Version.
*/
public final class VersionQualifier extends ResourceQualifier {
- /** Default pixel density value. This means the property is not set. */
+ /** Default version. This means the property is not set. */
private static final int DEFAULT_VERSION = -1;
private static final Pattern sVersionPattern = Pattern.compile("^v(\\d+)$");//$NON-NLS-1$
diff --git a/sdk-common/src/main/java/com/android/ide/common/signing/KeystoreHelper.java b/sdk-common/src/main/java/com/android/ide/common/signing/KeystoreHelper.java
index 4da72c5..6ecf95e 100644
--- a/sdk-common/src/main/java/com/android/ide/common/signing/KeystoreHelper.java
+++ b/sdk-common/src/main/java/com/android/ide/common/signing/KeystoreHelper.java
@@ -107,7 +107,7 @@
String javaHome = System.getProperty("java.home");
- if (javaHome != null && javaHome.length() > 0) {
+ if (javaHome != null && !javaHome.isEmpty()) {
keytoolCommand = javaHome + File.separator + "bin" + File.separator + keytoolCommand;
}
@@ -192,14 +192,11 @@
/**
* Returns the CertificateInfo for the given signing configuration.
*
- * Returns null if the key could not be found. If the passwords are wrong,
- * it throws an exception
- *
- * @param signingConfig the signing configuration
* @return the certificate info if it could be loaded.
- * @throws KeytoolException
- * @throws FileNotFoundException
+ * @throws KeytoolException If the password is wrong.
+ * @throws FileNotFoundException If the store file cannot be found.
*/
+ @NonNull
public static CertificateInfo getCertificateInfo(@Nullable String storeType, @NonNull File storeFile,
@NonNull String storePassword, @NonNull String keyPassword,
@NonNull String keyAlias)
@@ -210,19 +207,24 @@
storeType : KeyStore.getDefaultType());
FileInputStream fis = new FileInputStream(storeFile);
- //noinspection ConstantConditions
keyStore.load(fis, storePassword.toCharArray());
fis.close();
- //noinspection ConstantConditions
char[] keyPasswordArray = keyPassword.toCharArray();
PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(
keyAlias, new KeyStore.PasswordProtection(keyPasswordArray));
- if (entry != null) {
- return new CertificateInfo(entry.getPrivateKey(),
- (X509Certificate) entry.getCertificate());
+ if (entry == null) {
+ throw new KeytoolException(
+ String.format(
+ "No key with alias '%1$s' found in keystore %2$s",
+ keyAlias,
+ storeFile.getAbsolutePath()));
}
+
+ return new CertificateInfo(
+ entry.getPrivateKey(),
+ (X509Certificate) entry.getCertificate());
} catch (FileNotFoundException e) {
throw e;
} catch (Exception e) {
@@ -231,7 +233,5 @@
keyAlias, storeFile, e.getMessage()),
e);
}
-
- return null;
}
}
diff --git a/sdk-common/src/main/java/com/android/ide/common/util/AssetUtil.java b/sdk-common/src/main/java/com/android/ide/common/util/AssetUtil.java
new file mode 100644
index 0000000..3c1b560
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/util/AssetUtil.java
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.util;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Paint;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ConvolveOp;
+import java.awt.image.Kernel;
+import java.awt.image.Raster;
+import java.awt.image.RescaleOp;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A set of utility classes for manipulating {@link BufferedImage} objects and drawing them to
+ * {@link Graphics2D} canvases.
+ */
+public class AssetUtil {
+ /**
+ * Scales the given rectangle by the given scale factor.
+ *
+ * @param rect The rectangle to scale.
+ * @param scaleFactor The factor to scale by.
+ * @return The scaled rectangle.
+ */
+ public static Rectangle scaleRectangle(Rectangle rect, float scaleFactor) {
+ return new Rectangle(
+ Math.round(rect.x * scaleFactor),
+ Math.round(rect.y * scaleFactor),
+ Math.round(rect.width * scaleFactor),
+ Math.round(rect.height * scaleFactor));
+ }
+
+ /**
+ * Creates a new ARGB {@link BufferedImage} of the given width and height.
+ *
+ * @param width The width of the new image.
+ * @param height The height of the new image.
+ * @return The newly created image.
+ */
+ public static BufferedImage newArgbBufferedImage(int width, int height) {
+ return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ }
+
+ /**
+ * Smoothly scales the given {@link BufferedImage} to the given width and height using the
+ * {@link Image#SCALE_SMOOTH} algorithm (generally bicubic resampling or bilinear filtering).
+ *
+ * @param source The source image.
+ * @param width The destination width to scale to.
+ * @param height The destination height to scale to.
+ * @return A new, scaled image.
+ */
+ public static BufferedImage scaledImage(BufferedImage source, int width, int height) {
+ Image scaledImage = source.getScaledInstance(width, height, Image.SCALE_SMOOTH);
+ BufferedImage scaledBufImage = new BufferedImage(width, height,
+ BufferedImage.TYPE_INT_ARGB);
+ Graphics g = scaledBufImage.createGraphics();
+ g.drawImage(scaledImage, 0, 0, null);
+ g.dispose();
+ return scaledBufImage;
+ }
+
+ /**
+ * Applies a gaussian blur of the given radius to the given {@link BufferedImage} using a kernel
+ * convolution.
+ *
+ * @param source The source image.
+ * @param radius The blur radius, in pixels.
+ * @return A new, blurred image, or the source image if no blur is performed.
+ */
+ public static BufferedImage blurredImage(BufferedImage source, double radius) {
+ if (radius == 0) {
+ return source;
+ }
+
+ final int r = (int) Math.ceil(radius);
+ final int rows = r * 2 + 1;
+ final float[] kernelData = new float[rows * rows];
+
+ final double sigma = radius / 3;
+ final double sigma22 = 2 * sigma * sigma;
+ final double sqrtPiSigma22 = Math.sqrt(Math.PI * sigma22);
+ final double radius2 = radius * radius;
+
+ double total = 0;
+ int index = 0;
+ double distance2;
+
+ int x, y;
+ for (y = -r; y <= r; y++) {
+ for (x = -r; x <= r; x++) {
+ distance2 = 1.0 * x * x + 1.0 * y * y;
+ if (distance2 > radius2) {
+ kernelData[index] = 0;
+ } else {
+ kernelData[index] = (float) (Math.exp(-distance2 / sigma22) / sqrtPiSigma22);
+ }
+ total += kernelData[index];
+ ++index;
+ }
+ }
+
+ for (index = 0; index < kernelData.length; index++) {
+ kernelData[index] /= total;
+ }
+
+ // We first pad the image so the kernel can operate at the edges.
+ BufferedImage paddedSource = paddedImage(source, r);
+ BufferedImage blurredPaddedImage = operatedImage(paddedSource, new ConvolveOp(
+ new Kernel(rows, rows, kernelData), ConvolveOp.EDGE_ZERO_FILL, null));
+ return blurredPaddedImage.getSubimage(r, r, source.getWidth(), source.getHeight());
+ }
+
+ /**
+ * Inverts the alpha channel of the given {@link BufferedImage}. RGB data for the inverted area
+ * are undefined, so it's generally best to fill the resulting image with a color.
+ *
+ * @param source The source image.
+ * @return A new image with an alpha channel inverted from the original.
+ */
+ public static BufferedImage invertedAlphaImage(BufferedImage source) {
+ final float[] scaleFactors = new float[]{1, 1, 1, -1};
+ final float[] offsets = new float[]{0, 0, 0, 255};
+
+ return operatedImage(source, new RescaleOp(scaleFactors, offsets, null));
+ }
+
+ /**
+ * Applies a {@link BufferedImageOp} on the given {@link BufferedImage}.
+ *
+ * @param source The source image.
+ * @param op The operation to perform.
+ * @return A new image with the operation performed.
+ */
+ public static BufferedImage operatedImage(BufferedImage source, BufferedImageOp op) {
+ BufferedImage newImage = newArgbBufferedImage(source.getWidth(), source.getHeight());
+ Graphics2D g = (Graphics2D) newImage.getGraphics();
+ g.drawImage(source, op, 0, 0);
+ return newImage;
+ }
+
+ /**
+ * Fills the given {@link BufferedImage} with a {@link Paint}, preserving its alpha channel.
+ *
+ * @param source The source image.
+ * @param paint The paint to fill with.
+ * @return A new, painted/filled image.
+ */
+ public static BufferedImage filledImage(BufferedImage source, Paint paint) {
+ BufferedImage newImage = newArgbBufferedImage(source.getWidth(), source.getHeight());
+ Graphics2D g = (Graphics2D) newImage.getGraphics();
+ g.drawImage(source, 0, 0, null);
+ g.setComposite(AlphaComposite.SrcAtop);
+ g.setPaint(paint);
+ g.fillRect(0, 0, source.getWidth(), source.getHeight());
+ return newImage;
+ }
+
+ /**
+ * Pads the given {@link BufferedImage} on all sides by the given padding amount.
+ *
+ * @param source The source image.
+ * @param padding The amount to pad on all sides, in pixels.
+ * @return A new, padded image, or the source image if no padding is performed.
+ */
+ public static BufferedImage paddedImage(BufferedImage source, int padding) {
+ if (padding == 0) {
+ return source;
+ }
+
+ BufferedImage newImage = newArgbBufferedImage(
+ source.getWidth() + padding * 2, source.getHeight() + padding * 2);
+ Graphics2D g = (Graphics2D) newImage.getGraphics();
+ g.drawImage(source, padding, padding, null);
+ return newImage;
+ }
+
+ /**
+ * Trims the transparent pixels from the given {@link BufferedImage} (returns a sub-image).
+ *
+ * @param source The source image.
+ * @return A new, trimmed image, or the source image if no trim is performed.
+ */
+ public static BufferedImage trimmedImage(BufferedImage source) {
+ final int minAlpha = 1;
+ final int srcWidth = source.getWidth();
+ final int srcHeight = source.getHeight();
+ Raster raster = source.getRaster();
+ int l = srcWidth, t = srcHeight, r = 0, b = 0;
+
+ int alpha, x, y;
+ int[] pixel = new int[4];
+ for (y = 0; y < srcHeight; y++) {
+ for (x = 0; x < srcWidth; x++) {
+ raster.getPixel(x, y, pixel);
+ alpha = pixel[3];
+ if (alpha >= minAlpha) {
+ l = Math.min(x, l);
+ t = Math.min(y, t);
+ r = Math.max(x, r);
+ b = Math.max(y, b);
+ }
+ }
+ }
+
+ if (l > r || t > b) {
+ // No pixels, couldn't trim
+ return source;
+ }
+
+ return source.getSubimage(l, t, r - l + 1, b - t + 1);
+ }
+
+ /**
+ * Draws the given {@link BufferedImage} to the canvas, at the given coordinates, with the given
+ * {@link Effect}s applied. Note that drawn effects may be outside the bounds of the source
+ * image.
+ *
+ * @param g The destination canvas.
+ * @param source The source image.
+ * @param x The x offset at which to draw the image.
+ * @param y The y offset at which to draw the image.
+ * @param effects The list of effects to apply.
+ */
+ public static void drawEffects(Graphics2D g, BufferedImage source, int x, int y,
+ Effect[] effects) {
+ List<ShadowEffect> shadowEffects = new ArrayList<ShadowEffect>();
+ List<FillEffect> fillEffects = new ArrayList<FillEffect>();
+
+ for (Effect effect : effects) {
+ if (effect instanceof ShadowEffect) {
+ shadowEffects.add((ShadowEffect) effect);
+ } else if (effect instanceof FillEffect) {
+ fillEffects.add((FillEffect) effect);
+ }
+ }
+
+ Composite oldComposite = g.getComposite();
+ for (ShadowEffect effect : shadowEffects) {
+ if (effect.inner) {
+ continue;
+ }
+
+ // Outer shadow
+ g.setComposite(AlphaComposite.getInstance(
+ AlphaComposite.SRC_OVER, (float) effect.opacity));
+ g.drawImage(
+ filledImage(
+ blurredImage(source, effect.radius),
+ effect.color),
+ (int) effect.xOffset, (int) effect.yOffset, null);
+ }
+ g.setComposite(oldComposite);
+
+ // Inner shadow & fill effects.
+ final Rectangle imageRect = new Rectangle(0, 0, source.getWidth(), source.getHeight());
+ BufferedImage out = newArgbBufferedImage(imageRect.width, imageRect.height);
+ Graphics2D g2 = (Graphics2D) out.getGraphics();
+ double fillOpacity = 1.0;
+
+ g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
+ g2.drawImage(source, 0, 0, null);
+ g2.setComposite(AlphaComposite.SrcAtop);
+
+ // Gradient fill
+ for (FillEffect effect : fillEffects) {
+ g2.setPaint(effect.paint);
+ g2.fillRect(0, 0, imageRect.width, imageRect.height);
+ fillOpacity = Math.max(0, Math.min(1, effect.opacity));
+ }
+
+ // Inner shadows
+ for (ShadowEffect effect : shadowEffects) {
+ if (!effect.inner) {
+ continue;
+ }
+
+ BufferedImage innerShadowImage = newArgbBufferedImage(
+ imageRect.width, imageRect.height);
+ Graphics2D g3 = (Graphics2D) innerShadowImage.getGraphics();
+ g3.drawImage(source, (int) effect.xOffset, (int) effect.yOffset, null);
+ g2.setComposite(AlphaComposite.getInstance(
+ AlphaComposite.SRC_ATOP, (float) effect.opacity));
+ g2.drawImage(
+ filledImage(
+ blurredImage(invertedAlphaImage(innerShadowImage), effect.radius),
+ effect.color),
+ 0, 0, null);
+ }
+
+ g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) fillOpacity));
+ g.drawImage(out, x, y, null);
+ g.setComposite(oldComposite);
+ }
+
+ /**
+ * Draws the given {@link BufferedImage} to the canvas, centered, wholly contained within the
+ * bounds defined by the destination rectangle, and with preserved aspect ratio.
+ *
+ * @param g The destination canvas.
+ * @param source The source image.
+ * @param dstRect The destination rectangle in the destination canvas into which to draw the
+ * image.
+ */
+ public static void drawCenterInside(Graphics2D g, BufferedImage source, Rectangle dstRect) {
+ final int srcWidth = source.getWidth();
+ final int srcHeight = source.getHeight();
+ if (srcWidth * 1.0 / srcHeight > dstRect.width * 1.0 / dstRect.height) {
+ final int scaledWidth = Math.max(1, dstRect.width);
+ final int scaledHeight = Math.max(1, dstRect.width * srcHeight / srcWidth);
+ Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
+ g.drawImage(scaledImage,
+ dstRect.x,
+ dstRect.y + (dstRect.height - scaledHeight) / 2,
+ dstRect.x + dstRect.width,
+ dstRect.y + (dstRect.height - scaledHeight) / 2 + scaledHeight,
+ 0,
+ 0,
+ 0 + scaledWidth,
+ 0 + scaledHeight,
+ null);
+ } else {
+ final int scaledWidth = Math.max(1, dstRect.height * srcWidth / srcHeight);
+ final int scaledHeight = Math.max(1, dstRect.height);
+ Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
+ g.drawImage(scaledImage,
+ dstRect.x + (dstRect.width - scaledWidth) / 2,
+ dstRect.y,
+ dstRect.x + (dstRect.width - scaledWidth) / 2 + scaledWidth,
+ dstRect.y + dstRect.height,
+ 0,
+ 0,
+ 0 + scaledWidth,
+ 0 + scaledHeight,
+ null);
+ }
+ }
+
+ /**
+ * Draws the given {@link BufferedImage} to the canvas, centered and cropped to fill the
+ * bounds defined by the destination rectangle, and with preserved aspect ratio.
+ *
+ * @param g The destination canvas.
+ * @param source The source image.
+ * @param dstRect The destination rectangle in the destination canvas into which to draw the
+ * image.
+ */
+ public static void drawCenterCrop(Graphics2D g, BufferedImage source, Rectangle dstRect) {
+ final int srcWidth = source.getWidth();
+ final int srcHeight = source.getHeight();
+ if (srcWidth * 1.0 / srcHeight > dstRect.width * 1.0 / dstRect.height) {
+ final int scaledWidth = dstRect.height * srcWidth / srcHeight;
+ final int scaledHeight = dstRect.height;
+ Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
+ g.drawImage(scaledImage,
+ dstRect.x,
+ dstRect.y,
+ dstRect.x + dstRect.width,
+ dstRect.y + dstRect.height,
+ 0 + (scaledWidth - dstRect.width) / 2,
+ 0,
+ 0 + (scaledWidth - dstRect.width) / 2 + dstRect.width,
+ 0 + dstRect.height,
+ null);
+ } else {
+ final int scaledWidth = dstRect.width;
+ final int scaledHeight = dstRect.width * srcHeight / srcWidth;
+ Image scaledImage = scaledImage(source, scaledWidth, scaledHeight);
+ g.drawImage(scaledImage,
+ dstRect.x,
+ dstRect.y,
+ dstRect.x + dstRect.width,
+ dstRect.y + dstRect.height,
+ 0,
+ 0 + (scaledHeight - dstRect.height) / 2,
+ 0 + dstRect.width,
+ 0 + (scaledHeight - dstRect.height) / 2 + dstRect.height,
+ null);
+ }
+ }
+
+ /**
+ * An effect to apply in
+ * {@link AssetUtil#drawEffects(java.awt.Graphics2D, java.awt.image.BufferedImage, int, int, AssetUtil.Effect[])}
+ */
+ public abstract static class Effect {
+ }
+
+ /**
+ * An inner or outer shadow.
+ */
+ public static class ShadowEffect extends Effect {
+ public double xOffset;
+ public double yOffset;
+ public double radius;
+ public Color color;
+ public double opacity;
+ public boolean inner;
+
+ public ShadowEffect(double xOffset, double yOffset, double radius, Color color,
+ double opacity, boolean inner) {
+ this.xOffset = xOffset;
+ this.yOffset = yOffset;
+ this.radius = radius;
+ this.color = color;
+ this.opacity = opacity;
+ this.inner = inner;
+ }
+ }
+
+ /**
+ * A fill, defined by a paint.
+ */
+ public static class FillEffect extends Effect {
+ public Paint paint;
+ public double opacity;
+
+ public FillEffect(Paint paint, double opacity) {
+ this.paint = paint;
+ this.opacity = opacity;
+ }
+
+ public FillEffect(Paint paint) {
+ this.paint = paint;
+ this.opacity = 1.0;
+ }
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/PathBuilder.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/PathBuilder.java
new file mode 100644
index 0000000..2a814f4
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/PathBuilder.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.common.vectordrawable;
+
+/**
+ * Build a string for Svg file's path data.
+ */
+class PathBuilder {
+ private StringBuilder mPathData = new StringBuilder();
+
+ private String booleanToString(boolean flag) {
+ return flag ? "1" : "0";
+ }
+
+ public PathBuilder absoluteMoveTo(float x, float y) {
+ mPathData.append("M"+ x + "," + y);
+ return this;
+ }
+
+ public PathBuilder relativeMoveTo(float x, float y) {
+ mPathData.append("m"+ x + "," + y);
+ return this;
+ }
+
+ public PathBuilder absoluteLineTo(float x, float y) {
+ mPathData.append("L"+ x + "," + y);
+ return this;
+ }
+
+ public PathBuilder relativeLineTo(float x, float y) {
+ mPathData.append("l"+ x + "," + y);
+ return this;
+ }
+
+ public PathBuilder absoluteVerticalTo(float v) {
+ mPathData.append("V"+ v);
+ return this;
+ }
+
+ public PathBuilder relativeVerticalTo(float v) {
+ mPathData.append("v"+ v);
+ return this;
+ }
+
+ public PathBuilder absoluteHorizontalTo(float h) {
+ mPathData.append("H"+ h);
+ return this;
+ }
+
+ public PathBuilder relativeHorizontalTo(float h) {
+ mPathData.append("h"+ h);
+ return this;
+ }
+
+ public PathBuilder absoluteArcTo(float rx, float ry, boolean rotation,
+ boolean largeArc, boolean sweep, float x, float y) {
+ mPathData.append("A"+ rx + "," + ry + "," + booleanToString(rotation) + "," +
+ booleanToString(largeArc) + "," + booleanToString(sweep) + "," + x + "," + y );
+ return this;
+ }
+
+ public PathBuilder relativeArcTo(float rx, float ry, boolean rotation,
+ boolean largeArc, boolean sweep, float x, float y) {
+ mPathData.append("a"+ rx + "," + ry + "," + booleanToString(rotation) + "," +
+ booleanToString(largeArc) + "," + booleanToString(sweep) + "," + x + "," + y );
+ return this;
+ }
+
+ public PathBuilder absoluteClose() {
+ mPathData.append("Z");
+ return this;
+ }
+
+ public PathBuilder relativeClose() {
+ mPathData.append("z");
+ return this;
+ }
+
+ public String toString() {
+ return mPathData.toString();
+ }
+}
\ No newline at end of file
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/Svg2Vector.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/Svg2Vector.java
new file mode 100644
index 0000000..c3a1512
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/Svg2Vector.java
@@ -0,0 +1,658 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+import com.android.annotations.NonNull;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.*;
+import java.util.HashSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Converts SVG to VectorDrawable's XML
+ */
+public class Svg2Vector {
+ private static Logger logger = Logger.getLogger(Svg2Vector.class.getSimpleName());
+
+ public static final String SVG_POLYGON = "polygon";
+ public static final String SVG_RECT = "rect";
+ public static final String SVG_CIRCLE = "circle";
+ public static final String SVG_LINE = "line";
+ public static final String SVG_PATH = "path";
+ public static final String SVG_GROUP = "g";
+ public static final String SVG_TRANSFORM = "transform";
+ public static final String SVG_WIDTH = "width";
+ public static final String SVG_HEIGHT = "height";
+ public static final String SVG_VIEW_BOX = "viewBox";
+ public static final String SVG_STYLE = "style";
+ public static final String SVG_DISPLAY = "display";
+
+ public static final String SVG_D = "d";
+ public static final String SVG_STROKE_COLOR = "stroke";
+ public static final String SVG_STROKE_OPACITY = "stroke-opacity";
+ public static final String SVG_STROKE_LINEJOINE = "stroke-linejoin";
+ public static final String SVG_STROKE_LINECAP = "stroke-linecap";
+ public static final String SVG_STROKE_WIDTH = "stroke-width";
+ public static final String SVG_FILL_COLOR = "fill";
+ public static final String SVG_FILL_OPACITY = "fill-opacity";
+ public static final String SVG_OPACITY = "opacity";
+ public static final String SVG_CLIP = "clip";
+ public static final String SVG_POINTS = "points";
+
+ public static final ImmutableMap<String, String> presentationMap =
+ ImmutableMap.<String, String>builder()
+ .put(SVG_STROKE_COLOR, "android:strokeColor")
+ .put(SVG_STROKE_OPACITY, "android:strokeAlpha")
+ .put(SVG_STROKE_LINEJOINE, "android:strokeLinejoin")
+ .put(SVG_STROKE_LINECAP, "android:strokeLinecap")
+ .put(SVG_STROKE_WIDTH, "android:strokeWidth")
+ .put(SVG_FILL_COLOR, "android:fillColor")
+ .put(SVG_FILL_OPACITY, "android:fillAlpha")
+ .put(SVG_CLIP, "android:clip").put(SVG_OPACITY, "android:fillAlpha")
+ .build();
+
+ // List all the Svg nodes that we don't support. Categorized by the types.
+ private static final HashSet<String> unsupportedSvgNodes = Sets.newHashSet(
+ // Animation elements
+ "animate", "animateColor", "animateMotion", "animateTransform", "mpath", "set",
+ // Container elements
+ "a", "defs", "glyph", "marker", "mask", "missing-glyph", "pattern", "switch", "symbol",
+ // Filter primitive elements
+ "feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix",
+ "feDiffuseLighting", "feDisplacementMap", "feFlood", "feFuncA", "feFuncB", "feFuncG",
+ "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology",
+ "feOffset", "feSpecularLighting", "feTile", "feTurbulence",
+ // Font elements
+ "font", "font-face", "font-face-format", "font-face-name", "font-face-src", "font-face-uri",
+ "hkern", "vkern",
+ // Gradient elements
+ "linearGradient", "radialGradient", "stop",
+ // Graphics elements
+ "ellipse", "polyline", "text", "use",
+ // Light source elements
+ "feDistantLight", "fePointLight", "feSpotLight",
+ // Structural elements
+ "defs", "symbol", "use",
+ // Text content elements
+ "altGlyph", "altGlyphDef", "altGlyphItem", "glyph", "glyphRef", "textPath", "text", "tref",
+ "tspan",
+ // Text content child elements
+ "altGlyph", "textPath", "tref", "tspan",
+ // Uncategorized elements
+ "clipPath", "color-profile", "cursor", "filter", "foreignObject", "script", "view");
+
+ @NonNull
+ private static SvgTree parse(File f) throws Exception {
+ SvgTree svgTree = new SvgTree();
+ Document doc = svgTree.parse(f);
+ NodeList nSvgNode;
+
+ // Parse svg elements
+ nSvgNode = doc.getElementsByTagName("svg");
+ if (nSvgNode.getLength() != 1) {
+ throw new IllegalStateException("Not a proper SVG file");
+ }
+ Node rootNode = nSvgNode.item(0);
+ for (int i = 0; i < nSvgNode.getLength(); i++) {
+ Node nNode = nSvgNode.item(i);
+ if (nNode.getNodeType() == Node.ELEMENT_NODE) {
+ parseDimension(svgTree, nNode);
+ }
+ }
+
+ if (svgTree.viewBox == null) {
+ svgTree.logErrorLine("Missing \"viewBox\" in <svg> element", rootNode, SvgTree.SvgLogLevel.ERROR);
+ return svgTree;
+ }
+
+ if ((svgTree.w == 0 || svgTree.h == 0) && svgTree.viewBox[2] > 0 && svgTree.viewBox[3] > 0) {
+ svgTree.w = svgTree.viewBox[2];
+ svgTree.h = svgTree.viewBox[3];
+ }
+
+ // Parse transformation information.
+ // TODO: Properly handle transformation in the group level. In the "use" case, we treat
+ // it as global for now.
+ NodeList nUseTags;
+ svgTree.matrix = new float[6];
+ svgTree.matrix[0] = 1;
+ svgTree.matrix[3] = 1;
+
+ nUseTags = doc.getElementsByTagName("use");
+ for (int temp = 0; temp < nUseTags.getLength(); temp++) {
+ Node nNode = nUseTags.item(temp);
+ if (nNode.getNodeType() == Node.ELEMENT_NODE) {
+ parseTransformation(svgTree, nNode);
+ }
+ }
+
+ SvgGroupNode root = new SvgGroupNode(svgTree, rootNode, "root");
+ svgTree.setRoot(root);
+
+ // Parse all the group and path node recursively.
+ traverseSVGAndExtract(svgTree, root, rootNode);
+
+ svgTree.dump(root);
+
+ return svgTree;
+ }
+
+ private static void traverseSVGAndExtract(SvgTree svgTree, SvgGroupNode currentGroup, Node item) {
+ // Recursively traverse all the group and path nodes
+ NodeList allChildren = item.getChildNodes();
+
+ for (int i = 0; i < allChildren.getLength(); i++) {
+ Node currentNode = allChildren.item(i);
+ String nodeName = currentNode.getNodeName();
+ if (SVG_PATH.equals(nodeName) ||
+ SVG_RECT.equals(nodeName) ||
+ SVG_CIRCLE.equals(nodeName) ||
+ SVG_POLYGON.equals(nodeName) ||
+ SVG_LINE.equals(nodeName)) {
+ SvgLeafNode child = new SvgLeafNode(svgTree, currentNode, nodeName + i);
+
+ extractAllItemsAs(svgTree, child, currentNode);
+
+ currentGroup.addChild(child);
+ } else if (SVG_GROUP.equals(nodeName)) {
+ SvgGroupNode childGroup = new SvgGroupNode(svgTree, currentNode, "child" + i);
+ currentGroup.addChild(childGroup);
+ traverseSVGAndExtract(svgTree, childGroup, currentNode);
+ } else {
+ // For other fancy tags, like <refs>, they can contain children too.
+ // Report the unsupported nodes.
+ if (unsupportedSvgNodes.contains(nodeName)) {
+ svgTree.logErrorLine("<" + nodeName + "> is not supported", currentNode,
+ SvgTree.SvgLogLevel.ERROR);
+ }
+ traverseSVGAndExtract(svgTree, currentGroup, currentNode);
+ }
+ }
+
+ }
+
+ private static void parseTransformation(SvgTree avg, Node nNode) {
+ NamedNodeMap a = nNode.getAttributes();
+ int len = a.getLength();
+
+ for (int i = 0; i < len; i++) {
+ Node n = a.item(i);
+ String name = n.getNodeName();
+ String value = n.getNodeValue();
+ if (SVG_TRANSFORM.equals(name)) {
+ if (value.startsWith("matrix(")) {
+ value = value.substring("matrix(".length(), value.length() - 1);
+ String[] sp = value.split(" ");
+ for (int j = 0; j < sp.length; j++) {
+ avg.matrix[j] = Float.parseFloat(sp[j]);
+ }
+ }
+ } else if (name.equals("y")) {
+ Float.parseFloat(value);
+ } else if (name.equals("x")) {
+ Float.parseFloat(value);
+ }
+
+ }
+ }
+
+ private static void parseDimension(SvgTree avg, Node nNode) {
+ NamedNodeMap a = nNode.getAttributes();
+ int len = a.getLength();
+
+ for (int i = 0; i < len; i++) {
+ Node n = a.item(i);
+ String name = n.getNodeName();
+ String value = n.getNodeValue();
+ int subStringSize = value.length();
+ if (subStringSize > 2) {
+ if (value.endsWith("px")) {
+ subStringSize = subStringSize - 2;
+ }
+ }
+
+ if (SVG_WIDTH.equals(name)) {
+ avg.w = Float.parseFloat(value.substring(0, subStringSize));
+ } else if (SVG_HEIGHT.equals(name)) {
+ avg.h = Float.parseFloat(value.substring(0, subStringSize));
+ } else if (SVG_VIEW_BOX.equals(name)) {
+ avg.viewBox = new float[4];
+ String[] strbox = value.split(" ");
+ for (int j = 0; j < avg.viewBox.length; j++) {
+ avg.viewBox[j] = Float.parseFloat(strbox[j]);
+ }
+ }
+ }
+ if (avg.viewBox == null && avg.w != 0 && avg.h != 0) {
+ avg.viewBox = new float[4];
+ avg.viewBox[2] = avg.w;
+ avg.viewBox[3] = avg.h;
+ }
+ }
+
+ // Read the content from currentItem, and fill into "child"
+ private static void extractAllItemsAs(SvgTree avg, SvgLeafNode child, Node currentItem) {
+ Node currentGroup = currentItem.getParentNode();
+
+ boolean hasNodeAttr = false;
+ String styleContent = "";
+ boolean nothingToDisplay = false;
+
+ while (currentGroup != null && currentGroup.getNodeName().equals("g")) {
+ // Parse the group's attributes.
+ logger.log(Level.FINE, "Printing current parent");
+ printlnCommon(currentGroup);
+
+ NamedNodeMap attr = currentGroup.getAttributes();
+ Node nodeAttr = attr.getNamedItem(SVG_STYLE);
+ // Search for the "display:none", if existed, then skip this item.
+ if (nodeAttr != null) {
+ styleContent += nodeAttr.getTextContent() + ";";
+ logger.log(Level.FINE, "styleContent is :" + styleContent + "at number group ");
+ if (styleContent.contains("display:none")) {
+ logger.log(Level.FINE, "Found none style, skip the whole group");
+ nothingToDisplay = true;
+ break;
+ } else {
+ hasNodeAttr = true;
+ }
+ }
+
+ Node displayAttr = attr.getNamedItem(SVG_DISPLAY);
+ if (displayAttr != null && "none".equals(displayAttr.getNodeValue())) {
+ logger.log(Level.FINE, "Found display:none style, skip the whole group");
+ nothingToDisplay = true;
+ break;
+ }
+ currentGroup = currentGroup.getParentNode();
+ }
+
+ if (nothingToDisplay) {
+ // Skip this current whole item.
+ return;
+ }
+
+ logger.log(Level.FINE, "Print current item");
+ printlnCommon(currentItem);
+
+ if (hasNodeAttr && styleContent != null) {
+ addStyleToPath(child, styleContent);
+ }
+
+ Node currentGroupNode = currentItem;
+
+ if (SVG_PATH.equals(currentGroupNode.getNodeName())) {
+ extractPathItem(avg, child, currentGroupNode);
+ }
+
+ if (SVG_RECT.equals(currentGroupNode.getNodeName())) {
+ extractRectItem(avg, child, currentGroupNode);
+ }
+
+ if (SVG_CIRCLE.equals(currentGroupNode.getNodeName())) {
+ extractCircleItem(avg, child, currentGroupNode);
+ }
+
+ if (SVG_POLYGON.equals(currentGroupNode.getNodeName())) {
+ extractPolyItem(avg, child, currentGroupNode);
+ }
+
+ if (SVG_LINE.equals(currentGroupNode.getNodeName())) {
+ extractLineItem(avg, child, currentGroupNode);
+ }
+ }
+
+ private static void printlnCommon(Node n) {
+ logger.log(Level.FINE, " nodeName=\"" + n.getNodeName() + "\"");
+
+ String val = n.getNamespaceURI();
+ if (val != null) {
+ logger.log(Level.FINE, " uri=\"" + val + "\"");
+ }
+
+ val = n.getPrefix();
+
+ if (val != null) {
+ logger.log(Level.FINE, " pre=\"" + val + "\"");
+ }
+
+ val = n.getLocalName();
+ if (val != null) {
+ logger.log(Level.FINE, " local=\"" + val + "\"");
+ }
+
+ val = n.getNodeValue();
+ if (val != null) {
+ logger.log(Level.FINE, " nodeValue=");
+ if (val.trim().equals("")) {
+ // Whitespace
+ logger.log(Level.FINE, "[WS]");
+ } else {
+ logger.log(Level.FINE, "\"" + n.getNodeValue() + "\"");
+ }
+ }
+ }
+
+ /**
+ * Convert polygon element into a path.
+ */
+ private static void extractPolyItem(SvgTree avg, SvgLeafNode child, Node currentGroupNode) {
+ logger.log(Level.FINE, "Rect found" + currentGroupNode.getTextContent());
+ if (currentGroupNode.getNodeType() == Node.ELEMENT_NODE) {
+
+ NamedNodeMap a = currentGroupNode.getAttributes();
+ int len = a.getLength();
+
+ for (int itemIndex = 0; itemIndex < len; itemIndex++) {
+ Node n = a.item(itemIndex);
+ String name = n.getNodeName();
+ String value = n.getNodeValue();
+ if (name.equals(SVG_STYLE)) {
+ addStyleToPath(child, value);
+ } else if (presentationMap.containsKey(name)) {
+ child.fillPresentationAttributes(name, value);
+ } else if (name.equals(SVG_POINTS)) {
+ PathBuilder builder = new PathBuilder();
+ String[] split = value.split("[\\s,]+");
+ float baseX = Float.parseFloat(split[0]);
+ float baseY = Float.parseFloat(split[1]);
+ builder.absoluteMoveTo(baseX, baseY);
+ for (int j = 2; j < split.length; j += 2) {
+ float x = Float.parseFloat(split[j]);
+ float y = Float.parseFloat(split[j + 1]);
+ builder.relativeLineTo(x - baseX, y - baseY);
+ baseX = x;
+ baseY = y;
+ }
+ builder.relativeClose();
+ child.setPathData(builder.toString());
+ }
+ }
+ }
+ }
+
+ /**
+ * Convert rectangle element into a path.
+ */
+ private static void extractRectItem(SvgTree avg, SvgLeafNode child, Node currentGroupNode) {
+ logger.log(Level.FINE, "Rect found" + currentGroupNode.getTextContent());
+
+ if (currentGroupNode.getNodeType() == Node.ELEMENT_NODE) {
+ float x = 0;
+ float y = 0;
+ float width = Float.NaN;
+ float height = Float.NaN;
+
+ NamedNodeMap a = currentGroupNode.getAttributes();
+ int len = a.getLength();
+ boolean pureTransparent = false;
+ for (int j = 0; j < len; j++) {
+ Node n = a.item(j);
+ String name = n.getNodeName();
+ String value = n.getNodeValue();
+ if (name.equals(SVG_STYLE)) {
+ addStyleToPath(child, value);
+ if (value.contains("opacity:0;")) {
+ pureTransparent = true;
+ }
+ } else if (presentationMap.containsKey(name)) {
+ child.fillPresentationAttributes(name, value);
+ } else if (name.equals("clip-path") && value.startsWith("url(#SVGID_")) {
+
+ } else if (name.equals("x")) {
+ x = Float.parseFloat(value);
+ } else if (name.equals("y")) {
+ y = Float.parseFloat(value);
+ } else if (name.equals("width")) {
+ width = Float.parseFloat(value);
+ } else if (name.equals("height")) {
+ height = Float.parseFloat(value);
+ } else if (name.equals("style")) {
+
+ }
+
+ }
+
+ if (!pureTransparent && avg != null && !Float.isNaN(x) && !Float.isNaN(y)
+ && !Float.isNaN(width)
+ && !Float.isNaN(height)) {
+ // "M x, y h width v height h -width z"
+ PathBuilder builder = new PathBuilder();
+ builder.absoluteMoveTo(x, y);
+ builder.relativeHorizontalTo(width);
+ builder.relativeVerticalTo(height);
+ builder.relativeHorizontalTo(-width);
+ builder.relativeClose();
+ child.setPathData(builder.toString());
+ }
+ }
+ }
+
+ /**
+ * Convert circle element into a path.
+ */
+ private static void extractCircleItem(SvgTree avg, SvgLeafNode child, Node currentGroupNode) {
+ logger.log(Level.FINE, "circle found" + currentGroupNode.getTextContent());
+
+ if (currentGroupNode.getNodeType() == Node.ELEMENT_NODE) {
+ float cx = 0;
+ float cy = 0;
+ float radius = 0;
+
+ NamedNodeMap a = currentGroupNode.getAttributes();
+ int len = a.getLength();
+ boolean pureTransparent = false;
+ for (int j = 0; j < len; j++) {
+ Node n = a.item(j);
+ String name = n.getNodeName();
+ String value = n.getNodeValue();
+ if (name.equals(SVG_STYLE)) {
+ addStyleToPath(child, value);
+ if (value.contains("opacity:0;")) {
+ pureTransparent = true;
+ }
+ } else if (presentationMap.containsKey(name)) {
+ child.fillPresentationAttributes(name, value);
+ } else if (name.equals("clip-path") && value.startsWith("url(#SVGID_")) {
+
+ } else if (name.equals("cx")) {
+ cx = Float.parseFloat(value);
+ } else if (name.equals("cy")) {
+ cy = Float.parseFloat(value);
+ } else if (name.equals("r")) {
+ radius = Float.parseFloat(value);
+ }
+
+ }
+
+ if (!pureTransparent && avg != null && !Float.isNaN(cx) && !Float.isNaN(cy)) {
+ // "M cx cy m -r, 0 a r,r 0 1,1 (r * 2),0 a r,r 0 1,1 -(r * 2),0"
+ PathBuilder builder = new PathBuilder();
+ builder.absoluteMoveTo(cx, cy);
+ builder.relativeMoveTo(-radius, 0);
+ builder.relativeArcTo(radius, radius, false, true, true, 2 * radius, 0);
+ builder.relativeArcTo(radius, radius, false, true, true, -2 * radius, 0);
+ child.setPathData(builder.toString());
+ }
+ }
+ }
+
+ /**
+ * Convert line element into a path.
+ */
+ private static void extractLineItem(SvgTree avg, SvgLeafNode child, Node currentGroupNode) {
+ logger.log(Level.FINE, "line found" + currentGroupNode.getTextContent());
+
+ if (currentGroupNode.getNodeType() == Node.ELEMENT_NODE) {
+ float x1 = 0;
+ float y1 = 0;
+ float x2 = 0;
+ float y2 = 0;
+
+ NamedNodeMap a = currentGroupNode.getAttributes();
+ int len = a.getLength();
+ boolean pureTransparent = false;
+ for (int j = 0; j < len; j++) {
+ Node n = a.item(j);
+ String name = n.getNodeName();
+ String value = n.getNodeValue();
+ if (name.equals(SVG_STYLE)) {
+ addStyleToPath(child, value);
+ if (value.contains("opacity:0;")) {
+ pureTransparent = true;
+ }
+ } else if (presentationMap.containsKey(name)) {
+ child.fillPresentationAttributes(name, value);
+ } else if (name.equals("clip-path") && value.startsWith("url(#SVGID_")) {
+ // TODO: Handle clip path here.
+ } else if (name.equals("x1")) {
+ x1 = Float.parseFloat(value);
+ } else if (name.equals("y1")) {
+ y1 = Float.parseFloat(value);
+ } else if (name.equals("x2")) {
+ x2 = Float.parseFloat(value);
+ } else if (name.equals("y2")) {
+ y2 = Float.parseFloat(value);
+ }
+ }
+
+ if (!pureTransparent && avg != null && !Float.isNaN(x1) && !Float.isNaN(y1)
+ && !Float.isNaN(x2) && !Float.isNaN(y2)) {
+ // "M x1, y1 L x2, y2"
+ PathBuilder builder = new PathBuilder();
+ builder.absoluteMoveTo(x1, y1);
+ builder.absoluteLineTo(x2, y2);
+ child.setPathData(builder.toString());
+ }
+ }
+
+ }
+
+ private static void extractPathItem(SvgTree avg, SvgLeafNode child, Node currentGroupNode) {
+ logger.log(Level.FINE, "Path found " + currentGroupNode.getTextContent());
+
+ if (currentGroupNode.getNodeType() == Node.ELEMENT_NODE) {
+ Element eElement = (Element)currentGroupNode;
+
+ NamedNodeMap a = currentGroupNode.getAttributes();
+ int len = a.getLength();
+
+ for (int j = 0; j < len; j++) {
+ Node n = a.item(j);
+ String name = n.getNodeName();
+ String value = n.getNodeValue();
+ if (name.equals(SVG_STYLE)) {
+ addStyleToPath(child, value);
+ } else if (presentationMap.containsKey(name)) {
+ child.fillPresentationAttributes(name, value);
+ } else if (name.equals(SVG_D)) {
+ String pathData = value.replaceAll("(\\d)-", "$1,-");
+ child.setPathData(pathData);
+ }
+
+ }
+ }
+ }
+
+ private static void addStyleToPath(SvgLeafNode path, String value) {
+ logger.log(Level.FINE, "Style found is " + value);
+ if (value != null) {
+ String[] parts = value.split(";");
+ for (int k = parts.length - 1; k >= 0; k--) {
+ String subStyle = parts[k];
+ String[] nameValue = subStyle.split(":");
+ if (nameValue.length == 2 && nameValue[0] != null && nameValue[1] != null) {
+ if (presentationMap.containsKey(nameValue[0])) {
+ path.fillPresentationAttributes(nameValue[0], nameValue[1]);
+ } else if (nameValue[0].equals(SVG_OPACITY)) {
+ // TODO: This is hacky, since we don't have a group level
+ // android:opacity. This only works when the path didn't overlap.
+ path.fillPresentationAttributes(SVG_FILL_OPACITY, nameValue[1]);
+ }
+ }
+ }
+ }
+ }
+
+ private static final String head = "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n";
+
+ private static String getSizeString(float w, float h, float scaleFactor) {
+ String size = " android:width=\"" + (int) (w * scaleFactor) + "dp\"\n" +
+ " android:height=\"" + (int) (h * scaleFactor) + "dp\"\n";
+ return size;
+ }
+
+ private static void writeFile(OutputStream outStream, SvgTree svgTree) throws IOException {
+
+ OutputStreamWriter fw = new OutputStreamWriter(outStream);
+ fw.write(head);
+ float finalWidth = svgTree.w;
+ float finalHeight = svgTree.h;
+
+ fw.write(getSizeString(finalWidth, finalHeight, svgTree.mScaleFactor));
+
+ fw.write(" android:viewportWidth=\"" + svgTree.w + "\"\n");
+ fw.write(" android:viewportHeight=\"" + svgTree.h + "\">\n");
+
+ svgTree.normalize();
+ // TODO: this has to happen in the tree mode!!!
+ writeXML(svgTree, fw);
+ fw.write("</vector>\n");
+
+ fw.close();
+ }
+
+ private static void writeXML(SvgTree svgTree, OutputStreamWriter fw) throws IOException {
+ svgTree.getRoot().writeXML(fw);
+ }
+
+ /**
+ * Convert a SVG file into VectorDrawable's XML content, if no error is found.
+ *
+ * @param inputSVG the input SVG file
+ * @param outStream the converted VectorDrawable's content. This can be
+ * empty if there is any error found during parsing
+ * @return the error messages, which contain things like all the tags
+ * VectorDrawble don't support or exception message.
+ */
+ public static String parseSvgToXml(File inputSVG, OutputStream outStream) {
+ // Write all the error message during parsing into SvgTree. and return here as getErrorLog().
+ // We will also log the exceptions here.
+ String errorLog = null;
+ try {
+ SvgTree svgTree = parse(inputSVG);
+ errorLog = svgTree.getErrorLog();
+ // When there was anything in the input SVG file that we can't
+ // convert to VectorDrawable, we logged them as errors.
+ // After we logged all the errors, we skipped the XML file generation.
+ if (svgTree.canConvertToVectorDrawable()) {
+ writeFile(outStream, svgTree);
+ }
+ } catch (Exception e) {
+ errorLog = "EXCEPTION in parsing " + inputSVG.getName() + ":\n" + e.getMessage();
+ }
+ return errorLog;
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgGroupNode.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgGroupNode.java
new file mode 100644
index 0000000..3a0082d
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgGroupNode.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+import org.w3c.dom.Node;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Represent a SVG file's group element.
+ */
+class SvgGroupNode extends SvgNode {
+ private static Logger logger = Logger.getLogger(SvgGroupNode.class.getSimpleName());
+ private static final String INDENT_LEVEL = " ";
+ private ArrayList<SvgNode> mChildren = new ArrayList<SvgNode>();
+
+ public SvgGroupNode(SvgTree svgTree, Node docNode, String name) {
+ super(svgTree, docNode, name);
+ }
+
+ public void addChild(SvgNode child) {
+ mChildren.add(child);
+ }
+
+ @Override
+ public void dumpNode(String indent) {
+ // Print the current group.
+ logger.log(Level.FINE, indent + "current group is :" + getName());
+
+ // Then print all the children.
+ for (SvgNode node : mChildren) {
+ node.dumpNode(indent + INDENT_LEVEL);
+ }
+ }
+
+ @Override
+ public boolean isGroupNode() {
+ return true;
+ }
+
+ @Override
+ public void transform(float a, float b, float c, float d, float e, float f) {
+ for (SvgNode p : mChildren) {
+ p.transform(a, b, c, d, e, f);
+ }
+ }
+
+ @Override
+ public void writeXML(OutputStreamWriter writer) throws IOException {
+ for (SvgNode node : mChildren) {
+ node.writeXML(writer);
+ }
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgLeafNode.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgLeafNode.java
new file mode 100644
index 0000000..72bdd70
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgLeafNode.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+import com.android.annotations.Nullable;
+import com.google.common.collect.ImmutableMap;
+import org.w3c.dom.Node;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Represent a SVG file's leave element.
+ */
+class SvgLeafNode extends SvgNode {
+ private static Logger logger = Logger.getLogger(SvgLeafNode.class.getSimpleName());
+
+ private String mPathData;
+
+ // Key is the attributes for vector drawable, and the value is the converted from SVG.
+ private HashMap<String, String> mVdAttributesMap = new HashMap<String, String>();
+
+ public SvgLeafNode(SvgTree svgTree, Node node, String nodeName) {
+ super(svgTree, node, nodeName);
+ }
+
+ private String getAttributeValues(ImmutableMap<String, String> presentationMap) {
+ StringBuilder sb = new StringBuilder("/>\n");
+ for (String key : mVdAttributesMap.keySet()) {
+ String vectorDrawableAttr = presentationMap.get(key);
+ String svgValue = mVdAttributesMap.get(key);
+ String vdValue = svgValue.trim();
+ // There are several cases we need to convert from SVG format to
+ // VectorDrawable format. Like "none", "3px" or "rgb(255, 0, 0)"
+ if ("none".equals(vdValue)) {
+ vdValue = "#00000000";
+ } else if (vdValue.endsWith("px")){
+ vdValue = vdValue.substring(0, vdValue.length() - 2);
+ } else if (vdValue.startsWith("rgb")) {
+ vdValue = vdValue.substring(3, vdValue.length());
+ vdValue = convertRGBToHex(vdValue);
+ if (vdValue == null) {
+ getTree().logErrorLine("Unsupported Color format " + vdValue, getDocumentNode(),
+ SvgTree.SvgLogLevel.ERROR);
+ }
+ }
+ String attr = "\n " + vectorDrawableAttr + "=\"" +
+ vdValue + "\"";
+ sb.insert(0, attr);
+
+ }
+ return sb.toString();
+ }
+
+ public static int clamp(int val, int min, int max) {
+ return Math.max(min, Math.min(max, val));
+ }
+
+ /**
+ * SVG allows using rgb(int, int, int) or rgb(float%, float%, float%) to
+ * represent a color, but Android doesn't. Therefore, we need to convert
+ * them into #RRGGBB format.
+ * @param svgValue in either "(int, int, int)" or "(float%, float%, float%)"
+ * @return #RRGGBB in hex format, or null, if an error is found.
+ */
+ @Nullable
+ private String convertRGBToHex(String svgValue) {
+ // We don't support color keyword yet.
+ // http://www.w3.org/TR/SVG11/types.html#ColorKeywords
+ String result = null;
+ String functionValue = svgValue.trim();
+ functionValue = svgValue.substring(1, functionValue.length() - 1);
+ // After we cut the "(", ")", we can deal with the numbers.
+ String[] numbers = functionValue.split(",");
+ if (numbers.length != 3) {
+ return null;
+ }
+ int[] color = new int[3];
+ for (int i = 0; i < 3; i ++) {
+ String number = numbers[i];
+ number = number.trim();
+ if (number.endsWith("%")) {
+ float value = Float.parseFloat(number.substring(0, number.length() - 1));
+ color[i] = clamp((int)(value * 255.0f / 100.0f), 0, 255);
+ } else {
+ int value = Integer.parseInt(number);
+ color[i] = clamp(value, 0, 255);
+ }
+ }
+ StringBuilder builder = new StringBuilder();
+ builder.append("#");
+ for (int i = 0; i < 3; i ++) {
+ builder.append(String.format("%02X", color[i]));
+ }
+ result = builder.toString();
+ assert result.length() == 7;
+ return result;
+ }
+
+ @Override
+ public void dumpNode(String indent) {
+ logger.log(Level.FINE, indent + (mPathData != null ? mPathData : " null pathData ") +
+ (mName != null ? mName : " null name "));
+ }
+
+ public void setPathData(String pathData) {
+ mPathData = pathData;
+ }
+
+ @Override
+ public boolean isGroupNode() {
+ return false;
+ }
+
+ @Override
+ public void transform(float a, float b, float c, float d, float e, float f) {
+ if ("none".equals(mVdAttributesMap.get("fill")) || (mPathData == null)) {
+ // Nothing to draw and transform, early return.
+ return;
+ }
+ // TODO: We need to just apply the transformation to group.
+ VdPath.Node[] n = VdParser.parsePath(mPathData);
+ if (!(a == 1 && d == 1 && b == 0 && c == 0 && e == 0 && f == 0)) {
+ VdPath.Node.transform(a, b, c, d, e, f, n);
+ }
+ mPathData = VdPath.Node.NodeListToString(n);
+ }
+
+ @Override
+ public void writeXML(OutputStreamWriter writer) throws IOException {
+ String fillColor = mVdAttributesMap.get(Svg2Vector.SVG_FILL_COLOR);
+ String strokeColor = mVdAttributesMap.get(Svg2Vector.SVG_STROKE_COLOR);
+ logger.log(Level.FINE, "fill color " + fillColor);
+ boolean emptyFill = fillColor != null && ("none".equals(fillColor) || "#0000000".equals(fillColor));
+ boolean emptyStroke = strokeColor == null || "none".equals(strokeColor);
+ boolean emptyPath = mPathData == null;
+ boolean nothingToDraw = emptyPath || emptyFill && emptyStroke;
+ if (nothingToDraw) {
+ return;
+ }
+
+ writer.write(" <path\n");
+ if (!mVdAttributesMap.containsKey(Svg2Vector.SVG_FILL_COLOR)) {
+ logger.log(Level.FINE, "ADDING FILL SVG_FILL_COLOR");
+ writer.write(" android:fillColor=\"#FF000000\"\n");
+ }
+ writer.write(" android:pathData=\"" + mPathData + "\"");
+ writer.write(getAttributeValues(Svg2Vector.presentationMap));
+ }
+
+ public void fillPresentationAttributes(String name, String value) {
+ logger.log(Level.FINE, ">>>> PROP " + name + " = " + value);
+ if (value.startsWith("url(")) {
+ getTree().logErrorLine("Unsupported URL value: " + value, getDocumentNode(),
+ SvgTree.SvgLogLevel.ERROR);
+ return;
+ }
+ mVdAttributesMap.put(name, value);
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgNode.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgNode.java
new file mode 100644
index 0000000..494dafa
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgNode.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+import org.w3c.dom.Node;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+/**
+ * Parent class for a SVG file's node, can be either group or leave element.
+ */
+abstract class SvgNode {
+
+ protected String mName;
+ // Keep a reference to the tree in order to dump the error log.
+ private SvgTree mSvgTree;
+ // Use document node to get the line number for error reporting.
+ private Node mDocumentNode;
+
+ public SvgNode(SvgTree svgTree, Node node, String name) {
+ mName = name;
+ mSvgTree = svgTree;
+ mDocumentNode = node;
+ }
+
+ protected SvgTree getTree() {
+ return mSvgTree;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public Node getDocumentNode() {
+ return mDocumentNode;
+ }
+
+ /**
+ * dump the current node's debug info.
+ */
+ public abstract void dumpNode(String indent);
+
+ /**
+ * Write the Node content into the VectorDrawable's XML file.
+ */
+ public abstract void writeXML(OutputStreamWriter writer) throws IOException;
+
+ /**
+ * @return true the node is a group node.
+ */
+ public abstract boolean isGroupNode();
+
+ /**
+ * Transform the current Node with the transformation matrix.
+ */
+ public abstract void transform(float a, float b, float c, float d, float e, float f);
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgTree.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgTree.java
new file mode 100644
index 0000000..86263e5
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/SvgTree.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.blame.SourcePosition;
+import com.android.utils.PositionXmlParser;
+import com.google.common.base.Strings;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Represent the SVG file in an internal data structure as a tree.
+ */
+class SvgTree {
+ private static Logger logger = Logger.getLogger(SvgTree.class.getSimpleName());
+
+ public float w;
+ public float h;
+ public float[] matrix;
+ public float[] viewBox;
+ public float mScaleFactor = 1;
+
+ private SvgGroupNode mRoot;
+ private String mFileName;
+
+ private ArrayList<String> mErrorLines = new ArrayList<String>();
+
+ public enum SvgLogLevel {
+ ERROR,
+ WARNING
+ }
+
+ public Document parse(File f) throws Exception {
+ mFileName = f.getName();
+ Document doc = PositionXmlParser.parse(new FileInputStream(f), false);
+ return doc;
+ }
+
+ public void normalize() {
+ if (matrix != null) {
+ transform(matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
+ }
+
+ if (viewBox != null && (viewBox[0] != 0 || viewBox[1] != 0)) {
+ transform(1, 0, 0, 1, -viewBox[0], -viewBox[1]);
+ }
+ logger.log(Level.FINE, "matrix=" + Arrays.toString(matrix));
+ }
+
+ private void transform(float a, float b, float c, float d, float e, float f) {
+ mRoot.transform(a, b, c, d, e, f);
+ }
+
+ public void dump(SvgGroupNode root) {
+ logger.log(Level.FINE, "current file is :" + mFileName);
+ root.dumpNode("");
+ }
+
+ public void setRoot(SvgGroupNode root) {
+ mRoot = root;
+ }
+
+ @Nullable
+ public SvgGroupNode getRoot() {
+ return mRoot;
+ }
+
+ public void logErrorLine(String s, Node node, SvgLogLevel level) {
+ if (!Strings.isNullOrEmpty(s)) {
+ if (node != null) {
+ SourcePosition position = getPosition(node);
+ mErrorLines.add(level.name() + "@ line " + (position.getStartLine() + 1) +
+ " " + s + "\n");
+ } else {
+ mErrorLines.add(s);
+ }
+ }
+ }
+
+ /**
+ * @return Error log. Empty string if there are no errors.
+ */
+ @NonNull
+ public String getErrorLog() {
+ StringBuilder errorBuilder = new StringBuilder();
+ if (!mErrorLines.isEmpty()) {
+ errorBuilder.append("In " + mFileName + ":\n");
+ }
+ for (String log : mErrorLines) {
+ errorBuilder.append(log);
+ }
+ return errorBuilder.toString();
+ }
+
+ /**
+ * @return true when there is no error found when parsing the SVG file.
+ */
+ public boolean canConvertToVectorDrawable() {
+ return mErrorLines.isEmpty();
+ }
+
+ private SourcePosition getPosition(Node node) {
+ return PositionXmlParser.getPosition(node);
+ }
+
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdElement.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdElement.java
new file mode 100644
index 0000000..9422e8d
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdElement.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+/**
+ * Used to represent one VectorDrawble's element, can be a group or path.
+ */
+abstract class VdElement {
+ String mName;
+
+ public String getName() {
+ return mName;
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdGroup.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdGroup.java
new file mode 100644
index 0000000..5a6d37b
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdGroup.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+import java.util.ArrayList;
+
+/**
+ * Used to represent one VectorDrawble's group element.
+ * TODO: Add group transformation here.
+ */
+class VdGroup extends VdElement{
+
+ public VdGroup() {
+ mName = this.toString(); // to ensure paths have unique names
+ }
+ // Children can be either a {@link VdPath} or {@link VdGroup}
+ private ArrayList<VdElement> mChildren = new ArrayList<VdElement>();
+
+ public void add(VdElement pathOrGroup) {
+ mChildren.add(pathOrGroup);
+ }
+
+ public ArrayList<VdElement> getChildren() {
+ return mChildren;
+ }
+
+ public int size() {
+ return mChildren.size();
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdIcon.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdIcon.java
new file mode 100644
index 0000000..f61e4cb
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdIcon.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+import com.android.ide.common.util.AssetUtil;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.net.URL;
+
+/**
+ * VdIcon wrap every vector drawable from Material Library into an icon.
+ * All of them are shown in a table for developer to pick.
+ */
+public class VdIcon implements Icon, Comparable<VdIcon> {
+ private VdTree mVdTree;
+ private final String mName;
+ private final URL mUrl;
+
+ public VdIcon(URL url) {
+ setDynamicIcon(url);
+ mUrl = url;
+ String fileName = url.getFile();
+ mName = fileName.substring(fileName.lastIndexOf("/") + 1);
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public URL getURL() {
+ return mUrl;
+ }
+
+ public void setDynamicIcon(URL url) {
+ final VdParser p = new VdParser();
+ try {
+ mVdTree = p.parse(url.openStream(), null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void paintIcon(Component c, Graphics g, int x, int y) {
+ // We knew all the icons from Material library are square shape.
+ int minSize = Math.min(c.getWidth(), c.getHeight());
+ final BufferedImage image = AssetUtil.newArgbBufferedImage(minSize, minSize);
+ mVdTree.drawIntoImage(image);
+
+ // Draw in the center of the component.
+ Rectangle rect = new Rectangle(0, 0, c.getWidth(), c.getHeight());
+ AssetUtil.drawCenterInside((Graphics2D)g, image, rect);
+ }
+
+ @Override
+ public int getIconWidth() {
+ return (int) (mVdTree != null ? mVdTree.mPortWidth : 0);
+ }
+
+ @Override
+ public int getIconHeight() {
+ return (int) (mVdTree != null ? mVdTree.mPortHeight : 0);
+ }
+
+ @Override
+ public int compareTo(VdIcon other) {
+ return mName.compareTo(other.mName);
+ }
+}
\ No newline at end of file
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdNodeRender.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdNodeRender.java
new file mode 100644
index 0000000..9cba65f
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdNodeRender.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+import java.awt.geom.Path2D;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Given an array of VdPath.Node, generate a Path2D object.
+ * In another word, this is the engine which converts the pathData into
+ * a Path2D object, which is able to draw on Swing components.
+ * The logic and math here are the same as PathParser.java in framework.
+ */
+class VdNodeRender {
+ private static Logger logger = Logger.getLogger(VdNodeRender.class
+ .getSimpleName());
+
+ public static void creatPath(VdPath.Node[] node, Path2D path) {
+ float[] current = new float[6];
+ char lastCmd = ' ';
+ for (int i = 0; i < node.length; i++) {
+ addCommand(path, current, node[i].type, lastCmd,node[i].params);
+ lastCmd = node[i].type;
+ }
+ }
+
+ private static void addCommand(Path2D path, float[] current, char cmd,
+ char lastCmd, float[] val) {
+
+ int incr = 2;
+
+ float cx = current[0];
+ float cy = current[1];
+ float cpx = current[2];
+ float cpy = current[3];
+ float loopX = current[4];
+ float loopY = current[5];
+
+ switch (cmd) {
+ case 'z':
+ case 'Z':
+ path.closePath();
+ cx = loopX;
+ cy = loopY;
+ case 'm':
+ case 'M':
+ case 'l':
+ case 'L':
+ case 't':
+ case 'T':
+ incr = 2;
+ break;
+ case 'h':
+ case 'H':
+ case 'v':
+ case 'V':
+ incr = 1;
+ break;
+ case 'c':
+ case 'C':
+ incr = 6;
+ break;
+ case 's':
+ case 'S':
+ case 'q':
+ case 'Q':
+ incr = 4;
+ break;
+ case 'a':
+ case 'A':
+ incr = 7;
+ }
+
+ for (int k = 0; k < val.length; k += incr) {
+ boolean reflectCtrl = false;
+ float tempReflectedX, tempReflectedY;
+
+ switch (cmd) {
+ case 'm':
+ cx += val[k + 0];
+ cy += val[k + 1];
+ path.moveTo(cx, cy);
+ loopX = cx;
+ loopY = cy;
+ break;
+ case 'M':
+ cx = val[k + 0];
+ cy = val[k + 1];
+ path.moveTo(cx, cy);
+ loopX = cx;
+ loopY = cy;
+ break;
+ case 'l':
+ cx += val[k + 0];
+ cy += val[k + 1];
+ path.lineTo(cx, cy);
+ break;
+ case 'L':
+ cx = val[k + 0];
+ cy = val[k + 1];
+ path.lineTo(cx, cy);
+ break;
+ case 'z':
+ case 'Z':
+ path.closePath();
+ cx = loopX;
+ cy = loopY;
+ break;
+ case 'h':
+ cx += val[k + 0];
+ path.lineTo(cx, cy);
+ break;
+ case 'H':
+ path.lineTo(val[k + 0], cy);
+ cx = val[k + 0];
+ break;
+ case 'v':
+ cy += val[k + 0];
+ path.lineTo(cx, cy);
+ break;
+ case 'V':
+ path.lineTo(cx, val[k + 0]);
+ cy = val[k + 0];
+ break;
+ case 'c':
+ path.curveTo(cx + val[k + 0], cy + val[k + 1], cx + val[k + 2],
+ cy + val[k + 3], cx + val[k + 4], cy + val[k + 5]);
+ cpx = cx + val[k + 2];
+ cpy = cy + val[k + 3];
+ cx += val[k + 4];
+ cy += val[k + 5];
+ break;
+ case 'C':
+ path.curveTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
+ val[k + 4], val[k + 5]);
+ cx = val[k + 4];
+ cy = val[k + 5];
+ cpx = val[k + 2];
+ cpy = val[k + 3];
+ break;
+ case 's':
+ reflectCtrl = (lastCmd == 'c' || lastCmd == 's' || lastCmd == 'C' || lastCmd == 'S');
+ path.curveTo(reflectCtrl ? 2 * cx - cpx : cx, reflectCtrl ? 2
+ * cy - cpy : cy, cx + val[k + 0], cy + val[k + 1], cx
+ + val[k + 2], cy + val[k + 3]);
+
+ cpx = cx + val[k + 0];
+ cpy = cy + val[k + 1];
+ cx += val[k + 2];
+ cy += val[k + 3];
+ break;
+ case 'S':
+ reflectCtrl = (lastCmd == 'c' || lastCmd == 's' || lastCmd == 'C' || lastCmd == 'S');
+ path.curveTo(reflectCtrl ? 2 * cx - cpx : cx, reflectCtrl ? 2
+ * cy - cpy : cy, val[k + 0], val[k + 1], val[k + 2],
+ val[k + 3]);
+ cpx = (val[k + 0]);
+ cpy = (val[k + 1]);
+ cx = val[k + 2];
+ cy = val[k + 3];
+ break;
+ case 'q':
+ path.quadTo(cx + val[k + 0], cy + val[k + 1], cx + val[k + 2],
+ cy + val[k + 3]);
+ cpx = cx + val[k + 0];
+ cpy = cy + val[k + 1];
+ // Note that we have to update cpx first, since cx will be updated here.
+ cx += val[k + 2];
+ cy += val[k + 3];
+ break;
+ case 'Q':
+ path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
+ cx = val[k + 2];
+ cy = val[k + 3];
+ cpx = val[k + 0];
+ cpy = val[k + 1];
+ break;
+ case 't':
+ reflectCtrl = (lastCmd == 'q' || lastCmd == 't' || lastCmd == 'Q' || lastCmd == 'T');
+ tempReflectedX = reflectCtrl ? 2 * cx - cpx : cx;
+ tempReflectedY = reflectCtrl ? 2 * cy - cpy : cy;
+ path.quadTo(tempReflectedX, tempReflectedY, cx + val[k + 0], cy + val[k + 1]);
+ cpx = tempReflectedX;
+ cpy = tempReflectedY;
+ cx += val[k + 0];
+ cy += val[k + 1];
+ break;
+ case 'T':
+ reflectCtrl = (lastCmd == 'q' || lastCmd == 't' || lastCmd == 'Q' || lastCmd == 'T');
+ tempReflectedX = reflectCtrl ? 2 * cx - cpx : cx;
+ tempReflectedY = reflectCtrl ? 2 * cy - cpy : cy;
+ path.quadTo(tempReflectedX, tempReflectedY, val[k + 0], val[k + 1]);
+ cx = val[k + 0];
+ cy = val[k + 1];
+ cpx = tempReflectedX;
+ cpy = tempReflectedY;
+ break;
+ case 'a':
+ // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
+ drawArc(path, cx, cy, val[k + 5] + cx, val[k + 6] + cy,
+ val[k + 0], val[k + 1], val[k + 2], val[k + 3] != 0,
+ val[k + 4] != 0);
+ cx += val[k + 5];
+ cy += val[k + 6];
+ cpx = cx;
+ cpy = cy;
+
+ break;
+ case 'A':
+ drawArc(path, cx, cy, val[k + 5], val[k + 6], val[k + 0],
+ val[k + 1], val[k + 2], val[k + 3] != 0,
+ val[k + 4] != 0);
+ cx = val[k + 5];
+ cy = val[k + 6];
+ cpx = cx;
+ cpy = cy;
+ break;
+
+ }
+ lastCmd = cmd;
+ }
+ current[0] = cx;
+ current[1] = cy;
+ current[2] = cpx;
+ current[3] = cpy;
+ current[4] = loopX;
+ current[5] = loopY;
+
+ }
+
+ private static void drawArc(Path2D p, float x0, float y0, float x1,
+ float y1, float a, float b, float theta, boolean isMoreThanHalf,
+ boolean isPositiveArc) {
+
+ logger.log(Level.FINE, "(" + x0 + "," + y0 + ")-(" + x1 + "," + y1
+ + ") {" + a + " " + b + "}");
+ /* Convert rotation angle from degrees to radians */
+ double thetaD = theta * Math.PI / 180.0f;
+ /* Pre-compute rotation matrix entries */
+ double cosTheta = Math.cos(thetaD);
+ double sinTheta = Math.sin(thetaD);
+ /* Transform (x0, y0) and (x1, y1) into unit space */
+ /* using (inverse) rotation, followed by (inverse) scale */
+ double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
+ double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
+ double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
+ double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
+ logger.log(Level.FINE, "unit space (" + x0p + "," + y0p + ")-(" + x1p
+ + "," + y1p + ")");
+ /* Compute differences and averages */
+ double dx = x0p - x1p;
+ double dy = y0p - y1p;
+ double xm = (x0p + x1p) / 2;
+ double ym = (y0p + y1p) / 2;
+ /* Solve for intersecting unit circles */
+ double dsq = dx * dx + dy * dy;
+ if (dsq == 0.0) {
+ logger.log(Level.FINE, " Points are coincident");
+ return; /* Points are coincident */
+ }
+ double disc = 1.0 / dsq - 1.0 / 4.0;
+ if (disc < 0.0) {
+ logger.log(Level.FINE, "Points are too far apart " + dsq);
+ float adjust = (float) (Math.sqrt(dsq) / 1.99999);
+ drawArc(p, x0, y0, x1, y1, a * adjust, b * adjust, theta,
+ isMoreThanHalf, isPositiveArc);
+ return; /* Points are too far apart */
+ }
+ double s = Math.sqrt(disc);
+ double sdx = s * dx;
+ double sdy = s * dy;
+ double cx;
+ double cy;
+ if (isMoreThanHalf == isPositiveArc) {
+ cx = xm - sdy;
+ cy = ym + sdx;
+ } else {
+ cx = xm + sdy;
+ cy = ym - sdx;
+ }
+
+ double eta0 = Math.atan2((y0p - cy), (x0p - cx));
+ logger.log(Level.FINE, "eta0 = Math.atan2( " + (y0p - cy) + " , "
+ + (x0p - cx) + ") = " + Math.toDegrees(eta0));
+
+ double eta1 = Math.atan2((y1p - cy), (x1p - cx));
+ logger.log(Level.FINE, "eta1 = Math.atan2( " + (y1p - cy) + " , "
+ + (x1p - cx) + ") = " + Math.toDegrees(eta1));
+ double sweep = (eta1 - eta0);
+ if (isPositiveArc != (sweep >= 0)) {
+ if (sweep > 0) {
+ sweep -= 2 * Math.PI;
+ } else {
+ sweep += 2 * Math.PI;
+ }
+ }
+
+ cx *= a;
+ cy *= b;
+ double tcx = cx;
+ cx = cx * cosTheta - cy * sinTheta;
+ cy = tcx * sinTheta + cy * cosTheta;
+ logger.log(
+ Level.FINE,
+ "cx, cy, a, b, x0, y0, thetaD, eta0, sweep = " + cx + " , "
+ + cy + " , " + a + " , " + b + " , " + x0 + " , " + y0
+ + " , " + Math.toDegrees(thetaD) + " , "
+ + Math.toDegrees(eta0) + " , " + Math.toDegrees(sweep));
+
+ arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
+ }
+
+ /**
+ * Converts an arc to cubic Bezier segments and records them in p.
+ *
+ * @param p The target for the cubic Bezier segments
+ * @param cx The x coordinate center of the ellipse
+ * @param cy The y coordinate center of the ellipse
+ * @param a The radius of the ellipse in the horizontal direction
+ * @param b The radius of the ellipse in the vertical direction
+ * @param e1x E(eta1) x coordinate of the starting point of the arc
+ * @param e1y E(eta2) y coordinate of the starting point of the arc
+ * @param theta The angle that the ellipse bounding rectangle makes with the horizontal plane
+ * @param start The start angle of the arc on the ellipse
+ * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
+ */
+ private static void arcToBezier(Path2D p, double cx, double cy, double a,
+ double b, double e1x, double e1y, double theta, double start,
+ double sweep) {
+ // Taken from equations at:
+ // http://spaceroots.org/documents/ellipse/node8.html
+ // and http://www.spaceroots.org/documents/ellipse/node22.html
+
+ // Maximum of 45 degrees per cubic Bezier segment
+ int numSegments = Math.abs((int) Math.ceil(sweep * 4 / Math.PI));
+
+ double eta1 = start;
+ double cosTheta = Math.cos(theta);
+ double sinTheta = Math.sin(theta);
+ double cosEta1 = Math.cos(eta1);
+ double sinEta1 = Math.sin(eta1);
+ double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
+ double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
+
+ double anglePerSegment = sweep / numSegments;
+ for (int i = 0; i < numSegments; i++) {
+ double eta2 = eta1 + anglePerSegment;
+ double sinEta2 = Math.sin(eta2);
+ double cosEta2 = Math.cos(eta2);
+ double e2x = cx + (a * cosTheta * cosEta2)
+ - (b * sinTheta * sinEta2);
+ double e2y = cy + (a * sinTheta * cosEta2)
+ + (b * cosTheta * sinEta2);
+ double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
+ double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
+ double tanDiff2 = Math.tan((eta2 - eta1) / 2);
+ double alpha = Math.sin(eta2 - eta1)
+ * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
+ double q1x = e1x + alpha * ep1x;
+ double q1y = e1y + alpha * ep1y;
+ double q2x = e2x - alpha * ep2x;
+ double q2y = e2y - alpha * ep2y;
+
+ p.curveTo((float) q1x, (float) q1y, (float) q2x, (float) q2y,
+ (float) e2x, (float) e2y);
+ eta1 = eta2;
+ e1x = e2x;
+ e1y = e2y;
+ ep1x = ep2x;
+ ep1y = ep2y;
+ }
+ }
+
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdOverrideInfo.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdOverrideInfo.java
new file mode 100644
index 0000000..4935027
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdOverrideInfo.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+/**
+ * Used to represent info to override the VectorDrawble's XML file content.
+ */
+public class VdOverrideInfo {
+ private int mWidth;
+ private int mHeight;
+ private int mOpacity;
+ private boolean mAutoMirrored;
+
+ public VdOverrideInfo(int width, int height, int opacity, boolean autoMirrored) {
+ mWidth = width;
+ mHeight = height;
+ mOpacity = opacity;
+ mAutoMirrored = autoMirrored;
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public void setWidth(int width) {
+ mWidth = width;
+ }
+
+ public int getOpacity() {
+ return mOpacity;
+ }
+
+ public void setOpacity(int opacity) {
+ mOpacity = opacity;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ public void setHeight(int height) {
+ mHeight = height;
+ }
+
+ boolean needsOverrideWidth() {
+ return getWidth() > 0;
+ }
+
+ boolean needsOverrideHeight() {
+ return getHeight() > 0;
+ }
+
+ boolean needsOverrideOpacity() {
+ return getOpacity() < 100 && getOpacity() >= 0;
+ }
+
+ boolean needsOverrideAutoMirrored() {
+ return mAutoMirrored;
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdParser.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdParser.java
new file mode 100644
index 0000000..a1a4106
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdParser.java
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+import com.android.SdkConstants;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+/**
+ * Parse a VectorDrawble's XML file, and generate an internal tree representation,
+ * which can be used for drawing / previewing.
+ */
+class VdParser {
+ private static Logger logger = Logger.getLogger(VdParser.class.getSimpleName());
+
+ private static final String PATH_SHIFT_X = "shift-x";
+ private static final String PATH_SHIFT_Y = "shift-y";
+
+ private static final String SHAPE_VECTOR = "vector";
+ private static final String SHAPE_PATH = "path";
+ private static final String SHAPE_GROUP = "group";
+
+ private static final String PATH_ID = "android:name";
+ private static final String PATH_DESCRIPTION = "android:pathData";
+ private static final String PATH_FILL = "android:fillColor";
+ private static final String PATH_FILL_OPACTIY = "android:fillAlpha";
+ private static final String PATH_STROKE = "android:strokeColor";
+ private static final String PATH_STROKE_OPACTIY = "android:strokeAlpha";
+
+ private static final String PATH_STROKE_WIDTH = "android:strokeWidth";
+ private static final String PATH_ROTATION = "android:rotation";
+ private static final String PATH_ROTATION_X = "android:pivotX";
+ private static final String PATH_ROTATION_Y = "android:pivotY";
+ private static final String PATH_TRIM_START = "android:trimPathStart";
+ private static final String PATH_TRIM_END = "android:trimPathEnd";
+ private static final String PATH_TRIM_OFFSET = "android:trimPathOffset";
+ private static final String PATH_STROKE_LINECAP = "android:strokeLinecap";
+ private static final String PATH_STROKE_LINEJOIN = "android:strokeLinejoin";
+ private static final String PATH_STROKE_MITERLIMIT = "android:strokeMiterlimit";
+ private static final String PATH_CLIP = "android:clipToPath";
+ private static final String LINECAP_BUTT = "butt";
+ private static final String LINECAP_ROUND = "round";
+ private static final String LINECAP_SQUARE = "square";
+ private static final String LINEJOIN_MITER = "miter";
+ private static final String LINEJOIN_ROUND = "round";
+ private static final String LINEJOIN_BEVEL = "bevel";
+
+ interface ElemParser {
+ void parse(VdTree path, Attributes attributes);
+ }
+
+ ElemParser mParseSize = new ElemParser() {
+ @Override
+ public void parse(VdTree tree, Attributes attributes) {
+ parseSize(tree, attributes);
+ }
+ };
+
+ ElemParser mParsePath = new ElemParser() {
+ @Override
+ public void parse(VdTree tree, Attributes attributes) {
+ VdPath p = parsePathAttributes(attributes);
+ tree.add(p);
+ }
+ };
+
+ ElemParser mParseGroup = new ElemParser() {
+ @Override
+ public void parse(VdTree tree, Attributes attributes) {
+ VdGroup g = parseGroupAttributes(attributes);
+ tree.add(g);
+ }
+ };
+
+ HashMap<String, ElemParser> tagSwitch = new HashMap<String, ElemParser>();
+ {
+ tagSwitch.put(SHAPE_VECTOR, mParseSize);
+ tagSwitch.put(SHAPE_PATH, mParsePath);
+ tagSwitch.put(SHAPE_GROUP, mParseGroup);
+ // TODO: add <g> tag and start to build the tree.
+ }
+
+ // Note that the incoming file is the VectorDrawable's XML file, not the SVG.
+ // TODO: Use Document to parse and make sure no big performance difference.
+ public VdTree parse(InputStream is, StringBuilder vdErrorLog) {
+ try {
+ final VdTree tree = new VdTree();
+ SAXParserFactory spf = SAXParserFactory.newInstance();
+ SAXParser sp = spf.newSAXParser();
+ XMLReader xr = sp.getXMLReader();
+
+ xr.setContentHandler(new ContentHandler() {
+ String space = " ";
+
+ @Override
+ public void setDocumentLocator(Locator locator) {
+ }
+
+ @Override
+ public void startDocument() throws SAXException {
+ }
+
+ @Override
+ public void endDocument() throws SAXException {
+ }
+
+ @Override
+ public void startPrefixMapping(String s, String s2) throws SAXException {
+ }
+
+ @Override
+ public void endPrefixMapping(String s) throws SAXException {
+ }
+
+ @Override
+ public void startElement(String s, String s2, String s3, Attributes attributes)
+ throws SAXException {
+ String name = s3;
+ if (tagSwitch.containsKey(name)) {
+ tagSwitch.get(name).parse(tree, attributes);
+ }
+ space += " ";
+ }
+
+ @Override
+ public void endElement(String s, String s2, String s3) throws SAXException {
+ space = space.substring(1);
+ }
+
+ @Override
+ public void characters(char[] chars, int i, int i2) throws SAXException {
+ }
+
+ @Override
+ public void ignorableWhitespace(char[] chars, int i, int i2) throws SAXException {
+ }
+
+ @Override
+ public void processingInstruction(String s, String s2) throws SAXException {
+ }
+
+ @Override
+ public void skippedEntity(String s) throws SAXException {
+ }
+ });
+ xr.parse(new InputSource(is));
+ tree.parseFinish();
+ return tree;
+ } catch (Exception e) {
+ vdErrorLog.append("Exception while parsing XML file:\n" + e.getMessage());
+ return null;
+ }
+ }
+
+ public VdParser() {
+ }
+
+ private static int nextStart(String s, int end) {
+ char c;
+
+ while (end < s.length()) {
+ c = s.charAt(end);
+ // Note that 'e' or 'E' are not valid path commands, but could be
+ // used for floating point numbers' scientific notation.
+ // Therefore, when searching for next command, we should ignore 'e'
+ // and 'E'.
+ if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0))
+ && c != 'e' && c != 'E') {
+ return end;
+ }
+ end++;
+ }
+ return end;
+ }
+
+ public static VdPath.Node[] parsePath(String value) {
+ int start = 0;
+ int end = 1;
+
+ ArrayList<VdPath.Node> list = new ArrayList<VdPath.Node>();
+ while (end < value.length()) {
+ end = nextStart(value, end);
+ String s = value.substring(start, end);
+ float[] val = getFloats(s);
+
+ addNode(list, s.charAt(0), val);
+
+ start = end;
+ end++;
+ }
+ if ((end - start) == 1 && start < value.length()) {
+
+ addNode(list, value.charAt(start), new float[0]);
+ }
+ return list.toArray(new VdPath.Node[list.size()]);
+ }
+
+ private static class ExtractFloatResult {
+ // We need to return the position of the next separator and whether the
+ // next float starts with a '-' or a '.'.
+ int mEndPosition;
+ boolean mEndWithNegOrDot;
+ }
+
+ /**
+ * Copies elements from {@code original} into a new array, from indexes start (inclusive) to
+ * end (exclusive). The original order of elements is preserved.
+ * If {@code end} is greater than {@code original.length}, the result is padded
+ * with the value {@code 0.0f}.
+ *
+ * @param original the original array
+ * @param start the start index, inclusive
+ * @param end the end index, exclusive
+ * @return the new array
+ * @throws ArrayIndexOutOfBoundsException if {@code start < 0 || start > original.length}
+ * @throws IllegalArgumentException if {@code start > end}
+ * @throws NullPointerException if {@code original == null}
+ */
+ private static float[] copyOfRange(float[] original, int start, int end) {
+ if (start > end) {
+ throw new IllegalArgumentException();
+ }
+ int originalLength = original.length;
+ if (start < 0 || start > originalLength) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ int resultLength = end - start;
+ int copyLength = Math.min(resultLength, originalLength - start);
+ float[] result = new float[resultLength];
+ System.arraycopy(original, start, result, 0, copyLength);
+ return result;
+ }
+
+ /**
+ * Calculate the position of the next comma or space or negative sign
+ * @param s the string to search
+ * @param start the position to start searching
+ * @param result the result of the extraction, including the position of the
+ * the starting position of next number, whether it is ending with a '-'.
+ */
+ private static void extract(String s, int start, ExtractFloatResult result) {
+ // Now looking for ' ', ',', '.' or '-' from the start.
+ int currentIndex = start;
+ boolean foundSeparator = false;
+ result.mEndWithNegOrDot = false;
+ boolean secondDot = false;
+ boolean isExponential = false;
+ for (; currentIndex < s.length(); currentIndex++) {
+ boolean isPrevExponential = isExponential;
+ isExponential = false;
+ char currentChar = s.charAt(currentIndex);
+ switch (currentChar) {
+ case ' ':
+ case ',':
+ foundSeparator = true;
+ break;
+ case '-':
+ // The negative sign following a 'e' or 'E' is not a separator.
+ if (currentIndex != start && !isPrevExponential) {
+ foundSeparator = true;
+ result.mEndWithNegOrDot = true;
+ }
+ break;
+ case '.':
+ if (!secondDot) {
+ secondDot = true;
+ } else {
+ // This is the second dot, and it is considered as a separator.
+ foundSeparator = true;
+ result.mEndWithNegOrDot = true;
+ }
+ break;
+ case 'e':
+ case 'E':
+ isExponential = true;
+ break;
+ }
+ if (foundSeparator) {
+ break;
+ }
+ }
+ // When there is nothing found, then we put the end position to the end
+ // of the string.
+ result.mEndPosition = currentIndex;
+ }
+
+ /**
+ * parse the floats in the string this is an optimized version of parseFloat(s.split(",|\\s"));
+ *
+ * @param s the string containing a command and list of floats
+ * @return array of floats
+ */
+ private static float[] getFloats(String s) {
+ if (s.charAt(0) == 'z' || s.charAt(0) == 'Z') {
+ return new float[0];
+ }
+ try {
+ float[] results = new float[s.length()];
+ int count = 0;
+ int startPosition = 1;
+ int endPosition = 0;
+
+ ExtractFloatResult result = new ExtractFloatResult();
+ int totalLength = s.length();
+
+ // The startPosition should always be the first character of the
+ // current number, and endPosition is the character after the current
+ // number.
+ while (startPosition < totalLength) {
+ extract(s, startPosition, result);
+ endPosition = result.mEndPosition;
+
+ if (startPosition < endPosition) {
+ results[count++] = Float.parseFloat(
+ s.substring(startPosition, endPosition));
+ }
+
+ if (result.mEndWithNegOrDot) {
+ // Keep the '-' or '.' sign with next number.
+ startPosition = endPosition;
+ } else {
+ startPosition = endPosition + 1;
+ }
+ }
+ return copyOfRange(results, 0, count);
+ } catch (NumberFormatException e) {
+ throw new RuntimeException("error in parsing \"" + s + "\"", e);
+ }
+ }
+ // End of copy from PathParser.java
+ ////////////////////////////////////////////////////////////////
+ private static void addNode(ArrayList<VdPath.Node> list, char cmd, float[] val) {
+ list.add(new VdPath.Node(cmd, val));
+ }
+
+ public VdTree parse(URL r, StringBuilder vdErrorLog) throws Exception {
+ return parse(r.openStream(), vdErrorLog);
+ }
+
+ private void parseSize(VdTree vdTree, Attributes attributes) {
+
+ Pattern pattern = Pattern.compile("^\\s*(\\d+(\\.\\d+)*)\\s*([a-zA-Z]+)\\s*$");
+ HashMap<String, Integer> m = new HashMap<String, Integer>();
+ m.put(SdkConstants.UNIT_PX, 1);
+ m.put(SdkConstants.UNIT_DIP, 1);
+ m.put(SdkConstants.UNIT_DP, 1);
+ m.put(SdkConstants.UNIT_SP, 1);
+ m.put(SdkConstants.UNIT_PT, 1);
+ m.put(SdkConstants.UNIT_IN, 1);
+ m.put(SdkConstants.UNIT_MM, 1);
+ int len = attributes.getLength();
+
+ for (int i = 0; i < len; i++) {
+ String name = attributes.getQName(i);
+ String value = attributes.getValue(i);
+ Matcher matcher = pattern.matcher(value);
+ float size = 0;
+ if (matcher.matches()) {
+ float v = Float.parseFloat(matcher.group(1));
+ String unit = matcher.group(3).toLowerCase(Locale.getDefault());
+ size = v;
+ }
+ // -- Extract dimension units.
+
+ if ("android:width".equals(name)) {
+ vdTree.mBaseWidth = size;
+ } else if ("android:height".equals(name)) {
+ vdTree.mBaseHeight = size;
+ } else if ("android:viewportWidth".equals(name)) {
+ vdTree.mPortWidth = Float.parseFloat(value);
+ } else if ("android:viewportHeight".equals(name)) {
+ vdTree.mPortHeight = Float.parseFloat(value);
+ } else if ("android:alpha".equals(name)) {
+ vdTree.mRootAlpha = Float.parseFloat(value);
+ } else {
+ continue;
+ }
+
+ }
+ }
+
+ private VdPath parsePathAttributes(Attributes attributes) {
+ int len = attributes.getLength();
+ VdPath vgPath = new VdPath();
+
+ for (int i = 0; i < len; i++) {
+ String name = attributes.getQName(i);
+ String value = attributes.getValue(i);
+ logger.log(Level.FINE, "name " + name + "value " + value);
+ setNameValue(vgPath, name, value);
+ }
+ return vgPath;
+ }
+
+ private VdGroup parseGroupAttributes(Attributes attributes) {
+ int len = attributes.getLength();
+ VdGroup vgGroup = new VdGroup();
+
+ for (int i = 0; i < len; i++) {
+ String name = attributes.getQName(i);
+ String value = attributes.getValue(i);
+ logger.log(Level.FINE, "name " + name + "value " + value);
+ }
+ return vgGroup;
+ }
+
+ public void setNameValue(VdPath vgPath, String name, String value) {
+ if (PATH_DESCRIPTION.equals(name)) {
+ vgPath.mNode = parsePath(value);
+ } else if (PATH_ID.equals(name)) {
+ vgPath.mName = value;
+ } else if (PATH_FILL.equals(name)) {
+ vgPath.mFillColor = calculateColor(value);
+ if (!Float.isNaN(vgPath.mFillOpacity)) {
+ vgPath.mFillColor &= 0x00FFFFFF;
+ vgPath.mFillColor |= ((int) (0xFF * vgPath.mFillOpacity)) << 24;
+ }
+ } else if (PATH_STROKE.equals(name)) {
+ vgPath.mStrokeColor = calculateColor(value);
+ if (!Float.isNaN(vgPath.mStrokeOpacity)) {
+ vgPath.mStrokeColor &= 0x00FFFFFF;
+ vgPath.mStrokeColor |= ((int) (0xFF * vgPath.mStrokeOpacity)) << 24;
+ }
+ } else if (PATH_FILL_OPACTIY.equals(name)) {
+ vgPath.mFillOpacity = Float.parseFloat(value);
+ vgPath.mFillColor &= 0x00FFFFFF;
+ vgPath.mFillColor |= ((int) (0xFF * vgPath.mFillOpacity)) << 24;
+ } else if (PATH_STROKE_OPACTIY.equals(name)) {
+ vgPath.mStrokeOpacity = Float.parseFloat(value);
+ vgPath.mStrokeColor &= 0x00FFFFFF;
+ vgPath.mStrokeColor |= ((int) (0xFF * vgPath.mStrokeOpacity)) << 24;
+ } else if (PATH_STROKE_WIDTH.equals(name)) {
+ vgPath.mStrokeWidth = Float.parseFloat(value);
+ } else if (PATH_ROTATION.equals(name)) {
+ vgPath.mRotate = Float.parseFloat(value);
+ } else if (PATH_SHIFT_X.equals(name)) {
+ vgPath.mShiftX = Float.parseFloat(value);
+ } else if (PATH_SHIFT_Y.equals(name)) {
+ vgPath.mShiftY = Float.parseFloat(value);
+ } else if (PATH_ROTATION_Y.equals(name)) {
+ vgPath.mRotateY = Float.parseFloat(value);
+ } else if (PATH_ROTATION_X.equals(name)) {
+ vgPath.mRotateX = Float.parseFloat(value);
+ } else if (PATH_CLIP.equals(name)) {
+
+ vgPath.mClip = Boolean.parseBoolean(value);
+
+ } else if (PATH_TRIM_START.equals(name)) {
+ vgPath.mTrimPathStart = Float.parseFloat(value);
+ } else if (PATH_TRIM_END.equals(name)) {
+ vgPath.mTrimPathEnd = Float.parseFloat(value);
+ } else if (PATH_TRIM_OFFSET.equals(name)) {
+ vgPath.mTrimPathOffset = Float.parseFloat(value);
+ } else if (PATH_STROKE_LINECAP.equals(name)) {
+ if (LINECAP_BUTT.equals(value)) {
+ vgPath.mStrokeLineCap = 0;
+ } else if (LINECAP_ROUND.equals(value)) {
+ vgPath.mStrokeLineCap = 1;
+ } else if (LINECAP_SQUARE.equals(value)) {
+ vgPath.mStrokeLineCap = 2;
+ }
+ } else if (PATH_STROKE_LINEJOIN.equals(name)) {
+ if (LINEJOIN_MITER.equals(value)) {
+ vgPath.mStrokeLineJoin = 0;
+ } else if (LINEJOIN_ROUND.equals(value)) {
+ vgPath.mStrokeLineJoin = 1;
+ } else if (LINEJOIN_BEVEL.equals(value)) {
+ vgPath.mStrokeLineJoin = 2;
+ }
+ } else if (PATH_STROKE_MITERLIMIT.equals(name)) {
+ vgPath.mStrokeMiterlimit = Float.parseFloat(value);
+ } else {
+ logger.log(Level.FINE, ">>>>>> DID NOT UNDERSTAND ! \"" + name + "\" <<<<");
+ }
+
+ }
+
+ private int calculateColor(String value) {
+ int len = value.length();
+ int ret;
+ int k = 0;
+ switch (len) {
+ case 7: // #RRGGBB
+ ret = (int) Long.parseLong(value.substring(1), 16);
+ ret |= 0xFF000000;
+ break;
+ case 9: // #AARRGGBB
+ ret = (int) Long.parseLong(value.substring(1), 16);
+ break;
+ case 4: // #RGB
+ ret = (int) Long.parseLong(value.substring(1), 16);
+
+ k |= ((ret >> 8) & 0xF) * 0x110000;
+ k |= ((ret >> 4) & 0xF) * 0x1100;
+ k |= ((ret) & 0xF) * 0x11;
+ ret = k | 0xFF000000;
+ break;
+ case 5: // #ARGB
+ ret = (int) Long.parseLong(value.substring(1), 16);
+ k |= ((ret >> 16) & 0xF) * 0x11000000;
+ k |= ((ret >> 8) & 0xF) * 0x110000;
+ k |= ((ret >> 4) & 0xF) * 0x1100;
+ k |= ((ret) & 0xF) * 0x11;
+ break;
+ default:
+ return 0xFF000000;
+ }
+
+ logger.log(Level.FINE, "color = " + value + " = " + Integer.toHexString(ret));
+ return ret;
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdPath.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdPath.java
new file mode 100644
index 0000000..6315fff
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdPath.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+import java.awt.geom.Path2D;
+import java.util.Arrays;
+
+/**
+ * Used to represent one VectorDrawble's path element.
+ */
+class VdPath extends VdElement{
+ Node[] mNode = null;
+ int mStrokeColor = 0;
+ int mFillColor = 0;
+ float mStrokeWidth = 0;
+ float mRotate = 0;
+ float mShiftX = 0;
+ float mShiftY = 0;
+ float mRotateX = 0;
+ float mRotateY = 0;
+ float trimPathStart = 0;
+ float trimPathEnd = 1;
+ float trimPathOffset = 0;
+ int mStrokeLineCap = -1;
+ int mStrokeLineJoin = -1;
+ float mStrokeMiterlimit = -1;
+ boolean mClip = false;
+ float mStrokeOpacity = Float.NaN;
+ float mFillOpacity = Float.NaN;
+ float mTrimPathStart = 0;
+ float mTrimPathEnd = 1;
+ float mTrimPathOffset = 0;
+
+ public void toPath(Path2D path) {
+ path.reset();
+ if (mNode != null) {
+ VdNodeRender.creatPath(mNode, path);
+ }
+ }
+
+ public static class Node {
+ char type;
+ float[] params;
+
+ public Node(char type, float[] params) {
+ this.type = type;
+ this.params = params;
+ }
+
+ public Node(Node n) {
+ this.type = n.type;
+ this.params = Arrays.copyOf(n.params, n.params.length);
+ }
+
+ public static String NodeListToString(Node[] nodes) {
+ String s = "";
+ for (int i = 0; i < nodes.length; i++) {
+ Node n = nodes[i];
+ s += n.type;
+ int len = n.params.length;
+ for (int j = 0; j < len; j++) {
+ if (j > 0) {
+ s += ((j & 1) == 1) ? "," : " ";
+ }
+ // To avoid trailing zeros like 17.0, use this trick
+ float value = n.params[j];
+ if (value == (long) value) {
+ s += String.valueOf((long) value);
+ } else {
+ s += String.valueOf(value);
+ }
+
+ }
+ }
+ return s;
+ }
+
+ public static void transform(float a,
+ float b,
+ float c,
+ float d,
+ float e,
+ float f,
+ Node[] nodes) {
+ float[] pre = new float[2];
+ for (int i = 0; i < nodes.length; i++) {
+ nodes[i].transform(a, b, c, d, e, f, pre);
+ }
+ }
+
+ public void transform(float a,
+ float b,
+ float c,
+ float d,
+ float e,
+ float f,
+ float[] pre) {
+ int incr = 0;
+ float[] tempParams;
+ float[] origParams;
+ switch (type) {
+
+ case 'z':
+ case 'Z':
+ return;
+ case 'M':
+ case 'L':
+ case 'T':
+ incr = 2;
+ pre[0] = params[params.length - 2];
+ pre[1] = params[params.length - 1];
+ for (int i = 0; i < params.length; i += incr) {
+ matrix(a, b, c, d, e, f, i, i + 1);
+ }
+ break;
+ case 'm':
+ case 'l':
+ case 't':
+ incr = 2;
+ pre[0] += params[params.length - 2];
+ pre[1] += params[params.length - 1];
+ for (int i = 0; i < params.length; i += incr) {
+ matrix(a, b, c, d, 0, 0, i, i + 1);
+ }
+ break;
+ case 'h':
+ type = 'l';
+ pre[0] += params[params.length - 1];
+
+ tempParams = new float[params.length * 2];
+ origParams = params;
+ params = tempParams;
+ for (int i = 0; i < params.length; i += 2) {
+ params[i] = origParams[i / 2];
+ params[i + 1] = 0;
+ matrix(a, b, c, d, 0, 0, i, i + 1);
+ }
+
+ break;
+ case 'H':
+ type = 'L';
+ pre[0] = params[params.length - 1];
+ tempParams = new float[params.length * 2];
+ origParams = params;
+ params = tempParams;
+ for (int i = 0; i < params.length; i += 2) {
+ params[i] = origParams[i / 2];
+ params[i + 1] = pre[1];
+ matrix(a, b, c, d, e, f, i, i + 1);
+ }
+ break;
+ case 'v':
+ pre[1] += params[params.length - 1];
+ type = 'l';
+ tempParams = new float[params.length * 2];
+ origParams = params;
+ params = tempParams;
+ for (int i = 0; i < params.length; i += 2) {
+ params[i] = 0;
+ params[i + 1] = origParams[i / 2];
+ matrix(a, b, c, d, 0, 0, i, i + 1);
+ }
+ break;
+ case 'V':
+ type = 'L';
+ pre[1] = params[params.length - 1];
+ tempParams = new float[params.length * 2];
+ origParams = params;
+ params = tempParams;
+ for (int i = 0; i < params.length; i += 2) {
+ params[i] = pre[0];
+ params[i + 1] = origParams[i / 2];
+ matrix(a, b, c, d, e, f, i, i + 1);
+ }
+ break;
+ case 'C':
+ case 'S':
+ case 'Q':
+ pre[0] = params[params.length - 2];
+ pre[1] = params[params.length - 1];
+ for (int i = 0; i < params.length; i += 2) {
+ matrix(a, b, c, d, e, f, i, i + 1);
+ }
+ break;
+ case 's':
+ case 'q':
+ case 'c':
+ pre[0] += params[params.length - 2];
+ pre[1] += params[params.length - 1];
+ for (int i = 0; i < params.length; i += 2) {
+ matrix(a, b, c, d, 0, 0, i, i + 1);
+ }
+ break;
+ case 'a':
+ incr = 7;
+ pre[0] += params[params.length - 2];
+ pre[1] += params[params.length - 1];
+ for (int i = 0; i < params.length; i += incr) {
+ matrix(a, b, c, d, 0, 0, i, i + 1);
+ double ang = Math.toRadians(params[i + 2]);
+ params[i + 2] = (float) Math.toDegrees(ang + Math.atan2(b, d));
+ matrix(a, b, c, d, 0, 0, i + 5, i + 6);
+ }
+ break;
+ case 'A':
+ incr = 7;
+ pre[0] = params[params.length - 2];
+ pre[1] = params[params.length - 1];
+ for (int i = 0; i < params.length; i += incr) {
+ matrix(a, b, c, d, e, f, i, i + 1);
+ double ang = Math.toRadians(params[i + 2]);
+ params[i + 2] = (float) Math.toDegrees(ang + Math.atan2(b, d));
+ matrix(a, b, c, d, e, f, i + 5, i + 6);
+ }
+ break;
+
+ }
+ }
+
+ void matrix(float a,
+ float b,
+ float c,
+ float d,
+ float e,
+ float f,
+ int offx,
+ int offy) {
+ float inx = (offx < 0) ? 1 : params[offx];
+ float iny = (offy < 0) ? 1 : params[offy];
+ float x = inx * a + iny * c + e;
+ float y = inx * b + iny * d + f;
+ if (offx >= 0) {
+ params[offx] = x;
+ }
+ if (offy >= 0) {
+ params[offy] = y;
+ }
+ }
+ }
+
+ public VdPath() {
+ mName = this.toString(); // to ensure paths have unique names
+ }
+
+ /**
+ * TODO: support rotation attribute for stroke width
+ */
+ public void transform(float a, float b, float c, float d, float e, float f) {
+ mStrokeWidth *= Math.hypot(a + b, c + d);
+ Node.transform(a, b, c, d, e, f, mNode);
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdPreview.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdPreview.java
new file mode 100644
index 0000000..d5721a9
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdPreview.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.util.AssetUtil;
+import com.google.common.base.Charsets;
+import com.sun.org.apache.xml.internal.serialize.OutputFormat;
+import com.sun.org.apache.xml.internal.serialize.XMLSerializer;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import java.awt.image.BufferedImage;
+import java.io.*;
+
+/**
+ * Generate a Image based on the VectorDrawable's XML content.
+ *
+ * <p>This class also contains a main method, which can be used to preview a vector drawable file.
+ */
+public class VdPreview {
+
+ private static final String ANDROID_ALPHA = "android:alpha";
+ private static final String ANDROID_AUTO_MIRRORED = "android:autoMirrored";
+ private static final String ANDROID_HEIGHT = "android:height";
+ private static final String ANDROID_WIDTH = "android:width";
+ public static final int MAX_PREVIEW_IMAGE_SIZE = 4096;
+ public static final int MIN_PREVIEW_IMAGE_SIZE = 1;
+
+ /**
+ * This encapsulates the information used to determine the preview image size.
+ * The reason we have different ways here is that both Studio UI and build process need
+ * to use this common code path to generate images for vectordrawable.
+ * When {@value mUseWidth} is true, use {@code mImageMaxDimension} as the maximum
+ * dimension value while keeping the aspect ratio.
+ * Otherwise, use {@code mImageScale} to scale the image based on the XML's size information.
+ */
+ public static class TargetSize {
+ private boolean mUseWidth;
+
+ private int mImageMaxDimension;
+ private float mImageScale;
+
+ private TargetSize(boolean useWidth, int imageWidth, float imageScale) {
+ mUseWidth = useWidth;
+ mImageMaxDimension = imageWidth;
+ mImageScale = imageScale;
+ }
+
+ public static TargetSize createSizeFromWidth(int imageWidth) {
+ return new TargetSize(true, imageWidth, 0.0f);
+ }
+
+ public static TargetSize createSizeFromScale(float imageScale) {
+ return new TargetSize(false, 0, imageScale);
+ }
+ }
+
+ /**
+ * Since we allow overriding the vector drawable's size, we also need to keep
+ * the original size and aspect ratio.
+ */
+ public static class SourceSize {
+ public int getHeight() {
+ return mSourceHeight;
+ }
+
+ public int getWidth() {
+ return mSourceWidth;
+ }
+
+ private int mSourceWidth;
+ private int mSourceHeight;
+ }
+
+
+ /**
+ * @return a format object for XML formatting.
+ */
+ @NonNull
+ private static OutputFormat getPrettyPrintFormat() {
+ OutputFormat format = new OutputFormat();
+ format.setLineWidth(120);
+ format.setIndenting(true);
+ format.setIndent(4);
+ format.setEncoding("UTF-8");
+ format.setOmitComments(true);
+ return format;
+ }
+
+ /**
+ * Get the vector drawable's original size.
+ */
+ public static SourceSize getVdOriginalSize(@NonNull Document document) {
+ Element root = document.getDocumentElement();
+ SourceSize srcSize = new SourceSize();
+ // Update attributes, note that attributes as width and height are required,
+ // while others are optional.
+ NamedNodeMap attr = root.getAttributes();
+ Node nodeAttr = attr.getNamedItem(ANDROID_WIDTH);
+ assert nodeAttr != null;
+ srcSize.mSourceWidth = parseDimension(0, nodeAttr, false);
+
+ nodeAttr = attr.getNamedItem(ANDROID_HEIGHT);
+ assert nodeAttr != null;
+ srcSize.mSourceHeight = parseDimension(0, nodeAttr, false);
+ return srcSize;
+ }
+
+ /**
+ * The UI can override some properties of the Vector drawable.
+ * In order to override in an uniform way, we re-parse the XML file
+ * and pick the appropriate attributes to override.
+ *
+ * @param document the parsed document of original VectorDrawable's XML file.
+ * @param info incoming override information for VectorDrawable.
+ * @param errorLog log for the parsing errors and warnings.
+ * @param srcSize as an output, store the original size of the VectorDrawable
+ * @return the overridden XML file in one string. If exception happens
+ * or no attributes needs to be overriden, return null.
+ */
+ @Nullable
+ public static String overrideXmlContent(@NonNull Document document,
+ @NonNull VdOverrideInfo info,
+ @Nullable StringBuilder errorLog) {
+ boolean isXmlFileContentChanged = false;
+ Element root = document.getDocumentElement();
+
+ // Update attributes, note that attributes as width and height are required,
+ // while others are optional.
+ NamedNodeMap attr = root.getAttributes();
+ if (info.needsOverrideWidth()) {
+ Node nodeAttr = attr.getNamedItem(ANDROID_WIDTH);
+ int overrideValue = info.getWidth();
+ int originalValue = parseDimension(overrideValue, nodeAttr, true);
+ if (originalValue != overrideValue) {
+ isXmlFileContentChanged = true;
+ }
+ }
+ if (info.needsOverrideHeight()) {
+ Node nodeAttr = attr.getNamedItem(ANDROID_HEIGHT);
+ int overrideValue = info.getHeight();
+ int originalValue = parseDimension(overrideValue, nodeAttr, true);
+ if (originalValue != overrideValue) {
+ isXmlFileContentChanged = true;
+ }
+ }
+ if (info.needsOverrideOpacity()) {
+ Node nodeAttr = attr.getNamedItem(ANDROID_ALPHA);
+ String opacityValue = String.format("%.2f", info.getOpacity() / 100.0f);
+ if (nodeAttr != null) {
+ nodeAttr.setTextContent(opacityValue);
+ }
+ else {
+ root.setAttribute(ANDROID_ALPHA, opacityValue);
+ }
+ isXmlFileContentChanged = true;
+ }
+ // When auto mirror is set to true, then we always need to set it.
+ // Because SVG has no such attribute at all.
+ if (info.needsOverrideAutoMirrored()) {
+ Node nodeAttr = attr.getNamedItem(ANDROID_AUTO_MIRRORED);
+ if (nodeAttr != null) {
+ nodeAttr.setTextContent("true");
+ }
+ else {
+ root.setAttribute(ANDROID_AUTO_MIRRORED, "true");
+ }
+ isXmlFileContentChanged = true;
+ }
+
+ if (isXmlFileContentChanged) {
+ // Prettify the XML string from the document.
+ StringWriter stringOut = new StringWriter();
+ XMLSerializer serial = new XMLSerializer(stringOut, getPrettyPrintFormat());
+ try {
+ serial.serialize(document);
+ }
+ catch (IOException e) {
+ if (errorLog != null) {
+ errorLog.append("Exception while parsing XML file:\n").append(e.getMessage());
+ }
+ }
+ return stringOut.toString();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Query the dimension info and override it if needed.
+ *
+ * @param overrideValue the dimension value to override with.
+ * @param nodeAttr the node who contains dimension info.
+ * @param override if true then override the dimension.
+ * @return the original dimension value.
+ */
+ private static int parseDimension(int overrideValue, Node nodeAttr, boolean override) {
+ assert nodeAttr != null;
+ String content = nodeAttr.getTextContent();
+ assert content.endsWith("dp");
+ int originalValue = Integer.parseInt(content.substring(0, content.length() - 2));
+
+ if (override) {
+ nodeAttr.setTextContent(overrideValue + "dp");
+ }
+ return originalValue;
+ }
+
+ /**
+ * This generates an image according to the VectorDrawable's content {@code xmlFileContent}.
+ * At the same time, {@vdErrorLog} captures all the errors found during parsing.
+ * The size of image is determined by the {@code size}.
+ *
+ * @param targetSize the size of result image.
+ * @param xmlFileContent VectorDrawable's XML file's content.
+ * @param vdErrorLog log for the parsing errors and warnings.
+ * @return an preview image according to the VectorDrawable's XML
+ */
+ @Nullable
+ public static BufferedImage getPreviewFromVectorXml(@NonNull TargetSize targetSize,
+ @Nullable String xmlFileContent,
+ @Nullable StringBuilder vdErrorLog) {
+ if (xmlFileContent == null || xmlFileContent.isEmpty()) {
+ return null;
+ }
+ VdParser p = new VdParser();
+ VdTree vdTree;
+
+ InputStream inputStream = new ByteArrayInputStream(
+ xmlFileContent.getBytes(Charsets.UTF_8));
+ vdTree = p.parse(inputStream, vdErrorLog);
+ if (vdTree == null) {
+ return null;
+ }
+
+ // If the forceImageSize is set (>0), then we honor that.
+ // Otherwise, we will ask the vectorDrawable for the prefer size, then apply the imageScale.
+ float vdWidth = vdTree.getBaseWidth();
+ float vdHeight = vdTree.getBaseHeight();
+ float imageWidth;
+ float imageHeight;
+ int forceImageSize = targetSize.mImageMaxDimension;
+ float imageScale = targetSize.mImageScale;
+
+ if (forceImageSize > 0) {
+ // The goal here is to generate an image within certain size, while keeping the
+ // aspect ration as much as we can.
+ // If it is scaling too much to fit in, we log an error.
+ float maxVdSize = Math.max(vdWidth, vdHeight);
+ float ratioToForceImageSize = forceImageSize / maxVdSize;
+ float scaledWidth = ratioToForceImageSize * vdWidth;
+ float scaledHeight = ratioToForceImageSize * vdHeight;
+ imageWidth = Math.max(MIN_PREVIEW_IMAGE_SIZE, Math.min(MAX_PREVIEW_IMAGE_SIZE, scaledWidth));
+ imageHeight = Math.max(MIN_PREVIEW_IMAGE_SIZE, Math.min(MAX_PREVIEW_IMAGE_SIZE, scaledHeight));
+ if (scaledWidth != imageWidth || scaledHeight != imageHeight) {
+ vdErrorLog.append("Invalid image size, can't fit in a square whose size is" + forceImageSize);
+ }
+ } else {
+ imageWidth = vdWidth * imageScale;
+ imageHeight = vdHeight * imageScale;
+ }
+
+ // Create the image according to the vectorDrawable's aspect ratio.
+
+ BufferedImage image = AssetUtil.newArgbBufferedImage((int)imageWidth, (int)imageHeight);
+ vdTree.drawIntoImage(image);
+ return image;
+ }
+
+ public static void main(String[] args) {
+ System.out.println("Hello from sdk-common-lib.");
+ }
+}
\ No newline at end of file
diff --git a/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdTree.java b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdTree.java
new file mode 100644
index 0000000..883b821
--- /dev/null
+++ b/sdk-common/src/main/java/com/android/ide/common/vectordrawable/VdTree.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+import com.android.ide.common.util.AssetUtil;
+
+import java.awt.*;
+import java.awt.geom.Path2D;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Used to represent the whole VectorDrawable XML file's tree.
+ */
+class VdTree {
+ private static Logger logger = Logger.getLogger(VdTree.class.getSimpleName());
+
+ VdGroup mCurrentGroup = new VdGroup();
+ ArrayList<VdElement> mChildren;
+
+ float mBaseWidth = 1;
+ float mBaseHeight = 1;
+ float mPortWidth = 1;
+ float mPortHeight = 1;
+ float mRootAlpha = 1;
+
+ /**
+ * Ensure there is at least one animation for every path in group (linking
+ * them by names) Build the "current" path based on the first group
+ */
+ void parseFinish() {
+ mChildren = mCurrentGroup.getChildren();
+ }
+
+ void add(VdElement pathOrGroup) {
+ mCurrentGroup.add(pathOrGroup);
+ }
+
+ float getBaseWidth(){
+ return mBaseWidth;
+ }
+
+ float getBaseHeight(){
+ return mBaseHeight;
+ }
+
+ private void drawInternal(Graphics g, int w, int h) {
+ float scaleX = w / mPortWidth;
+ float scaleY = h / mPortHeight;
+ float minScale = Math.min(scaleX, scaleY);
+
+ if (mChildren == null) {
+ logger.log(Level.FINE, "no pathes");
+ return;
+ }
+ ((Graphics2D) g).scale(scaleX, scaleY);
+
+ Rectangle bounds = null;
+ for (int i = 0; i < mChildren.size(); i++) {
+ // TODO: do things differently when it is a path or group!!
+ VdPath path = (VdPath) mChildren.get(i);
+ logger.log(Level.FINE, "mCurrentPaths[" + i + "]=" + path.getName() +
+ Integer.toHexString(path.mFillColor));
+ if (mChildren.get(i) != null) {
+ Rectangle r = drawPath(path, g, w, h, minScale);
+ if (bounds == null) {
+ bounds = r;
+ } else {
+ bounds.add(r);
+ }
+ }
+ }
+ logger.log(Level.FINE, "Rectangle " + bounds);
+ logger.log(Level.FINE, "Port " + mPortWidth + "," + mPortHeight);
+ double right = mPortWidth - bounds.getMaxX();
+ double bot = mPortHeight - bounds.getMaxY();
+ logger.log(Level.FINE, "x " + bounds.getMinX() + ", " + right);
+ logger.log(Level.FINE, "y " + bounds.getMinY() + ", " + bot);
+ }
+
+ private Rectangle drawPath(VdPath path, Graphics canvas, int w, int h, float scale) {
+
+ Path2D path2d = new Path2D.Double();
+ Graphics2D g = (Graphics2D) canvas;
+ path.toPath(path2d);
+
+ // TODO: Use AffineTransform to apply group's transformation info.
+ double theta = Math.toRadians(path.mRotate);
+ g.rotate(theta, path.mRotateX, path.mRotateY);
+ if (path.mClip) {
+ logger.log(Level.FINE, "CLIP");
+
+ g.setColor(Color.RED);
+ g.fill(path2d);
+
+ }
+ if (path.mFillColor != 0) {
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setColor(new Color(path.mFillColor, true));
+ g.fill(path2d);
+ }
+ if (path.mStrokeColor != 0) {
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setStroke(new BasicStroke(path.mStrokeWidth));
+ g.setColor(new Color(path.mStrokeColor, true));
+ g.draw(path2d);
+ }
+
+ g.rotate(-theta, path.mRotateX, path.mRotateY);
+ return path2d.getBounds();
+ }
+
+ /**
+ * Draw the VdTree into an image.
+ * If the root alpha is less than 1.0, then draw into a temporary image,
+ * then draw into the result image applying alpha blending.
+ */
+ public void drawIntoImage(BufferedImage image) {
+ Graphics2D gFinal = (Graphics2D) image.getGraphics();
+ int width = image.getWidth();
+ int height = image.getHeight();
+ gFinal.setColor(new Color(255, 255, 255, 0));
+ gFinal.fillRect(0, 0, width, height);
+
+ float rootAlpha = mRootAlpha;
+ if (rootAlpha < 1.0) {
+ BufferedImage alphaImage = AssetUtil.newArgbBufferedImage(width, height);
+ Graphics2D gTemp = (Graphics2D)alphaImage.getGraphics();
+ drawInternal(gTemp, width, height);
+ gFinal.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, rootAlpha));
+ gFinal.drawImage(alphaImage, 0, 0, null);
+ gTemp.dispose();
+ } else {
+ drawInternal(gFinal, width, height);
+ }
+ gFinal.dispose();
+ }
+}
diff --git a/sdk-common/src/main/java/com/android/ide/common/xml/AndroidManifestParser.java b/sdk-common/src/main/java/com/android/ide/common/xml/AndroidManifestParser.java
index ea34226..257cdfe 100644
--- a/sdk-common/src/main/java/com/android/ide/common/xml/AndroidManifestParser.java
+++ b/sdk-common/src/main/java/com/android/ide/common/xml/AndroidManifestParser.java
@@ -531,7 +531,7 @@
if (attributeName.equals(attributes.getLocalName(i)) &&
((hasNamespace &&
SdkConstants.NS_RESOURCES.equals(attributes.getURI(i))) ||
- (hasNamespace == false && attributes.getURI(i).length() == 0))) {
+ (hasNamespace == false && attributes.getURI(i).isEmpty()))) {
return attributes.getValue(i);
}
}
@@ -555,7 +555,7 @@
if (attributeName.equals(attributes.getLocalName(i)) &&
((hasNamespace &&
SdkConstants.NS_RESOURCES.equals(attributes.getURI(i))) ||
- (hasNamespace == false && attributes.getURI(i).length() == 0))) {
+ (hasNamespace == false && attributes.getURI(i).isEmpty()))) {
String attr = attributes.getValue(i);
if (attr != null) {
return Boolean.valueOf(attr);
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/MessageJsonSerializerTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/MessageJsonSerializerTest.java
new file mode 100644
index 0000000..fdb803e
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/blame/MessageJsonSerializerTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.blame;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Enclosed.class)
+public class MessageJsonSerializerTest {
+
+ private static GsonBuilder sGsonBuilder = new GsonBuilder();
+
+ static {
+ MessageJsonSerializer.registerTypeAdapters(sGsonBuilder);
+ }
+
+ @RunWith(Parameterized.class)
+ public static class DeserializeTest {
+
+ private static Gson sGson;
+
+ @Parameterized.Parameter(value = 0)
+ public Message message;
+
+ @Parameterized.Parameter(value = 1)
+ public String serializedMessage;
+
+ @Parameterized.Parameters(name = "fromJson(\"{1}\") should give {0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][]{
+ {new Message(
+ Message.Kind.ERROR,
+ "some error text",
+ new SourceFilePosition(
+ new SourceFile(new File("/path/file.java")),
+ new SourcePosition(1, 3, 5))),
+ "{"
+ + "\"kind\":\"Error\", "
+ + "\"text\":\"some error text\","
+ + "\"sources\": {"
+ + "\"file\":\"/path/file.java\","
+ + "\"position\":{"
+ + "\"startLine\":1,"
+ + "\"startColumn\":3,"
+ + "\"startOffset\":5"
+ + "}"
+ + "}}"
+ }, {
+ new Message(
+ Message.Kind.ERROR,
+ "errorText",
+ new SourceFilePosition(
+ new File("error/source"),
+ new SourcePosition(1,2,3,4,5,6))
+ ),
+ "{\"kind\":\"ERROR\",\"text\":\"errorText\",\"sourcePath\":\"error/source\","
+ + "\"position\":{\"startLine\":1,\"startColumn\":2,\"startOffset\":3,"
+ + "\"endLine\":4,\"endColumn\":5,\"endOffset\":6},\"original\":\"\"}\n"
+ }, {new Message(Message.Kind.SIMPLE, "something else", new SourceFilePosition(SourceFile.UNKNOWN, SourcePosition.UNKNOWN)),
+ "{\"kind\":\"SIMPLE\","
+ + "\"text\":\"something else\",\"position\":{},\"original\":\"something else\"}"
+ }, {
+ new Message(
+ Message.Kind.SIMPLE,
+ "Warning: AndroidManifest.xml already defines debuggable (in http://"
+ + "schemas.android.com/apk/res/android); using existing value "
+ + "in manifest.",
+ SourceFilePosition.UNKNOWN),
+ "{\"kind\":\"simple\",\"text\":\"Warning: AndroidManifest.xml already defines "
+ + "debuggable (in http://schemas.android.com/apk/res/android); using "
+ + "existing value in manifest.\",\"sources\":\"\"}"
+ }, {
+ new Message(
+ Message.Kind.UNKNOWN,
+ "Text.",
+ SourceFilePosition.UNKNOWN),
+ "{\"text\":\"Text.\"}",
+
+ }});
+ }
+
+ @BeforeClass
+ public static void initGson() {
+ sGson = sGsonBuilder.create();
+ }
+
+ @AfterClass
+ public static void removeGson() {
+ sGson = null;
+ }
+
+ @Test
+ public void check() {
+ assertEquals(message, sGson.fromJson(serializedMessage, Message.class));
+ }
+
+
+ }
+
+ @RunWith(Parameterized.class)
+ public static class RoundTripTest {
+
+ private static Gson sGson;
+
+ @Parameterized.Parameter
+ public Message message;
+
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][]{{
+ new Message(
+ Message.Kind.ERROR,
+ "some error text",
+ "original error text",
+ new SourceFilePosition(
+ new SourceFile(new File("/path/file.java")),
+ new SourcePosition(1, 3, 5)))
+ }, {
+ new Message(
+ Message.Kind.SIMPLE,
+ "something else",
+ new SourceFilePosition(SourceFile.UNKNOWN, SourcePosition.UNKNOWN))
+ }, {
+ new Message(
+ Message.Kind.SIMPLE,
+ "Warning: AndroidManifest.xml already defines debuggable (in http://"
+ + "schemas.android.com/apk/res/android); using existing value "
+ + "in manifest.",
+ SourceFilePosition.UNKNOWN)
+ }});
+ }
+
+ @BeforeClass
+ public static void initGson() {
+ sGson = sGsonBuilder.create();
+ }
+
+ @AfterClass
+ public static void removeGson() {
+ sGson = null;
+ }
+
+ @Test
+ public void check() {
+ assertEquals(message, sGson.fromJson(sGson.toJson(message), Message.class));
+ }
+
+
+ }
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/ParsingProcessOutputHandlerTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/ParsingProcessOutputHandlerTest.java
new file mode 100644
index 0000000..c065001
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/blame/ParsingProcessOutputHandlerTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.blame;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.blame.parser.ParsingFailedException;
+import com.android.ide.common.blame.parser.PatternAwareOutputParser;
+import com.android.ide.common.blame.parser.ToolOutputParser;
+import com.android.ide.common.blame.parser.util.OutputLineReader;
+import com.android.ide.common.process.ProcessException;
+import com.android.ide.common.process.ProcessOutput;
+import com.android.ide.common.res2.RecordingLogger;
+import com.android.utils.ILogger;
+import com.google.common.base.Charsets;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+public class ParsingProcessOutputHandlerTest {
+
+ private static ParsingProcessOutputHandler sParsingProcessOutputHandler;
+
+ private static RecordingLogger sLogger;
+
+ private static PatternAwareOutputParser sFakePatternParser;
+
+ private static MessageReceiver sMessageReceiver;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ sLogger = new RecordingLogger();
+ sFakePatternParser = new FakePatternAwareOutputParser();
+
+ sMessageReceiver = Mockito.mock(MessageReceiver.class);
+ sParsingProcessOutputHandler = new ParsingProcessOutputHandler(
+ new ToolOutputParser(sFakePatternParser, sLogger),
+ sMessageReceiver);
+ }
+
+ @Test
+ public void testRewriteMessages() throws IOException, ProcessException {
+
+ String original = "error example\ntwo line error\nnext line\nsomething else";
+
+ sParsingProcessOutputHandler.handleOutput(processOutputFromErrorString(original));
+
+ Mockito.verify(sMessageReceiver).receiveMessage(new Message(
+ Message.Kind.ERROR,
+ "errorText",
+ "originalText",
+ new SourceFilePosition(
+ new SourceFile(FakePatternAwareOutputParser.ERROR_EXAMPLE_FILE),
+ new SourcePosition(1, 2, 3, 4, 5, 6)
+ )));
+
+ Mockito.verify(sMessageReceiver).receiveMessage(new Message(
+ Message.Kind.WARNING,
+ "two line warning",
+ new SourceFilePosition(
+ new SourceFile(FakePatternAwareOutputParser.TWO_LINE_ERROR_FILE),
+ new SourcePosition(1, 2, -1)
+ )));
+
+
+ Mockito.verify(sMessageReceiver).receiveMessage(new Message(
+ Message.Kind.SIMPLE,
+ "something else",
+ SourceFilePosition.UNKNOWN));
+ Mockito.verifyNoMoreInteractions(sMessageReceiver);
+
+ String expected = "AGPBI: {"
+ + "\"kind\":\"error\","
+ + "\"text\":\"errorText\","
+ + "\"sources\":[{"
+ + "\"file\":\"" + FakePatternAwareOutputParser.ERROR_EXAMPLE_FILE.getAbsolutePath()
+ + "\",\"position\":{\"startLine\":1,\"startColumn\":2,\"startOffset\":3,"
+ + "\"endLine\":4,\"endColumn\":5,\"endOffset\":6}}],"
+ + "\"original\":\"original_text\"}\n"
+ + "AGPBI: {\"kind\":\"warning\","
+ + "\"text\":\"two line warning\","
+ + "\"sources\":[{\"file\":\"" +
+ FakePatternAwareOutputParser.TWO_LINE_ERROR_FILE.getAbsolutePath() + "\","
+ + "\"position\":{\"startLine\":1,\"startColumn\":2}}]}\n"
+ + "AGPBI: {\"kind\":\"simple\","
+ + "\"text\":\"something else\","
+ + "\"sources\":[{}]}";
+ }
+
+ @Test
+ public void parseException() throws IOException, ProcessException {
+ String original = "two line error";
+
+ sParsingProcessOutputHandler.handleOutput(processOutputFromErrorString(original));
+
+ Mockito.verifyNoMoreInteractions(sMessageReceiver);
+ }
+
+ private static ProcessOutput processOutputFromErrorString(String original) throws IOException {
+ ProcessOutput processOutput = sParsingProcessOutputHandler.createOutput();
+ processOutput.getErrorOutput().write(original.getBytes(Charsets.UTF_8));
+ return processOutput;
+ }
+
+ private static class FakePatternAwareOutputParser implements PatternAwareOutputParser {
+ static final File ERROR_EXAMPLE_FILE = new File("error/source");
+ static final File TWO_LINE_ERROR_FILE = new File("error/source/2");
+ @Override
+ public boolean parse(@NonNull String line, @NonNull OutputLineReader reader,
+ @NonNull List<Message> messages, @NonNull ILogger logger)
+ throws ParsingFailedException {
+ if (line.equals("two line error")) {
+ String nextLine = reader.readLine();
+ if ("next line".equals(nextLine)) {
+ messages.add(new Message(
+ Message.Kind.WARNING,
+ "two line warning",
+ "two line warning",
+ new SourceFilePosition(
+ TWO_LINE_ERROR_FILE,
+ new SourcePosition(1, 2, -1))));
+ } else {
+ throw new ParsingFailedException();
+ }
+ return true;
+ }
+ if (line.equals("error example")) {
+ messages.add(
+ new Message(
+ Message.Kind.ERROR,
+ "errorText",
+ "original_text",
+ new SourceFilePosition(
+ ERROR_EXAMPLE_FILE,
+ new SourcePosition(1, 2, 3, 4, 5, 6))));
+ return true;
+ }
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/SourceFileJsonTypeAdapterTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/SourceFileJsonTypeAdapterTest.java
new file mode 100644
index 0000000..4f79e2a
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/blame/SourceFileJsonTypeAdapterTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.blame;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Enclosed.class)
+public class SourceFileJsonTypeAdapterTest {
+
+ private static GsonBuilder sGsonBuilder = new GsonBuilder()
+ .registerTypeAdapter(SourceFile.class, new SourceFileJsonTypeAdapter());
+
+ @RunWith(Parameterized.class)
+ public static class RoundTripTest {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][]{
+ {new SourceFile(new File("/path/to/a/file.java"))},
+ {new SourceFile(new File("/path/to/a/file.java"), "Description")},
+ {new SourceFile("Description")},
+ {SourceFile.UNKNOWN},
+ });
+ }
+
+ @Parameterized.Parameter
+ public SourceFile mSourceFile;
+
+ private static Gson sGson;
+
+ @BeforeClass
+ public static void initGson() {
+ sGson = sGsonBuilder.create();
+ }
+
+ @Test
+ public void test() {
+ assertEquals(mSourceFile, sGson.fromJson(sGson.toJson(mSourceFile), SourceFile.class));
+ }
+
+ @AfterClass
+ public static void removeGson() {
+ sGson = null;
+ }
+ }
+
+ @RunWith(Parameterized.class)
+ public static class DeserializeTest {
+
+ @Parameterized.Parameters(name = "fromJson(\"{1}\") should be {0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][]{
+ {
+ new SourceFile(new File("/path/file.java")),
+ "\"/path/file.java\""
+ },
+ {
+ new SourceFile(new File("/path/f.java")),
+ "{\"path\":\"/path/f.java\"}"
+ },
+ {
+ new SourceFile(new File("/path/file.java"), "Description"),
+ "{\"description\":\"Description\", \"path\":\"/path/file.java\"}"
+ },
+ {
+ new SourceFile("Description"),
+ "{\"description\":\"Description\"}"
+ },
+ {
+ SourceFile.UNKNOWN,
+ "{}"
+ },
+ {
+ SourceFile.UNKNOWN,
+ "\"\""
+ },
+ {
+ SourceFile.UNKNOWN,
+ "{\"foo\":\"\"}"
+ },
+ {
+ SourceFile.UNKNOWN,
+ "{\"foo\": {\"bar\" : \":)\"}}"
+ },
+ });
+ }
+
+ @Parameterized.Parameter(value = 0)
+ public SourceFile mSourceFile;
+
+ @Parameterized.Parameter(value = 1)
+ public String jsonString;
+
+
+ private static Gson sGson;
+
+ @BeforeClass
+ public static void initGson() {
+ sGson = sGsonBuilder.create();
+ }
+
+ @Test
+ public void test() {
+ assertEquals(mSourceFile, sGson.fromJson(jsonString, SourceFile.class));
+ }
+
+ @AfterClass
+ public static void removeGson() {
+ sGson = null;
+ }
+ }
+
+
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/SourceFilePositionJsonSerializerTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/SourceFilePositionJsonSerializerTest.java
new file mode 100644
index 0000000..e775370
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/blame/SourceFilePositionJsonSerializerTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.blame;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Enclosed.class)
+public class SourceFilePositionJsonSerializerTest {
+
+ private static GsonBuilder sGsonBuilder = new GsonBuilder();
+
+ static {
+ MessageJsonSerializer.registerTypeAdapters(sGsonBuilder);
+ }
+
+ private static Object[][] allPairings(Object[] x, Object[] y) {
+ int totalSize = x.length * y.length;
+
+ final Object[][] matrix = new Object[totalSize][];
+ for (int i = 0; i < totalSize; i++) {
+ matrix[i] = new Object[2];
+ matrix[i][0] = x[i / y.length];
+ matrix[i][1] = y[i % y.length];
+ }
+
+ return matrix;
+
+ }
+
+ @RunWith(Parameterized.class)
+ public static class RoundTripTest {
+
+ private static Gson sGson;
+
+ @Parameterized.Parameter
+ public SourceFile mSourceFile;
+
+ @Parameterized.Parameter(value = 1)
+ public SourcePosition mSourcePosition;
+
+ @Parameterized.Parameters(name = "SourceFilePosition({0}, {1})")
+ public static Collection<Object[]> data() {
+
+ return Arrays.asList(allPairings(new SourceFile[]{
+ new SourceFile(new File("/path/to/a/file.java")),
+ new SourceFile(new File("/path/to/a/file.java"), "Description"),
+ new SourceFile("Description"),
+ SourceFile.UNKNOWN},
+ SourcePositionJsonTypeAdapterTest.mExamples));
+ }
+
+ @BeforeClass
+ public static void initGson() {
+ sGson = sGsonBuilder.create();
+ }
+
+ @AfterClass
+ public static void removeGson() {
+ sGson = null;
+ }
+
+ @Test
+ public void test() {
+ SourceFilePosition item = new SourceFilePosition(mSourceFile, mSourcePosition);
+ assertEquals(item, sGson.fromJson(sGson.toJson(item), SourceFilePosition.class));
+ }
+ }
+
+ @RunWith(Parameterized.class)
+ public static class DeserializeTest {
+
+ private static Gson sGson;
+
+ @Parameterized.Parameter(value = 0)
+ public SourceFilePosition mSourceFilePosition;
+
+ @Parameterized.Parameter(value = 1)
+ public String jsonString;
+
+ @Parameterized.Parameters(name = "fromJson( {1} ) → {0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][]{
+ {new SourceFilePosition(
+ new SourceFile(new File("/path/file.java")),
+ SourcePosition.UNKNOWN),
+ "{\"file\":\"/path/file.java\"}"},
+ {new SourceFilePosition(
+ new SourceFile(new File("/path/file.java"), "Description"),
+ new SourcePosition(1, 3, 4)),
+ "{file:{\"description\":\"Description\",\"path\":\"/path/file.java\"},"
+ + "position:{startLine:1, startColumn:3,startOffset:4}}"},
+ {new SourceFilePosition(
+ new SourceFile("Description"),
+ new SourcePosition(11, 22, 33, 66, 77, 88)),
+ "{\"file\":{\"description\":\"Description\"}, " + "position: {"
+ + "\"startLine\":11,\"startColumn\":22,\"startOffset\":33,"
+ + "\"endLine\":66,\"endColumn\":77,\"endOffset\":88" + "}}"},
+ {new SourceFilePosition(
+ SourceFile.UNKNOWN,
+ new SourcePosition(11, 22, 33, 66, 77, 88)),
+ "{ position: {\"startLine\":11,\"startColumn\":22,\"startOffset\":33,"
+ + "\"endLine\":66,\"endColumn\":77,\"endOffset\":88" + "}, "
+ + "\"invalid_something\":[\"ignored tree\"]}"},
+ {new SourceFilePosition(SourceFile.UNKNOWN, SourcePosition.UNKNOWN), "{}"},});
+ }
+
+ @BeforeClass
+ public static void initGson() {
+ sGson = sGsonBuilder.create();
+ }
+
+ @AfterClass
+ public static void removeGson() {
+ sGson = null;
+ }
+
+ @Test
+ public void test() {
+ assertEquals(mSourceFilePosition, sGson.fromJson(jsonString, SourceFilePosition.class));
+ }
+ }
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/SourcePositionJsonTypeAdapterTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/SourcePositionJsonTypeAdapterTest.java
new file mode 100644
index 0000000..d58143d
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/blame/SourcePositionJsonTypeAdapterTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.common.blame;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+
+@RunWith(Enclosed.class)
+public class SourcePositionJsonTypeAdapterTest {
+
+ /* package */ static SourcePosition[] mExamples = new SourcePosition[]{
+ new SourcePosition(-1, -1, -1),
+ new SourcePosition(11, 22, 34),
+ new SourcePosition(11, 22, -1),
+ new SourcePosition(11, -1, 34),
+ new SourcePosition(-1, 22, 34),
+ new SourcePosition(11, 22, 33, 66, 77, 88),
+ new SourcePosition(11, 22, -1, 66, 77, -1),
+ new SourcePosition(11, -1, -1, 66, -1, -1),
+ new SourcePosition(11, -1, -1, 11, -1, -1),
+ new SourcePosition(11, 22, 33, 66, 77, 88),
+ new SourcePosition(-1, -1, 33, -1, -1, 88),
+ new SourcePosition(-1, -1, 33, -1, -1, 33),
+ new SourcePosition(11, 22, 33, 11, 22, 33)};
+
+ @RunWith(Parameterized.class)
+ public static class RoundTripTest {
+
+ private static Gson sGson;
+
+ @Parameterized.Parameter
+ public SourcePosition mSourcePosition;
+
+ @Parameterized.Parameter(value = 1)
+ public String mIgnoredName;
+
+ @Parameterized.Parameters(name = "{0} new SourcePosition({1})")
+ public static Iterable<Object[]> data() {
+ return Iterables.transform(Arrays.asList(mExamples),
+ new Function<SourcePosition, Object[]>() {
+ @Override
+ public Object[] apply(SourcePosition input) {
+ return new Object[]{input, Joiner.on(", ").join(input.getStartLine(),
+ input.getStartColumn(),
+ input.getStartOffset(), input.getEndLine(),
+ input.getEndColumn(), input.getEndOffset())};
+ }
+ });
+ }
+
+ @Test
+ public void roundTrip() {
+ String json = sGson.toJson(mSourcePosition);
+ SourcePosition m2 = sGson.fromJson(json, SourcePosition.class);
+ assertEquals(mSourcePosition, m2);
+ }
+
+ @BeforeClass
+ public static void initGson() {
+ sGson = newGson();
+ }
+
+ @AfterClass
+ public static void removeGson() {
+ sGson = null;
+ }
+ }
+
+ public static class DeserializeTest {
+
+ private Gson mGson = newGson();
+
+ @Test
+ public void testSimpleDeserialize() {
+ String json2 = "{\"startLine\":245}";
+ SourcePosition range2 =
+ mGson.fromJson(json2, SourcePosition.class);
+ assertEquals(new SourcePosition(245, -1, -1), range2);
+ }
+
+ @Test
+ public void testDeserialize() {
+ String json
+ = "{\"startLine\":11,\"startColumn\":22,\"startOffset\":33,"
+ + "\"endLine\":66,\"endColumn\":77,\"endOffset\":88, "
+ + "\"invalid\":[\"ignored\"]}";
+ SourcePosition range =
+ mGson.fromJson(json, SourcePosition.class);
+ assertEquals(range, new SourcePosition(11, 22, 33, 66, 77, 88));
+ }
+ }
+
+ private static Gson newGson() {
+ return new GsonBuilder()
+ .registerTypeAdapter(
+ SourcePosition.class,
+ new SourcePositionJsonTypeAdapter())
+ .create();
+ }
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/parser/AaptOutputParserTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/parser/AaptOutputParserTest.java
index 8058d3f..de9cb54 100644
--- a/sdk-common/src/test/java/com/android/ide/common/blame/parser/AaptOutputParserTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/blame/parser/AaptOutputParserTest.java
@@ -23,17 +23,17 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.ide.common.blame.output.GradleMessage;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
import com.android.ide.common.blame.parser.aapt.AaptOutputParser;
import com.android.ide.common.blame.parser.aapt.AbstractAaptOutputParser;
-import com.android.utils.SdkUtils;
import com.android.utils.StdLogger;
import com.android.utils.StringHelper;
import com.google.common.base.Charsets;
import com.google.common.io.Closeables;
import com.google.common.io.Files;
-import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import java.io.BufferedWriter;
@@ -48,9 +48,6 @@
* Tests for {@link ToolOutputParser}.
*/
public class AaptOutputParserTest extends TestCase {
-
- private static final String NEWLINE = SdkUtils.getLineSeparator();
-
private File sourceFile;
private String sourceFilePath;
@@ -58,7 +55,7 @@
private ToolOutputParser parser;
@Nullable
- private static String getSystemIndependentSourcePath(@NonNull GradleMessage message) {
+ private static String getSystemIndependentSourcePath(@NonNull Message message) {
String sourcePath = message.getSourcePath();
return sourcePath == null ? null : sourcePath.replace('\\', '/');
}
@@ -69,21 +66,21 @@
}
@NonNull
- private static String toString(@NonNull List<GradleMessage> messages) {
+ private static String toString(@NonNull List<Message> messages) {
StringBuilder sb = new StringBuilder();
for (int i = 0, n = messages.size(); i < n; i++) {
- GradleMessage message = messages.get(i);
+ Message message = messages.get(i);
sb.append(Integer.toString(i)).append(':').append(' ');
sb.append(
StringHelper.capitalize(message.getKind().toString().toLowerCase(Locale.US)))
.append(':'); // INFO => Info
sb.append(message.getText());
- if (message.getSourcePath() != null) {
+ if (!message.getSourceFilePositions().isEmpty() &&
+ !message.getSourceFilePositions().get(0).getPosition().equals(
+ SourcePosition.UNKNOWN)) {
sb.append('\n');
sb.append('\t');
- sb.append(message.getSourcePath());
- sb.append(':').append(Long.toString(message.getLineNumber()));
- sb.append(':').append(Long.toString(message.getColumn()));
+ sb.append(message.getSourceFilePositions().get(0).toString());
}
sb.append('\n');
}
@@ -91,14 +88,6 @@
return sb.toString();
}
- public static void assertEquals(String expected, String actual) {
- if (expected != null || actual != null) {
- if (expected == null || !expected.equals(actual)) {
- throw new ComparisonFailure(null, expected, actual);
- }
- }
- }
-
@Override
public void setUp() throws Exception {
super.setUp();
@@ -116,11 +105,11 @@
public void testParseDisplayingUnhandledMessages() {
String output = " **--- HELLO WORLD ---**";
- List<GradleMessage> gradleMessages = parser.parseToolOutput(output);
+ List<Message> gradleMessages = parser.parseToolOutput(output);
assertEquals(1, gradleMessages.size());
- GradleMessage message = gradleMessages.get(0);
+ Message message = gradleMessages.get(0);
assertEquals(output, message.getText());
- assertEquals(GradleMessage.Kind.SIMPLE, message.getKind());
+ assertEquals(Message.Kind.SIMPLE, message.getKind());
}
public void testParseAaptOutputWithRange1() throws IOException {
@@ -132,7 +121,7 @@
String messageText = "No resource found that matches the given name (at 'label' with value "
+ "'@string/app_name2').";
String err = sourceFilePath + ":4: error: Error: " + messageText;
- Collection<GradleMessage> messages = parser.parseToolOutput(err);
+ Collection<Message> messages = parser.parseToolOutput(err);
assertHasCorrectErrorMessage(messages, messageText, 4, 61);
}
@@ -149,7 +138,7 @@
String messageText = "No resource found that matches the given name (at 'label' with value "
+ "'@string/app_name2').";
String err = sourceFilePath + ":4: error: Error: " + messageText;
- Collection<GradleMessage> messages = parser.parseToolOutput(err);
+ Collection<Message> messages = parser.parseToolOutput(err);
assertHasCorrectErrorMessage(messages, messageText, 5, 8);
}
@@ -166,7 +155,7 @@
" <item name='android:gravity'>left</item>");
String messageText = "Resource entry repeatedStyle1 already has bag item android:gravity.";
String err = sourceFilePath + ":6: error: " + messageText;
- Collection<GradleMessage> messages = parser.parseToolOutput(err);
+ Collection<Message> messages = parser.parseToolOutput(err);
assertHasCorrectErrorMessage(messages, messageText, 6, 17);
}
@@ -181,7 +170,7 @@
" <item name='android:gravity'>left</item>");
String messageText = "Originally defined here.";
String err = sourceFilePath + ":3: " + messageText;
- Collection<GradleMessage> messages = parser.parseToolOutput(err);
+ Collection<Message> messages = parser.parseToolOutput(err);
assertHasCorrectErrorMessage(messages, messageText, 3, 5);
}
@@ -194,7 +183,7 @@
" <item name='nonexistent'>left</item>");
String messageText = "No resource found that matches the given name: attr 'nonexistent'.";
String err = sourceFilePath + ":3: error: Error: " + messageText;
- Collection<GradleMessage> messages = parser.parseToolOutput(err);
+ Collection<Message> messages = parser.parseToolOutput(err);
assertHasCorrectErrorMessage(messages, messageText, 3, 17);
}
@@ -205,7 +194,7 @@
" <style>");
String messageText = "A 'name' attribute is required for <style>";
String err = sourceFilePath + ":2: error: " + messageText;
- Collection<GradleMessage> messages = parser.parseToolOutput(err);
+ Collection<Message> messages = parser.parseToolOutput(err);
assertHasCorrectErrorMessage(messages, messageText, 2, 3);
}
@@ -215,7 +204,7 @@
" <item>");
String messageText = "A 'type' attribute is required for <item>";
String err = sourceFilePath + ":2: error: " + messageText;
- Collection<GradleMessage> messages = parser.parseToolOutput(err);
+ Collection<Message> messages = parser.parseToolOutput(err);
assertHasCorrectErrorMessage(messages, messageText, 2, 3);
}
@@ -225,7 +214,7 @@
" <item>");
String messageText = "A 'name' attribute is required for <item>";
String err = sourceFilePath + ":2: error: " + messageText;
- Collection<GradleMessage> messages = parser.parseToolOutput(err);
+ Collection<Message> messages = parser.parseToolOutput(err);
assertHasCorrectErrorMessage(messages, messageText, 2, 3);
}
@@ -236,7 +225,7 @@
" <item name='android:layout_width'></item>");
String messageText = "String types not allowed (at 'android:layout_width' with value '').";
String err = sourceFilePath + ":3: error: " + messageText;
- Collection<GradleMessage> messages = parser.parseToolOutput(err);
+ Collection<Message> messages = parser.parseToolOutput(err);
assertHasCorrectErrorMessage(messages, messageText, 3, 21);
}
@@ -253,7 +242,7 @@
" android:layout_marginLeft=''");
String messageText = "String types not allowed (at 'layout_marginTop' with value '').";
String err = sourceFilePath + ":5: error: Error: " + messageText;
- Collection<GradleMessage> messages = parser.parseToolOutput(err);
+ Collection<Message> messages = parser.parseToolOutput(err);
assertHasCorrectErrorMessage(messages, messageText, 8, 34);
}
@@ -270,7 +259,7 @@
" android:layout_marginLeft=''");
String messageText = "String types not allowed (at 'layout_marginLeft' with value '').";
String err = sourceFilePath + ":5: error: Error: " + messageText;
- Collection<GradleMessage> messages = parser.parseToolOutput(err);
+ Collection<Message> messages = parser.parseToolOutput(err);
assertHasCorrectErrorMessage(messages, messageText, 9, 35);
}
@@ -284,7 +273,7 @@
" android:id=''");
String messageText = "String types not allowed (at 'id' with value '').";
String err = sourceFilePath + ":5: error: Error: " + messageText;
- Collection<GradleMessage> messages = parser.parseToolOutput(err);
+ Collection<Message> messages = parser.parseToolOutput(err);
assertHasCorrectErrorMessage(messages, messageText, 6, 20);
}
@@ -310,33 +299,28 @@
Closeables.close(out, true /* swallowIOException */);
}
}
- // String homePath = PathManager.getHomePath();
- // assertNotNull(homePath);
- // // The relative paths in the output file below is relative to the sdk-common directory in tools/base
- // // (it's from one of the unit tests there)
- // File rootDir = new File(homePath, ".." + File.separator + "base" + File.separator + "sdk-common");
- // if (!rootDir.isDirectory()) {
- // // Also check for an IDEA project structure where sdk-common is located in this location:
- // rootDir = new File(homePath, "android" + File.separator + "tools-base" + File.separator + "sdk-common");
- // if (!rootDir.isDirectory()) {
- // return false;
- // }
- // }
- // AbstractAaptOutputParser.ourRootDir = rootDir;
- // return true;
- //}
- private void assertHasCorrectErrorMessage(@NonNull Collection<GradleMessage> messages,
+ /**
+ * Assert the error message is correct.
+ *
+ * @param messages a collection of Messages
+ * @param expectedText the text the single gradle message should have.
+ * @param expectedLine the 1-based line
+ * @param expectedColumn the 1-based column.
+ */
+ private void assertHasCorrectErrorMessage(@NonNull Collection<Message> messages,
@NonNull String expectedText,
- long expectedLine,
- long expectedColumn) {
+ int expectedLine,
+ int expectedColumn) {
assertEquals("[message count]", 1, messages.size());
- GradleMessage message = messages.iterator().next();
- assertEquals("[file path]", sourceFilePath, message.getSourcePath());
- assertEquals("[message severity]", GradleMessage.Kind.ERROR, message.getKind());
+ Message message = messages.iterator().next();
+ assertEquals("[source file position count]", 1, message.getSourceFilePositions().size());
+ SourceFilePosition position = message.getSourceFilePositions().get(0);
+ assertEquals("[file path]", sourceFilePath, position.getFile().toString());
+ assertEquals("[message severity]", Message.Kind.ERROR, message.getKind());
assertEquals("[message text]", expectedText, message.getText());
- assertEquals("[position line]", expectedLine, message.getLineNumber());
- assertEquals("[position column]", expectedColumn, message.getColumn());
+ assertEquals("[position line]", expectedLine, position.getPosition().getStartLine() + 1);
+ assertEquals("[position column]", expectedColumn, position.getPosition().getStartColumn() + 1);
}
public void testRedirectValueLinksOutput() throws Exception {
@@ -445,23 +429,24 @@
String messageText
= "String types not allowed (at 'drawable_ref' with value '@drawable/stat_notify_sync_anim0').";
String err = sourceFilePath + ":46: error: Error: " + messageText;
- Collection<GradleMessage> messages = parser.parseToolOutput(err);
+ Collection<Message> messages = parser.parseToolOutput(err);
assertEquals(1, messages.size());
assertEquals("[message count]", 1, messages.size());
- GradleMessage message = messages.iterator().next();
+ Message message = messages.iterator().next();
assertNotNull(message);
// NOT sourceFilePath; should be translated back from source comment
- assertEquals("[file path]",
- "src/test/resources/testData/resources/baseSet/values/values.xml",
- getSystemIndependentSourcePath(message));
+ assertEquals(new File ("src/test/resources/testData/resources/baseSet/values/values.xml").getAbsolutePath(),
+ getSystemIndependentSourcePath(message));
- assertEquals("[message severity]", GradleMessage.Kind.ERROR, message.getKind());
+ assertEquals("[message severity]", Message.Kind.ERROR, message.getKind());
assertEquals("[message text]", messageText, message.getText());
- assertEquals("[position line]", 9, message.getLineNumber());
- assertEquals("[position column]", 35, message.getColumn());
+ assertEquals(1, message.getSourceFilePositions().size());
+ SourcePosition pos = message.getSourceFilePositions().get(0).getPosition();
+ assertEquals("[position line]", 9, pos.getStartLine() + 1);
+ assertEquals("[position column]", 35, pos.getStartColumn() + 1);
}
public void testRedirectFileLinksOutput() throws Exception {
@@ -498,21 +483,18 @@
String messageText = "Random error message here";
String err = sourceFilePath + ":4: error: Error: " + messageText;
- Collection<GradleMessage> messages = parser.parseToolOutput(err);
- assertEquals(1, messages.size());
-
+ Collection<Message> messages = parser.parseToolOutput(err);
assertEquals("[message count]", 1, messages.size());
- GradleMessage message = messages.iterator().next();
+ Message message = messages.iterator().next();
assertNotNull(message);
// NOT sourceFilePath; should be translated back from source comment
- String expected
- = "src/test/resources/testData/resources/incMergeData/filesVsValues/main/layout/main.xml";
+ String expected = new File("src/test/resources/testData/resources/incMergeData/filesVsValues/main/layout/main.xml")
+ .getAbsolutePath();
assertEquals("[file path]", expected, getSystemIndependentSourcePath(message));
-
- assertEquals("[message severity]", GradleMessage.Kind.ERROR, message.getKind());
+ assertEquals("[message severity]", Message.Kind.ERROR, message.getKind());
assertEquals("[message text]", messageText, message.getText());
- assertEquals("[position line]", 4, message.getLineNumber());
+ assertEquals("[position line]", 4, message.getSourceFilePositions().get(0).getPosition().getStartLine() + 1);
//assertEquals("[position column]", 35, message.getColumn());
// TODO: Test encoding issues (e.g. & in path where the XML source comment would be & instead)
@@ -596,7 +578,7 @@
"5: Simple::BlankProject1:processDebugManifest UP-TO-DATE\n" +
"6: Simple::BlankProject1:processDebugResources\n" +
"7: Error:Integer types not allowed (at 'new_name' with value '50').\n" +
- "\t" + source.getPath() + ":5:28\n" +
+ "\t" + source.getPath() + ":5:28-30\n" +
"8: Simple::BlankProject1:processDebugResources FAILED\n"; /* +
"9: Error:Error while executing aapt command\n" +
"10: Error:Integer types not allowed (at 'new_name' with value '50').\n" +
@@ -675,7 +657,7 @@
"5: Simple::BlankProject1:processDebugManifest UP-TO-DATE\n" +
"6: Simple::BlankProject1:processDebugResources\n" +
"7: Error:Integer types not allowed (at 'new_name' with value '50').\n" +
- "\t" + source.getPath() + ":5:28\n" +
+ "\t" + source.getPath() + ":5:28-30\n" +
"8: Simple::BlankProject1:processDebugResources FAILED\n";
String actual = toString(parser.parseToolOutput(output));
@@ -771,7 +753,7 @@
"11: Simple::BlankProject1:processDebugResources\n" +
"12: Error:No resource identifier found for attribute 'slayout_alignParentTop' in package 'android'\n"
+
- "\t" + source.getPath() + ":12:-1\n" +
+ "\t" + source.getPath() + ":12\n" +
"13: Simple::BlankProject1:processDebugResources FAILED\n",
toString(parser.parseToolOutput(output)));
@@ -804,28 +786,9 @@
"6: Simple::AudioPlayer:processDebugManifest UP-TO-DATE\n" +
"7: Simple::AudioPlayer:processDebugResources\n" +
"8: Error:Error parsing XML: mismatched tag\n" +
- "\t" + sourceFilePath + ":101:-1\n" +
+ "\t" + sourceFilePath + ":101\n" +
"9: Simple::AudioPlayer:processDebugResources FAILED\n",
toString(parser.parseToolOutput(output)));
sourceFile.delete();
}
-
- private static class ComparisonFailure extends AssertionFailedError {
-
- private final String fExpected;
-
- private final String fActual;
-
- public ComparisonFailure(String message, String expected, String actual) {
- super(message);
- this.fExpected = expected;
- this.fActual = actual;
- }
-
- @Override
- public String getMessage() {
- return "expected:<" + fExpected + "> but was:<" + fActual + ">";
-
- }
- }
}
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/parser/GradleMessageRewriterTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/parser/GradleMessageRewriterTest.java
deleted file mode 100644
index 141f761..0000000
--- a/sdk-common/src/test/java/com/android/ide/common/blame/parser/GradleMessageRewriterTest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.ide.common.blame.parser;
-
-import com.android.annotations.NonNull;
-import com.android.ide.common.blame.SourcePosition;
-import com.android.ide.common.blame.output.GradleMessage;
-import com.android.ide.common.blame.output.GradleMessageRewriter;
-import com.android.ide.common.blame.parser.util.OutputLineReader;
-import com.android.ide.common.res2.RecordingLogger;
-import com.android.utils.ILogger;
-
-import junit.framework.TestCase;
-
-import java.util.List;
-
-public class GradleMessageRewriterTest extends TestCase {
-
- private GradleMessageRewriter mGradleMessageRewriter;
-
- private ILogger mLogger;
-
- private PatternAwareOutputParser mFakePatternParser;
-
- @Override
- public void setUp() throws Exception {
- mLogger = new RecordingLogger();
- mFakePatternParser = new FakePatternAwareOutputParser();
-
- mGradleMessageRewriter = new GradleMessageRewriter(
- new ToolOutputParser(mFakePatternParser, mLogger),
- GradleMessageRewriter.ErrorFormatMode.MACHINE_PARSABLE);
- }
-
- public void testRewriteGradleMessages() {
- String original = "error example\ntwo line error\nnext line\nsomething else";
- String rewriten = mGradleMessageRewriter.rewriteMessages(original);
- String expected = "AGPBI: {\"kind\":\"ERROR\",\"text\":\"errorText\",\"sourcePath\":\"error/source\","
- + "\"position\":{\"startLine\":1,\"startColumn\":2,\"startOffset\":3,"
- + "\"endLine\":4,\"endColumn\":5,\"endOffset\":6},\"original\":\"\"}\n"
- + "AGPBI: {\"kind\":\"WARNING\",\"text\":"
- + "\"two line warning\",\"sourcePath\":\"sourcePath\","
- + "\"position\":{\"startLine\":1,\"startColumn\":2},\"original\":\"\"}\n"
- + "AGPBI: {\"kind\":\"SIMPLE\","
- + "\"text\":\"something else\",\"position\":{},\"original\":\"something else\"}";
- assertEquals(expected.trim(), rewriten.trim());
- }
-
- public void testParseException() {
- String original = "two line error";
- String rewriten = mGradleMessageRewriter.rewriteMessages(original);
- assertEquals(original, rewriten);
- }
-
- private class FakePatternAwareOutputParser implements PatternAwareOutputParser {
- @Override
- public boolean parse(@NonNull String line, @NonNull OutputLineReader reader,
- @NonNull List<GradleMessage> messages, @NonNull ILogger logger)
- throws ParsingFailedException {
- if (line.equals("two line error")) {
- String nextLine = reader.readLine();
- if ("next line".equals(nextLine)) {
- messages.add(new GradleMessage(GradleMessage.Kind.WARNING, "two line warning",
- "sourcePath", new SourcePosition(1, 2, -1), ""));
- } else {
- throw new ParsingFailedException();
- }
- return true;
- }
- if (line.equals("error example")) {
- messages.add(
- new GradleMessage(
- GradleMessage.Kind.ERROR,
- "errorText",
- "error/source",
- new SourcePosition(1, 2, 3, 4, 5, 6),
- ""));
- return true;
- }
- return false;
- }
- }
-}
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/parser/LegacyNdkOutputParserTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/parser/LegacyNdkOutputParserTest.java
new file mode 100644
index 0000000..43e5bcb
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/blame/parser/LegacyNdkOutputParserTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.common.blame.parser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import com.android.ide.common.blame.Message;
+import com.android.utils.StdLogger;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Tests for {@link LegacyNdkOutputParser}.
+ */
+public class LegacyNdkOutputParserTest {
+ @Rule
+ public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ private ToolOutputParser mParser;
+
+ private File mSourceFile;
+
+ @Before
+ public void setUp() throws IOException {
+ mParser = new ToolOutputParser(new LegacyNdkOutputParser(),
+ new StdLogger(StdLogger.Level.VERBOSE));
+ mSourceFile = mTemporaryFolder.newFile();
+ }
+
+ @Test
+ public void testParseUnresolvedInclude() {
+ String path = mSourceFile.getAbsolutePath();
+ String err = path +
+ ":35:18: fatal error: fake.h: No such file or directory\n" +
+ " #include \"fake.h\"\n" +
+ " ^";
+ List<Message> messages = mParser.parseToolOutput(err);
+ assertEquals("[message count]", 1, messages.size());
+ Message message = messages.iterator().next();
+ assertNotNull(message);
+
+ assertEquals("fake.h: No such file or directory", message.getText());
+ assertEquals(Message.Kind.ERROR, message.getKind());
+ assertEquals(path, message.getSourcePath());
+ assertEquals(35, message.getLineNumber());
+ assertEquals(18, message.getColumn());
+ }
+
+ @Test
+ public void testParseUnknownMessage() {
+ String path = mSourceFile.getAbsolutePath();
+ String err = "In file included from " + path + ":35:18,";
+ List<Message> messages = mParser.parseToolOutput(err);
+ assertEquals("[message count]", 1, messages.size());
+ Message message = messages.iterator().next();
+ assertNotNull(message);
+
+ assertEquals("(Unknown) In file included", message.getText());
+ assertEquals(Message.Kind.INFO, message.getKind());
+ assertEquals(path, message.getSourcePath());
+ assertEquals(35, message.getLineNumber());
+ assertEquals(18, message.getColumn());
+ }
+
+ @Test
+ public void testParseUnknownMessage2() {
+ String path = mSourceFile.getAbsolutePath();
+ String err = " from " + path + ":35:18:";
+ List<Message> messages = mParser.parseToolOutput(err);
+ assertEquals("[message count]", 1, messages.size());
+ Message message = messages.iterator().next();
+ assertNotNull(message);
+
+ assertEquals("(Unknown)", message.getText());
+ assertEquals(Message.Kind.INFO, message.getKind());
+ assertEquals(path, message.getSourcePath());
+ assertEquals(35, message.getLineNumber());
+ assertEquals(18, message.getColumn());
+ }
+}
\ No newline at end of file
diff --git a/sdk-common/src/test/java/com/android/ide/common/blame/parser/SourcePositionJsonTypeAdapterTest.java b/sdk-common/src/test/java/com/android/ide/common/blame/parser/SourcePositionJsonTypeAdapterTest.java
deleted file mode 100644
index cde1e47..0000000
--- a/sdk-common/src/test/java/com/android/ide/common/blame/parser/SourcePositionJsonTypeAdapterTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.ide.common.blame.parser;
-
-import com.android.ide.common.blame.SourcePosition;
-import com.android.ide.common.blame.SourcePositionJsonTypeAdapter;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-
-import junit.framework.TestCase;
-
-public class SourcePositionJsonTypeAdapterTest extends TestCase {
-
- private Gson gsonSerializer;
-
- private Gson gsonDeserializer;
-
- private SourcePosition[] mExamples = new SourcePosition[]{
- new SourcePosition(-1, -1, -1),
- new SourcePosition(11, 22, 34),
- new SourcePosition(11, 22, -1),
- new SourcePosition(11, -1, 34),
- new SourcePosition(-1, 22, 34),
- new SourcePosition(11, 22, 33, 66, 77, 88),
- new SourcePosition(11, 22, -1, 66, 77, -1),
- new SourcePosition(11, -1, -1, 66, -1, -1),
- new SourcePosition(11, -1, -1, 11, -1, -1),
- new SourcePosition(11, 22, 33, 66, 77, 88),
- new SourcePosition(-1, -1, 33, -1, -1, 88),
- new SourcePosition(-1, -1, 33, -1, -1, 33),
- new SourcePosition(11, 22, 33, 11, 22, 33)};
-
- @Override
- public void setUp() {
- gsonSerializer = new GsonBuilder()
- .registerTypeAdapter(
- SourcePosition.class,
- new SourcePositionJsonTypeAdapter())
- .create();
- gsonDeserializer = new GsonBuilder()
- .registerTypeAdapter(
- SourcePosition.class,
- new SourcePositionJsonTypeAdapter())
- .create();
- }
-
-
- public void testSerializeDeserializeRoundtrip() {
- for (SourcePosition range : mExamples) {
- testRoundTripExample(range);
- }
- }
-
- private void testRoundTripExample(SourcePosition m1) {
- String json = gsonSerializer.toJson(m1);
- SourcePosition m2 =
- gsonDeserializer.fromJson(json, SourcePosition.class);
- assertEquals(m1, m2);
- }
-
-
- public void testSimpleDeserialize() {
- String json2 = "{\"startLine\":245}";
- SourcePosition range2 =
- gsonDeserializer.fromJson(json2, SourcePosition.class);
- assertEquals(new SourcePosition(245, -1, -1), range2);
- }
-
- public void testDeserialize() {
- String json
- = "{\"startLine\":11,\"startColumn\":22,\"startOffset\":33,\"endLine\":66,\"endColumn\":77,\"endOffset\":88}";
- SourcePosition range =
- gsonDeserializer.fromJson(json, SourcePosition.class);
- assertEquals(range, new SourcePosition(11, 22, 33, 66, 77, 88));
- }
-}
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 18129ff..209ef5d 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
@@ -4,7 +4,6 @@
import static com.android.ide.common.rendering.HardwareConfigHelper.getNexusLabel;
import static com.android.ide.common.rendering.HardwareConfigHelper.isGeneric;
import static com.android.ide.common.rendering.HardwareConfigHelper.isNexus;
-import static com.android.ide.common.rendering.HardwareConfigHelper.isRound;
import static com.android.ide.common.rendering.HardwareConfigHelper.isTv;
import static com.android.ide.common.rendering.HardwareConfigHelper.isWear;
import static com.android.ide.common.rendering.HardwareConfigHelper.nexusRank;
@@ -77,37 +76,37 @@
assertNotNull(qvga);
assertFalse(isWear(qvga));
assertFalse(isTv(qvga));
- assertFalse(isRound(qvga));
+ assertFalse(qvga.isScreenRound());
Device nexus5 = deviceManager.getDevice("Nexus 5", "Google");
assertNotNull(nexus5);
assertFalse(isWear(nexus5));
assertFalse(isTv(nexus5));
- assertFalse(isRound(nexus5));
+ assertFalse(nexus5.isScreenRound());
Device square = deviceManager.getDevice("wear_square", "Google");
assertNotNull(square);
assertTrue(isWear(square));
- assertFalse(isRound(square));
+ assertFalse(square.isScreenRound());
assertFalse(isTv(square));
Device round = deviceManager.getDevice("wear_round", "Google");
assertNotNull(round);
assertTrue(isWear(round));
- assertTrue(isRound(round));
+ assertTrue(round.isScreenRound());
assertFalse(isTv(round));
Device tv1080p = deviceManager.getDevice("tv_1080p", "Google");
assertNotNull(tv1080p);
assertTrue(isTv(tv1080p));
assertFalse(isWear(tv1080p));
- assertFalse(isRound(tv1080p));
+ assertFalse(tv1080p.isScreenRound());
Device tv720p = deviceManager.getDevice("tv_1080p", "Google");
assertNotNull(tv720p);
assertFalse(isWear(tv720p));
assertTrue(isTv(tv720p));
- assertFalse(isRound(tv720p));
+ assertFalse(tv720p.isScreenRound());
}
public void testNexusRank() {
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
index 6f796f8..19a3214 100644
--- 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
@@ -156,7 +156,7 @@
try {
manager.setActive(true, myCredential);
- System.loadLibrary("fontmanager");
+ System.loadLibrary("jsound");
} catch (UnsatisfiedLinkError e) {
// pass - library may not be present on all JDKs
} finally {
diff --git a/sdk-common/src/test/java/com/android/ide/common/repository/SdkMavenRepositoryTest.java b/sdk-common/src/test/java/com/android/ide/common/repository/SdkMavenRepositoryTest.java
index 1068a4d..e2665b3 100644
--- a/sdk-common/src/test/java/com/android/ide/common/repository/SdkMavenRepositoryTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/repository/SdkMavenRepositoryTest.java
@@ -99,6 +99,24 @@
assertEquals("google", SdkMavenRepository.GOOGLE.getDirName());
}
+ @SuppressWarnings("ConstantConditions")
+ public void testGetByGroupId() {
+ assertSame(SdkMavenRepository.ANDROID, SdkMavenRepository.getByGroupId(
+ GradleCoordinate.parseCoordinateString(
+ "com.android.support:appcompat-v7:13.0.0").getGroupId()));
+ assertSame(SdkMavenRepository.ANDROID, SdkMavenRepository.getByGroupId(
+ GradleCoordinate.parseCoordinateString(
+ "com.android.support.test:espresso:0.2").getGroupId()));
+ assertSame(SdkMavenRepository.GOOGLE, SdkMavenRepository.getByGroupId(
+ GradleCoordinate.parseCoordinateString(
+ "com.google.android.gms:play-services:5.2.08").getGroupId()));
+ assertSame(SdkMavenRepository.GOOGLE, SdkMavenRepository.getByGroupId(
+ GradleCoordinate.parseCoordinateString(
+ "com.google.android.gms:play-services-wearable:5.0.77").getGroupId()));
+ assertNull(SdkMavenRepository.getByGroupId(GradleCoordinate.parseCoordinateString(
+ "com.google.guava:guava:11.0.2").getGroupId()));
+ }
+
/**
* Environment variable or system property containing the full path to an SDK install
*/
diff --git a/sdk-common/src/test/java/com/android/ide/common/res2/BaseTestCase.java b/sdk-common/src/test/java/com/android/ide/common/res2/BaseTestCase.java
index 1232ea0..3047ef9 100644
--- a/sdk-common/src/test/java/com/android/ide/common/res2/BaseTestCase.java
+++ b/sdk-common/src/test/java/com/android/ide/common/res2/BaseTestCase.java
@@ -39,7 +39,7 @@
for (String resKey : dataItemKeys) {
List<? extends DataItem> items = map.get(resKey);
- assertTrue("resource '" + resKey + "' is missing!", items.size() > 0);
+ assertTrue("resource '" + resKey + "' is missing!", !items.isEmpty());
}
}
diff --git a/sdk-common/src/test/java/com/android/ide/common/res2/FileResourceNameValidatorTest.java b/sdk-common/src/test/java/com/android/ide/common/res2/FileResourceNameValidatorTest.java
index 1f33214..21e6fd4 100644
--- a/sdk-common/src/test/java/com/android/ide/common/res2/FileResourceNameValidatorTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/res2/FileResourceNameValidatorTest.java
@@ -53,8 +53,11 @@
{"foo.jpg", ResourceType.DRAWABLE, null},
{"foo.jpeg", ResourceType.DRAWABLE, null},
{"foo.bmp", ResourceType.DRAWABLE, null},
+ {"foo.webp", ResourceType.DRAWABLE, null},
{"foo.other.png", ResourceType.DRAWABLE, "'.'" + IS_NOT_A_VALID_ETC},
{"foo.xml", ResourceType.XML, null},
+ {"foo.xsd", ResourceType.XML, null},
+ {"_foo.png", ResourceType.DRAWABLE, null},
{"foo.png", ResourceType.XML, "The file name must end with .xml"},
{"foo.8.xml", ResourceType.DRAWABLE, "'.'" + IS_NOT_A_VALID_ETC},
{"foo", ResourceType.DRAWABLE, THE_FILE_NAME_MUST_END_WITH_XML_OR_PNG},
@@ -62,8 +65,8 @@
{"foo", ResourceType.RAW, null},
{"foo", ResourceType.RAW, null},
{"foo.txt", ResourceType.DRAWABLE, THE_FILE_NAME_MUST_END_WITH_XML_OR_PNG},
- {"Foo.png", ResourceType.DRAWABLE,
- "File-based resource names must start with a lowercase letter"},
+ {"1foo.png", ResourceType.DRAWABLE, "The resource name must start with a letter"},
+ {"Foo.png", ResourceType.DRAWABLE, "'F'" + IS_NOT_A_VALID_ETC},
{"foo$.png", ResourceType.DRAWABLE, "'$'" + IS_NOT_A_VALID_ETC},
{"bAr.png", ResourceType.DRAWABLE, "'A'" + IS_NOT_A_VALID_ETC},
{"enum.png", ResourceType.DRAWABLE,
diff --git a/sdk-common/src/test/java/com/android/ide/common/res2/MergingExceptionTest.java b/sdk-common/src/test/java/com/android/ide/common/res2/MergingExceptionTest.java
index d82e1ca..bed8a07 100644
--- a/sdk-common/src/test/java/com/android/ide/common/res2/MergingExceptionTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/res2/MergingExceptionTest.java
@@ -16,50 +16,123 @@
package com.android.ide.common.res2;
-import com.android.ide.common.blame.FilePosition;
-import com.android.ide.common.blame.SourcePosition;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
-import junit.framework.TestCase;
+import com.android.ide.common.blame.Message;
+import com.android.ide.common.blame.SourceFilePosition;
+import com.android.ide.common.blame.SourcePosition;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+import org.junit.Test;
+import org.xml.sax.SAXParseException;
import java.io.File;
+import java.io.IOException;
+import java.util.List;
-public class MergingExceptionTest extends TestCase {
- @SuppressWarnings("ThrowableInstanceNeverThrown")
+@SuppressWarnings({"ThrowableInstanceNeverThrown", "ThrowableResultOfMethodCallIgnored"})
+public class MergingExceptionTest {
+
+ private static final File file = new File("/some/random/path");
+
+ @Test
public void testGetMessage() {
- File file = new File("/some/random/path");
assertEquals("Error: My error message",
- new MergingException("My error message").getMessage());
+ MergingException.withMessage("My error message").build().getMessage());
assertEquals("Error: My error message",
- new MergingException("Error: My error message").getMessage());
+ MergingException.wrapException(new Exception()).withMessage(
+ "Error: My error message").build().getMessage());
+ assertEquals("Error: My error message",
+ MergingException.withMessage("Error: My error message").build().getMessage());
assertEquals("/some/random/path: Error: My error message",
- new MergingException("My error message").addFile(file).getMessage());
+ MergingException.wrapException(new Exception("My error message")).withFile(file)
+ .build().getMessage());
+ MergingException.Builder builder2 = MergingException
+ .wrapException(new Exception("My error message")).withFile(file)
+ .withPosition(new SourcePosition(49, -1, -1));
assertEquals("/some/random/path:50: Error: My error message",
- new MergingException("My error message").addFilePosition(
- new FilePosition(file, new SourcePosition(50, -1, -1)))
+ builder2.build()
.getMessage());
+ MergingException.Builder builder1 = MergingException
+ .wrapException(new Exception("My error message")).withFile(file)
+ .withPosition(new SourcePosition(49, 3, -1));
assertEquals("/some/random/path:50:4: Error: My error message",
- new MergingException("My error message").addFilePosition(
- new FilePosition(file, new SourcePosition(50, 4, -1)))
+ builder1.build()
.getMessage());
+ MergingException.Builder builder = MergingException
+ .wrapException(new Exception("My error message")).withFile(file)
+ .withPosition(new SourcePosition(49, 3, -1));
assertEquals("/some/random/path:50:4: Error: My error message",
- new MergingException("My error message").addFilePosition(
- new FilePosition(file, new SourcePosition(50, 4, -1)))
+ builder.build()
.getLocalizedMessage());
assertEquals("/some/random/path: Error: My error message",
- new MergingException("/some/random/path: My error message").addFile(file)
- .getMessage());
+ MergingException.withMessage("/some/random/path: My error message").withFile(file)
+ .build().getMessage());
assertEquals("/some/random/path: Error: My error message",
- new MergingException("/some/random/path My error message").addFile(file)
- .getMessage());
+ MergingException.withMessage("/some/random/path My error message").withFile(file)
+ .build().getMessage());
// end of string handling checks
assertEquals("/some/random/path: Error: ",
- new MergingException("/some/random/path").addFile(file).getMessage());
+ MergingException.withMessage("/some/random/path").withFile(file).build()
+ .getMessage());
assertEquals("/some/random/path: Error: ",
- new MergingException("/some/random/path").addFile(file).getMessage());
+ MergingException.withMessage("/some/random/path").withFile(file).build().getMessage());
assertEquals("/some/random/path: Error: ",
- new MergingException("/some/random/path:").addFile(file).getMessage());
+ MergingException.withMessage("/some/random/path:").withFile(file).build().getMessage());
assertEquals("/some/random/path: Error: ",
- new MergingException("/some/random/path: ").addFile(file).getMessage());
+ MergingException.withMessage("/some/random/path: ").withFile(file).build().getMessage());
+ }
+
+
+ @Test
+ public void testWrapSaxParseExceptionWithLocation() {
+ SAXParseException saxParseException = new SAXParseException("message", "", "", 5, 7);
+ List<Message> messages = MergingException
+ .wrapException(saxParseException).withFile(file).build().getMessages();
+ assertEquals(new Message(Message.Kind.ERROR, "message",
+ new SourceFilePosition(file, new SourcePosition(4, 6, -1))), messages.get(0));
+ }
+
+ @Test
+ public void testWrapSaxParseExceptionWithoutLocation() {
+ SAXParseException saxParseException = new SAXParseException("message", "", "", -1, -1);
+
+ List<Message> messages = MergingException
+ .wrapException(saxParseException).withFile(file).build().getMessages();
+ assertEquals(new Message(Message.Kind.ERROR, "message",
+ new SourceFilePosition(file, SourcePosition.UNKNOWN)), messages.get(0));
+ }
+
+ @Test
+ public void testThrowIfNonEmpty() throws MergingException{
+ MergingException.throwIfNonEmpty(ImmutableList.<Message>of());
+ try {
+ MergingException.throwIfNonEmpty(ImmutableList.<Message>of(
+ new Message(Message.Kind.ERROR, "Message", SourceFilePosition.UNKNOWN)));
+ fail();
+ } catch (MergingException e) {
+ // ok
+ }
+ }
+
+
+ @Test
+ public void testMergingExceptionWithNullMessage() {
+ MergingException exception = MergingException.wrapException(new IOException()).build();
+ Message message = Iterables.getOnlyElement(exception.getMessages());
+ assertNotNull(message.getText());
+ assertNotNull(message.getRawMessage());
+ }
+
+ @Test
+ public void testConsumerExceptionWithNullMessage() {
+ MergingException exception = new MergeConsumer.ConsumerException(new IOException());
+ Message message = Iterables.getOnlyElement(exception.getMessages());
+ assertNotNull(message.getText());
+ assertNotNull(message.getRawMessage());
}
}
diff --git a/sdk-common/src/test/java/com/android/ide/common/res2/PartialFileResourceNameValidatorTest.java b/sdk-common/src/test/java/com/android/ide/common/res2/PartialFileResourceNameValidatorTest.java
index 611ab3c..3fd5f8d 100644
--- a/sdk-common/src/test/java/com/android/ide/common/res2/PartialFileResourceNameValidatorTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/res2/PartialFileResourceNameValidatorTest.java
@@ -16,18 +16,12 @@
package com.android.ide.common.res2;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import com.android.annotations.Nullable;
import com.android.resources.ResourceType;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
-import java.io.File;
import java.util.Arrays;
import java.util.Collection;
@@ -56,8 +50,7 @@
{"foo", ResourceType.RAW, null},
{"foo", ResourceType.RAW, null},
{"foo.txt", ResourceType.DRAWABLE, THE_FILE_NAME_MUST_END_WITH_XML_OR_PNG},
- {"Foo.png", ResourceType.DRAWABLE,
- "File-based resource names must start with a lowercase letter"},
+ {"Foo.png", ResourceType.DRAWABLE, "'F'" + IS_NOT_A_VALID_ETC},
{"foo$.png", ResourceType.DRAWABLE, "'$'" + IS_NOT_A_VALID_ETC},
{"enum.png", ResourceType.DRAWABLE,
"enum is not a valid resource name (reserved Java keyword)"},
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 9206dc6..d2d64e6 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
@@ -59,6 +59,9 @@
@Mock
PngCruncher mPngCruncher;
+ @Mock
+ ResourcePreprocessor mPreprocessor;
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -327,7 +330,7 @@
File folder = Files.createTempDir();
merger.writeBlobTo(folder,
- new MergedResourceWriter(Files.createTempDir(), mPngCruncher, false, false, null));
+ getConsumer());
ResourceMerger loadedMerger = new ResourceMerger();
assertTrue(loadedMerger.loadFromBlob(folder, true /*incrementalState*/));
@@ -397,7 +400,7 @@
File folder = Files.createTempDir();
merger.writeBlobTo(folder,
- new MergedResourceWriter(Files.createTempDir(), mPngCruncher, false, false, null));
+ getConsumer());
ResourceMerger loadedMerger = new ResourceMerger();
assertTrue(loadedMerger.loadFromBlob(folder, true /*incrementalState*/));
@@ -530,8 +533,7 @@
File resFolder = getFolderCopy(new File(root, "resOut"));
// write the content of the resource merger.
- MergedResourceWriter writer = new MergedResourceWriter(resFolder, mPngCruncher, false,
- false, null);
+ MergedResourceWriter writer = getConsumer(resFolder);
resourceMerger.mergeData(writer, false /*doCleanUp*/);
// Check the content.
@@ -645,8 +647,7 @@
File resFolder = getFolderCopy(new File(root, "resOut"));
// write the content of the resource merger.
- MergedResourceWriter writer = new MergedResourceWriter(resFolder, mPngCruncher, false,
- false, null);
+ MergedResourceWriter writer = getConsumer(resFolder);
resourceMerger.mergeData(writer, false /*doCleanUp*/);
// Check the content.
@@ -723,8 +724,7 @@
File resFolder = getFolderCopy(new File(root, "resOut"));
// write the content of the resource merger.
- MergedResourceWriter writer = new MergedResourceWriter(resFolder, mPngCruncher, false,
- false, null);
+ MergedResourceWriter writer = getConsumer(resFolder);
resourceMerger.mergeData(writer, false /*doCleanUp*/);
// Check the content.
@@ -823,8 +823,7 @@
File resFolder = getFolderCopy(new File(root, "resOut"));
// write the content of the resource merger.
- MergedResourceWriter writer = new MergedResourceWriter(resFolder, mPngCruncher, false,
- false, null);
+ MergedResourceWriter writer = getConsumer(resFolder);
resourceMerger.mergeData(writer, false /*doCleanUp*/);
// deleted layout/file_replaced_by_alias.xml
@@ -860,7 +859,7 @@
// write merger1 on disk to test writing empty ResourceSets.
File folder = Files.createTempDir();
merger1.writeBlobTo(folder,
- new MergedResourceWriter(Files.createTempDir(), mPngCruncher, false, false, null));
+ getConsumer());
// reload it
ResourceMerger loadedMerger = new ResourceMerger();
@@ -999,8 +998,7 @@
File outFolder = getFolderCopy(new File(root, "out"));
// write the content of the resource merger.
- MergedResourceWriter writer = new MergedResourceWriter(outFolder, mPngCruncher, false,
- false, null);
+ MergedResourceWriter writer = getConsumer(outFolder);
resourceMerger.mergeData(writer, false /*doCleanUp*/);
File outDrawableFolder = new File(outFolder, ResourceFolderType.DRAWABLE.getName());
@@ -1049,7 +1047,7 @@
File folder = Files.createTempDir();
folder.deleteOnExit();
- MergedResourceWriter writer = new MergedResourceWriter(folder, mPngCruncher, false, false, null);
+ MergedResourceWriter writer = getConsumer(folder);
resourceMerger.mergeData(writer, false /*doCleanUp*/);
// load the result as a set.
@@ -1323,7 +1321,7 @@
File folder = Files.createTempDir();
folder.deleteOnExit();
- MergedResourceWriter writer = new MergedResourceWriter(folder, mPngCruncher, false, false, null);
+ MergedResourceWriter writer = getConsumer(folder);
resourceMerger.mergeData(writer, false /*doCleanUp*/);
// load the result as a set.
@@ -1401,7 +1399,7 @@
File folder = Files.createTempDir();
- MergedResourceWriter writer = new MergedResourceWriter(folder, mPngCruncher, false, false, null);
+ MergedResourceWriter writer = getConsumer(folder);
resourceMerger.mergeData(writer, false /*doCleanUp*/);
return folder;
@@ -1529,7 +1527,7 @@
}
try {
merger.writeBlobTo(folder,
- new MergedResourceWriter(Files.createTempDir(), mPngCruncher, false, false, null));
+ getConsumer());
} catch (MergingException e) {
File file = new File(folder, "merger.xml");
assertEquals(file.getPath() + ": Error: (Permission denied)",
@@ -1544,7 +1542,7 @@
File folder = Files.createTempDir();
merger.writeBlobTo(folder,
- new MergedResourceWriter(Files.createTempDir(), mPngCruncher, false, false, null));
+ getConsumer());
// new merger to read the blob
ResourceMerger loadedMerger = new ResourceMerger();
@@ -1564,7 +1562,9 @@
file = file.getAbsoluteFile();
assertEquals(
file.getPath() +
- ": Error: File-based resource names must start with a lowercase letter",
+ ": Error: 'A' is not a valid file-based resource name character: "
+ + "File-based resource names must contain only lowercase a-z, 0-9,"
+ + " or underscore",
e.getMessage());
return;
}
@@ -1584,7 +1584,7 @@
file = file.getAbsoluteFile();
assertEquals(
file.getPath() +
- ": Error: File-based resource names must start with a lowercase letter",
+ ": Error: The resource name must start with a letter",
e.getMessage());
return;
}
@@ -1603,14 +1603,12 @@
resourceMerger.addDataSet(resourceSet);
- File folder = Files.createTempDir();
- MergedResourceWriter writer = new MergedResourceWriter(folder, mPngCruncher, false,
- false, null);
+ MergedResourceWriter writer = getConsumer();
resourceMerger.mergeData(writer, false /*doCleanUp*/);
} catch (MergingException e) {
File file = new File(root, "values" + File.separator + "dimens.xml");
file = file.getAbsoluteFile();
- assertEquals(file.getPath() + ":3:5: Error: The content of elements must consist "
+ assertEquals(file.getPath() + ":4:6: Error: The content of elements must consist "
+ "of well-formed character data or markup.",
e.getMessage());
return;
@@ -1630,14 +1628,12 @@
resourceMerger.addDataSet(resourceSet);
- File folder = Files.createTempDir();
- MergedResourceWriter writer = new MergedResourceWriter(folder, mPngCruncher, false,
- false, null);
+ MergedResourceWriter writer = getConsumer();
resourceMerger.mergeData(writer, false /*doCleanUp*/);
} catch (MergingException e) {
File file = new File(root, "values" + File.separator + "dimens.xml");
file = file.getAbsoluteFile();
- assertEquals(file.getPath() + ":1:16: Error: Open quote is expected for "
+ assertEquals(file.getPath() + ":2:17: Error: Open quote is expected for "
+ "attribute \"{1}\" associated with an element type \"name\".",
e.getMessage());
return;
@@ -1689,4 +1685,20 @@
return item.getIgnoredFromDiskMerge();
}
}
+
+ @NonNull
+ private MergedResourceWriter getConsumer() {
+ return getConsumer(Files.createTempDir());
+ }
+
+ @NonNull
+ private MergedResourceWriter getConsumer(File tempDir) {
+ return new MergedResourceWriter(
+ tempDir,
+ mPngCruncher,
+ false,
+ false,
+ null,
+ mPreprocessor);
+ }
}
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 6166830..f64e33c 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
@@ -112,16 +112,18 @@
set.loadFromFiles(logger);
} catch (DuplicateDataException e) {
gotException = true;
+
+
+
+
String message = e.getMessage();
// Clean up paths etc for unit test
int index = message.indexOf("dupSet");
assertTrue(index != -1);
String prefix = message.substring(0, index);
- message = message.replaceAll(prefix, "<PREFIX>").replace('\\','/');
- assertEquals("<PREFIX>dupSet/res2/drawable/icon.png\t<PREFIX>dupSet/res1/drawable/icon.png: "
- + "Error: Duplicate resources: "
- + "<PREFIX>dupSet/res2/drawable/icon.png:drawable/icon, "
- + "<PREFIX>dupSet/res1/drawable/icon.png:drawable/icon", message);
+ message = message.replace(prefix, "<PREFIX>").replace('\\','/');
+ assertEquals("<PREFIX>dupSet/res1/drawable/icon.png\t<PREFIX>dupSet/res2/drawable/icon.png: "
+ + "Error: Duplicate resources", message);
}
checkLogger(logger);
@@ -141,7 +143,7 @@
} catch (MergingException e) {
gotException = true;
assertEquals(new File(root, "values" + separator + "dimens.xml").getAbsolutePath() +
- ":0:0: Error: Content is not allowed in prolog.",
+ ":1:1: Error: Content is not allowed in prolog.",
e.getMessage());
}
@@ -204,7 +206,7 @@
} catch (MergingException e) {
gotException = true;
assertEquals(new File(root, "values" + separator + "values.xml").getAbsolutePath() +
- ":6:5: Error: The element type \"declare-styleable\" "
+ ":7:6: Error: The element type \"declare-styleable\" "
+ "must be terminated by the matching end-tag \"</declare-styleable>\".",
e.getMessage());
}
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/LocaleManagerTest.java b/sdk-common/src/test/java/com/android/ide/common/resources/LocaleManagerTest.java
index 040c198..f7df995 100644
--- a/sdk-common/src/test/java/com/android/ide/common/resources/LocaleManagerTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/LocaleManagerTest.java
@@ -68,6 +68,17 @@
assertEquals("German", LocaleManager.getLanguageName("de"));
assertEquals("Hindi", LocaleManager.getLanguageName("hi"));
+ // Check deprecated codes
+ assertEquals("Indonesian", LocaleManager.getLanguageName("id"));
+ assertEquals("Indonesian", LocaleManager.getLanguageName("in"));
+ assertEquals("Indonesian", LocaleManager.getLanguageName("ind"));
+ assertEquals("Hebrew", LocaleManager.getLanguageName("he"));
+ assertEquals("Hebrew", LocaleManager.getLanguageName("iw"));
+ assertEquals("Hebrew", LocaleManager.getLanguageName("heb"));
+ assertEquals("Yiddish", LocaleManager.getLanguageName("yi"));
+ assertEquals("Yiddish", LocaleManager.getLanguageName("ji"));
+ assertEquals("Yiddish", LocaleManager.getLanguageName("yid"));
+
// 3 letter language lookup
assertEquals("English", LocaleManager.getLanguageName("eng"));
assertEquals("Norwegian", LocaleManager.getLanguageName("nor"));
@@ -175,6 +186,17 @@
assertEquals("nb", LocaleManager.getLanguageAlpha2("nob"));
assertEquals("NOR", LocaleManager.getRegionAlpha3("NO"));
assertEquals("NO", LocaleManager.getRegionAlpha2("NOR"));
+
+ // Check deprecated codes
+ assertEquals("in", LocaleManager.getLanguageAlpha2("ind"));
+ assertEquals("ind", LocaleManager.getLanguageAlpha3("in"));
+ assertEquals("ind", LocaleManager.getLanguageAlpha3("id"));
+ assertEquals("iw", LocaleManager.getLanguageAlpha2("heb"));
+ assertEquals("heb", LocaleManager.getLanguageAlpha3("he"));
+ assertEquals("heb", LocaleManager.getLanguageAlpha3("iw"));
+ assertEquals("ji", LocaleManager.getLanguageAlpha2("yid"));
+ assertEquals("yid", LocaleManager.getLanguageAlpha3("yi"));
+ assertEquals("yid", LocaleManager.getLanguageAlpha3("ji"));
}
@SuppressWarnings("ConstantConditions")
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/ResourceItemResolverTest.java b/sdk-common/src/test/java/com/android/ide/common/resources/ResourceItemResolverTest.java
index 9270fc7..6bb4a49 100644
--- a/sdk-common/src/test/java/com/android/ide/common/resources/ResourceItemResolverTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/ResourceItemResolverTest.java
@@ -17,9 +17,9 @@
package com.android.ide.common.resources;
import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.api.ArrayResourceValue;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.common.res2.MergingException;
import com.android.ide.common.res2.ResourceRepository;
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.resources.ResourceType;
@@ -39,6 +39,11 @@
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
+ " <string name=\"ok\">Ok</string>\n"
+ + " <array name=\"my_fw_array\">\"\n"
+ + " <item> fw_value1</item>\n" // also test trimming.
+ + " <item>fw_value2\n</item>\n"
+ + " <item>fw_value3</item>\n"
+ + " </array>\n"
+ "</resources>\n",
"values/themes.xml", ""
@@ -105,7 +110,12 @@
+ " <string name=\"menu_settings\">Settings</string>\n"
+ " <string name=\"dummy\" translatable=\"false\">Ignore Me</string>\n"
+ " <string name=\"wallpaper_instructions\">Tap picture to set portrait wallpaper</string>\n"
- + " <string name=\"xliff_string\">First: <xliff:g id=\"firstName\">%1$s</xliff:g> Last: <xliff:g id=\"lastName\">%2$s</xliff:g></string>"
+ + " <string name=\"xliff_string\">First: <xliff:g id=\"firstName\">%1$s</xliff:g> Last: <xliff:g id=\"lastName\">%2$s</xliff:g></string>\n"
+ + " <array name=\"my_array\">\"\n"
+ + " <item> value1</item>\n" // also test trimming.
+ + " <item>value2\n</item>\n"
+ + " <item>value3</item>\n"
+ + " </array>\n"
+ "</resources>\n",
"values-es/strings.xml", ""
@@ -199,6 +209,20 @@
assertEquals("#ffffffff", resolver.resolveResValue(
resolver.findResValue("@android:color/bright_foreground_dark", false)).getValue());
+ // Test array values.
+ ResourceValue resValue = resolver.findResValue("@array/my_array", false);
+ assertTrue(resValue instanceof ArrayResourceValue);
+ assertEquals(3, ((ArrayResourceValue) resValue).getElementCount());
+ assertEquals("value1", ((ArrayResourceValue) resValue).getElement(0));
+ assertEquals("value2", ((ArrayResourceValue) resValue).getElement(1));
+ assertEquals("value3", ((ArrayResourceValue) resValue).getElement(2));
+ resValue = resolver.findResValue("@android:array/my_fw_array", false);
+ assertTrue(resValue instanceof ArrayResourceValue);
+ assertEquals(3, ((ArrayResourceValue) resValue).getElementCount());
+ assertEquals("fw_value1", ((ArrayResourceValue) resValue).getElement(0));
+ assertEquals("fw_value2", ((ArrayResourceValue) resValue).getElement(1));
+ assertEquals("fw_value3", ((ArrayResourceValue) resValue).getElement(2));
+
// Now do everything over again, but this time without a resource resolver.
// Also set a lookup chain.
@@ -251,8 +275,19 @@
+ "@android:color/background_dark => #ff000000",
ResourceItemResolver.getDisplayString("?foo", chain));
- assertEquals("?foo => ?android:colorForeground => @color/bright_foreground_light => "
- + "@android:color/background_dark => #ff000000",
- ResourceItemResolver.getDisplayString("?foo", chain));
+ // Test array values.
+ resValue = resolver.findResValue("@array/my_array", false);
+ assertTrue(resValue instanceof ArrayResourceValue);
+ assertEquals(3, ((ArrayResourceValue) resValue).getElementCount());
+ assertEquals("value1", ((ArrayResourceValue) resValue).getElement(0));
+ assertEquals("value2", ((ArrayResourceValue) resValue).getElement(1));
+ assertEquals("value3", ((ArrayResourceValue) resValue).getElement(2));
+ resValue = resolver.findResValue("@android:array/my_fw_array", false);
+ assertTrue(resValue instanceof ArrayResourceValue);
+ assertEquals(3, ((ArrayResourceValue) resValue).getElementCount());
+ assertEquals("fw_value1", ((ArrayResourceValue) resValue).getElement(0));
+ assertEquals("fw_value2", ((ArrayResourceValue) resValue).getElement(1));
+ assertEquals("fw_value3", ((ArrayResourceValue) resValue).getElement(2));
+
}
}
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 f61b679..78beca2 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
@@ -8,8 +8,6 @@
import com.android.resources.Density;
import com.android.resources.ResourceType;
import com.google.common.collect.Lists;
-
-import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import java.io.IOException;
@@ -86,6 +84,10 @@
+ " </style>"
+ " <style name=\"RandomStyle2\" parent=\"RandomStyle\">\n"
+ " </style>"
+ + " <style name=\"Theme.FakeTheme\" parent=\"\">\n"
+ + " </style>"
+ + " <style name=\"Theme\" parent=\"RandomStyle\">\n"
+ + " </style>"
+ "</resources>\n",
"values/strings.xml", ""
@@ -199,6 +201,8 @@
// isTheme
assertFalse(resolver.isTheme(resolver.findResValue("@style/RandomStyle", false), null));
assertFalse(resolver.isTheme(resolver.findResValue("@style/RandomStyle2", false), null));
+ assertFalse(resolver.isTheme(resolver.findResValue("@style/Theme.FakeTheme", false), null));
+ assertFalse(resolver.isTheme(resolver.findResValue("@style/Theme", false), null));
// check XML escaping in value resources
StyleResourceValue randomStyle = (StyleResourceValue) resolver.findResValue(
"@style/RandomStyle", false);
@@ -259,7 +263,7 @@
try {
val = resolver.resolveValue(ResourceType.STRING, "bright_foreground_dark",
"@color/background_light", false);
- } catch (AssertionFailedError expected) {
+ } catch (AssertionError expected) {
failed = true;
}
assertTrue("incorrect resource returned: " + val, failed);
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/ResourceUrlTest.java b/sdk-common/src/test/java/com/android/ide/common/resources/ResourceUrlTest.java
index 426390c..55739d7 100644
--- a/sdk-common/src/test/java/com/android/ide/common/resources/ResourceUrlTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/ResourceUrlTest.java
@@ -67,6 +67,10 @@
assertEquals("foo", ResourceUrl.parse("?android:foo").name);
assertTrue(ResourceUrl.parse("?android:foo").framework);
assertTrue(ResourceUrl.parse("?android:foo").theme);
+ assertFalse(ResourceUrl.parse("?foo", false).framework);
+ assertTrue(ResourceUrl.parse("?android:foo", false).framework);
+ assertTrue(ResourceUrl.parse("?foo", true).framework);
+ assertTrue(ResourceUrl.parse("?attr/foo", true).framework);
assertEquals("@+id/foo", ResourceUrl.parse("@+id/foo").toString());
assertEquals("@layout/foo", ResourceUrl.parse("@layout/foo").toString());
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/TestResourceRepository.java b/sdk-common/src/test/java/com/android/ide/common/resources/TestResourceRepository.java
index 492ea1c..60e9ee6 100644
--- a/sdk-common/src/test/java/com/android/ide/common/resources/TestResourceRepository.java
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/TestResourceRepository.java
@@ -1,8 +1,8 @@
package com.android.ide.common.resources;
import static com.android.SdkConstants.FD_RES;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.annotations.NonNull;
import com.android.ide.common.res2.MergingException;
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 07f42aa..bb29ac7 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
@@ -18,9 +18,11 @@
import com.android.ide.common.res2.ResourceFile;
import com.android.ide.common.res2.ResourceItem;
+import com.android.resources.Density;
import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.android.resources.ScreenOrientation;
+import com.android.resources.ScreenRound;
import com.android.resources.UiMode;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
@@ -228,6 +230,26 @@
}
+ public void testIsRoundMatch() {
+ FolderConfiguration configForFolder = FolderConfiguration
+ .getConfigForFolder("values-en-round");
+ assertNotNull(configForFolder);
+ assertNotNull(configForFolder.getScreenRoundQualifier());
+ assertEquals(ScreenRound.ROUND, configForFolder.getScreenRoundQualifier().getValue());
+ runConfigMatchTest("en-rgb-Round-Port-HDPI-notouch-12key", 4,
+ "",
+ "en",
+ "fr-rCa",
+ "en-notround-hdpi",
+ "en-notouch");
+
+ runConfigMatchTest("en-rgb-Round-Port-HDPI-notouch-12key", 2,
+ "",
+ "en",
+ "en-round-hdpi",
+ "port-12key");
+ }
+
// --- helper methods
private static final class MockConfigurable implements Configurable {
@@ -427,4 +449,50 @@
configBcpEn.findMatchingConfigurables(
Arrays.asList(itemBlank, itemBcpEn, itemEn, itemDe)));
}
+
+ public void testFromQualifierString() throws Exception {
+ FolderConfiguration blankFolder = FolderConfiguration.getConfigForQualifierString("");
+ FolderConfiguration enFolder = FolderConfiguration.getConfigForQualifierString("en");
+ FolderConfiguration deFolder = FolderConfiguration.getConfigForQualifierString("de");
+ FolderConfiguration deBcp47Folder = FolderConfiguration.getConfigForQualifierString("b+de");
+ FolderConfiguration twoQualifiersFolder =
+ FolderConfiguration.getConfigForQualifierString("de-hdpi");
+
+ assertNotNull(enFolder);
+ assertNotNull(deFolder);
+ assertNotNull(deBcp47Folder);
+ assertFalse(enFolder.isMatchFor(deFolder));
+ assertFalse(deFolder.isMatchFor(enFolder));
+ assertFalse(enFolder.isMatchFor(deBcp47Folder));
+ assertFalse(deBcp47Folder.isMatchFor(enFolder));
+
+ assertTrue(enFolder.isMatchFor(blankFolder));
+ assertTrue(deFolder.isMatchFor(blankFolder));
+ assertTrue(deBcp47Folder.isMatchFor(blankFolder));
+
+ assertEquals("de", twoQualifiersFolder.getLocaleQualifier().getLanguage());
+ assertEquals(Density.HIGH, twoQualifiersFolder.getDensityQualifier().getValue());
+ }
+
+ public void testCopyOf() throws Exception {
+ FolderConfiguration deBcp47Folder = FolderConfiguration.getConfigForFolder("values-b+de");
+ FolderConfiguration copy = FolderConfiguration.copyOf(deBcp47Folder);
+ assertTrue(copy.isMatchFor(deBcp47Folder));
+
+ copy.setLocaleQualifier(new LocaleQualifier("en"));
+ assertEquals("en", copy.getLocaleQualifier().getLanguage());
+ assertEquals("de", deBcp47Folder.getLocaleQualifier().getLanguage());
+
+ copy.setDensityQualifier(new DensityQualifier(Density.HIGH));
+ assertEquals(Density.HIGH, copy.getDensityQualifier().getValue());
+ assertNull(deBcp47Folder.getDensityQualifier());
+
+ FolderConfiguration blankFolder = FolderConfiguration.getConfigForFolder("values");
+ copy = FolderConfiguration.copyOf(blankFolder);
+ assertTrue(copy.isMatchFor(blankFolder));
+
+ copy.setVersionQualifier(new VersionQualifier(21));
+ assertEquals(21, copy.getVersionQualifier().getVersion());
+ assertNull(blankFolder.getVersionQualifier());
+ }
}
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/configuration/ScreenOrientationQualifierTest.java b/sdk-common/src/test/java/com/android/ide/common/resources/configuration/ScreenOrientationQualifierTest.java
index 3aac5f3..f74fe9d 100644
--- a/sdk-common/src/test/java/com/android/ide/common/resources/configuration/ScreenOrientationQualifierTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/configuration/ScreenOrientationQualifierTest.java
@@ -46,7 +46,7 @@
assertEquals("port", config.getScreenOrientationQualifier().toString()); //$NON-NLS-1$
}
- public void testLanscape() {
+ public void testLandscape() {
assertEquals(true, soq.checkAndSet("land", config)); //$NON-NLS-1$
assertTrue(config.getScreenOrientationQualifier() != null);
assertEquals(ScreenOrientation.LANDSCAPE,
diff --git a/sdk-common/src/test/java/com/android/ide/common/resources/configuration/ScreenRoundQualifierTest.java b/sdk-common/src/test/java/com/android/ide/common/resources/configuration/ScreenRoundQualifierTest.java
new file mode 100644
index 0000000..c70ae9a
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/resources/configuration/ScreenRoundQualifierTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.resources.configuration;
+
+import com.android.resources.ScreenRound;
+
+import junit.framework.TestCase;
+
+public class ScreenRoundQualifierTest extends TestCase {
+
+ private ScreenRoundQualifier soq;
+ private FolderConfiguration config;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ soq = new ScreenRoundQualifier();
+ config = new FolderConfiguration();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ soq = null;
+ config = null;
+ }
+
+ public void testRound() {
+ assertEquals(true, soq.checkAndSet("round", config));
+ assertTrue(config.getScreenRoundQualifier() != null);
+ assertEquals(ScreenRound.ROUND, config.getScreenRoundQualifier().getValue());
+ assertEquals("round", config.getScreenRoundQualifier().toString());
+ }
+
+ public void testNotRound() {
+ assertEquals(true, soq.checkAndSet("notround", config));
+ assertTrue(config.getScreenRoundQualifier() != null);
+ assertEquals(ScreenRound.NOTROUND,
+ config.getScreenRoundQualifier().getValue());
+ assertEquals("notround", config.getScreenRoundQualifier().toString());
+ }
+
+ public void testFailures() {
+ assertEquals(false, soq.checkAndSet("", config));
+ assertEquals(false, soq.checkAndSet("ROUND", config));
+ assertEquals(false, soq.checkAndSet("not-round", config));
+ }
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/util/GeneratorTest.java b/sdk-common/src/test/java/com/android/ide/common/util/GeneratorTest.java
new file mode 100644
index 0000000..14c7f76
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/util/GeneratorTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.common.util;
+
+import junit.framework.TestCase;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Shared test infrastructure for asset (either bitmap or vector) generator.
+ */
+public abstract class GeneratorTest extends TestCase {
+ protected abstract String getTestDataRelPath();
+
+ protected String generateGoldenImage(File targetDir,
+ BufferedImage goldenImage,
+ String missingFilePath,
+ String filePath) throws IOException {
+ if (targetDir == null) {
+ fail(
+ "Did not find " + missingFilePath + ". Set $ANDROID_SRC to have it created automatically");
+ }
+ File fileName = new File(targetDir, filePath);
+ assertFalse(fileName.exists());
+ if (!fileName.getParentFile().exists()) {
+ boolean mkdir = fileName.getParentFile().mkdirs();
+ assertTrue(fileName.getParent(), mkdir);
+ }
+
+ ImageIO.write(goldenImage, "PNG", fileName);
+ return fileName.getPath();
+ }
+
+ public static void assertImageSimilar(String imageName,
+ BufferedImage goldenImage,
+ BufferedImage image,
+ float maxPercentDifferent) throws IOException {
+ assertTrue("Widths differ too much for " + imageName,
+ Math.abs(goldenImage.getWidth() - image.getWidth()) < 2);
+ assertTrue("Widths differ too much for " + imageName,
+ Math.abs(goldenImage.getHeight() - image.getHeight()) < 2);
+
+ assertEquals(BufferedImage.TYPE_INT_ARGB, image.getType());
+
+ if (goldenImage.getType() != BufferedImage.TYPE_INT_ARGB) {
+ BufferedImage temp = new BufferedImage(goldenImage.getWidth(), goldenImage.getHeight(),
+ BufferedImage.TYPE_INT_ARGB);
+ temp.getGraphics().drawImage(goldenImage, 0, 0, null);
+ goldenImage = temp;
+ }
+ assertEquals(BufferedImage.TYPE_INT_ARGB, goldenImage.getType());
+
+ int imageWidth = Math.min(goldenImage.getWidth(), image.getWidth());
+ int imageHeight = Math.min(goldenImage.getHeight(), image.getHeight());
+
+ // Blur the images to account for the scenarios where there are pixel
+ // differences
+ // in where a sharp edge occurs
+ // goldenImage = blur(goldenImage, 6);
+ // image = blur(image, 6);
+
+ int width = 3 * imageWidth;
+ int height = imageHeight;
+ BufferedImage deltaImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ Graphics g = deltaImage.getGraphics();
+
+ // Compute delta map
+ long delta = 0;
+ for (int y = 0; y < imageHeight; y++) {
+ for (int x = 0; x < imageWidth; x++) {
+ int goldenRgb = goldenImage.getRGB(x, y);
+ int rgb = image.getRGB(x, y);
+ if (goldenRgb == rgb) {
+ deltaImage.setRGB(imageWidth + x, y, 0x00808080);
+ continue;
+ }
+
+ // If the pixels have no opacity, don't delta colors at all
+ if (((goldenRgb & 0xFF000000) == 0) && (rgb & 0xFF000000) == 0) {
+ deltaImage.setRGB(imageWidth + x, y, 0x00808080);
+ continue;
+ }
+
+ int deltaR = ((rgb & 0xFF0000) >>> 16) - ((goldenRgb & 0xFF0000) >>> 16);
+ int newR = 128 + deltaR & 0xFF;
+ int deltaG = ((rgb & 0x00FF00) >>> 8) - ((goldenRgb & 0x00FF00) >>> 8);
+ int newG = 128 + deltaG & 0xFF;
+ int deltaB = (rgb & 0x0000FF) - (goldenRgb & 0x0000FF);
+ int newB = 128 + deltaB & 0xFF;
+
+ int avgAlpha =
+ ((((goldenRgb & 0xFF000000) >>> 24) + ((rgb & 0xFF000000) >>> 24)) / 2) << 24;
+
+ int newRGB = avgAlpha | newR << 16 | newG << 8 | newB;
+ deltaImage.setRGB(imageWidth + x, y, newRGB);
+
+ delta += Math.abs(deltaR);
+ delta += Math.abs(deltaG);
+ delta += Math.abs(deltaB);
+ }
+ }
+
+ // 3 different colors, 256 color levels
+ long total = imageHeight * imageWidth * 3L * 256L;
+ float percentDifference = (float)(delta * 100 / (double)total);
+
+ if (percentDifference > maxPercentDifferent) {
+ // Expected on the left
+ // Golden on the right
+ g.drawImage(goldenImage, 0, 0, null);
+ g.drawImage(image, 2 * imageWidth, 0, null);
+
+ // Labels
+ if (imageWidth > 80) {
+ g.setColor(Color.RED);
+ g.drawString("Expected", 10, 20);
+ g.drawString("Actual", 2 * imageWidth + 10, 20);
+ }
+
+ File output = new File(getTempDir(), "delta-" + imageName.replace(File.separatorChar, '_'));
+ if (output.exists()) {
+ output.delete();
+ }
+ ImageIO.write(deltaImage, "PNG", output);
+ String message =
+ String.format("Images differ (by %.1f%%) - see details in %s", percentDifference, output);
+ System.out.println(message);
+ fail(message);
+ }
+
+ g.dispose();
+ }
+
+ protected static File getTempDir() {
+ if (System.getProperty("os.name").equals("Mac OS X")) {
+ return new File("/tmp"); //$NON-NLS-1$
+ }
+
+ return new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
+ }
+
+ /**
+ * Get the location to write missing golden files to
+ */
+ protected File getTargetDir() {
+ // Set $ANDROID_SRC to point to your git AOSP working tree
+ String sdk = System.getenv("ANDROID_SRC");
+ if (sdk != null) {
+ File root = new File(sdk);
+ if (root.exists()) {
+ File testData = new File(root, getTestDataRelPath().replace('/', File.separatorChar));
+ if (testData.exists()) {
+ return testData;
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/vectordrawable/VectorDrawbleGeneratorTest.java b/sdk-common/src/test/java/com/android/ide/common/vectordrawable/VectorDrawbleGeneratorTest.java
new file mode 100644
index 0000000..77664f7
--- /dev/null
+++ b/sdk-common/src/test/java/com/android/ide/common/vectordrawable/VectorDrawbleGeneratorTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.vectordrawable;
+
+import com.android.ide.common.util.GeneratorTest;
+import com.android.testutils.TestUtils;
+import junit.framework.TestCase;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.*;
+
+@SuppressWarnings("javadoc")
+public class VectorDrawbleGeneratorTest extends GeneratorTest {
+ private static final String TEST_DATA_REL_PATH =
+ "tools/base/sdk-common/src/test/resources/testData/vectordrawable";
+
+ @Override
+ protected String getTestDataRelPath() {
+ return TEST_DATA_REL_PATH;
+ }
+
+ private void checkVectorConversion(String testFileName) throws IOException {
+ String imageName = testFileName + ".png";
+ String svgName = testFileName + ".svg";
+
+ String parentDir = "vectordrawable" + File.separator;
+ File parentDirFile = TestUtils.getRoot("vectordrawable");
+
+ File svgFile = new File(parentDirFile, svgName);
+ OutputStream outStream = new ByteArrayOutputStream();
+ try {
+ Svg2Vector.parseSvgToXml(svgFile, outStream);
+ }
+ catch (Exception e) {
+ TestCase.assertTrue("Failure: Exception in Svg2Vector.parseSvgToXml!", false);
+ }
+
+ final VdPreview.TargetSize imageTargetSize = VdPreview.TargetSize.createSizeFromWidth(24);
+ StringBuilder builder = new StringBuilder();
+ BufferedImage image = VdPreview.getPreviewFromVectorXml(imageTargetSize, outStream.toString(), builder);
+
+ String pngPath = parentDir + imageName;
+ File pngFile = new File(parentDirFile, imageName);
+ InputStream is = new FileInputStream(pngFile);
+ if (is == null) {
+ // Generate golden images here.
+ generateGoldenImage(getTargetDir(), image, pngPath, parentDir + imageName);
+ } else {
+ BufferedImage goldenImage = ImageIO.read(is);
+ assertImageSimilar(pngPath, goldenImage, image, 1.0f);
+ }
+ }
+
+ //public void testControlPoints01() throws Exception {
+ // checkVectorConversion("test_control_points_01");
+ //}
+ //
+ //public void testControlPoints02() throws Exception {
+ // checkVectorConversion("test_control_points_02");
+ //}
+
+ public void testControlPoints03() throws Exception {
+ checkVectorConversion("test_control_points_03");
+ }
+
+ public void testIconContentCut() throws Exception {
+ checkVectorConversion("ic_content_cut_24px");
+ }
+
+ public void testIconInput() throws Exception {
+ checkVectorConversion("ic_input_24px");
+ }
+
+ public void testIconLiveHelp() throws Exception {
+ checkVectorConversion("ic_live_help_24px");
+ }
+
+ public void testIconLocalLibrary() throws Exception {
+ checkVectorConversion("ic_local_library_24px");
+ }
+
+ public void testIconLocalPhone() throws Exception {
+ checkVectorConversion("ic_local_phone_24px");
+ }
+
+ public void testIconMicOff() throws Exception {
+ checkVectorConversion("ic_mic_off_24px");
+ }
+
+ public void testShapes() throws Exception {
+ checkVectorConversion("ic_shapes");
+ }
+
+ public void testIconTempHigh() throws Exception {
+ checkVectorConversion("ic_temp_high");
+ }
+
+ public void testIconPlusSign() throws Exception {
+ checkVectorConversion("ic_plus_sign");
+ }
+}
diff --git a/sdk-common/src/test/java/com/android/ide/common/xml/SupportsScreensTest.java b/sdk-common/src/test/java/com/android/ide/common/xml/SupportsScreensTest.java
index aa88fc7..ef48708 100644
--- a/sdk-common/src/test/java/com/android/ide/common/xml/SupportsScreensTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/xml/SupportsScreensTest.java
@@ -16,8 +16,6 @@
package com.android.ide.common.xml;
-import com.android.ide.common.xml.AndroidManifestParser;
-import com.android.ide.common.xml.ManifestData;
import com.android.ide.common.xml.ManifestData.SupportsScreens;
import java.io.InputStream;
diff --git a/sdk-common/src/test/java/com/android/ide/common/xml/XmlPrettyPrinterTest.java b/sdk-common/src/test/java/com/android/ide/common/xml/XmlPrettyPrinterTest.java
index 730fbae..d30e215 100644
--- a/sdk-common/src/test/java/com/android/ide/common/xml/XmlPrettyPrinterTest.java
+++ b/sdk-common/src/test/java/com/android/ide/common/xml/XmlPrettyPrinterTest.java
@@ -915,7 +915,7 @@
Node text = doc.createTextNode(" This is my text ");
child3.appendChild(text);
- String xml = XmlUtils.toXml(doc, true);
+ String xml = XmlUtils.toXml(doc);
assertEquals(
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<myroot baz=\"baz\" foo=\"bar\"><mychild/><hasComment><!--This is my comment--></hasComment><hasText> This is my text </hasText></myroot>",
@@ -958,7 +958,7 @@
Document doc = parse(xml);
assertNotNull(doc);
- String formatted = XmlUtils.toXml(doc, true);
+ String formatted = XmlUtils.toXml(doc);
assertEquals(""
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
@@ -994,7 +994,7 @@
Document doc = parse(xml);
assertNotNull(doc);
- String formatted = XmlUtils.toXml(doc, true);
+ String formatted = XmlUtils.toXml(doc);
assertEquals(xml, formatted);
xml = XmlPrettyPrinter.prettyPrint(doc, false);
@@ -1022,7 +1022,7 @@
Document doc = parse(xml);
assertNotNull(doc);
- String formatted = XmlUtils.toXml(doc, true);
+ String formatted = XmlUtils.toXml(doc);
assertEquals(""
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<resources>\n"
@@ -1058,7 +1058,7 @@
Document doc = parse(xml);
assertNotNull(doc);
- xml = XmlUtils.toXml(doc, true);
+ xml = XmlUtils.toXml(doc);
assertEquals(""
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<!-- ============== --><!-- Generic styles --><!-- ============== --><root/>",
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_content_cut_24px.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_content_cut_24px.png
new file mode 100644
index 0000000..a92c02a
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_content_cut_24px.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_content_cut_24px.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_content_cut_24px.svg
new file mode 100644
index 0000000..b89a0d0
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_content_cut_24px.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M9.64 7.64c.23-.5.36-1.05.36-1.64 0-2.21-1.79-4-4-4S2 3.79 2 6s1.79 4 4 4c.59 0 1.14-.13 1.64-.36L10 12l-2.36 2.36C7.14 14.13 6.59 14 6 14c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4c0-.59-.13-1.14-.36-1.64L12 14l7 7h3v-1L9.64 7.64zM6 8c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm0 12c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm6-7.5c-.28 0-.5-.22-.5-.5s.22-.5.5-.5.5.22.5.5-.22.5-.5.5zM19 3l-6 6 2 2 7-7V3z"/></svg>
\ No newline at end of file
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_input_24px.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_input_24px.png
new file mode 100644
index 0000000..2a564a2
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_input_24px.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_input_24px.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_input_24px.svg
new file mode 100644
index 0000000..64d1d0b
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_input_24px.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M21 3.01H3c-1.1 0-2 .9-2 2V9h2V4.99h18v14.03H3V15H1v4.01c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98v-14c0-1.11-.9-2-2-2zM11 16l4-4-4-4v3H1v2h10v3z"/></svg>
\ No newline at end of file
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_live_help_24px.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_live_help_24px.png
new file mode 100644
index 0000000..081843c
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_live_help_24px.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_live_help_24px.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_live_help_24px.svg
new file mode 100644
index 0000000..abe3129
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_live_help_24px.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h4l3 3 3-3h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-6 16h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 11.9 13 12.5 13 14h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"/></svg>
\ No newline at end of file
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_local_library_24px.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_library_24px.png
new file mode 100644
index 0000000..674628d
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_library_24px.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_local_library_24px.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_library_24px.svg
new file mode 100644
index 0000000..5fa5109
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_library_24px.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 11.55C9.64 9.35 6.48 8 3 8v11c3.48 0 6.64 1.35 9 3.55 2.36-2.19 5.52-3.55 9-3.55V8c-3.48 0-6.64 1.35-9 3.55zM12 8c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3z"/></svg>
\ No newline at end of file
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_local_phone_24px.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_phone_24px.png
new file mode 100644
index 0000000..58ea1c7
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_phone_24px.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_local_phone_24px.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_phone_24px.svg
new file mode 100644
index 0000000..5908b6a
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_local_phone_24px.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/></svg>
\ No newline at end of file
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_mic_off_24px.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_mic_off_24px.png
new file mode 100644
index 0000000..a188c50
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_mic_off_24px.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_mic_off_24px.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_mic_off_24px.svg
new file mode 100644
index 0000000..4c27fb5
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_mic_off_24px.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 11h-1.7c0 .74-.16 1.43-.43 2.05l1.23 1.23c.56-.98.9-2.09.9-3.28zm-4.02.17c0-.06.02-.11.02-.17V5c0-1.66-1.34-3-3-3S9 3.34 9 5v.18l5.98 5.99zM4.27 3L3 4.27l6.01 6.01V11c0 1.66 1.33 3 2.99 3 .22 0 .44-.03.65-.08l1.66 1.66c-.71.33-1.5.52-2.31.52-2.76 0-5.3-2.1-5.3-5.1H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c.91-.13 1.77-.45 2.54-.9L19.73 21 21 19.73 4.27 3z"/></svg>
\ No newline at end of file
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_plus_sign.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_plus_sign.png
new file mode 100644
index 0000000..f760018
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_plus_sign.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_plus_sign.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_plus_sign.svg
new file mode 100644
index 0000000..3e2ea1f
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_plus_sign.svg
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_shapes.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_shapes.png
new file mode 100644
index 0000000..e97567b
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_shapes.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_shapes.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_shapes.svg
new file mode 100644
index 0000000..c834caa
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_shapes.svg
@@ -0,0 +1,12 @@
+<svg width="36" height="36" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
+ <g>
+ <g id="svg_1">
+ <polygon points="16.41,24.832 17.504,26.004 24,20 17.504,14 16.41,15.168 19.496,18 2,18 2,22 19.5,22" id="svg_2"/>
+ </g>
+ <g id="svg_3">
+ <rect id="svg_5" opacity="0.5" fill="#FF0000" height="12" width="12"/>
+ <circle id="svg_6" opacity="0.5" fill="#00FF00" r="10" cy="12" cx="12"/>
+ <line id="svg_8" stroke="#0000FF" stroke-width="2" y2="12" x2="24" y1="0" x1="0"/>
+ </g>
+ </g>
+</svg>
\ No newline at end of file
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_temp_high.png b/sdk-common/src/test/resources/testData/vectordrawable/ic_temp_high.png
new file mode 100644
index 0000000..5a32de5
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_temp_high.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/ic_temp_high.svg b/sdk-common/src/test/resources/testData/vectordrawable/ic_temp_high.svg
new file mode 100644
index 0000000..0ad5628
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/ic_temp_high.svg
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_01.png b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_01.png
new file mode 100644
index 0000000..50c19f1
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_01.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_01.svg b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_01.svg
new file mode 100644
index 0000000..d4813aa
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_01.svg
@@ -0,0 +1,7 @@
+<svg width="800" height="800.0000000000001" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
+ <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
+ <g>
+ <title>Layer 1</title>
+ <path d="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299" id="svg_1" stroke="#FF0000" fill="none"/>
+ </g>
+</svg>
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_02.png b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_02.png
new file mode 100644
index 0000000..96b2f81
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_02.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_02.svg b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_02.svg
new file mode 100644
index 0000000..0ba747e
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_02.svg
@@ -0,0 +1,7 @@
+<svg width="800" height="800.0000000000001" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
+ <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
+ <g>
+ <title>Layer 1</title>
+ <path d="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299" id="svg_1" stroke="#FF0000" fill="none"/>
+ </g>
+</svg>
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_03.png b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_03.png
new file mode 100644
index 0000000..940fa57
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_03.png
Binary files differ
diff --git a/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_03.svg b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_03.svg
new file mode 100644
index 0000000..c8e1d47
--- /dev/null
+++ b/sdk-common/src/test/resources/testData/vectordrawable/test_control_points_03.svg
@@ -0,0 +1,7 @@
+<svg width="800" height="600" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
+ <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
+ <g>
+ <title>Layer 1</title>
+ <path d="m0,200q200,250 200,0t200,0Q500,400,600,200T800,200" id="svg_1" stroke="#000000" fill="#000000"/>
+ </g>
+</svg>
diff --git a/sdklib/build.gradle b/sdklib/build.gradle
index 45a3cf6..bc76ff4 100644
--- a/sdklib/build.gradle
+++ b/sdklib/build.gradle
@@ -1,13 +1,5 @@
-buildscript {
- repositories {
- maven { url = uri(rootProject.cloneArtifacts.repository) }
- }
- dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0"
- }
-}
-
apply plugin: 'java'
+apply plugin: 'jacoco'
apply plugin: 'sdk-java-lib'
evaluationDependsOn(':base:dvlib')
@@ -20,6 +12,7 @@
compile project(':base:layoutlib-api')
compile project(':base:dvlib')
+ compile 'com.google.code.gson:gson:2.2.4'
compile 'org.apache.commons:commons-compress:1.8.1'
compile 'org.apache.httpcomponents:httpclient:4.1.1'
compile 'org.apache.httpcomponents:httpmime:4.1'
diff --git a/sdklib/sdklib-base.iml b/sdklib/sdklib-base.iml
index 5f22ace..9a3eb80 100644
--- a/sdklib/sdklib-base.iml
+++ b/sdklib/sdklib-base.iml
@@ -16,5 +16,6 @@
<orderEntry type="library" exported="" name="http-client" level="project" />
<orderEntry type="library" exported="" name="commons-compress" level="project" />
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
+ <orderEntry type="library" exported="" name="gson" level="project" />
</component>
-</module>
+</module>
\ No newline at end of file
diff --git a/sdklib/sdklib.iml b/sdklib/sdklib.iml
index e868d2c..9240161 100644
--- a/sdklib/sdklib.iml
+++ b/sdklib/sdklib.iml
@@ -16,5 +16,6 @@
<orderEntry type="library" exported="" name="http-client" level="project" />
<orderEntry type="library" exported="" name="commons-compress" level="project" />
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
+ <orderEntry type="library" exported="" name="gson" level="project" />
</component>
-</module>
+</module>
\ No newline at end of file
diff --git a/sdklib/src/main/java/com/android/sdklib/AndroidTargetHash.java b/sdklib/src/main/java/com/android/sdklib/AndroidTargetHash.java
index 0d5d653..e4520be 100755
--- a/sdklib/src/main/java/com/android/sdklib/AndroidTargetHash.java
+++ b/sdklib/src/main/java/com/android/sdklib/AndroidTargetHash.java
@@ -89,7 +89,7 @@
return new AndroidVersion(api, suffix);
}
}
- } else if (hashString.length() > 0 && Character.isDigit(hashString.charAt(0))) {
+ } else if (!hashString.isEmpty() && Character.isDigit(hashString.charAt(0))) {
// For convenience, interpret a single integer as the proper "android-NN" form.
try {
int api = Integer.parseInt(hashString);
diff --git a/sdklib/src/main/java/com/android/sdklib/IAndroidTarget.java b/sdklib/src/main/java/com/android/sdklib/IAndroidTarget.java
index 349c5df..6606bc8 100644
--- a/sdklib/src/main/java/com/android/sdklib/IAndroidTarget.java
+++ b/sdklib/src/main/java/com/android/sdklib/IAndroidTarget.java
@@ -80,15 +80,18 @@
int NO_USB_ID = 0;
/** An optional library provided by an Android Target */
- interface IOptionalLibrary {
+ interface OptionalLibrary {
/** The name of the library, as used in the manifest (<uses-library>). */
+ @NonNull
String getName();
- /** The file name of the jar file. */
- String getJarName();
- /** Absolute OS path to the jar file. */
- String getJarPath();
+ /** Location of the jar file. */
+ @NonNull
+ File getJar();
/** Description of the library. */
+ @NonNull
String getDescription();
+ /** Whether the library requires a manifest entry */
+ boolean isManifestEntryRequired();
}
/**
@@ -187,6 +190,32 @@
List<String> getBootClasspath();
/**
+ * Returns a list of optional libraries for this target.
+ *
+ * These libraries are not automatically added to the classpath.
+ * Using them requires adding a <code>uses-library</code> entry in the manifest.
+ *
+ * @return a list of libraries.
+ *
+ * @see OptionalLibrary#getName()
+ */
+ @NonNull
+ List<OptionalLibrary> getOptionalLibraries();
+
+ /**
+ * Returns the additional libraries for this target.
+ *
+ * These libraries are automatically added to the classpath, but using them requires
+ * adding a <code>uses-library</code> entry in the manifest.
+ *
+ * @return a list of libraries.
+ *
+ * @see OptionalLibrary#getName()
+ */
+ @NonNull
+ List<OptionalLibrary> getAdditionalLibraries();
+
+ /**
* Returns whether the target is able to render layouts.
*/
boolean hasRenderingLibrary();
@@ -215,12 +244,6 @@
File getDefaultSkin();
/**
- * Returns the available optional libraries for this target.
- * @return an array of optional libraries or <code>null</code> if there is none.
- */
- IOptionalLibrary[] getOptionalLibraries();
-
- /**
* Returns the list of libraries available for a given platform.
*
* @return an array of libraries provided by the platform or <code>null</code> if there is none.
diff --git a/sdklib/src/main/java/com/android/sdklib/SdkVersionInfo.java b/sdklib/src/main/java/com/android/sdklib/SdkVersionInfo.java
index aefb450..cfedbf1 100755
--- a/sdklib/src/main/java/com/android/sdklib/SdkVersionInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/SdkVersionInfo.java
@@ -30,7 +30,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 = 22;
+ public static final int HIGHEST_KNOWN_API = 23;
/**
* Like {@link #HIGHEST_KNOWN_API} but does not include preview platforms
@@ -90,6 +90,7 @@
case 20: return "4.4";
case 21: return "5.0";
case 22: return "5.1";
+ case 23: return "5.X";
// If you add more versions here, also update #getBuildCodes and
// #HIGHEST_KNOWN_API
@@ -134,6 +135,9 @@
case 21:
case 22:
return "Lollipop";
+ case 23:
+ return "MNC";
+
// If you add more versions here, also update #getBuildCodes and
// #HIGHEST_KNOWN_API
@@ -176,6 +180,7 @@
case 20: return "KITKAT_WATCH"; //$NON-NLS-1$
case 21: return "LOLLIPOP"; //$NON-NLS-1$
case 22: return "LOLLIPOP_MR1"; //$NON-NLS-1$
+ case 23: return "MNC"; //$NON-NLS-1$
// If you add more versions here, also update #getAndroidName and
// #HIGHEST_KNOWN_API
}
diff --git a/sdklib/src/main/java/com/android/sdklib/SystemImage.java b/sdklib/src/main/java/com/android/sdklib/SystemImage.java
index f25ab57..ded928f 100755
--- a/sdklib/src/main/java/com/android/sdklib/SystemImage.java
+++ b/sdklib/src/main/java/com/android/sdklib/SystemImage.java
@@ -23,8 +23,10 @@
import com.android.sdklib.internal.androidTarget.PlatformTarget;
import com.android.sdklib.io.FileOp;
import com.android.sdklib.repository.descriptors.IdDisplay;
+import com.google.common.base.Objects;
import java.io.File;
+import java.util.Arrays;
import java.util.Locale;
@@ -37,6 +39,7 @@
public static final IdDisplay DEFAULT_TAG = new IdDisplay("default", //$NON-NLS-1$
"Default"); //$NON-NLS-1$
+ @Deprecated
private final LocationType mLocationtype;
private final IdDisplay mTag;
private final IdDisplay mAddonVendor;
@@ -225,7 +228,10 @@
return mLocation;
}
- /** Indicates the location strategy for this system image in the SDK. */
+ /**
+ * Indicates the location strategy for this system image in the SDK.
+ * @deprecated
+ */
@NonNull
@Override
public LocationType getLocationType() {
@@ -299,5 +305,21 @@
return sb.toString();
}
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof SystemImage)) {
+ return false;
+ }
+ SystemImage other = (SystemImage)o;
+ return mTag.equals(other.mTag) && mAbiType.equals(other.getAbiType()) && Objects.equal(mAddonVendor, other.mAddonVendor)
+ && mLocation.equals(other.mLocation) && Arrays.equals(mSkins, other.mSkins);
+ }
+ @Override
+ public int hashCode() {
+ int hashCode = Objects.hashCode(mTag, mAbiType, mAddonVendor, mLocation);
+ hashCode *= 37;
+ hashCode += Arrays.hashCode(mSkins);
+ return hashCode;
+ }
}
diff --git a/sdklib/src/main/java/com/android/sdklib/build/ApkBuilderMain.java b/sdklib/src/main/java/com/android/sdklib/build/ApkBuilderMain.java
index e6f6329..92b19f6 100644
--- a/sdklib/src/main/java/com/android/sdklib/build/ApkBuilderMain.java
+++ b/sdklib/src/main/java/com/android/sdklib/build/ApkBuilderMain.java
@@ -120,7 +120,7 @@
}
} while (index < args.length);
- if (zipArchives.size() == 0) {
+ if (zipArchives.isEmpty()) {
printAndExit("No zip archive, there must be one for the resources");
}
diff --git a/sdklib/src/main/java/com/android/sdklib/build/JarListSanitizer.java b/sdklib/src/main/java/com/android/sdklib/build/JarListSanitizer.java
index 9e95f41..a618d47 100644
--- a/sdklib/src/main/java/com/android/sdklib/build/JarListSanitizer.java
+++ b/sdklib/src/main/java/com/android/sdklib/build/JarListSanitizer.java
@@ -209,7 +209,7 @@
}
// the actual clean up.
- if (results.size() > 0) {
+ if (!results.isEmpty()) {
for (File f : results) {
jarList.remove(f.getAbsolutePath());
}
diff --git a/sdklib/src/main/java/com/android/sdklib/devices/Device.java b/sdklib/src/main/java/com/android/sdklib/devices/Device.java
index 067b5e4..242a830 100644
--- a/sdklib/src/main/java/com/android/sdklib/devices/Device.java
+++ b/sdklib/src/main/java/com/android/sdklib/devices/Device.java
@@ -20,6 +20,7 @@
import com.android.annotations.Nullable;
import com.android.dvlib.DeviceSchema;
import com.android.resources.ScreenOrientation;
+import com.android.resources.ScreenRound;
import java.awt.Dimension;
import java.util.ArrayList;
@@ -39,6 +40,7 @@
* the {@link DeviceSchema} standards.
*/
public final class Device {
+
/** Name of the device */
@NonNull
private final String mName;
@@ -262,10 +264,25 @@
*
* @return the optional boot.props of the device. Can be null or empty.
*/
+ @NonNull
public Map<String, String> getBootProps() {
return mBootProps;
}
+ /**
+ * A convenience method to get if the screen for this device is round.
+ */
+ public boolean isScreenRound() {
+ return getDefaultHardware().getScreen().getScreenRound() == ScreenRound.ROUND;
+ }
+
+ /**
+ * A convenience method to get the chin size for this device.
+ */
+ public int getChinSize() {
+ return getDefaultHardware().getScreen().getChin();
+ }
+
public static class Builder {
private String mName;
private String mId;
@@ -347,6 +364,14 @@
return false;
}
+ /**
+ * Only for use by the {@link DeviceParser}, so that it can modify the states after they've
+ * been added.
+ */
+ List<State> getAllStates() {
+ return mState;
+ }
+
public void setMeta(@NonNull Meta meta) {
mMeta = meta;
}
diff --git a/sdklib/src/main/java/com/android/sdklib/devices/DeviceManager.java b/sdklib/src/main/java/com/android/sdklib/devices/DeviceManager.java
index abdeeea..f2b1734 100644
--- a/sdklib/src/main/java/com/android/sdklib/devices/DeviceManager.java
+++ b/sdklib/src/main/java/com/android/sdklib/devices/DeviceManager.java
@@ -30,6 +30,8 @@
import com.android.sdklib.repository.PkgProps;
import com.android.utils.ILogger;
import com.google.common.base.Charsets;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
@@ -70,10 +72,10 @@
private static final Pattern PATH_PROPERTY_PATTERN =
Pattern.compile('^' + PkgProps.EXTRA_PATH + '=' + DEVICE_PROFILES_PROP + '$');
private ILogger mLog;
- private Collection<Device> mVendorDevices;
- private Collection<Device> mSysImgDevices;
- private Collection<Device> mUserDevices;
- private Collection<Device> mDefaultDevices;
+ private Table<String, String, Device> mVendorDevices;
+ private Table<String, String, Device> mSysImgDevices;
+ private Table<String, String, Device> mUserDevices;
+ private Table<String, String, Device> mDefaultDevices;
private final Object mLock = new Object();
private final List<DevicesChangedListener> sListeners = new ArrayList<DevicesChangedListener>();
private final String mOsSdkPath;
@@ -181,19 +183,19 @@
@Nullable
public Device getDevice(@NonNull String id, @NonNull String manufacturer) {
initDevicesLists();
- Device d = getDeviceImpl(mUserDevices, id, manufacturer);
+ Device d = mUserDevices.get(id, manufacturer);
if (d != null) {
return d;
}
- d = getDeviceImpl(mSysImgDevices, id, manufacturer);
+ d = mSysImgDevices.get(id, manufacturer);
if (d != null) {
return d;
}
- d = getDeviceImpl(mDefaultDevices, id, manufacturer);
+ d = mDefaultDevices.get(id, manufacturer);
if (d != null) {
return d;
}
- d = getDeviceImpl(mVendorDevices, id, manufacturer);
+ d = mVendorDevices.get(id, manufacturer);
return d;
}
@@ -230,20 +232,20 @@
@NonNull
public Collection<Device> getDevices(@NonNull EnumSet<DeviceFilter> deviceFilter) {
initDevicesLists();
- LinkedHashSet<Device> devices = new LinkedHashSet<Device>();
+ Table<String, String, Device> devices = HashBasedTable.create();
if (mUserDevices != null && (deviceFilter.contains(DeviceFilter.USER))) {
- devices.addAll(mUserDevices);
+ devices.putAll(mUserDevices);
}
if (mDefaultDevices != null && (deviceFilter.contains(DeviceFilter.DEFAULT))) {
- devices.addAll(mDefaultDevices);
+ devices.putAll(mDefaultDevices);
}
if (mVendorDevices != null && (deviceFilter.contains(DeviceFilter.VENDOR))) {
- devices.addAll(mVendorDevices);
+ devices.putAll(mVendorDevices);
}
if (mSysImgDevices != null && (deviceFilter.contains(DeviceFilter.SYSTEM_IMAGES))) {
- devices.addAll(mSysImgDevices);
+ devices.putAll(mSysImgDevices);
}
- return Collections.unmodifiableSet(devices);
+ return Collections.unmodifiableCollection(devices.values());
}
private void initDevicesLists() {
@@ -265,25 +267,22 @@
if (mDefaultDevices != null) {
return false;
}
- InputStream stream = null;
+ InputStream stream = DeviceManager.class
+ .getResourceAsStream(SdkConstants.FN_DEVICES_XML);
try {
- stream = DeviceManager.class.getResourceAsStream(SdkConstants.FN_DEVICES_XML);
+ assert stream != null : SdkConstants.FN_DEVICES_XML + " not bundled in sdklib.";
mDefaultDevices = DeviceParser.parse(stream);
return true;
} catch (IllegalStateException e) {
// The device builders can throw IllegalStateExceptions if
// build gets called before everything is properly setup
mLog.error(e, null);
- mDefaultDevices = new LinkedHashSet<Device>();
+ mDefaultDevices = HashBasedTable.create();
} catch (Exception e) {
mLog.error(e, "Error reading default devices");
- mDefaultDevices = new LinkedHashSet<Device>();
+ mDefaultDevices = HashBasedTable.create();
} finally {
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException ignore) {}
- }
+ Closeables.closeQuietly(stream);
}
}
return false;
@@ -300,49 +299,34 @@
return false;
}
- mVendorDevices = new LinkedHashSet<Device>();
+ mVendorDevices = HashBasedTable.create();
// Load builtin devices
- InputStream stream = null;
+ InputStream stream = DeviceManager.class.getResourceAsStream("nexus.xml");
try {
- stream = DeviceManager.class.getResourceAsStream("nexus.xml");
- mVendorDevices.addAll(DeviceParser.parse(stream));
+ mVendorDevices.putAll(DeviceParser.parse(stream));
} catch (Exception e) {
mLog.error(e, "Could not load nexus devices");
} finally {
- try {
- Closeables.close(stream, true /* swallowIOException */);
- } catch (IOException e) {
- // Cannot happen
- }
+ Closeables.closeQuietly(stream);
}
- stream = null;
+ stream = DeviceManager.class.getResourceAsStream("wear.xml");
try {
- stream = DeviceManager.class.getResourceAsStream("wear.xml");
- mVendorDevices.addAll(DeviceParser.parse(stream));
+ mVendorDevices.putAll(DeviceParser.parse(stream));
} catch (Exception e) {
mLog.error(e, "Could not load wear devices");
} finally {
- try {
- Closeables.close(stream, true /* swallowIOException */);
- } catch (IOException e) {
- // Cannot happen
- }
+ Closeables.closeQuietly(stream);
}
- stream = null;
+ stream = DeviceManager.class.getResourceAsStream("tv.xml");
try {
- stream = DeviceManager.class.getResourceAsStream("tv.xml");
- mVendorDevices.addAll(DeviceParser.parse(stream));
+ mVendorDevices.putAll(DeviceParser.parse(stream));
} catch (Exception e) {
mLog.error(e, "Could not load tv devices");
} finally {
- try {
- Closeables.close(stream, true /* swallowIOException */);
- } catch (IOException e) {
- // Cannot happen
- }
+ Closeables.closeQuietly(stream);
}
if (mOsSdkPath != null) {
@@ -352,7 +336,7 @@
for (File deviceDir : deviceDirs) {
File deviceXml = new File(deviceDir, SdkConstants.FN_DEVICES_XML);
if (deviceXml.isFile()) {
- mVendorDevices.addAll(loadDevices(deviceXml));
+ mVendorDevices.putAll(loadDevices(deviceXml));
}
}
return true;
@@ -370,7 +354,7 @@
if (mSysImgDevices != null) {
return false;
}
- mSysImgDevices = new LinkedHashSet<Device>();
+ mSysImgDevices = HashBasedTable.create();
if (mOsSdkPath == null) {
return false;
@@ -399,7 +383,7 @@
File deviceXml = new File(abiFolder, SdkConstants.FN_DEVICES_XML);
if (fop.isFile(deviceXml)) {
- mSysImgDevices.addAll(loadDevices(deviceXml));
+ mSysImgDevices.putAll(loadDevices(deviceXml));
}
}
}
@@ -419,14 +403,14 @@
}
// User devices should be saved out to
// $HOME/.android/devices.xml
- mUserDevices = new LinkedHashSet<Device>();
+ mUserDevices = HashBasedTable.create();
File userDevicesFile = null;
try {
userDevicesFile = new File(
AndroidLocation.getFolder(),
SdkConstants.FN_DEVICES_XML);
if (userDevicesFile.exists()) {
- mUserDevices.addAll(DeviceParser.parse(userDevicesFile));
+ mUserDevices.putAll(DeviceParser.parse(userDevicesFile));
return true;
}
} catch (AndroidLocationException e) {
@@ -464,7 +448,7 @@
assert mUserDevices != null;
}
if (mUserDevices != null) {
- mUserDevices.add(d);
+ mUserDevices.put(d.getId(), d.getManufacturer(), d);
}
changed = true;
}
@@ -480,16 +464,9 @@
assert mUserDevices != null;
}
if (mUserDevices != null) {
- Iterator<Device> it = mUserDevices.iterator();
- while (it.hasNext()) {
- Device userDevice = it.next();
- if (userDevice.getId().equals(d.getId())
- && userDevice.getManufacturer().equals(d.getManufacturer())) {
- it.remove();
- notifyListeners();
- return;
- }
-
+ if (mUserDevices.contains(d.getId(), d.getManufacturer())) {
+ mUserDevices.remove(d.getId(), d.getManufacturer());
+ notifyListeners();
}
}
}
@@ -523,15 +500,15 @@
return;
}
- if (mUserDevices.size() == 0) {
+ if (mUserDevices.isEmpty()) {
userDevicesFile.delete();
return;
}
synchronized (mLock) {
- if (mUserDevices.size() > 0) {
+ if (!mUserDevices.isEmpty()) {
try {
- DeviceWriter.writeToXml(new FileOutputStream(userDevicesFile), mUserDevices);
+ DeviceWriter.writeToXml(new FileOutputStream(userDevicesFile), mUserDevices.values());
} catch (FileNotFoundException e) {
mLog.warning("Couldn't open file: %1$s", e.getMessage());
} catch (ParserConfigurationException e) {
@@ -573,7 +550,7 @@
props.put(HardwareProperties.HW_ORIENTATION_SENSOR,
getBooleanVal(sensors.contains(Sensor.GYROSCOPE)));
props.put(HardwareProperties.HW_AUDIO_INPUT, getBooleanVal(hw.hasMic()));
- props.put(HardwareProperties.HW_SDCARD, getBooleanVal(hw.getRemovableStorage().size() > 0));
+ props.put(HardwareProperties.HW_SDCARD, getBooleanVal(!hw.getRemovableStorage().isEmpty()));
props.put(HardwareProperties.HW_LCD_DENSITY,
Integer.toString(hw.getScreen().getPixelDensity().getDpiValue()));
props.put(HardwareProperties.HW_PROXIMITY_SENSOR,
@@ -668,7 +645,7 @@
}
@NonNull
- private Collection<Device> loadDevices(@NonNull File deviceXml) {
+ private Table<String, String, Device> loadDevices(@NonNull File deviceXml) {
try {
return DeviceParser.parse(deviceXml);
} catch (SAXException e) {
@@ -684,7 +661,7 @@
// build gets called before everything is properly setup
mLog.error(e, null);
}
- return new LinkedHashSet<Device>();
+ return HashBasedTable.create();
}
private void notifyListeners() {
diff --git a/sdklib/src/main/java/com/android/sdklib/devices/DeviceParser.java b/sdklib/src/main/java/com/android/sdklib/devices/DeviceParser.java
index d9613a0..059f9fb 100644
--- a/sdklib/src/main/java/com/android/sdklib/devices/DeviceParser.java
+++ b/sdklib/src/main/java/com/android/sdklib/devices/DeviceParser.java
@@ -16,6 +16,9 @@
package com.android.sdklib.devices;
+import static com.android.SdkConstants.VALUE_FALSE;
+import static com.android.SdkConstants.VALUE_TRUE;
+
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.dvlib.DeviceSchema;
@@ -26,10 +29,14 @@
import com.android.resources.NavigationState;
import com.android.resources.ScreenOrientation;
import com.android.resources.ScreenRatio;
+import com.android.resources.ScreenRound;
import com.android.resources.ScreenSize;
import com.android.resources.TouchScreen;
import com.android.resources.UiMode;
+import com.google.common.base.Splitter;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
@@ -52,8 +59,11 @@
public class DeviceParser {
private static class DeviceHandler extends DefaultHandler {
- private static final String sSpaceRegex = "[\\s]+";
- private final List<Device> mDevices = new ArrayList<Device>();
+ private static final Splitter sSpaceSplitter = Splitter.on(' ').omitEmptyStrings();
+ private static final String ROUND_BOOT_PROP = "ro.emulator.circular";
+ private static final String CHIN_BOOT_PROP = "ro.emu.win_outset_bottom_px";
+
+ private final Table<String, String, Device> mDevices = HashBasedTable.create();
private final StringBuilder mStringAccumulator = new StringBuilder();
private final File mParentFolder;
private Meta mMeta;
@@ -70,7 +80,7 @@
}
@NonNull
- public List<Device> getDevices() {
+ public Table<String, String, Device> getDevices() {
return mDevices;
}
@@ -126,7 +136,8 @@
@Override
public void endElement(String uri, String localName, String name) throws SAXException {
if (DeviceSchema.NODE_DEVICE.equals(localName)) {
- mDevices.add(mBuilder.build());
+ Device device = mBuilder.build();
+ mDevices.put(device.getId(), device.getManufacturer(), device);
} else if (DeviceSchema.NODE_NAME.equals(localName)) {
mBuilder.setName(getString(mStringAccumulator));
} else if (DeviceSchema.NODE_ID.equals(localName)) {
@@ -338,6 +349,7 @@
assert mBootProp != null && mBootProp.length == 2 &&
mBootProp[0] != null && mBootProp[1] != null;
mBuilder.addBootProp(mBootProp[0], mBootProp[1]);
+ checkAndSetIfRound(mBootProp[0], mBootProp[1]);
mBootProp = null;
} else if (DeviceSchema.NODE_SKIN.equals(localName)) {
String path = getString(mStringAccumulator).replace('/', File.separatorChar);
@@ -350,9 +362,31 @@
throw e;
}
- private List<String> getStringList(StringBuilder stringAccumulator) {
+ private void checkAndSetIfRound(String bootPropKey, String bootPropValue) {
+ // This is a ugly hack. To keep the existing devices.xmls working, the roundness of the
+ // screen is stored in a boot property.
+ ScreenRound roundness = null;
+ if (ROUND_BOOT_PROP.equals(bootPropKey)) {
+ if (VALUE_TRUE.equals(bootPropValue)) {
+ roundness = ScreenRound.ROUND;
+ } else if (VALUE_FALSE.equals(bootPropValue)) {
+ roundness = ScreenRound.NOTROUND;
+ }
+ for (State state : mBuilder.getAllStates()) {
+ state.getHardware().getScreen().setScreenRound(roundness);
+ }
+ }
+ if (CHIN_BOOT_PROP.equals(bootPropKey)) {
+ int chin = Integer.parseInt(bootPropValue);
+ for (State state : mBuilder.getAllStates()) {
+ state.getHardware().getScreen().setChin(chin);
+ }
+ }
+ }
+
+ private static List<String> getStringList(StringBuilder stringAccumulator) {
List<String> filteredStrings = new ArrayList<String>();
- for (String s : getString(mStringAccumulator).split(sSpaceRegex)) {
+ for (String s : sSpaceSplitter.split(stringAccumulator)) {
if (s != null && !s.isEmpty()) {
filteredStrings.add(s.trim());
}
@@ -413,40 +447,46 @@
}
@NonNull
- public static List<Device> parse(@NonNull File devicesFile)
+ public static Table<String, String, Device> parse(@NonNull File devicesFile)
throws SAXException, ParserConfigurationException, IOException {
- InputStream stream = null;
- try {
- stream = new FileInputStream(devicesFile);
- return parseImpl(stream, devicesFile.getAbsoluteFile().getParentFile());
- } finally {
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException ignore) {}
- }
- }
+ // stream closed by parseImpl.
+ @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
+ InputStream stream = new FileInputStream(devicesFile);
+ return parseImpl(stream, devicesFile.getAbsoluteFile().getParentFile());
}
+ /**
+ * This method closes the stream.
+ */
@NonNull
- public static List<Device> parse(@NonNull InputStream devices)
+ public static Table<String, String, Device> parse(@NonNull InputStream devices)
throws SAXException, IOException, ParserConfigurationException {
return parseImpl(devices, null);
}
+ /**
+ * After parsing, this method closes the stream.
+ */
@NonNull
- private static List<Device> parseImpl(@NonNull InputStream devices, @Nullable File parentDir)
+ private static Table<String, String, Device> parseImpl(@NonNull InputStream devices, @Nullable File parentDir)
throws SAXException, IOException, ParserConfigurationException {
- if (!devices.markSupported()) {
- devices = new BufferedInputStream(devices);
+ try {
+ if (!devices.markSupported()) {
+ //noinspection IOResourceOpenedButNotSafelyClosed
+ devices = new BufferedInputStream(devices); // closed in the finally block.
+ }
+ devices.mark(500000);
+ int version = DeviceSchema.getXmlSchemaVersion(devices);
+ SAXParser parser = getParser(version);
+ DeviceHandler dHandler = new DeviceHandler(parentDir);
+ devices.reset();
+ parser.parse(devices, dHandler);
+ return dHandler.getDevices();
}
- devices.mark(500000);
- int version = DeviceSchema.getXmlSchemaVersion(devices);
- SAXParser parser = getParser(version);
- DeviceHandler dHandler = new DeviceHandler(parentDir);
- devices.reset();
- parser.parse(devices, dHandler);
- return dHandler.getDevices();
+ finally {
+ // It's better to close the stream here since we may have created it above.
+ devices.close();
+ }
}
@NonNull
diff --git a/sdklib/src/main/java/com/android/sdklib/devices/Screen.java b/sdklib/src/main/java/com/android/sdklib/devices/Screen.java
index cd665c2..3230974 100644
--- a/sdklib/src/main/java/com/android/sdklib/devices/Screen.java
+++ b/sdklib/src/main/java/com/android/sdklib/devices/Screen.java
@@ -16,8 +16,10 @@
package com.android.sdklib.devices;
+import com.android.annotations.Nullable;
import com.android.resources.Density;
import com.android.resources.ScreenRatio;
+import com.android.resources.ScreenRound;
import com.android.resources.ScreenSize;
import com.android.resources.TouchScreen;
@@ -34,6 +36,9 @@
private Multitouch mMultitouch;
private TouchScreen mMechanism;
private ScreenType mScreenType;
+ private int mChin;
+ @Nullable
+ private ScreenRound mScreenRound;
public ScreenSize getSize() {
return mScreenSize;
@@ -123,6 +128,33 @@
mScreenType = screenType;
}
+ @Nullable
+ public ScreenRound getScreenRound() {
+ return mScreenRound;
+ }
+
+ public void setScreenRound(@Nullable ScreenRound screenRound) {
+ mScreenRound = screenRound;
+ }
+
+ /**
+ * Get the "chin" height in pixels. This is for round screens with a flat section at the
+ * bottom. The "chin" height is the largest perpendicular distance from the flat section to
+ * the original circle.
+ * @return The offset in pixels.
+ */
+ public int getChin() {
+ return mChin;
+ }
+
+ /**
+ * Sets the "chin" height in pixels.
+ * @see #getChin()
+ */
+ public void setChin(int chin) {
+ mChin = chin;
+ }
+
/**
* Returns a copy of the object that shares no state with it,
* but is initialized to equivalent values.
@@ -142,6 +174,8 @@
s.mMultitouch = mMultitouch;
s.mMechanism = mMechanism;
s.mScreenType = mScreenType;
+ s.mScreenRound = mScreenRound;
+ s.mChin = mChin;
return s;
}
@@ -164,7 +198,9 @@
&& s.mYdpi == mYdpi
&& s.mMultitouch == mMultitouch
&& s.mMechanism == mMechanism
- && s.mScreenType == mScreenType;
+ && s.mScreenType == mScreenType
+ && s.mScreenRound == mScreenRound
+ && s.mChin == mChin;
}
@Override
@@ -184,6 +220,10 @@
hash = 31 * hash + mMultitouch.ordinal();
hash = 31 * hash + mMechanism.ordinal();
hash = 31 * hash + mScreenType.ordinal();
+ hash = 31 * hash + mChin;
+ if (mScreenRound != null) {
+ hash = 31 * hash + mScreenRound.ordinal();
+ }
return hash;
}
@@ -212,6 +252,10 @@
sb.append(mMechanism);
sb.append(", mScreenType=");
sb.append(mScreenType);
+ sb.append(", mScreenRound=");
+ sb.append(mScreenRound);
+ sb.append(", mChin=");
+ sb.append(mChin);
sb.append("]");
return sb.toString();
}
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 004439a..52eef2d 100644
--- a/sdklib/src/main/java/com/android/sdklib/devices/nexus.xml
+++ b/sdklib/src/main/java/com/android/sdklib/devices/nexus.xml
@@ -54,7 +54,7 @@
armeabi
</d:abi>
<d:dock> </d:dock>
- <d:power-type>plugged-in</d:power-type>
+ <d:power-type>battery</d:power-type>
<d:skin>nexus_one</d:skin>
</d:hardware>
<d:software>
@@ -137,7 +137,7 @@
armeabi
</d:abi>
<d:dock> </d:dock>
- <d:power-type>plugged-in</d:power-type>
+ <d:power-type>battery</d:power-type>
<d:skin>nexus_s</d:skin>
</d:hardware>
<d:software>
diff --git a/sdklib/src/main/java/com/android/sdklib/devices/wear.xml b/sdklib/src/main/java/com/android/sdklib/devices/wear.xml
index d585109..7b297fb 100644
--- a/sdklib/src/main/java/com/android/sdklib/devices/wear.xml
+++ b/sdklib/src/main/java/com/android/sdklib/devices/wear.xml
@@ -185,4 +185,87 @@
</d:boot-prop>
</d:boot-props>
</d:device>
+ <d:device>
+ <d:name>Android Wear Round Chin</d:name>
+ <d:id>wear_round_chin_320_290</d:id>
+ <d:manufacturer>Google</d:manufacturer>
+ <d:meta/>
+ <d:hardware>
+ <d:screen>
+ <d:screen-size>small</d:screen-size>
+ <d:diagonal-length>1.65</d:diagonal-length>
+ <d:pixel-density>tvdpi</d:pixel-density>
+ <d:screen-ratio>notlong</d:screen-ratio>
+ <d:dimensions>
+ <d:x-dimension>320</d:x-dimension>
+ <d:y-dimension>290</d:y-dimension>
+ </d:dimensions>
+ <d:xdpi>205</d:xdpi>
+ <d:ydpi>205</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>
+ Bluetooth
+ Wifi
+ NFC
+ </d:networking>
+ <d:sensors>
+ Accelerometer
+ Barometer
+ Compass
+ GPS
+ Gyroscope
+ LightSensor
+ ProximitySensor
+ </d:sensors>
+ <d:mic>true</d:mic>
+ <d:keyboard>qwerty</d:keyboard>
+ <d:nav>dpad</d:nav>
+ <d:ram unit="MiB">512</d:ram>
+ <d:buttons>hard</d:buttons>
+ <d:internal-storage unit="KiB">7811891</d:internal-storage>
+ <d:removable-storage unit="TiB"/>
+ <d:cpu>Qualcomm Snapdragon S4 Pro</d:cpu>
+ <d:gpu>Adreno 320</d:gpu>
+ <d:abi>armeabi-v7a</d:abi>
+ <d:dock/>
+ <d:power-type>battery</d:power-type>
+ <d:skin>AndroidWearRoundChin320x290</d:skin>
+ </d:hardware>
+ <d:software>
+ <d:api-level>22-</d:api-level>
+ <d:live-wallpaper-support>true</d:live-wallpaper-support>
+ <d:bluetooth-profiles/>
+ <d:gl-version>2.0</d:gl-version>
+ <d:gl-extensions/>
+ <d:status-bar>false</d:status-bar>
+ </d:software>
+ <d:state name="Portrait">
+ <d:description>The device in portrait orientation</d:description>
+ <d:screen-orientation>port</d:screen-orientation>
+ <d:keyboard-state>keyshidden</d:keyboard-state>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:state>
+ <d:state name="Landscape" default="true">
+ <d:description>The device in landscape orientation</d:description>
+ <d:screen-orientation>land</d:screen-orientation>
+ <d:keyboard-state>keyshidden</d:keyboard-state>
+ <d:nav-state>navexposed</d:nav-state>
+ </d:state>
+ <d:tag-id>android-wear</d:tag-id>
+ <d:boot-props>
+ <d:boot-prop>
+ <d:prop-name>ro.emulator.circular</d:prop-name>
+ <d:prop-value>true</d:prop-value>
+ </d:boot-prop>
+ <d:boot-prop>
+ <d:prop-name>ro.emu.win_outset_bottom_px</d:prop-name>
+ <d:prop-value>30</d:prop-value>
+ </d:boot-prop>
+ </d:boot-props>
+ </d:device>
</d:devices>
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/AddOnTarget.java b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/AddOnTarget.java
index 15ceef3..83a3e55 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/AddOnTarget.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/AddOnTarget.java
@@ -25,11 +25,11 @@
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISystemImage;
import com.android.sdklib.repository.descriptors.IdDisplay;
+import com.google.common.collect.ImmutableList;
import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -41,40 +41,6 @@
*/
public final class AddOnTarget implements IAndroidTarget {
- private static final class OptionalLibrary implements IOptionalLibrary {
- private final String mJarName;
- private final String mJarPath;
- private final String mName;
- private final String mDescription;
-
- OptionalLibrary(String jarName, String jarPath, String name, String description) {
- mJarName = jarName;
- mJarPath = jarPath;
- mName = name;
- mDescription = description;
- }
-
- @Override
- public String getJarName() {
- return mJarName;
- }
-
- @Override
- public String getJarPath() {
- return mJarPath;
- }
-
- @Override
- public String getName() {
- return mName;
- }
-
- @Override
- public String getDescription() {
- return mDescription;
- }
- }
-
private final String mLocation;
private final PlatformTarget mBasePlatform;
private final String mName;
@@ -87,7 +53,7 @@
private File[] mSkins;
private File mDefaultSkin;
- private IOptionalLibrary[] mLibraries;
+ private ImmutableList<OptionalLibrary> mLibraries;
private int mVendorId = NO_USB_ID;
/**
@@ -115,7 +81,7 @@
boolean hasRenderingLibrary,
boolean hasRenderingResources,
PlatformTarget basePlatform) {
- if (location.endsWith(File.separator) == false) {
+ if (!location.endsWith(File.separator)) {
location = location + File.separator;
}
@@ -135,15 +101,19 @@
// handle the optional libraries.
if (libMap != null) {
- mLibraries = new IOptionalLibrary[libMap.size()];
- int index = 0;
+ ImmutableList.Builder<OptionalLibrary> builder = ImmutableList.builder();
for (Entry<String, String[]> entry : libMap.entrySet()) {
String jarFile = entry.getValue()[0];
String desc = entry.getValue()[1];
- mLibraries[index++] = new OptionalLibrary(jarFile,
- mLocation + SdkConstants.OS_ADDON_LIBS_FOLDER + jarFile,
- entry.getKey(), desc);
+ builder.add(new OptionalLibraryImpl(
+ entry.getKey(),
+ new File(mLocation, SdkConstants.OS_ADDON_LIBS_FOLDER + jarFile),
+ desc,
+ true /*requireManifestEntry*/));
}
+ mLibraries = builder.build();
+ } else {
+ mLibraries = ImmutableList.of();
}
}
@@ -288,7 +258,19 @@
@Override @NonNull
public List<String> getBootClasspath() {
- return Collections.singletonList(getPath(IAndroidTarget.ANDROID_JAR));
+ return mBasePlatform.getBootClasspath();
+ }
+
+ @NonNull
+ @Override
+ public List<OptionalLibrary> getOptionalLibraries() {
+ return mBasePlatform.getOptionalLibraries();
+ }
+
+ @NonNull
+ @Override
+ public List<OptionalLibrary> getAdditionalLibraries() {
+ return mLibraries;
}
@Override
@@ -308,11 +290,6 @@
return mDefaultSkin;
}
- @Override
- public IOptionalLibrary[] getOptionalLibraries() {
- return mLibraries;
- }
-
/**
* Returns the list of libraries of the underlying platform.
*
@@ -364,7 +341,7 @@
// The receiver is an add-on. There are 2 big use cases: The add-on has libraries
// or the add-on doesn't (in which case we consider it a platform).
- if (mLibraries == null || mLibraries.length == 0) {
+ if (mLibraries.isEmpty()) {
return mBasePlatform.canRunOn(target);
} else {
// the only targets that can run the receiver are the same add-on in the same or later
@@ -412,7 +389,7 @@
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
- public int compareTo(IAndroidTarget target) {
+ public int compareTo(@NonNull IAndroidTarget target) {
// quick check.
if (this == target) {
return 0;
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/MissingTarget.java b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/MissingTarget.java
new file mode 100644
index 0000000..4454040
--- /dev/null
+++ b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/MissingTarget.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdklib.internal.androidTarget;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.sdklib.AndroidTargetHash;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.BuildToolInfo;
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.ISystemImage;
+import com.android.sdklib.SdkVersionInfo;
+import com.android.sdklib.repository.descriptors.IdDisplay;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A target that we don't have, but is referenced (e.g. by a system image).
+ */
+public class MissingTarget implements IAndroidTarget {
+
+ private final String mVendor;
+
+ private final AndroidVersion mVersion;
+
+ private final List<ISystemImage> mSystemImages = Lists.newArrayList();
+
+ private final String mName;
+
+ public MissingTarget(String vendor, String name, AndroidVersion version) {
+ mVendor = vendor;
+ mVersion = version;
+ mName = name;
+ }
+
+ @Override
+ public String getLocation() {
+ return null;
+ }
+
+ @Override
+ public String getVendor() {
+ return mVendor;
+ }
+
+ @Override
+ public String getName() {
+ return mName;
+ }
+
+ @Override
+ public String getFullName() {
+ return getName();
+ }
+
+ @Override
+ public String getClasspathName() {
+ return null;
+ }
+
+ @Override
+ public String getShortClasspathName() {
+ return null;
+ }
+
+ @Override
+ public String getDescription() {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public AndroidVersion getVersion() {
+ return mVersion;
+ }
+
+ @Override
+ public String getVersionName() {
+ return SdkVersionInfo.getAndroidName(getVersion().getApiLevel());
+ }
+
+ @Override
+ public int getRevision() {
+ return 0;
+ }
+
+ @Override
+ public boolean isPlatform() {
+ return mVendor == null;
+ }
+
+ @Override
+ public IAndroidTarget getParent() {
+ return null;
+ }
+
+ @Override
+ public String getPath(int pathId) {
+ return null;
+ }
+
+ @Override
+ public File getFile(int pathId) {
+ return null;
+ }
+
+ @Override
+ public BuildToolInfo getBuildToolInfo() {
+ return null;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getBootClasspath() {
+ return ImmutableList.of();
+ }
+
+ @NonNull
+ @Override
+ public List<OptionalLibrary> getOptionalLibraries() {
+ return ImmutableList.of();
+ }
+
+ @NonNull
+ @Override
+ public List<OptionalLibrary> getAdditionalLibraries() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public boolean hasRenderingLibrary() {
+ return false;
+ }
+
+ @NonNull
+ @Override
+ public File[] getSkins() {
+ return new File[0];
+ }
+
+ @Nullable
+ @Override
+ public File getDefaultSkin() {
+ return null;
+ }
+
+ @Override
+ public String[] getPlatformLibraries() {
+ return new String[0];
+ }
+
+ @Override
+ public String getProperty(String name) {
+ return null;
+ }
+
+ @Override
+ public Integer getProperty(String name, Integer defaultValue) {
+ return null;
+ }
+
+ @Override
+ public Boolean getProperty(String name, Boolean defaultValue) {
+ return null;
+ }
+
+ @Override
+ public Map<String, String> getProperties() {
+ return null;
+ }
+
+ @Override
+ public int getUsbVendorId() {
+ return 0;
+ }
+
+ @Override
+ public ISystemImage[] getSystemImages() {
+ return mSystemImages.toArray(new ISystemImage[mSystemImages.size()]);
+ }
+
+ public void addSystemImage(ISystemImage image) {
+ mSystemImages.add(image);
+ }
+
+ @Nullable
+ @Override
+ public ISystemImage getSystemImage(@NonNull IdDisplay tag, @NonNull String abiType) {
+ for (ISystemImage image : mSystemImages) {
+ if (tag.equals(image.getTag()) && abiType.equals(image.getAbiType())) {
+ return image;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean canRunOn(IAndroidTarget target) {
+ return false;
+ }
+
+ @Override
+ public String hashString() {
+ return AndroidTargetHash.getTargetHashString(this);
+ }
+
+ @Override
+ public int compareTo(IAndroidTarget o) {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof MissingTarget)) {
+ return false;
+ }
+ MissingTarget other = (MissingTarget) obj;
+ return Objects.equal(mVendor, other.mVendor) && Objects.equal(mVersion, other.mVersion);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mVendor, mVersion);
+ }
+}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/OptionalLibraryImpl.java b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/OptionalLibraryImpl.java
new file mode 100644
index 0000000..97f7eff
--- /dev/null
+++ b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/OptionalLibraryImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdklib.internal.androidTarget;
+
+import com.android.annotations.NonNull;
+import com.android.sdklib.IAndroidTarget;
+
+import java.io.File;
+
+/**
+ * Internal implementation of OptionalLibrary
+ */
+public class OptionalLibraryImpl implements IAndroidTarget.OptionalLibrary {
+
+ @NonNull
+ private final String mLibraryName;
+ @NonNull
+ private final File mJarFile;
+ @NonNull
+ private final String mDescription;
+ private final boolean mRequireManifestEntry;
+
+ public OptionalLibraryImpl(
+ @NonNull String libraryName,
+ @NonNull File jarFile,
+ @NonNull String description,
+ boolean requireManifestEntry) {
+ mLibraryName = libraryName;
+ mJarFile = jarFile;
+ mDescription = description;
+ mRequireManifestEntry = requireManifestEntry;
+ }
+
+ @Override
+ @NonNull
+ public String getName() {
+ return mLibraryName;
+ }
+
+ @Override
+ @NonNull
+ public File getJar() {
+ return mJarFile;
+ }
+
+ @Override
+ @NonNull
+ public String getDescription() {
+ return mDescription;
+ }
+
+ @Override
+ public boolean isManifestEntryRequired() {
+ return mRequireManifestEntry;
+ }
+}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/PlatformTarget.java b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/PlatformTarget.java
index 4825977..cdd5a9e 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/PlatformTarget.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/androidTarget/PlatformTarget.java
@@ -27,6 +27,7 @@
import com.android.sdklib.SdkManager.LayoutlibVersion;
import com.android.sdklib.repository.descriptors.IdDisplay;
import com.android.utils.SparseArray;
+import com.google.common.collect.ImmutableList;
import java.io.File;
import java.util.Arrays;
@@ -54,6 +55,7 @@
private final SparseArray<String> mPaths = new SparseArray<String>();
private File[] mSkins;
private final ISystemImage[] mSystemImages;
+ private final List<OptionalLibrary> mOptionalLibraries;
private final LayoutlibVersion mLayoutlibVersion;
private final BuildToolInfo mBuildToolInfo;
@@ -78,6 +80,7 @@
LayoutlibVersion layoutlibVersion,
ISystemImage[] systemImages,
Map<String, String> properties,
+ List<OptionalLibrary> optionalLibraries,
@NonNull BuildToolInfo buildToolInfo) {
if (!platformOSPath.endsWith(File.separator)) {
platformOSPath = platformOSPath + File.separator;
@@ -91,6 +94,7 @@
mBuildToolInfo = buildToolInfo;
mSystemImages = systemImages == null ? new ISystemImage[0] : systemImages;
Arrays.sort(mSystemImages);
+ mOptionalLibraries = ImmutableList.copyOf(optionalLibraries);
if (mVersion.isPreview()) {
mName = String.format(PLATFORM_NAME_PREVIEW, mVersionName);
@@ -235,7 +239,6 @@
return new File(getPath(pathId));
}
-
@Override
public BuildToolInfo getBuildToolInfo() {
return mBuildToolInfo;
@@ -243,7 +246,25 @@
@Override @NonNull
public List<String> getBootClasspath() {
- return Collections.singletonList(getPath(IAndroidTarget.ANDROID_JAR));
+ return ImmutableList.of(getPath(IAndroidTarget.ANDROID_JAR));
+ }
+
+ @NonNull
+ @Override
+ public List<OptionalLibrary> getOptionalLibraries() {
+ return mOptionalLibraries;
+ }
+
+ /**
+ * Always returns null, as a standard platform has no additional libraries.
+ *
+ * {@inheritDoc}
+ * @see com.android.sdklib.IAndroidTarget#getAdditionalLibraries()
+ */
+ @NonNull
+ @Override
+ public List<OptionalLibrary> getAdditionalLibraries() {
+ return ImmutableList.of();
}
/**
@@ -284,17 +305,6 @@
}
/**
- * Always returns null, as a standard platform ha no optional libraries.
- *
- * {@inheritDoc}
- * @see com.android.sdklib.IAndroidTarget#getOptionalLibraries()
- */
- @Override
- public IOptionalLibrary[] getOptionalLibraries() {
- return null;
- }
-
- /**
* Currently always return a fixed list with "android.test.runner" in it.
* <p/>
* TODO change the fixed library list to be build-dependent later.
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/avd/AvdManager.java b/sdklib/src/main/java/com/android/sdklib/internal/avd/AvdManager.java
index 99def1d..6e9ab71 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/avd/AvdManager.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/avd/AvdManager.java
@@ -825,7 +825,7 @@
// Config file.
HashMap<String, String> values = new HashMap<String, String>();
- if (setImagePathProperties(target, tag, abiType, values, log) == false) {
+ if (setImagePathProperties(target, tag, abiType, values, log) == false) {
log.error(null, "Failed to set image path properties in the AVD folder.");
needCleanup = true;
return null;
@@ -928,7 +928,7 @@
values.put(AVD_INI_SKIN_PATH, skinPath);
}
- if (sdcard != null && sdcard.length() > 0) {
+ if (sdcard != null && !sdcard.isEmpty()) {
// Sdcard is possibly a size. In that case we create a file called 'sdcard.img'
// in the AVD folder, and do not put any value in config.ini.
@@ -1088,7 +1088,7 @@
report.append(String.format(", %s processor", AvdInfo.getPrettyAbiType(tag, abiType)));
// display the chosen hardware config
- if (finalHardwareValues.size() > 0) {
+ if (!finalHardwareValues.isEmpty()) {
report.append(",\nwith the following hardware config:\n");
List<String> keys = new ArrayList<String>(finalHardwareValues.keySet());
Collections.sort(keys);
@@ -1868,7 +1868,7 @@
Map<String, String> map = new HashMap<String, String>();
while ((line = reader.readLine()) != null) {
line = line.trim();
- if (line.length() > 0 && line.charAt(0) != '#') {
+ if (!line.isEmpty() && line.charAt(0) != '#') {
Matcher m = INI_LINE_PATTERN.matcher(line);
if (m.matches()) {
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/avd/HardwareProperties.java b/sdklib/src/main/java/com/android/sdklib/internal/avd/HardwareProperties.java
index 8bf07cc..6b91b45 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/avd/HardwareProperties.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/avd/HardwareProperties.java
@@ -207,7 +207,7 @@
String line = null;
HardwareProperty prop = null;
while ((line = reader.readLine()) != null) {
- if (line.length() > 0 && line.charAt(0) != '#') {
+ if (!line.isEmpty() && line.charAt(0) != '#') {
Matcher m = PATTERN_PROP.matcher(line);
if (m.matches()) {
String key = m.group(1);
@@ -249,7 +249,7 @@
int n = 0;
for (int i = 0; i < v.length; i++) {
String s = v[i] = v[i].trim();
- if (s.length() > 0) {
+ if (!s.isEmpty()) {
n++;
}
}
@@ -257,7 +257,7 @@
n = 0;
for (int i = 0; i < v.length; i++) {
String s = v[i];
- if (s.length() > 0) {
+ if (!s.isEmpty()) {
prop.mEnum[n++] = s;
}
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/build/KeystoreHelper.java b/sdklib/src/main/java/com/android/sdklib/internal/build/KeystoreHelper.java
index d339ea5..6ba4e72 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/build/KeystoreHelper.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/build/KeystoreHelper.java
@@ -77,7 +77,7 @@
String javaHome = System.getProperty("java.home");
- if (javaHome != null && javaHome.length() > 0) {
+ if (javaHome != null && !javaHome.isEmpty()) {
keytoolCommand = javaHome + File.separator + "bin" + File.separator + keytoolCommand;
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/project/ProjectCreator.java b/sdklib/src/main/java/com/android/sdklib/internal/project/ProjectCreator.java
index 9588de2..c5712d7 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/project/ProjectCreator.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/project/ProjectCreator.java
@@ -1224,7 +1224,7 @@
// with an actual AndroidManifest.xml file. The nodes may not have the requested
// attributes though, if which case we should warn.
- if (packageName == null || packageName.length() == 0) {
+ if (packageName == null || packageName.isEmpty()) {
mLog.error(null,
"Missing <manifest package=\"...\"> in '%1$s'",
manifestFile.getName());
@@ -1252,7 +1252,7 @@
}
}
- if (activityName.length() == 0) {
+ if (activityName.isEmpty()) {
mLog.warning("Missing <activity %1$s:name=\"...\"> in '%2$s'.\n" +
"No activity will be generated.",
AndroidXPathFactory.DEFAULT_NS_PREFIX, manifestFile.getName());
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/AddonsListFetcher.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/AddonsListFetcher.java
index 55dbfd7..816c7f0 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/AddonsListFetcher.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/AddonsListFetcher.java
@@ -579,7 +579,7 @@
strUrl = baseUrl + strUrl;
}
- if (strUrl.length() > 0 && strName.length() > 0) {
+ if (!strUrl.isEmpty() && !strName.isEmpty()) {
sites.add(new Site(strUrl, strName, type));
}
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/LocalSdkParser.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/LocalSdkParser.java
index 1e3b8fb..c756d1f 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/LocalSdkParser.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/LocalSdkParser.java
@@ -768,7 +768,7 @@
props.load(fis);
// To be valid, there must be at least one property in it.
- if (props.size() > 0) {
+ if (!props.isEmpty()) {
return props;
}
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/SdkStats.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/SdkStats.java
index 98f0abc..cb1af74 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/SdkStats.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/SdkStats.java
@@ -556,7 +556,7 @@
getTextContent().trim();
if (codeName == null || versName == null ||
- codeName.length() == 0 || versName.length() == 0) {
+ codeName.isEmpty() || versName.isEmpty()) {
// bad names. ignore.
continue;
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/UrlOpener.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/UrlOpener.java
index 44f050a..338463c 100644
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/UrlOpener.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/UrlOpener.java
@@ -468,7 +468,7 @@
String domain = result.getDomain();
// proceed in case there is indeed a user
- if (user != null && user.length() > 0) {
+ if (user != null && !user.isEmpty()) {
Credentials credentials = new NTCredentials(user, password,
workstation, domain);
httpClient.getCredentialsProvider().setCredentials(authScope, credentials);
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/archives/ArchiveInstaller.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/archives/ArchiveInstaller.java
index 73f5625..d4aa340 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/archives/ArchiveInstaller.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/archives/ArchiveInstaller.java
@@ -288,9 +288,9 @@
String etag = props.getProperty(HttpHeaders.ETAG);
String lastMod = props.getProperty(HttpHeaders.LAST_MODIFIED);
- if (etag != null && etag.length() > 0) {
+ if (etag != null && !etag.isEmpty()) {
headers.add(new BasicHeader(HttpHeaders.IF_MATCH, etag));
- } else if (lastMod != null && lastMod.length() > 0) {
+ } else if (lastMod != null && !lastMod.isEmpty()) {
headers.add(new BasicHeader(HttpHeaders.IF_MATCH, lastMod));
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/AddonPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/AddonPackage.java
index 9d138a4..f5c404d 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/AddonPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/AddonPackage.java
@@ -22,7 +22,7 @@
import com.android.annotations.VisibleForTesting.Visibility;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
+import com.android.sdklib.IAndroidTarget.OptionalLibrary;
import com.android.sdklib.SdkManager;
import com.android.sdklib.internal.repository.sources.SdkSource;
import com.android.sdklib.repository.AddonManifestIniProps;
@@ -36,12 +36,14 @@
import com.android.sdklib.repository.descriptors.PkgDesc;
import com.android.sdklib.repository.local.LocalAddonPkgInfo;
import com.android.utils.Pair;
+import com.google.common.collect.ImmutableList;
import org.w3c.dom.Node;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
@@ -160,17 +162,17 @@
SdkRepoConstants.NODE_NAME);
// The old <name> is equivalent to the new <name-display>
- if (nameDisp.length() == 0) {
+ if (nameDisp.isEmpty()) {
nameDisp = name;
}
// For a missing id, we simply use a sanitized version of the display name
- if (nameId.length() == 0) {
- nameId = LocalAddonPkgInfo.sanitizeDisplayToNameId(name.length() > 0 ? name : nameDisp);
+ if (nameId.isEmpty()) {
+ nameId = LocalAddonPkgInfo.sanitizeDisplayToNameId(!name.isEmpty() ? name : nameDisp);
}
- assert nameId.length() > 0;
- assert nameDisp.length() > 0;
+ assert !nameId.isEmpty();
+ assert !nameDisp.isEmpty();
mNameId = nameId.trim();
mDisplayName = nameDisp.trim();
@@ -186,18 +188,18 @@
SdkAddonConstants.NODE_VENDOR);
// The old <vendor> is equivalent to the new <vendor-display>
- if (vendorDisp.length() == 0) {
+ if (vendorDisp.isEmpty()) {
vendorDisp = vendor;
}
// For a missing id, we simply use a sanitized version of the display vendor
- if (vendorId.length() == 0) {
- boolean hasVendor = vendor.length() > 0;
+ if (vendorId.isEmpty()) {
+ boolean hasVendor = !vendor.isEmpty();
vendorId = LocalAddonPkgInfo.sanitizeDisplayToNameId(hasVendor ? vendor : vendorDisp);
}
- assert vendorId.length() > 0;
- assert vendorDisp.length() > 0;
+ assert !vendorId.isEmpty();
+ assert !vendorDisp.isEmpty();
mVendorId = vendorId.trim();
mVendorDisplay = vendorDisp.trim();
@@ -259,17 +261,19 @@
String name = getProperty(props, PkgProps.ADDON_NAME, target.getName());
// The old <name> is equivalent to the new <name-display>
- if (nameDisp.length() == 0) {
+ //noinspection ConstantConditions
+ if (nameDisp.isEmpty()) {
nameDisp = name;
}
// For a missing id, we simply use a sanitized version of the display name
- if (nameId.length() == 0) {
- nameId = LocalAddonPkgInfo.sanitizeDisplayToNameId(name.length() > 0 ? name : nameDisp);
+ //noinspection ConstantConditions
+ if (nameId.isEmpty()) {
+ nameId = LocalAddonPkgInfo.sanitizeDisplayToNameId(!name.isEmpty() ? name : nameDisp);
}
- assert nameId.length() > 0;
- assert nameDisp.length() > 0;
+ assert !nameId.isEmpty();
+ assert !nameDisp.isEmpty();
mNameId = nameId.trim();
mDisplayName = nameDisp.trim();
@@ -282,18 +286,21 @@
String vendor = getProperty(props, PkgProps.ADDON_VENDOR, target.getVendor());
// The old <vendor> is equivalent to the new <vendor-display>
- if (vendorDisp.length() == 0) {
+ //noinspection ConstantConditions
+ if (vendorDisp.isEmpty()) {
vendorDisp = vendor;
}
// For a missing id, we simply use a sanitized version of the display vendor
- if (vendorId.length() == 0) {
- boolean hasVendor = vendor.length() > 0;
+ //noinspection ConstantConditions
+ if (vendorId.isEmpty()) {
+ //noinspection ConstantConditions
+ boolean hasVendor = !vendor.isEmpty();
vendorId = LocalAddonPkgInfo.sanitizeDisplayToNameId(hasVendor ? vendor : vendorDisp);
}
- assert vendorId.length() > 0;
- assert vendorDisp.length() > 0;
+ assert !vendorId.isEmpty();
+ assert !vendorDisp.isEmpty();
mVendorId = vendorId.trim();
mVendorDisplay = vendorDisp.trim();
@@ -303,13 +310,14 @@
mVersion = target.getVersion();
mLayoutlibVersion = new LayoutlibVersionMixin(props);
- IOptionalLibrary[] optLibs = target.getOptionalLibraries();
- if (optLibs == null || optLibs.length == 0) {
+ List<OptionalLibrary> optLibs = target.getAdditionalLibraries();
+ if (optLibs.isEmpty()) {
mLibs = new Lib[0];
} else {
- mLibs = new Lib[optLibs.length];
- for (int i = 0; i < optLibs.length; i++) {
- mLibs[i] = new Lib(optLibs[i].getName(), optLibs[i].getDescription());
+ mLibs = new Lib[optLibs.size()];
+ for (int i = 0; i < optLibs.size(); i++) {
+ OptionalLibrary optionalLibrary = optLibs.get(i);
+ mLibs[i] = new Lib(optionalLibrary.getName(), optionalLibrary.getDescription());
}
}
@@ -588,7 +596,7 @@
getDisplayVendor());
String d = getDescription();
- if (d != null && d.length() > 0) {
+ if (d != null && !d.isEmpty()) {
s += '\n' + d;
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BrokenPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BrokenPackage.java
index 454bc53..3ee5ed2 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BrokenPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BrokenPackage.java
@@ -165,12 +165,12 @@
public String getLongDescription() {
String s = mLongDescription;
- if (s != null && s.length() != 0) {
+ if (s != null && !s.isEmpty()) {
return s;
}
s = getDescription();
- if (s != null && s.length() != 0) {
+ if (s != null && !s.isEmpty()) {
return s;
}
return getShortDescription();
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BuildToolPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BuildToolPackage.java
index 73faba3..2aa30ae 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BuildToolPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/BuildToolPackage.java
@@ -26,6 +26,7 @@
import com.android.sdklib.repository.FullRevision.PreviewComparison;
import com.android.sdklib.repository.IDescription;
import com.android.sdklib.repository.PkgProps;
+import com.android.sdklib.repository.PreciseRevision;
import com.android.sdklib.repository.descriptors.IPkgDesc;
import com.android.sdklib.repository.descriptors.PkgDesc;
@@ -229,7 +230,7 @@
*/
@Override
public String installId() {
- return INSTALL_ID_BASE + getRevision().toString().replace(' ', '_');
+ return getPkgDesc().getInstallId();
}
/**
@@ -270,7 +271,7 @@
@Override
public String getLongDescription() {
String s = getDescription();
- if (s == null || s.length() == 0) {
+ if (s == null || s.isEmpty()) {
s = getShortDescription();
}
@@ -297,7 +298,21 @@
@Override
public File getInstallFolder(String osSdkRoot, SdkManager sdkManager) {
File folder = new File(osSdkRoot, SdkConstants.FD_BUILD_TOOLS);
- folder = new File(folder, getRevision().toString().replace(' ', '_'));
+ StringBuilder sb = new StringBuilder();
+
+ PreciseRevision revision = getPkgDesc().getPreciseRevision();
+ int[] version = revision.toIntArray(false);
+ for (int i = 0; i < version.length; i++) {
+ sb.append(version[i]);
+ if (i != version.length - 1) {
+ sb.append('.');
+ }
+ }
+ if (getPkgDesc().getPreciseRevision().isPreview()) {
+ sb.append(PkgDesc.PREVIEW_SUFFIX);
+ }
+
+ folder = new File(folder, sb.toString());
return folder;
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/DocPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/DocPackage.java
index 5e51f40..3582f5b 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/DocPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/DocPackage.java
@@ -70,7 +70,7 @@
PackageParserUtils.getXmlInt (packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);
String codeName =
PackageParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME);
- if (codeName.length() == 0) {
+ if (codeName.isEmpty()) {
codeName = null;
}
mVersion = new AndroidVersion(apiLevel, codeName);
@@ -216,7 +216,7 @@
@Override
public String getLongDescription() {
String s = getDescription();
- if (s == null || s.length() == 0) {
+ if (s == null || s.isEmpty()) {
s = getShortDescription();
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ExtraPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ExtraPackage.java
index 8390fcb..fccbd21 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ExtraPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ExtraPackage.java
@@ -128,24 +128,24 @@
String vid =
PackageParserUtils.getXmlString(packageNode, RepoConstants.NODE_VENDOR_ID);
- if (vid.length() == 0) {
+ if (vid.isEmpty()) {
// If vid is missing, use the old <vendor> attribute.
// Note that in a valid XML, vendor-id cannot be an empty string.
// The only reason vid can be empty is when <vendor-id> is missing, which
// happens in an addon-3 schema, in which case the old <vendor> needs to be used.
String vendor = PackageParserUtils.getXmlString(packageNode, RepoConstants.NODE_VENDOR);
vid = sanitizeLegacyVendor(vendor);
- if (vname.length() == 0) {
+ if (vname.isEmpty()) {
vname = vendor;
}
}
- if (vname.length() == 0) {
+ if (vname.isEmpty()) {
// The vendor-display name can be empty, in which case we use the vendor-id.
vname = vid;
}
mVendor = new IdDisplay(vid.trim(), vname.trim());
- if (name.length() == 0) {
+ if (name.isEmpty()) {
// If name is missing, use the <path> attribute as done in an addon-3 schema.
name = LocalExtraPkgInfo.getPrettyName(mVendor, mPath);
}
@@ -179,7 +179,7 @@
String path = child.getTextContent();
if (path != null) {
path = path.trim();
- if (path.length() > 0) {
+ if (!path.isEmpty()) {
paths.add(path);
}
}
@@ -251,22 +251,22 @@
String vid = vendorId != null ? vendorId :
getProperty(props, PkgProps.EXTRA_VENDOR_ID, ""); //$NON-NLS-1$
- if (vid == null || vid.length() == 0) {
+ if (vid == null || vid.isEmpty()) {
// If vid is missing, use the old <vendor> attribute.
// <vendor> did not exist prior to schema repo-v3 and tools r8.
String vendor = getProperty(props, PkgProps.EXTRA_VENDOR, ""); //$NON-NLS-1$
vid = sanitizeLegacyVendor(vendor);
- if (vname == null || vname.length() == 0) {
+ if (vname == null || vname.isEmpty()) {
vname = vendor;
}
}
- if (vname == null || vname.length() == 0) {
+ if (vname == null || vname.isEmpty()) {
// The vendor-display name can be empty, in which case we use the vendor-id.
vname = vid;
}
mVendor = new IdDisplay(vid.trim(), vname.trim());
- if (name == null || name.length() == 0) {
+ if (name == null || name.isEmpty()) {
// If name is missing, use the <path> attribute as done in an addon-3 schema.
name = LocalExtraPkgInfo.getPrettyName(mVendor, mPath);
}
@@ -279,10 +279,10 @@
String projectFiles = getProperty(props, PkgProps.EXTRA_PROJECT_FILES, null);
ArrayList<String> filePaths = new ArrayList<String>();
- if (projectFiles != null && projectFiles.length() > 0) {
+ if (projectFiles != null && !projectFiles.isEmpty()) {
for (String filePath : projectFiles.split(Pattern.quote(File.pathSeparator))) {
filePath = filePath.trim();
- if (filePath.length() > 0) {
+ if (!filePath.isEmpty()) {
filePaths.add(filePath);
}
}
@@ -330,7 +330,7 @@
props.setProperty(PkgProps.EXTRA_PROJECT_FILES, sb.toString());
}
- if (mOldPaths != null && mOldPaths.length() > 0) {
+ if (mOldPaths != null && !mOldPaths.isEmpty()) {
props.setProperty(PkgProps.EXTRA_OLD_PATHS, mOldPaths);
}
}
@@ -398,7 +398,7 @@
// Sanitize the path
String path = mPath.replaceAll("[^a-zA-Z0-9-]+", "_"); //$NON-NLS-1$
- if (path.length() == 0 || path.equals("_")) { //$NON-NLS-1$
+ if (path.isEmpty() || path.equals("_")) { //$NON-NLS-1$
int h = path.hashCode();
path = String.format("extra%08x", h); //$NON-NLS-1$
}
@@ -427,7 +427,7 @@
// and cannot be empty. Let's be defensive and enforce that anyway since things
// like "____" are still valid values that we don't want to allow.
- if (vendorDisplay != null && vendorDisplay.length() > 0) {
+ if (vendorDisplay != null && !vendorDisplay.isEmpty()) {
String vendor = vendorDisplay.trim();
// Sanitize the vendor
vendor = vendor.replaceAll("[^a-zA-Z0-9-]+", "_"); //$NON-NLS-1$
@@ -511,7 +511,7 @@
getVendorDisplay());
String d = getDescription();
- if (d != null && d.length() > 0) {
+ if (d != null && !d.isEmpty()) {
s += '\n' + d;
}
@@ -585,12 +585,12 @@
File path = new File(osSdkRoot, SdkConstants.FD_EXTRAS);
String vendor = getVendorId();
- if (vendor != null && vendor.length() > 0) {
+ if (vendor != null && !vendor.isEmpty()) {
path = new File(path, vendor);
}
String name = getPath();
- if (name != null && name.length() > 0) {
+ if (name != null && !name.isEmpty()) {
path = new File(path, name);
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/Package.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/Package.java
index d441e67..4cf6f9b 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/Package.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/Package.java
@@ -153,8 +153,13 @@
descUrl = "";
}
- mLicense = new License(getProperty(props, PkgProps.PKG_LICENSE, license),
- getProperty(props, PkgProps.PKG_LICENSE_REF, null));
+ license = getProperty(props, PkgProps.PKG_LICENSE, license);
+ if (license != null) {
+ mLicense = new License(license, getProperty(props, PkgProps.PKG_LICENSE_REF, null));
+ }
+ else {
+ mLicense = null;
+ }
mListDisplay = getProperty(props, PkgProps.PKG_LIST_DISPLAY, ""); //$NON-NLS-1$
mDescription = getProperty(props, PkgProps.PKG_DESC, description);
mDescUrl = getProperty(props, PkgProps.PKG_DESC_URL, descUrl);
@@ -258,28 +263,28 @@
public void saveProperties(@NonNull Properties props) {
if (mLicense != null) {
String license = mLicense.getLicense();
- if (license != null && license.length() > 0) {
+ if (license != null && !license.isEmpty()) {
props.setProperty(PkgProps.PKG_LICENSE, license);
}
String licenseRef = mLicense.getLicenseRef();
- if (licenseRef != null && licenseRef.length() > 0) {
+ if (licenseRef != null && !licenseRef.isEmpty()) {
props.setProperty(PkgProps.PKG_LICENSE_REF, licenseRef);
}
}
- if (mListDisplay != null && mListDisplay.length() > 0) {
+ if (mListDisplay != null && !mListDisplay.isEmpty()) {
props.setProperty(PkgProps.PKG_LIST_DISPLAY, mListDisplay);
}
- if (mDescription != null && mDescription.length() > 0) {
+ if (mDescription != null && !mDescription.isEmpty()) {
props.setProperty(PkgProps.PKG_DESC, mDescription);
}
- if (mDescUrl != null && mDescUrl.length() > 0) {
+ if (mDescUrl != null && !mDescUrl.isEmpty()) {
props.setProperty(PkgProps.PKG_DESC_URL, mDescUrl);
}
- if (mReleaseNote != null && mReleaseNote.length() > 0) {
+ if (mReleaseNote != null && !mReleaseNote.isEmpty()) {
props.setProperty(PkgProps.PKG_RELEASE_NOTE, mReleaseNote);
}
- if (mReleaseUrl != null && mReleaseUrl.length() > 0) {
+ if (mReleaseUrl != null && !mReleaseUrl.isEmpty()) {
props.setProperty(PkgProps.PKG_RELEASE_URL, mReleaseUrl);
}
if (mObsolete != null) {
@@ -567,17 +572,17 @@
isObsolete() ? " (Obsolete)" : ""));
s = getDescUrl();
- if (s != null && s.length() > 0) {
+ if (s != null && !s.isEmpty()) {
sb.append(String.format("\n\nMore information at %1$s", s));
}
s = getReleaseNote();
- if (s != null && s.length() > 0) {
+ if (s != null && !s.isEmpty()) {
sb.append("\n\nRelease note:\n").append(s);
}
s = getReleaseNoteUrl();
- if (s != null && s.length() > 0) {
+ if (s != null && !s.isEmpty()) {
sb.append("\nRelease note URL: ").append(s);
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformPackage.java
index 96b1641..ff4e4bb 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformPackage.java
@@ -89,7 +89,7 @@
PackageParserUtils.getXmlInt (packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);
String codeName =
PackageParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME);
- if (codeName.length() == 0) {
+ if (codeName.isEmpty()) {
codeName = null;
}
mVersion = new AndroidVersion(apiLevel, codeName);
@@ -285,7 +285,7 @@
@Override
public String getLongDescription() {
String s = getDescription();
- if (s == null || s.length() == 0) {
+ if (s == null || s.isEmpty()) {
s = getShortDescription();
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformToolPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformToolPackage.java
index d757a40..85c6953 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformToolPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/PlatformToolPackage.java
@@ -231,7 +231,7 @@
@Override
public String getLongDescription() {
String s = getDescription();
- if (s == null || s.length() == 0) {
+ if (s == null || s.isEmpty()) {
s = getShortDescription();
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SamplePackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SamplePackage.java
index f9cca7f..d31805e 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SamplePackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SamplePackage.java
@@ -88,7 +88,7 @@
PackageParserUtils.getXmlInt (packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);
String codeName =
PackageParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME);
- if (codeName.length() == 0) {
+ if (codeName.isEmpty()) {
codeName = null;
}
mVersion = new AndroidVersion(apiLevel, codeName);
@@ -272,7 +272,7 @@
@Override
public String getLongDescription() {
String s = getDescription();
- if (s == null || s.length() == 0) {
+ if (s == null || s.isEmpty()) {
s = getShortDescription();
}
@@ -359,7 +359,7 @@
if (installFolder != null && installFolder.isDirectory()) {
// Get the hash computed during the last installation
String storedHash = readContentHash(installFolder);
- if (storedHash != null && storedHash.length() > 0) {
+ if (storedHash != null && !storedHash.isEmpty()) {
// Get the hash of the folder now
String currentHash = computeContentHash(installFolder);
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SourcePackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SourcePackage.java
index 234b3e7..6942431 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SourcePackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SourcePackage.java
@@ -80,7 +80,7 @@
PackageParserUtils.getXmlInt(packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);
String codeName =
PackageParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME);
- if (codeName.length() == 0) {
+ if (codeName.isEmpty()) {
codeName = null;
}
mVersion = new AndroidVersion(apiLevel, codeName);
@@ -284,7 +284,7 @@
@Override
public String getLongDescription() {
String s = getDescription();
- if (s == null || s.length() == 0) {
+ if (s == null || s.isEmpty()) {
s = getShortDescription();
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SystemImagePackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SystemImagePackage.java
index 7bc4bf6..c5ce04a 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SystemImagePackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/SystemImagePackage.java
@@ -83,7 +83,7 @@
PackageParserUtils.getXmlInt(packageNode, SdkRepoConstants.NODE_API_LEVEL, 0);
String codeName =
PackageParserUtils.getXmlString(packageNode, SdkRepoConstants.NODE_CODENAME);
- if (codeName.length() == 0) {
+ if (codeName.isEmpty()) {
codeName = null;
}
mVersion = new AndroidVersion(apiLevel, codeName);
@@ -125,8 +125,8 @@
SdkAddonConstants.NODE_VENDOR_DISPLAY,
vendorId);
- assert vendorId.length() > 0;
- assert vendorDisp.length() > 0;
+ assert !vendorId.isEmpty();
+ assert !vendorDisp.isEmpty();
vendor = new IdDisplay(vendorId, vendorDisp);
@@ -186,8 +186,8 @@
}
else {
// An add-on system-image
- assert vendorId.length() > 0;
- assert vendorDisp.length() > 0;
+ assert !vendorId.isEmpty();
+ assert !vendorDisp.isEmpty();
vendor = new IdDisplay(vendorId, vendorDisp);
@@ -480,7 +480,7 @@
@Override
public String getLongDescription() {
String s = getDescription();
- if (s == null || s.length() == 0) {
+ if (s == null || s.isEmpty()) {
s = getShortDescription();
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ToolPackage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ToolPackage.java
index f58e609..1b8b3b8 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ToolPackage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/packages/ToolPackage.java
@@ -215,7 +215,7 @@
@Override
public String getLongDescription() {
String s = getDescription();
- if (s == null || s.length() == 0) {
+ if (s == null || s.isEmpty()) {
s = getShortDescription();
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkRepoSource.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkRepoSource.java
index 2d5dbb1..3d2efb5 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkRepoSource.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkRepoSource.java
@@ -263,7 +263,7 @@
}
// we must have found the root node, and it must have an XML namespace prefix.
- if (oldRoot == null || prefix == null || prefix.length() == 0) {
+ if (oldRoot == null || prefix == null || prefix.isEmpty()) {
return null;
}
@@ -362,7 +362,7 @@
Node node = findChild(archive, null, prefix, RepoConstants.NODE_URL);
String url = node == null ? null : node.getTextContent().trim();
- if (url == null || url.length() == 0) {
+ if (url == null || url.isEmpty()) {
continue;
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkSource.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkSource.java
index 9996989..54ebd34 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkSource.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/sources/SdkSource.java
@@ -294,7 +294,7 @@
@Override
public String getShortDescription() {
- if (mUiName != null && mUiName.length() > 0) {
+ if (mUiName != null && !mUiName.isEmpty()) {
String host = "malformed URL";
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ArchiveInfo.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ArchiveInfo.java
index 5aaf936..c209838 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ArchiveInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ArchiveInfo.java
@@ -98,7 +98,7 @@
* want to install.
*/
public boolean isDependencyFor() {
- return mDependencyFor.size() > 0;
+ return !mDependencyFor.isEmpty();
}
/**
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ISettingsPage.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ISettingsPage.java
index 095923f..1da1b01 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ISettingsPage.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/ISettingsPage.java
@@ -74,9 +74,9 @@
/**
* Setting to enabling previews in the package list
* Type: Boolean.
- * Default: False.
+ * Default: True.
*/
- String KEY_ENABLE_PREVIEWS = "sdkman.enable.previews"; //$NON-NLS-1$
+ String KEY_ENABLE_PREVIEWS = "sdkman.enable.previews2"; //$NON-NLS-1$
/**
* Setting to set the density of the monitor.
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/PackageLoader.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/PackageLoader.java
index 7a3da42..c0314d4 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/PackageLoader.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/PackageLoader.java
@@ -445,7 +445,7 @@
// We override SdkRepoConstants.URL_GOOGLE_SDK_SITE if this is defined
String baseUrl = System.getenv("SDK_TEST_BASE_URL"); //$NON-NLS-1$
if (baseUrl != null) {
- if (baseUrl.length() > 0 && baseUrl.endsWith("/")) { //$NON-NLS-1$
+ if (!baseUrl.isEmpty() && baseUrl.endsWith("/")) { //$NON-NLS-1$
if (url.startsWith(SdkRepoConstants.URL_GOOGLE_SDK_SITE)) {
url = baseUrl + url.substring(SdkRepoConstants.URL_GOOGLE_SDK_SITE.length());
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterLogic.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterLogic.java
index 330fddb..ff2000f 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterLogic.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterLogic.java
@@ -697,7 +697,7 @@
}
}
- if (aiFound.size() > 0) {
+ if (!aiFound.isEmpty()) {
ArchiveInfo[] result = aiFound.toArray(new ArchiveInfo[aiFound.size()]);
Arrays.sort(result);
return result;
@@ -876,6 +876,10 @@
Package p = a.getParentPackage();
if (p instanceof PlatformToolPackage) {
FullRevision r = ((PlatformToolPackage) p).getRevision();
+ // If computing dependencies for a non-preview package, don't offer preview dependencies
+ if (r.isPreview() && !rev.isPreview()) {
+ continue;
+ }
if (r.compareTo(localRev) >= compareThreshold) {
localRev = r;
localAiMax = ai;
@@ -902,6 +906,11 @@
Package p = a.getParentPackage();
if (p instanceof PlatformToolPackage) {
FullRevision r = ((PlatformToolPackage) p).getRevision();
+ // If computing dependencies for a non-preview package, don't offer preview dependencies
+ if (r.isPreview() && !rev.isPreview()) {
+ continue;
+ }
+
if (r.compareTo(localRev) >= compareThreshold) {
localRev = r;
localAiMax = null;
@@ -934,6 +943,11 @@
for (Package p : remotePkgs) {
if (p instanceof PlatformToolPackage) {
FullRevision r = ((PlatformToolPackage) p).getRevision();
+ // If computing dependencies for a non-preview package, don't offer preview dependencies
+ if (r.isPreview() && !rev.isPreview()) {
+ continue;
+ }
+
if (r.compareTo(rev) >= 0) {
// Make sure there's at least one valid archive here
for (Archive a : p.getArchives()) {
@@ -1358,7 +1372,7 @@
protected void fetchRemotePackages(
final Collection<Package> remotePkgs,
final SdkSource[] remoteSources) {
- if (remotePkgs.size() > 0) {
+ if (!remotePkgs.isEmpty()) {
return;
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterNoWindow.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterNoWindow.java
index d59d9e2..8183e1b 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterNoWindow.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SdkUpdaterNoWindow.java
@@ -226,11 +226,11 @@
Properties props = System.getProperties();
- if (proxyHost != null && proxyHost.length() > 0) {
+ if (proxyHost != null && !proxyHost.isEmpty()) {
props.setProperty(JAVA_PROP_HTTP_PROXY_HOST, proxyHost);
props.setProperty(JAVA_PROP_HTTPS_PROXY_HOST, proxyHost);
}
- if (proxyPort != null && proxyPort.length() > 0) {
+ if (proxyPort != null && !proxyPort.isEmpty()) {
props.setProperty(JAVA_PROP_HTTP_PROXY_PORT, proxyPort);
props.setProperty(JAVA_PROP_HTTPS_PROXY_PORT, proxyPort);
}
@@ -453,7 +453,7 @@
byte[] readBuffer = new byte[2048];
String reply = readLine(readBuffer).trim();
mSdkLog.info("\n"); //$NON-NLS-1$
- if (reply.length() > 0 && reply.length() <= 3) {
+ if (!reply.isEmpty() && reply.length() <= 3) {
char c = reply.charAt(0);
if (c == 'y' || c == 'Y') {
return true;
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SettingsController.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SettingsController.java
index f94dfad..c15cbf7 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SettingsController.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/SettingsController.java
@@ -193,7 +193,10 @@
* @see ISettingsPage#KEY_ENABLE_PREVIEWS
*/
public boolean getEnablePreviews() {
- return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_ENABLE_PREVIEWS));
+ return Boolean.parseBoolean(
+ mProperties.getProperty(
+ ISettingsPage.KEY_ENABLE_PREVIEWS,
+ Boolean.TRUE.toString()));
}
/**
@@ -288,6 +291,7 @@
setShowUpdateOnly(mSettings.getShowUpdateOnly());
setSetting(ISettingsPage.KEY_ASK_ADB_RESTART, mSettings.getAskBeforeAdbRestart());
setSetting(ISettingsPage.KEY_USE_DOWNLOAD_CACHE, mSettings.getUseDownloadCache());
+ setSetting(ISettingsPage.KEY_ENABLE_PREVIEWS, mSettings.getEnablePreviews());
} catch (Exception e) {
if (mSdkLog != null) {
@@ -375,11 +379,11 @@
// Only change the proxy if have something in the preferences.
// Do not erase the default settings by empty values.
- if (proxyHost != null && proxyHost.length() > 0) {
+ if (proxyHost != null && !proxyHost.isEmpty()) {
props.setProperty(JAVA_PROP_HTTP_PROXY_HOST, proxyHost);
props.setProperty(JAVA_PROP_HTTPS_PROXY_HOST, proxyHost);
}
- if (proxyPort != null && proxyPort.length() > 0) {
+ if (proxyPort != null && !proxyPort.isEmpty()) {
props.setProperty(JAVA_PROP_HTTP_PROXY_PORT, proxyPort);
props.setProperty(JAVA_PROP_HTTPS_PROXY_PORT, proxyPort);
}
diff --git a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/UpdaterData.java b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/UpdaterData.java
index 3c1c42c..c1b691c 100755
--- a/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/UpdaterData.java
+++ b/sdklib/src/main/java/com/android/sdklib/internal/repository/updater/UpdaterData.java
@@ -789,7 +789,7 @@
Collections.sort(archives);
- if (archives.size() > 0) {
+ if (!archives.isEmpty()) {
return installArchives(archives, flags);
}
return null;
@@ -845,7 +845,7 @@
List<ArchiveInfo> archives = getRemoteArchives_NoGUI(includeAll);
// Filter the selected archives to only keep the ones matching the filter
- if (pkgFilter != null && pkgFilter.size() > 0 && archives != null && archives.size() > 0) {
+ if (pkgFilter != null && !pkgFilter.isEmpty() && archives != null && !archives.isEmpty()) {
// Map filter types to an SdkRepository Package type,
// e.g. create a map "platform" => PlatformPackage.class
HashMap<String, Class<? extends Package>> pkgMap =
@@ -862,7 +862,7 @@
Package p = a.getParentPackage();
if (p != null) {
String iid = p.installId().toLowerCase(Locale.US);
- if (iid != null && iid.length() > 0 && !installIdMap.containsKey(iid)) {
+ if (iid != null && !iid.isEmpty() && !installIdMap.containsKey(iid)) {
installIdMap.put(iid, p);
}
}
@@ -886,7 +886,7 @@
if (installIdMap.containsKey(iid)) {
userFilteredInstallIds.add(iid);
- } else if (iid.replaceAll("[0-9]+", "").length() == 0) {//$NON-NLS-1$ //$NON-NLS-2$
+ } else if (iid.replaceAll("[0-9]+", "").isEmpty()) {//$NON-NLS-1$ //$NON-NLS-2$
// An all-digit number is a package index requested by the user.
int index = Integer.parseInt(iid);
userFilteredIndices.put(index, index);
@@ -1144,9 +1144,9 @@
License lic = p.getLicense();
if (lic != null &&
lic.getLicenseRef() != null &&
- lic.getLicense().length() > 0 &&
+ !lic.getLicense().isEmpty() &&
lic.getLicense() != null &&
- lic.getLicense().length() > 0) {
+ !lic.getLicense().isEmpty()) {
return lic;
}
}
@@ -1246,7 +1246,7 @@
* This can be called from any thread.
*/
public void broadcastOnSdkLoaded() {
- if (mListeners.size() > 0) {
+ if (!mListeners.isEmpty()) {
runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -1267,7 +1267,7 @@
* This can be called from any thread.
*/
private void broadcastOnSdkReload() {
- if (mListeners.size() > 0) {
+ if (!mListeners.isEmpty()) {
runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -1288,7 +1288,7 @@
* This can be called from any thread.
*/
private void broadcastPreInstallHook() {
- if (mListeners.size() > 0) {
+ if (!mListeners.isEmpty()) {
runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -1309,7 +1309,7 @@
* This can be called from any thread.
*/
private void broadcastPostInstallHook() {
- if (mListeners.size() > 0) {
+ if (!mListeners.isEmpty()) {
runOnUiThread(new Runnable() {
@Override
public void run() {
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/License.java b/sdklib/src/main/java/com/android/sdklib/repository/License.java
index b280b56..7040ec1 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/License.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/License.java
@@ -18,6 +18,15 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.sdklib.SdkManager;
+import com.android.utils.FileUtils;
+import com.google.common.base.Charsets;
+import com.google.common.hash.Hashing;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
/**
* License text, with an optional license XML reference.
@@ -25,15 +34,14 @@
public class License {
private final String mLicense;
private final String mLicenseRef;
+ private final String mLicenseHash;
- public License(@NonNull String license) {
- mLicense = license;
- mLicenseRef = null;
- }
+ private static final String LICENSE_DIR = "licenses";
public License(@NonNull String license, @Nullable String licenseRef) {
mLicense = license;
mLicenseRef = licenseRef;
+ mLicenseHash = Hashing.sha1().hashBytes(mLicense.getBytes()).toString();
}
/** Returns the license text. Never null. */
@@ -42,6 +50,12 @@
return mLicense;
}
+ /** Returns the hash of the license text. Never null. */
+ @NonNull
+ public String getLicenseHash() {
+ return mLicenseHash;
+ }
+
/**
* Returns the license XML reference.
* Could be null, e.g. in tests or synthetic packages
@@ -106,5 +120,57 @@
}
return true;
}
+
+ /**
+ * Checks whether this license has previously been accepted.
+ * @param sdkRoot The root directory of the Android SDK
+ * @return true if this license has already been accepted
+ */
+ public boolean checkAccepted(@Nullable File sdkRoot) {
+ if (sdkRoot == null) {
+ return false;
+ }
+ File licenseDir = new File(sdkRoot, LICENSE_DIR);
+ File licenseFile = new File(licenseDir, mLicenseRef == null ? mLicenseHash : mLicenseRef);
+ if (!licenseFile.exists()) {
+ return false;
+ }
+ try {
+ String hash = Files.readFirstLine(licenseFile, Charsets.UTF_8);
+ return hash.equals(mLicenseHash);
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Marks this license as accepted.
+ *
+ * @param sdkRoot The root directory of the Android SDK
+ * @return true if the acceptance was persisted successfully.
+ */
+ public boolean setAccepted(@Nullable File sdkRoot) {
+ if (sdkRoot == null) {
+ return false;
+ }
+ if (checkAccepted(sdkRoot)) {
+ return true;
+ }
+ File licenseDir = new File(sdkRoot, LICENSE_DIR);
+ if (licenseDir.exists() && !licenseDir.isDirectory()) {
+ return false;
+ }
+ if (!licenseDir.exists()) {
+ licenseDir.mkdir();
+ }
+ File licenseFile = new File(licenseDir, mLicenseRef == null ? mLicenseHash : mLicenseRef);
+ try {
+ Files.write(mLicenseHash, licenseFile, Charsets.UTF_8);
+ }
+ catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/PreciseRevision.java b/sdklib/src/main/java/com/android/sdklib/repository/PreciseRevision.java
index 531a695..81e9744 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/PreciseRevision.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/PreciseRevision.java
@@ -104,7 +104,7 @@
@Override
public int[] toIntArray(boolean includePreview) {
int[] result;
- if (mPrecision >= PRECISION_PREVIEW && isPreview()) {
+ if (mPrecision >= PRECISION_PREVIEW) {
if (includePreview) {
result = new int[mPrecision];
result[3] = getPreview();
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/RepoConstants.java b/sdklib/src/main/java/com/android/sdklib/repository/RepoConstants.java
index f69c5e1..44f6f15 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/RepoConstants.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/RepoConstants.java
@@ -22,6 +22,7 @@
/**
* Public constants common to the sdk-repository and sdk-addon XML Schemas.
+ * @deprecated moved to studio
*/
public class RepoConstants {
@@ -209,5 +210,4 @@
return stream;
}
-
}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/RepoXsdUtil.java b/sdklib/src/main/java/com/android/sdklib/repository/RepoXsdUtil.java
new file mode 100644
index 0000000..805fa8a
--- /dev/null
+++ b/sdklib/src/main/java/com/android/sdklib/repository/RepoXsdUtil.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdklib.repository;
+
+import com.google.common.collect.Lists;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.stream.StreamSource;
+
+/**
+ * Utilities related to the respository XSDs.
+ */
+public class RepoXsdUtil {
+
+ public static final String NODE_IMPORT = "import";
+ public static final String NODE_INCLUDE = "include";
+
+ public static final String ATTR_SCHEMA_LOCATION = "schemaLocation";
+
+
+ /**
+ * Gets StreamSources for the given xsd (implied by the name and version), as well as any xsds imported or included by the main one.
+ *
+ * @param rootElement The root of the filename of the XML schema. This is by convention the same
+ * as the root element declared by the schema.
+ * @param version The XML schema revision number, an integer >= 1.
+ */
+ public static StreamSource[] getXsdStream(final String rootElement, int version) {
+ String filename = String.format("%1$s-%2$02d.xsd", rootElement, version); //$NON-NLS-1$
+ final List<StreamSource> streams = Lists.newArrayList();
+ InputStream stream = null;
+ try {
+ stream = RepoXsdUtil.class.getResourceAsStream(filename);
+ if (stream == null) {
+ filename = String.format("%1$s-%2$d.xsd", rootElement, version); //$NON-NLS-1$
+ stream = RepoXsdUtil.class.getResourceAsStream(filename);
+ }
+ if (stream == null) {
+ // Try the alternate schemas that are not published yet.
+ // This allows us to internally test with new schemas before the
+ // public repository uses it.
+ filename = String.format("-%1$s-%2$02d.xsd", rootElement, version); //$NON-NLS-1$
+ stream = RepoXsdUtil.class.getResourceAsStream(filename);
+ }
+
+ // Parse the schema and find any imports or includes so we can return them as well.
+ // Currently transitive includes are not supported.
+ SAXParserFactory.newInstance().newSAXParser().parse(stream, new DefaultHandler() {
+ @Override
+ public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
+ name = name.substring(name.indexOf(':') + 1);
+ if (name.equals(NODE_IMPORT) || name.equals(NODE_INCLUDE)) {
+ String importFile = attributes.getValue(ATTR_SCHEMA_LOCATION);
+ streams.add(new StreamSource(RepoXsdUtil.class.getResourceAsStream(importFile)));
+ }
+ }
+ });
+ // create and add the first stream again, since SaxParser closes the original one
+ streams.add(new StreamSource(RepoXsdUtil.class.getResourceAsStream(filename)));
+ } catch (Exception e) {
+ // Some implementations seem to return null on failure,
+ // others throw an exception. We want to return null.
+ return null;
+ }
+ return streams.toArray(new StreamSource[streams.size()]);
+ }
+}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonConstants.java b/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonConstants.java
index a3b16be..805f85b 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonConstants.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonConstants.java
@@ -21,6 +21,7 @@
/**
* Public constants for the sdk-addon XML Schema.
+ * @deprecated moved to studio
*/
public class SdkAddonConstants extends RepoConstants {
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonsListConstants.java b/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonsListConstants.java
index 4f6b897..3468b77 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonsListConstants.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/SdkAddonsListConstants.java
@@ -21,6 +21,7 @@
/**
* Public constants for the sdk-addons-list XML Schema.
+ * @deprecated moved to studio
*/
public class SdkAddonsListConstants {
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/SdkRepoConstants.java b/sdklib/src/main/java/com/android/sdklib/repository/SdkRepoConstants.java
index b9e5f1c..7b32543 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/SdkRepoConstants.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/SdkRepoConstants.java
@@ -25,6 +25,7 @@
/**
* Public constants for the sdk-repository XML Schema.
+ * @deprecated moved to studio
*/
public class SdkRepoConstants extends RepoConstants {
@@ -32,7 +33,7 @@
* The latest version of the sdk-repository XML Schema.
* Valid version numbers are between 1 and this number, included.
*/
- public static final int NS_LATEST_VERSION = 10;
+ public static final int NS_LATEST_VERSION = 11;
/**
* The min version of the sdk-repository XML Schema we'll try to load.
@@ -114,7 +115,6 @@
/** A source package. */
public static final String NODE_SOURCE = "source"; //$NON-NLS-1$
-
/**
* List of possible nodes in a repository XML. Used to populate options automatically
* in the no-GUI mode.
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/SdkStatsConstants.java b/sdklib/src/main/java/com/android/sdklib/repository/SdkStatsConstants.java
index 22f8aa2..df3e5d6 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/SdkStatsConstants.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/SdkStatsConstants.java
@@ -21,6 +21,7 @@
/**
* Public constants for the sdk-stats XML Schema.
+ * @deprecated moved to studio
*/
public class SdkStatsConstants {
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/SdkSysImgConstants.java b/sdklib/src/main/java/com/android/sdklib/repository/SdkSysImgConstants.java
index 3ea0091..d3e5752 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/SdkSysImgConstants.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/SdkSysImgConstants.java
@@ -21,6 +21,7 @@
/**
* Public constants for the sdk-sys-img XML Schema.
+ * @deprecated moved to studio
*/
public class SdkSysImgConstants extends RepoConstants {
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/IPkgDesc.java b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/IPkgDesc.java
index afba846..5effda3 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/IPkgDesc.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/IPkgDesc.java
@@ -83,10 +83,10 @@
/**
* Returns the package's revision or null. This will come from the {@link FullRevision} or
* {@link MajorRevision}, with the precision set as appropriate.
- * @return A non-null value if {@link #hasMajorRevision()} or {@link #hasFullRevision()}
- * is true; otherwise a null value.
+ * @return A representation of {@link #getMajorRevision()} or {@link #getFullRevision()},
+ * depending on which one exists.
*/
- @Nullable
+ @NonNull
PreciseRevision getPreciseRevision();
/**
@@ -140,26 +140,50 @@
/**
* Indicates whether <em>this</em> package descriptor is an update for the given
- * existing descriptor.
+ * existing descriptor. Preview versions are never considered updates for non-
+ * previews, and vice versa.
*
* @param existingDesc A non-null existing descriptor.
* @return True if this package is an update for the given one.
*/
boolean isUpdateFor(@NonNull IPkgDesc existingDesc);
+ /**
+ * Indicates whether <em>this</em> package descriptor is an update for the given
+ * existing descriptor, using the given comparison method.
+ *
+ * @param existingDesc A non-null existing descriptor.
+ * @param previewComparison The {@link FullRevision.PreviewComparison} method to use
+ * when comparing the packages.
+ * @return True if this package is an update for the given one.
+ */
+ boolean isUpdateFor(@NonNull IPkgDesc existingDesc,
+ @NonNull FullRevision.PreviewComparison previewComparison);
+
/**
- * Returns a stable string id that can be used to reference this package.
- * @return A stable string id that can be used to reference this package.
+ * Returns a stable string id that can be used to reference this package, including
+ * a suffix indicating that this package is a preview if it is.
*/
@NonNull
String getInstallId();
/**
+ * Returns a stable string id that can be used to reference this package, which
+ * excludes the preview suffix.
+ */
+ String getBaseInstallId();
+
+ /**
* Returns the canonical location where such a package would be installed.
* @param sdkLocation The root of the SDK.
* @return the canonical location where such a package would be installed.
*/
@NonNull
File getCanonicalInstallFolder(@NonNull File sdkLocation);
+
+ /**
+ * @return True if the revision of this package is a preview.
+ */
+ boolean isPreview();
}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDesc.java b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDesc.java
index 3e905a8..6c1e152 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDesc.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDesc.java
@@ -23,10 +23,10 @@
import com.android.sdklib.AndroidTargetHash;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.SystemImage;
-import com.android.sdklib.repository.License;
import com.android.sdklib.io.FileOp;
import com.android.sdklib.repository.FullRevision;
import com.android.sdklib.repository.FullRevision.PreviewComparison;
+import com.android.sdklib.repository.License;
import com.android.sdklib.repository.MajorRevision;
import com.android.sdklib.repository.NoPreviewRevision;
import com.android.sdklib.repository.PreciseRevision;
@@ -47,6 +47,7 @@
* methods provided in the base {@link PkgDesc}.
*/
public class PkgDesc implements IPkgDesc {
+ public static final String PREVIEW_SUFFIX = "-preview";
private final PkgType mType;
private final FullRevision mFullRevision;
private final MajorRevision mMajorRevision;
@@ -186,7 +187,7 @@
return mMajorRevision;
}
- @Nullable
+ @NonNull
@Override
public final PreciseRevision getPreciseRevision() {
if (mMajorRevision == null) {
@@ -202,6 +203,11 @@
return mAndroidVersion;
}
+ @Override
+ public boolean isPreview() {
+ return getPreciseRevision().isPreview();
+ }
+
@Nullable
@Override
public String getPath() {
@@ -238,10 +244,19 @@
@Override
public String getInstallId() {
+ String id = getBaseInstallId();
+ if (getPreciseRevision().isPreview()) {
+ return id + PREVIEW_SUFFIX;
+ }
+ return id;
+ }
+
+ @Override
+ public String getBaseInstallId() {
StringBuilder sb = new StringBuilder();
/* iid patterns:
- tools, platform-tools => FOLDER / FOLDER-preview
+ tools, platform-tools => FOLDER
build-tools => FOLDER-REV
doc, sample, source => ENUM-API
extra => ENUM-VENDOR.id-PATH
@@ -255,17 +270,25 @@
case PKG_TOOLS:
case PKG_PLATFORM_TOOLS:
sb.append(mType.getFolderName());
- if (getFullRevision().isPreview()) {
- sb.append("-preview");
- }
break;
case PKG_BUILD_TOOLS:
- sb.append(mType.getFolderName());
- sb.append('-').append(getFullRevision().toString());
+ sb.append(mType.getFolderName()).append('-');
+ // Add version number without the preview revision number. This is to make preview
+ // packages be updatable to the next revision.
+ int[] version = getPreciseRevision().toIntArray(false);
+ for (int i = 0; i < version.length; i++) {
+ sb.append(version[i]);
+ if (i != version.length - 1) {
+ sb.append('.');
+ }
+ }
break;
case PKG_DOC:
+ sb.append("doc");
+ break;
+
case PKG_SAMPLE:
case PKG_SOURCE:
sb.append(mType.toString().toLowerCase(Locale.US).replace("pkg_", ""));
@@ -280,7 +303,8 @@
break;
case PKG_PLATFORM:
- sb.append(AndroidTargetHash.PLATFORM_HASH_PREFIX).append(getAndroidVersion().getApiString());
+ sb.append(AndroidTargetHash.PLATFORM_HASH_PREFIX)
+ .append(getAndroidVersion().getApiString());
break;
case PKG_ADDON:
@@ -310,7 +334,11 @@
.append(getVendor().getId())
.append('-')
.append(getAndroidVersion().getApiString());
- break;
+ break;
+
+ case PKG_NDK:
+ sb.append("ndk");
+ break;
default:
throw new IllegalArgumentException("IID not defined for type " + mType.toString());
@@ -347,13 +375,16 @@
case PKG_PLATFORM:
case PKG_SAMPLE:
case PKG_SOURCE:
- f = FileOp.append(f, AndroidTargetHash.PLATFORM_HASH_PREFIX + sanitize(getAndroidVersion().getApiString()));
+ f = FileOp.append(f, AndroidTargetHash.PLATFORM_HASH_PREFIX + sanitize(
+ getAndroidVersion().getApiString()));
break;
case PKG_SYS_IMAGE:
f = FileOp.append(f,
- AndroidTargetHash.PLATFORM_HASH_PREFIX + sanitize(getAndroidVersion().getApiString()),
- sanitize(SystemImage.DEFAULT_TAG.equals(getTag()) ? "android" : getTag().getId()),
+ AndroidTargetHash.PLATFORM_HASH_PREFIX + sanitize(
+ getAndroidVersion().getApiString()),
+ sanitize(SystemImage.DEFAULT_TAG.equals(getTag()) ? "android"
+ : getTag().getId()),
sanitize(getPath())); // path==abi
break;
@@ -376,7 +407,8 @@
break;
default:
- throw new IllegalArgumentException("CanonicalFolder not defined for type " + mType.toString());
+ throw new IllegalArgumentException(
+ "CanonicalFolder not defined for type " + mType.toString());
}
return f;
@@ -394,10 +426,16 @@
*/
@Override
public boolean isUpdateFor(@NonNull IPkgDesc existingDesc) {
+ return isUpdateFor(existingDesc, PreviewComparison.COMPARE_NUMBER);
+ }
+
+ @Override
+ public boolean isUpdateFor(@NonNull IPkgDesc existingDesc,
+ @NonNull PreviewComparison previewComparison) {
if (mCustomIsUpdateFor != null) {
return mCustomIsUpdateFor.isUpdateFor(this, existingDesc);
} else {
- return isGenericUpdateFor(existingDesc);
+ return isGenericUpdateFor(existingDesc, previewComparison);
}
}
@@ -407,9 +445,11 @@
* as needed.
*
* @param existingDesc A non-null package descriptor to compare with.
+ * @param previewComparison The type of preview comparison to do.
* @return True if this package descriptor would generally update the given one.
*/
- private boolean isGenericUpdateFor(@NonNull IPkgDesc existingDesc) {
+ private boolean isGenericUpdateFor(@NonNull IPkgDesc existingDesc,
+ PreviewComparison previewComparison) {
if (existingDesc == null || !getType().equals(existingDesc.getType())) {
return false;
@@ -454,7 +494,8 @@
// Packages that have a full revision are generally updates if it increases
// but keeps the same kind of preview (e.g. previews are only updates by previews.)
if (hasFullRevision() &&
- getFullRevision().isPreview() == existingDesc.getFullRevision().isPreview()) {
+ (previewComparison == PreviewComparison.IGNORE
+ || existingDesc.isPreview() == isPreview())) {
// If both packages match in their preview type (both previews or both not previews)
// then is the RC/preview number an update?
return getFullRevision().compareTo(existingDesc.getFullRevision(),
@@ -558,10 +599,13 @@
// Flags for list description pattern string, used in PkgType:
// $MAJ $FULL $API $PATH $TAG $VEND $NAME (for extras)
- result = result.replace("$MAJ", hasMajorRevision() ? getMajorRevision().toShortString() : "");
- result = result.replace("$FULL", hasFullRevision() ? getFullRevision() .toShortString() : "");
- result = result.replace("$API", hasAndroidVersion() ? getAndroidVersion().getApiString() : "");
- result = result.replace("$PATH", hasPath() ? getPath() : "");
+ result = result
+ .replace("$MAJ", hasMajorRevision() ? getMajorRevision().toShortString() : "");
+ result = result
+ .replace("$FULL", hasFullRevision() ? getFullRevision().toShortString() : "");
+ result = result
+ .replace("$API", hasAndroidVersion() ? getAndroidVersion().getApiString() : "");
+ result = result.replace("$PATH", hasPath() ? getPath() : "");
result = result.replace("$TAG", hasTag() && !getTag().equals(SystemImage.DEFAULT_TAG) ?
getTag().getDisplay() : "");
result = result.replace("$VEND", hasVendor() ? getVendor().getDisplay() : "");
@@ -808,7 +852,7 @@
*
* @param revision The revision of the tool package.
* @param minPlatformToolsRev The {@code min-platform-tools-rev}.
- * Use {@link FullRevision#NOT_SPECIFIED} to indicate there is no requirement.
+ * Use {@link FullRevision#NOT_SPECIFIED} to indicate there is no requirement.
* @return A {@link PkgDesc} describing this tool package.
*/
@NonNull
@@ -849,10 +893,11 @@
// Generic test checks that the preview type is the same (both previews or not).
// Build tool is different in that the full revision must be an exact match
// and not an increase.
- return thisPkgDesc.isGenericUpdateFor(existingDesc) &&
- thisPkgDesc.getFullRevision().compareTo(
- existingDesc.getFullRevision(),
- PreviewComparison.COMPARE_TYPE) == 0;
+ return thisPkgDesc
+ .isGenericUpdateFor(existingDesc, PreviewComparison.COMPARE_NUMBER) &&
+ thisPkgDesc.getFullRevision().compareTo(
+ existingDesc.getFullRevision(),
+ PreviewComparison.COMPARE_TYPE) == 0;
}
};
return p;
@@ -903,7 +948,7 @@
@NonNull
public static Builder newExtra(@NonNull IdDisplay vendor,
@NonNull String path,
- @NonNull String displayName,
+ @Nullable String displayName,
@Nullable String[] oldPaths,
@NonNull NoPreviewRevision revision) {
Builder p = new Builder(PkgType.PKG_EXTRA);
@@ -1057,6 +1102,19 @@
return p;
}
+ /**
+ * Creates a new NDK package descriptor.
+ *
+ * @param revision The revision of the NDK package.
+ * @return A {@link PkgDesc} describing this NDK package.
+ */
+ @NonNull
+ public static Builder newNdk(@NonNull FullRevision revision) {
+ Builder p = new Builder(PkgType.PKG_NDK);
+ p.mFullRevision = revision;
+ return p;
+ }
+
public Builder setLicense(@Nullable License license) {
mLicense = license;
return this;
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDescExtra.java b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDescExtra.java
index d2c02f6..b5f2efb 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDescExtra.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgDescExtra.java
@@ -45,7 +45,7 @@
@Nullable IdDisplay vendor,
@Nullable FullRevision minToolsRev,
@Nullable FullRevision minPlatformToolsRev,
- @NonNull String nameDisplay,
+ @Nullable String nameDisplay,
@Nullable final String[] oldPaths) {
super(type,
license,
@@ -76,7 +76,7 @@
@NonNull
@Override
public String getNameDisplay() {
- return mNameDisplay;
+ return mNameDisplay == null ? String.format("Unknown (%s)", getInstallId()) : mNameDisplay;
}
// ---- Helpers ----
@@ -90,7 +90,7 @@
*/
@NonNull
public static String[] convertOldPaths(@Nullable String oldPathsProperty) {
- if (oldPathsProperty == null || oldPathsProperty.length() == 0) {
+ if (oldPathsProperty == null || oldPathsProperty.isEmpty()) {
return new String[0];
}
return oldPathsProperty.split(";"); //$NON-NLS-1$
@@ -147,7 +147,7 @@
if (otherPath != null && thisPath != null && thisVendor != null) {
if (otherPath.equals(thisVendor + '-' + thisPath) &&
(otherVendor == null ||
- otherVendor.length() == 0 ||
+ otherVendor.isEmpty() ||
otherVendor.equals(thisVendor))) {
return true;
}
@@ -155,7 +155,7 @@
if (thisPath != null && otherPath != null && otherVendor != null) {
if (thisPath.equals(otherVendor + '-' + otherPath) &&
(thisVendor == null ||
- thisVendor.length() == 0 ||
+ thisVendor.isEmpty() ||
thisVendor.equals(otherVendor))) {
return true;
}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgType.java b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgType.java
index 251f627..9f0ccc2 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgType.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/descriptors/PkgType.java
@@ -64,7 +64,7 @@
/** Filter the SDK/docs folder.
* Has {@link MajorRevision}. */
PKG_DOC(0x0010, SdkConstants.FD_DOCS,
- "Documentation for Android SDK $API{?$MAJ>1:, rev $MAJ}",
+ "Documentation for Android SDK",
true /*maj-r*/, false, true /*api*/, false, false, false, false, false),
/** Filter the SDK/platforms.
@@ -114,7 +114,11 @@
* Cast the descriptor to {@link IPkgDescExtra} to get extra's specific attributes. */
PKG_EXTRA(0x4000, SdkConstants.FD_EXTRAS,
"{|$NAME|$VEND $PATH|}{?$FULL>1:, rev $FULL}",
- false, true /*full-r*/, false, true /*path*/, false, true /*vend*/, false, false);
+ false, true /*full-r*/, false, true /*path*/, false, true /*vend*/, false, false),
+
+ /** The SDK/ndk folder. */
+ PKG_NDK(0x8000, SdkConstants.FD_NDK, "",
+ false, true, false, false, false, false, false, false);
/** A collection of all the known PkgTypes. */
public static final EnumSet<PkgType> PKG_ALL = EnumSet.allOf(PkgType.class);
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonPkgInfo.java
index f378837..7dda921 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonPkgInfo.java
@@ -54,7 +54,8 @@
private static final Pattern PATTERN_USB_IDS = Pattern.compile(
"^0x[a-f0-9]{4}$", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
- private final @NonNull IPkgDescAddon mAddonDesc;
+ @NonNull
+ private final IPkgDescAddon mAddonDesc;
public LocalAddonPkgInfo(@NonNull LocalSdk localSdk,
@NonNull File localDir,
@@ -234,7 +235,7 @@
Map<String, File> skinsMap = new TreeMap<String, File>();
- for (File f : parseSkinFolder(targetSkinFolder)) {
+ for (File f : PackageParserUtils.parseSkinFolder(targetSkinFolder, fileOp)) {
skinsMap.put(f.getName().toLowerCase(Locale.US), f);
}
for (ISystemImage si : systemImages) {
@@ -446,20 +447,7 @@
final IdDisplay tag = mAddonDesc.getName();
final String abi = d.getPath();
if (abi != null && !tagToAbiFound.containsEntry(tag, abi)) {
- List<File> parsedSkins = parseSkinFolder(
- new File(pkg.getLocalDir(), SdkConstants.FD_SKINS));
- File[] skins = FileOp.EMPTY_FILE_ARRAY;
- if (!parsedSkins.isEmpty()) {
- skins = parsedSkins.toArray(new File[parsedSkins.size()]);
- }
-
- found.add(new SystemImage(
- pkg.getLocalDir(),
- LocationType.IN_SYSTEM_IMAGE,
- tag,
- mAddonDesc.getVendor(),
- abi,
- skins));
+ found.add(((LocalAddonSysImgPkgInfo)pkg).getSystemImage());
tagToAbiFound.put(tag, abi);
}
}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonSysImgPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonSysImgPkgInfo.java
index 60c9cf5..e02c8e9 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonSysImgPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalAddonSysImgPkgInfo.java
@@ -19,6 +19,7 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.ISystemImage;
import com.android.sdklib.repository.MajorRevision;
import com.android.sdklib.repository.descriptors.IPkgDesc;
import com.android.sdklib.repository.descriptors.IdDisplay;
@@ -36,7 +37,8 @@
public class LocalAddonSysImgPkgInfo extends LocalPkgInfo {
- private final @NonNull IPkgDesc mDesc;
+ @NonNull
+ private final IPkgDesc mDesc;
public LocalAddonSysImgPkgInfo(@NonNull LocalSdk localSdk,
@NonNull File localDir,
@@ -56,4 +58,8 @@
public IPkgDesc getDesc() {
return mDesc;
}
+
+ public ISystemImage getSystemImage() {
+ return LocalSysImgPkgInfo.getSystemImage(mDesc, getLocalDir(), getLocalSdk().getFileOp());
+ }
}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalBuildToolPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalBuildToolPkgInfo.java
index f40c34a..0251cc3 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalBuildToolPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalBuildToolPkgInfo.java
@@ -29,8 +29,10 @@
public class LocalBuildToolPkgInfo extends LocalPkgInfo {
- private final @Nullable BuildToolInfo mBuildToolInfo;
- private final @NonNull IPkgDesc mDesc;
+ @Nullable
+ private final BuildToolInfo mBuildToolInfo;
+ @NonNull
+ private final IPkgDesc mDesc;
public LocalBuildToolPkgInfo(@NonNull LocalSdk localSdk,
@NonNull File localDir,
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDirInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDirInfo.java
index 23b68d1..7e3a791 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDirInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDirInfo.java
@@ -224,7 +224,7 @@
return mDir.equals(((MapComparator) obj).mDir);
}
return false;
- };
+ }
/**
* Helper for Map.contains() to make sure we're comparing the inner directory File
@@ -270,6 +270,6 @@
return mDir.equals(((MapComparator) obj).mDir);
}
return false;
- };
+ }
}
}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDocPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDocPkgInfo.java
index 56aca59..004f450 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDocPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalDocPkgInfo.java
@@ -27,7 +27,8 @@
public class LocalDocPkgInfo extends LocalPkgInfo {
- private final @NonNull IPkgDesc mDesc;
+ @NonNull
+ private final IPkgDesc mDesc;
public LocalDocPkgInfo(@NonNull LocalSdk localSdk,
@NonNull File localDir,
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalExtraPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalExtraPkgInfo.java
index 7aaf69d..5ec811b 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalExtraPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalExtraPkgInfo.java
@@ -29,7 +29,8 @@
public class LocalExtraPkgInfo extends LocalPkgInfo {
- private final @NonNull IPkgDescExtra mDesc;
+ @NonNull
+ private final IPkgDescExtra mDesc;
public LocalExtraPkgInfo(@NonNull LocalSdk localSdk,
@NonNull File localDir,
@@ -76,7 +77,7 @@
// and that "vendor" would end up in the path when we reload the extra from
// disk. Detect this and compensate.
String disp = vendor == null ? null : vendor.getDisplay();
- if (disp != null && disp.length() > 0) {
+ if (disp != null && !disp.isEmpty()) {
if (name.startsWith(disp + "-")) { //$NON-NLS-1$
name = name.substring(disp.length() + 1);
}
@@ -86,11 +87,11 @@
if (name != null) {
name = name.replaceAll("[ _\t\f-]+", " ").trim(); //$NON-NLS-1$ //$NON-NLS-2$
}
- if (name == null || name.length() == 0) {
+ if (name == null || name.isEmpty()) {
name = "Unknown Extra";
}
- if (disp != null && disp.length() > 0) {
+ if (disp != null && !disp.isEmpty()) {
name = disp + " " + name; //$NON-NLS-1$
name = name.replaceAll("[ _\t\f-]+", " ").trim(); //$NON-NLS-1$ //$NON-NLS-2$
}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalNdkPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalNdkPkgInfo.java
new file mode 100644
index 0000000..8858300
--- /dev/null
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalNdkPkgInfo.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdklib.repository.local;
+
+import com.android.annotations.NonNull;
+import com.android.sdklib.repository.FullRevision;
+import com.android.sdklib.repository.descriptors.IPkgDesc;
+import com.android.sdklib.repository.descriptors.PkgDesc;
+
+import java.io.File;
+import java.util.Properties;
+
+/**
+ * Local package representing the Android NDK
+ */
+public class LocalNdkPkgInfo extends LocalPkgInfo {
+ @NonNull
+ private final IPkgDesc mDesc;
+
+ protected LocalNdkPkgInfo(@NonNull LocalSdk localSdk,
+ @NonNull File localDir,
+ @NonNull Properties sourceProps,
+ @NonNull FullRevision revision) {
+ super(localSdk, localDir, sourceProps);
+ mDesc = PkgDesc.Builder.newNdk(revision).setDescriptionShort("Android NDK").setListDisplay("Android NDK").create();
+ }
+
+ @NonNull
+ @Override
+ public IPkgDesc getDesc() {
+ return mDesc;
+ }
+}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformPkgInfo.java
index d3781ff..534677e 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformPkgInfo.java
@@ -19,12 +19,15 @@
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.annotations.VisibleForTesting;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.IAndroidTarget.OptionalLibrary;
import com.android.sdklib.ISystemImage;
import com.android.sdklib.ISystemImage.LocationType;
import com.android.sdklib.SdkManager.LayoutlibVersion;
import com.android.sdklib.SystemImage;
+import com.android.sdklib.internal.androidTarget.OptionalLibraryImpl;
import com.android.sdklib.internal.androidTarget.PlatformTarget;
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.io.FileOp;
@@ -36,12 +39,27 @@
import com.android.sdklib.repository.descriptors.IdDisplay;
import com.android.sdklib.repository.descriptors.PkgDesc;
import com.android.sdklib.repository.descriptors.PkgType;
+import com.google.common.base.Charsets;
+import com.google.common.collect.Lists;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.TreeMultimap;
+import com.google.common.io.Files;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
import java.io.File;
import java.io.FileNotFoundException;
-import java.util.*;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
@SuppressWarnings("ConstantConditions")
public class LocalPlatformPkgInfo extends LocalPkgInfo {
@@ -238,10 +256,11 @@
layoutlibVersion,
systemImages,
platformProp,
+ getOptionalLibraries(platformFolder),
sdk.getLatestBuildTool());
// add the skins from the platform. Make a copy to not modify the original collection.
- List<File> skins = new ArrayList<File>(parseSkinFolder(pt.getFile(IAndroidTarget.SKINS)));
+ List<File> skins = new ArrayList<File>(PackageParserUtils.parseSkinFolder(pt.getFile(IAndroidTarget.SKINS), fileOp));
// add the system-image specific skins, if any.
for (ISystemImage systemImage : systemImages) {
@@ -299,19 +318,7 @@
IdDisplay tag = d.getTag();
String abi = d.getPath();
if (tag != null && abi != null && !tagToAbiFound.containsEntry(tag, abi)) {
- List<File> parsedSkins = parseSkinFolder(
- new File(pkg.getLocalDir(), SdkConstants.FD_SKINS));
- File[] skins = FileOp.EMPTY_FILE_ARRAY;
- if (!parsedSkins.isEmpty()) {
- skins = parsedSkins.toArray(new File[parsedSkins.size()]);
- }
-
- found.add(new SystemImage(
- pkg.getLocalDir(),
- LocationType.IN_SYSTEM_IMAGE,
- tag,
- abi,
- skins));
+ found.add(((LocalSysImgPkgInfo)pkg).getSystemImage());
tagToAbiFound.put(tag, abi);
}
}
@@ -362,39 +369,57 @@
return found.toArray(new ISystemImage[found.size()]);
}
- /**
- * Parses the skin folder and builds the skin list.
- * @param skinRootFolder The path to the skin root folder.
- */
- @NonNull
- protected List<File> parseSkinFolder(@NonNull File skinRootFolder) {
- IFileOp fileOp = getLocalSdk().getFileOp();
-
- if (fileOp.isDirectory(skinRootFolder)) {
- ArrayList<File> skinList = new ArrayList<File>();
-
- File[] files = fileOp.listFiles(skinRootFolder);
-
- for (File skinFolder : files) {
- if (fileOp.isDirectory(skinFolder)) {
- // check for layout file
- File layout = new File(skinFolder, SdkConstants.FN_SKIN_LAYOUT);
-
- if (fileOp.isFile(layout)) {
- // for now we don't parse the content of the layout and
- // simply add the directory to the list.
- skinList.add(skinFolder);
- }
- }
- }
-
- Collections.sort(skinList);
- return skinList;
+ private List<OptionalLibrary> getOptionalLibraries(@NonNull File platformDir) {
+ File optionalDir = new File(platformDir, "optional");
+ if (!optionalDir.isDirectory()) {
+ return Collections.emptyList();
}
- return Collections.emptyList();
+ File optionalJson = new File(optionalDir, "optional.json");
+ if (!optionalJson.isFile()) {
+ return Collections.emptyList();
+ }
+
+ return getLibsFromJson(optionalJson);
}
+ public static class Library {
+ String name;
+ String jar;
+ boolean manifest;
+ }
+
+
+ @VisibleForTesting
+ static List<OptionalLibrary> getLibsFromJson(@NonNull File jsonFile) {
+
+ Gson gson = new Gson();
+
+ try {
+ Type collectionType = new TypeToken<Collection<Library>>() {
+ }.getType();
+ Collection<Library> libs = gson
+ .fromJson(Files.newReader(jsonFile, Charsets.UTF_8), collectionType);
+
+ // convert into the right format.
+ List<OptionalLibrary> optionalLibraries = Lists.newArrayListWithCapacity(libs.size());
+
+ File rootFolder = jsonFile.getParentFile();
+ for (Library lib : libs) {
+ optionalLibraries.add(new OptionalLibraryImpl(
+ lib.name,
+ new File(rootFolder, lib.jar),
+ lib.name,
+ lib.manifest));
+ }
+
+ return optionalLibraries;
+ } catch (FileNotFoundException e) {
+ // shouldn't happen since we've checked the file is here, but can happen in
+ // some cases (too many files open).
+ return Collections.emptyList();
+ }
+ }
/** List of items in the platform to check when parsing it. These paths are relative to the
* platform root folder. */
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformToolPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformToolPkgInfo.java
index cd66b1c..281c64e 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformToolPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalPlatformToolPkgInfo.java
@@ -26,7 +26,8 @@
public class LocalPlatformToolPkgInfo extends LocalPkgInfo {
- private final @NonNull IPkgDesc mDesc;
+ @NonNull
+ private final IPkgDesc mDesc;
public LocalPlatformToolPkgInfo(@NonNull LocalSdk localSdk,
@NonNull File localDir,
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSamplePkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSamplePkgInfo.java
index d4ed505..d8159d6 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSamplePkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSamplePkgInfo.java
@@ -33,7 +33,8 @@
*/
public class LocalSamplePkgInfo extends LocalPkgInfo {
- private final @NonNull IPkgDesc mDesc;
+ @NonNull
+ private final IPkgDesc mDesc;
public LocalSamplePkgInfo(@NonNull LocalSdk localSdk,
@NonNull File localDir,
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSdk.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSdk.java
index 04d298b..144ed5b 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSdk.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSdk.java
@@ -22,11 +22,9 @@
import com.android.annotations.VisibleForTesting;
import com.android.annotations.VisibleForTesting.Visibility;
import com.android.annotations.concurrency.GuardedBy;
-import com.android.sdklib.AndroidTargetHash;
-import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.*;
import com.android.sdklib.AndroidVersion.AndroidVersionException;
-import com.android.sdklib.BuildToolInfo;
-import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.internal.androidTarget.MissingTarget;
import com.android.sdklib.io.FileOp;
import com.android.sdklib.io.IFileOp;
import com.android.sdklib.repository.FullRevision;
@@ -37,21 +35,12 @@
import com.android.sdklib.repository.descriptors.IdDisplay;
import com.android.sdklib.repository.descriptors.PkgDescExtra;
import com.android.sdklib.repository.descriptors.PkgType;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.TreeMultimap;
+import com.google.common.collect.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Properties;
+import java.util.*;
/**
* This class keeps information on the current locally installed SDK.
@@ -185,8 +174,8 @@
private BuildToolInfo mLegacyBuildTools;
/** Cache of targets from local sdk. See {@link #getTargets()}. */
@GuardedBy(value="mLocalPackages")
- private final List<IAndroidTarget> mCachedTargets = new ArrayList<IAndroidTarget>();
- private boolean mReloadTargets = true;
+ private List<IAndroidTarget> mCachedTargets = null;
+ private Set<MissingTarget> mCachedMissingTargets = null;
/**
* Creates an initial LocalSdk instance with an unknown location.
@@ -270,11 +259,12 @@
mVisitedDirs.removeAll(filter);
mLocalPackages.removeAll(filter);
}
- }
- // Clear the targets if the platforms or addons are being cleared
- if (filters.contains(PkgType.PKG_PLATFORM) || filters.contains(PkgType.PKG_ADDON)) {
- mReloadTargets = true;
+ // Clear the targets if the platforms or addons are being cleared
+ if (filters.contains(PkgType.PKG_PLATFORM) || filters.contains(PkgType.PKG_ADDON)) {
+ mCachedMissingTargets = null;
+ mCachedTargets = null;
+ }
}
}
@@ -453,14 +443,11 @@
*/
@Nullable
public LocalPkgInfo getPkgInfo(@NonNull PkgType filter) {
-
- assert filter == PkgType.PKG_TOOLS ||
- filter == PkgType.PKG_PLATFORM_TOOLS ||
- filter == PkgType.PKG_DOC;
-
if (filter != PkgType.PKG_TOOLS &&
filter != PkgType.PKG_PLATFORM_TOOLS &&
- filter != PkgType.PKG_DOC) {
+ filter != PkgType.PKG_DOC &&
+ filter != PkgType.PKG_NDK) {
+ assert false;
return null;
}
@@ -468,7 +455,7 @@
synchronized (mLocalPackages) {
Collection<LocalPkgInfo> existing = mLocalPackages.get(filter);
assert existing.size() <= 1;
- if (existing.size() > 0) {
+ if (!existing.isEmpty()) {
return existing.iterator().next();
}
@@ -485,6 +472,8 @@
case PKG_DOC:
info = scanDoc(uniqueDir);
break;
+ case PKG_NDK:
+ info = scanNdk(uniqueDir);
default:
break;
}
@@ -544,7 +533,8 @@
for (PkgType filter : filters) {
if (filter == PkgType.PKG_TOOLS ||
filter == PkgType.PKG_PLATFORM_TOOLS ||
- filter == PkgType.PKG_DOC) {
+ filter == PkgType.PKG_DOC ||
+ filter == PkgType.PKG_NDK) {
LocalPkgInfo info = getPkgInfo(filter);
if (info != null) {
list.add(info);
@@ -598,6 +588,7 @@
case PKG_TOOLS:
case PKG_PLATFORM_TOOLS:
case PKG_DOC:
+ case PKG_NDK:
break;
default:
throw new IllegalArgumentException(
@@ -711,26 +702,71 @@
@NonNull
public IAndroidTarget[] getTargets() {
synchronized (mLocalPackages) {
- if (mReloadTargets) {
+ if (mCachedTargets == null) {
+ List<IAndroidTarget> result = Lists.newArrayList();
LocalPkgInfo[] pkgsInfos = getPkgsInfos(EnumSet.of(PkgType.PKG_PLATFORM,
PkgType.PKG_ADDON));
- int n = pkgsInfos.length;
- mCachedTargets.clear();
- for (int i = 0; i < n; i++) {
- LocalPkgInfo info = pkgsInfos[i];
+ for (LocalPkgInfo info : pkgsInfos) {
assert info instanceof LocalPlatformPkgInfo;
- if (info instanceof LocalPlatformPkgInfo) {
- IAndroidTarget target = ((LocalPlatformPkgInfo) info).getAndroidTarget();
- if (target != null) {
- mCachedTargets.add(target);
- }
+ IAndroidTarget target = ((LocalPlatformPkgInfo) info).getAndroidTarget();
+ if (target != null) {
+ result.add(target);
}
}
+ mCachedTargets = result;
}
return mCachedTargets.toArray(new IAndroidTarget[mCachedTargets.size()]);
}
}
+ public IAndroidTarget[] getTargets(boolean includeMissing) {
+ IAndroidTarget[] result = getTargets();
+ if (includeMissing) {
+ result = ObjectArrays.concat(result, getMissingTargets(), IAndroidTarget.class);
+ }
+ return result;
+ }
+
+ @NonNull
+ public IAndroidTarget[] getMissingTargets() {
+ synchronized (mLocalPackages) {
+ if (mCachedMissingTargets == null) {
+ Map<MissingTarget, MissingTarget> result = Maps.newHashMap();
+ Set<ISystemImage> seen = Sets.newHashSet();
+ for (IAndroidTarget target : getTargets()) {
+ Collections.addAll(seen, target.getSystemImages());
+ }
+ for (LocalPkgInfo local : getPkgsInfos(PkgType.PKG_ADDON_SYS_IMAGE)) {
+ LocalAddonSysImgPkgInfo info = (LocalAddonSysImgPkgInfo)local;
+ ISystemImage image = info.getSystemImage();
+ if (!seen.contains(image)) {
+ addOrphanedSystemImage(image, info.getDesc(), result);
+ }
+ }
+ for (LocalPkgInfo local : getPkgsInfos(PkgType.PKG_SYS_IMAGE)) {
+ LocalSysImgPkgInfo info = (LocalSysImgPkgInfo)local;
+ ISystemImage image = info.getSystemImage();
+ if (!seen.contains(image)) {
+ addOrphanedSystemImage(image, info.getDesc(), result);
+ }
+ }
+ mCachedMissingTargets = result.keySet();
+ }
+ return mCachedMissingTargets.toArray(new IAndroidTarget[mCachedMissingTargets.size()]);
+ }
+ }
+
+ private static void addOrphanedSystemImage(ISystemImage image, IPkgDesc desc, Map<MissingTarget, MissingTarget> targets) {
+ IdDisplay vendor = desc.getVendor();
+ MissingTarget target = new MissingTarget(vendor == null ? null : vendor.getDisplay(), desc.getTag().getDisplay(), desc.getAndroidVersion());
+ MissingTarget existing = targets.get(target);
+ if (existing == null) {
+ existing = target;
+ targets.put(target, target);
+ }
+ existing.addSystemImage(image);
+ }
+
/**
* Returns a target from a hash that was generated by {@link IAndroidTarget#hashString()}.
*
@@ -740,7 +776,7 @@
@Nullable
public IAndroidTarget getTargetFromHashString(@Nullable String hash) {
if (hash != null) {
- IAndroidTarget[] targets = getTargets();
+ IAndroidTarget[] targets = getTargets(true);
for (IAndroidTarget target : targets) {
if (target != null && hash.equals(AndroidTargetHash.getTargetHashString(target))) {
return target;
@@ -843,6 +879,23 @@
}
/**
+ * Try to find an NDK package at the given location.
+ * Returns null if not found.
+ */
+ @Nullable
+ private LocalNdkPkgInfo scanNdk(@NonNull File ndkFolder) {
+ // Can we find some properties?
+ Properties props = parseProperties(new File(ndkFolder, SdkConstants.FN_SOURCE_PROP));
+ FullRevision rev = PackageParserUtils.getPropertyFull(props, PkgProps.PKG_REVISION);
+ if (rev == null) {
+ return null;
+ }
+
+ return new LocalNdkPkgInfo(this, ndkFolder, props, rev);
+ }
+
+
+ /**
* Helper used by scanXyz methods below to check whether a directory should be visited.
* It can be skipped if it's not a directory or if it's already marked as visited in
* mVisitedDirs for the given package type -- in which case the directory is added to
@@ -1167,7 +1220,7 @@
props.load(fis);
// To be valid, there must be at least one property in it.
- if (props.size() > 0) {
+ if (!props.isEmpty()) {
return props;
}
}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSourcePkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSourcePkgInfo.java
index 20ce2bc..eb822de 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSourcePkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSourcePkgInfo.java
@@ -32,7 +32,8 @@
*/
public class LocalSourcePkgInfo extends LocalPkgInfo {
- private final @NonNull IPkgDesc mDesc;
+ @NonNull
+ private final IPkgDesc mDesc;
public LocalSourcePkgInfo(@NonNull LocalSdk localSdk,
@NonNull File localDir,
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSysImgPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSysImgPkgInfo.java
index fc52a2f..f9e8148 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSysImgPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalSysImgPkgInfo.java
@@ -16,17 +16,25 @@
package com.android.sdklib.repository.local;
+import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.ISystemImage;
import com.android.sdklib.SystemImage;
+import com.android.sdklib.io.FileOp;
+import com.android.sdklib.io.IFileOp;
import com.android.sdklib.repository.MajorRevision;
import com.android.sdklib.repository.PkgProps;
import com.android.sdklib.repository.descriptors.IPkgDesc;
import com.android.sdklib.repository.descriptors.IdDisplay;
import com.android.sdklib.repository.descriptors.PkgDesc;
+import com.google.common.base.Objects;
import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Locale;
import java.util.Properties;
@@ -39,15 +47,16 @@
public class LocalSysImgPkgInfo extends LocalPkgInfo {
- private final @NonNull IPkgDesc mDesc;
+ @NonNull
+ private final IPkgDesc mDesc;
public LocalSysImgPkgInfo(@NonNull LocalSdk localSdk,
- @NonNull File localDir,
- @NonNull Properties sourceProps,
- @NonNull AndroidVersion version,
- @Nullable IdDisplay tag,
- @NonNull String abi,
- @NonNull MajorRevision revision) {
+ @NonNull File localDir,
+ @NonNull Properties sourceProps,
+ @NonNull AndroidVersion version,
+ @Nullable IdDisplay tag,
+ @NonNull String abi,
+ @NonNull MajorRevision revision) {
super(localSdk, localDir, sourceProps);
mDesc = PkgDesc.Builder.newSysImg(version, tag, abi, revision).create();
}
@@ -65,8 +74,7 @@
@NonNull
public static IdDisplay extractTagFromProps(Properties props) {
if (props != null) {
- String tagId = props.getProperty(PkgProps.SYS_IMG_TAG_ID,
- SystemImage.DEFAULT_TAG.getId());
+ String tagId = props.getProperty(PkgProps.SYS_IMG_TAG_ID, SystemImage.DEFAULT_TAG.getId());
String tagDisp = props.getProperty(PkgProps.SYS_IMG_TAG_DISPLAY, ""); //$NON-NLS-1$
if (tagDisp == null || tagDisp.isEmpty()) {
tagDisp = tagIdToDisplay(tagId);
@@ -92,7 +100,7 @@
name = name.replaceAll(" +", " "); //$NON-NLS-1$ //$NON-NLS-2$
name = name.trim();
- if (name.length() > 0) {
+ if (!name.isEmpty()) {
char c = name.charAt(0);
if (!Character.isUpperCase(c)) {
StringBuilder sb = new StringBuilder(name);
@@ -103,6 +111,25 @@
return name;
}
- // TODO create package on demand if needed. This might not be needed
- // since typically system-images are retrieved via IAndroidTarget.
+ public SystemImage getSystemImage() {
+ return getSystemImage(mDesc, getLocalDir(), getLocalSdk().getFileOp());
+ }
+
+ static SystemImage getSystemImage(IPkgDesc desc, File localDir, @NonNull IFileOp fileOp) {
+ final IdDisplay tag = desc.getTag();
+ final String abi = desc.getPath();
+ List<File> parsedSkins = PackageParserUtils.parseSkinFolder(new File(localDir, SdkConstants.FD_SKINS), fileOp);
+ File[] skins = FileOp.EMPTY_FILE_ARRAY;
+ if (!parsedSkins.isEmpty()) {
+ skins = parsedSkins.toArray(new File[parsedSkins.size()]);
+ }
+
+ return new SystemImage(
+ localDir,
+ ISystemImage.LocationType.IN_SYSTEM_IMAGE,
+ tag,
+ desc.getVendor(),
+ abi,
+ skins);
+ }
}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalToolPkgInfo.java b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalToolPkgInfo.java
index 94a4e95..91306ea 100755
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/LocalToolPkgInfo.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/LocalToolPkgInfo.java
@@ -26,7 +26,8 @@
public class LocalToolPkgInfo extends LocalPkgInfo {
- private final @NonNull IPkgDesc mDesc;
+ @NonNull
+ private final IPkgDesc mDesc;
public LocalToolPkgInfo(@NonNull LocalSdk localSdk,
@NonNull File localDir,
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/local/PackageParserUtils.java b/sdklib/src/main/java/com/android/sdklib/repository/local/PackageParserUtils.java
index 09cecbb..e8cc4b2 100644
--- a/sdklib/src/main/java/com/android/sdklib/repository/local/PackageParserUtils.java
+++ b/sdklib/src/main/java/com/android/sdklib/repository/local/PackageParserUtils.java
@@ -15,13 +15,19 @@
*/
package com.android.sdklib.repository.local;
+import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.sdklib.io.IFileOp;
import com.android.sdklib.repository.FullRevision;
import com.android.sdklib.repository.MajorRevision;
import com.android.sdklib.repository.NoPreviewRevision;
import com.android.sdklib.repository.PkgProps;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Properties;
/**
@@ -124,4 +130,37 @@
}
return props.getProperty(propKey, defaultValue);
}
+
+
+ /**
+ * Parses the skin folder and builds the skin list.
+ * @param skinRootFolder The path to the skin root folder.
+ */
+ @NonNull
+ public static List<File> parseSkinFolder(@NonNull File skinRootFolder, @NonNull IFileOp fileOp) {
+ if (fileOp.isDirectory(skinRootFolder)) {
+ ArrayList<File> skinList = new ArrayList<File>();
+
+ File[] files = fileOp.listFiles(skinRootFolder);
+
+ for (File skinFolder : files) {
+ if (fileOp.isDirectory(skinFolder)) {
+ // check for layout file
+ File layout = new File(skinFolder, SdkConstants.FN_SKIN_LAYOUT);
+
+ if (fileOp.isFile(layout)) {
+ // for now we don't parse the content of the layout and
+ // simply add the directory to the list.
+ skinList.add(skinFolder);
+ }
+ }
+ }
+
+ Collections.sort(skinList);
+ return skinList;
+ }
+
+ return Collections.emptyList();
+ }
+
}
diff --git a/sdklib/src/main/java/com/android/sdklib/repository/sdk-repository-11.xsd b/sdklib/src/main/java/com/android/sdklib/repository/sdk-repository-11.xsd
new file mode 100755
index 0000000..8e9f736
--- /dev/null
+++ b/sdklib/src/main/java/com/android/sdklib/repository/sdk-repository-11.xsd
@@ -0,0 +1,680 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<xsd:schema
+ targetNamespace="http://schemas.android.com/sdk/android/repository/11"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:sdk="http://schemas.android.com/sdk/android/repository/11"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ version="1">
+
+ <!-- The repository contains a collection of downloadable items known as
+ "packages". Each package has a type and various attributes and contains
+ a list of file "archives" that can be downloaded for specific OSes.
+
+ An Android SDK repository is a web site that contains a "repository.xml"
+ file that conforms to this XML Schema.
+
+ History:
+ - v1 is used by the SDK Updater in Tools r3 and r4.
+
+ - v2 is used by the SDK Updater in Tools r5:
+ - It introduces a new <sample> repository type. Previously samples
+ were included in the <platform> packages. Instead this package is used
+ and and the samples are installed in $SDK/samples.
+ - All repository types have a new <obsolete> node. It works as a marker
+ to indicate the package is obsolete and should not be selected by default.
+ The UI also hides these out by default.
+
+ - v3 is used by the SDK Updater in Tools r8:
+ - It introduces a new <platform-tool> repository type. Previously platform-specific
+ tools were included in the <platform> packages. Instead this package is used
+ and platform-specific tools are installed in $SDK/platform-tools
+ - There's a new element <min-platform-tools-rev> in <tool>. The tool package now
+ requires that at least some minimal version of <platform-tool> be installed.
+ - It removes the <addon> repository type, which is now in its own XML Schema.
+
+ - v4 is used by the SDK Updater in Tools r12:
+ - <extra> element now has a <project-files> element that contains 1 or
+ or more <path>, each indicating the relative path of a file that this package
+ can contribute to installed projects.
+ - <platform> element now has a mandatory <layoutlib> that indicates the API
+ and revision of that layout library for this particular platform.
+
+ - v5 is used by the SDK Manager in Tools r14:
+ - <extra> now has an <old-paths> element, a ;-separated list of old paths that
+ should be detected and migrated to the new <path> for that package.
+ - <platform> has a new optional <abi-included> that describes the ABI of the
+ system image included in the platform, if any.
+ - New <system-image> package type, to store system images outside of <platform>s.
+ - New <source> package type.
+
+ - v6 is used by the SDK Manager in Tools r18:
+ - <extra> packages are removed. They are served only by the addon XML.
+ - <platform>, <system-image>, <source>, <tool>, <platform-tool>, <doc>
+ and <sample> get a new optional field <beta-rc> which can be used to indicate
+ the package is a Beta Release Candidate and not a final release.
+
+ - v7 is used by the SDK Manager in Tools r20:
+ - For <tool> and <platform-tool> packages, the <revision> element becomes a
+ a "full revision" element with <major>, <minor>, <micro> and <preview> sub-elements.
+ - The <beta-rc> element is no longer supported, it is replaced by
+ <revision> -> <preview> and is only for <tool> and <platform-tool> packages.
+ - <min-tools-rev> and <min-platform-tools-rev> also become a full revision element.
+
+ - v8 is used by the SDK Manager in Tools r22.0:
+ - It introduces the new <build-tool> repository type, which contains build-specific
+ tools that were before placed in either <tool> or <platform-tool> (e.g. ant,
+ aapt, aidl, etc.)
+ - <tool> has a new "min-build-tool" attribute.
+
+ - v9 is used by the SDK Manager in Tools r22.6:
+ - It introduces a sub-tag for the <system-image> repository type.
+
+ - v10 is used by the SDK Manager in Tools r22.6.4:
+ - It introduces a <list-display> string for all package types.
+ - It removes the <system-image> type (system images are handled by sdk-sys-img-3.xsd)
+ - it changes <archive os=... arch=...> to sub-elements: <host-os>, <host-bits>,
+ <jvm-bits> and <min-jvm-version>.
+ -->
+
+ <xsd:element name="sdk-repository" type="sdk:repositoryType" />
+
+ <xsd:complexType name="repositoryType">
+ <xsd:annotation>
+ <xsd:documentation>
+ The repository contains a collection of downloadable packages.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
+ <xsd:element name="platform" type="sdk:platformType" />
+ <xsd:element name="source" type="sdk:sourceType" />
+ <xsd:element name="tool" type="sdk:toolType" />
+ <xsd:element name="platform-tool" type="sdk:platformToolType" />
+ <xsd:element name="build-tool" type="sdk:buildToolType" />
+ <xsd:element name="doc" type="sdk:docType" />
+ <xsd:element name="sample" type="sdk:sampleType" />
+ <xsd:element name="license" type="sdk:licenseType" />
+ <xsd:element name="ndk" type="sdk:ndkType" />
+ </xsd:choice>
+ </xsd:complexType>
+
+ <!-- The definition of an NDK package. -->
+
+ <xsd:complexType name="ndkType">
+ <xsd:annotation>
+ <xsd:documentation>An NDK package.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:all>
+ <!-- The revision, an int > 0, incremented each time a new
+ package is generated. -->
+ <xsd:element name="revision" type="xsd:positiveInteger" />
+
+ <!-- The optional display list item. When missing, it is auto-computed. -->
+ <xsd:element name="list-display" type="xsd:normalizedString" minOccurs="0" />
+
+ <!-- The optional license of this package. If present, users will have
+ to agree to it before downloading. -->
+ <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+ <!-- The optional description of this package. -->
+ <xsd:element name="description" type="xsd:string" minOccurs="0" />
+ <!-- A list of file archives for this package. -->
+ <xsd:element name="archives" type="sdk:archivesType" />
+ <!-- An optional element indicating the package is obsolete.
+ The string content is however currently not defined and ignored. -->
+ <xsd:element name="obsolete" type="xsd:string" minOccurs="0" />
+ </xsd:all>
+ </xsd:complexType>
+
+ <!-- The definition of an SDK platform package. -->
+
+ <xsd:complexType name="platformType">
+ <xsd:annotation>
+ <xsd:documentation>An SDK platform package.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:all>
+ <!-- The Android platform version. It is string such as "1.0". -->
+ <xsd:element name="version" type="xsd:normalizedString" />
+ <!-- The Android API Level for the platform. An int > 0. -->
+ <xsd:element name="api-level" type="xsd:positiveInteger" />
+ <!-- The optional codename for this platform, if it's a preview. -->
+ <xsd:element name="codename" type="xsd:string" minOccurs="0" />
+ <!-- The revision, an int > 0, incremented each time a new
+ package is generated. -->
+ <xsd:element name="revision" type="xsd:positiveInteger" />
+
+ <!-- The optional display list item. When missing, it is auto-computed. -->
+ <xsd:element name="list-display" type="xsd:normalizedString" minOccurs="0" />
+
+ <!-- Information on the layoutlib packaged in this platform. -->
+ <xsd:element name="layoutlib" type="sdk:layoutlibType" />
+
+ <!-- optional elements -->
+
+ <!-- The optional license of this package. If present, users will have
+ to agree to it before downloading. -->
+ <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+ <!-- The optional description of this package. -->
+ <xsd:element name="description" type="xsd:string" minOccurs="0" />
+ <!-- The optional description URL of this package -->
+ <xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
+ <!-- The optional release note for this package. -->
+ <xsd:element name="release-note" type="xsd:string" minOccurs="0" />
+ <!-- The optional release note URL of this package -->
+ <xsd:element name="release-url" type="xsd:token" minOccurs="0" />
+ <!-- A list of file archives for this package. -->
+ <xsd:element name="archives" type="sdk:archivesType" />
+ <!-- The minimal revision of tools required by this package.
+ Optional. If present, must be a revision element. -->
+ <xsd:element name="min-tools-rev" type="sdk:revisionType" minOccurs="0" />
+
+ <!-- The ABI of the system image *included* in this platform, if any.
+ When the field is present, it means the platform already embeds one
+ system image. A platform can also have any number of external
+ <system-image> associated with it. -->
+ <xsd:element name="included-abi" type="sdk:abiType" minOccurs="0" />
+
+ <!-- An optional element indicating the package is obsolete.
+ The string content is however currently not defined and ignored. -->
+ <xsd:element name="obsolete" type="xsd:string" minOccurs="0" />
+ </xsd:all>
+ </xsd:complexType>
+
+
+ <!-- The definition of a layout library used by a platform. -->
+
+ <xsd:complexType name="layoutlibType" >
+ <xsd:annotation>
+ <xsd:documentation>
+ Version information for a layoutlib included in a platform.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:all>
+ <!-- The layoutlib API level, an int > 0,
+ incremented with each new incompatible lib. -->
+ <xsd:element name="api" type="xsd:positiveInteger" />
+ <!-- The incremental minor revision for that API, e.g. in case of bug fixes.
+ Optional. An int >= 0, assumed to be 0 if the element is missing. -->
+ <xsd:element name="revision" type="xsd:nonNegativeInteger" minOccurs="0" />
+ </xsd:all>
+ </xsd:complexType>
+
+
+ <!-- The definition of the ABI supported by a platform's system image. -->
+
+ <xsd:simpleType name="abiType">
+ <xsd:annotation>
+ <xsd:documentation>The ABI of a platform's system image.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="armeabi" />
+ <xsd:enumeration value="armeabi-v7a" />
+ <xsd:enumeration value="x86" />
+ <xsd:enumeration value="mips" />
+ </xsd:restriction>
+ </xsd:simpleType>
+
+
+ <!-- The definition of a source package. -->
+
+ <xsd:complexType name="sourceType" >
+ <xsd:annotation>
+ <xsd:documentation>
+ Sources for a platform.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:all>
+ <!-- api-level + codename identifies the platform to which this source belongs. -->
+
+ <!-- The Android API Level for the platform. An int > 0. -->
+ <xsd:element name="api-level" type="xsd:positiveInteger" />
+ <!-- The optional codename for this platform, if it's a preview. -->
+ <xsd:element name="codename" type="xsd:string" minOccurs="0" />
+
+ <!-- The revision, an int > 0, incremented each time a new
+ package is generated. -->
+ <xsd:element name="revision" type="xsd:positiveInteger" />
+
+ <!-- The optional display list item. When missing, it is auto-computed. -->
+ <xsd:element name="list-display" type="xsd:normalizedString" minOccurs="0" />
+
+ <!-- The optional license of this package. If present, users will have
+ to agree to it before downloading. -->
+ <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+ <!-- The optional description of this package. -->
+ <xsd:element name="description" type="xsd:string" minOccurs="0" />
+ <!-- The optional description URL of this package -->
+ <xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
+ <!-- The optional release note for this package. -->
+ <xsd:element name="release-note" type="xsd:string" minOccurs="0" />
+ <!-- The optional release note URL of this package -->
+ <xsd:element name="release-url" type="xsd:token" minOccurs="0" />
+
+ <!-- A list of file archives for this package. -->
+ <xsd:element name="archives" type="sdk:archivesType" />
+
+ <!-- An optional element indicating the package is obsolete.
+ The string content is however currently not defined and ignored. -->
+ <xsd:element name="obsolete" type="xsd:string" minOccurs="0" />
+ </xsd:all>
+ </xsd:complexType>
+
+
+ <!-- The definition of an SDK tool package. -->
+
+ <xsd:complexType name="toolType" >
+ <xsd:annotation>
+ <xsd:documentation>An SDK tool package.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:all>
+ <!-- The full revision (major.minor.micro.preview), incremented each
+ time a new package is generated. -->
+ <xsd:element name="revision" type="sdk:revisionType" />
+
+ <!-- The optional display list item. When missing, it is auto-computed. -->
+ <xsd:element name="list-display" type="xsd:normalizedString" minOccurs="0" />
+
+ <!-- The optional license of this package. If present, users will have
+ to agree to it before downloading. -->
+ <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+ <!-- The optional description of this package. -->
+ <xsd:element name="description" type="xsd:string" minOccurs="0" />
+ <!-- The optional description URL of this package -->
+ <xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
+ <!-- The optional release note for this package. -->
+ <xsd:element name="release-note" type="xsd:string" minOccurs="0" />
+ <!-- The optional release note URL of this package -->
+ <xsd:element name="release-url" type="xsd:token" minOccurs="0" />
+ <!-- A list of file archives for this package. -->
+ <xsd:element name="archives" type="sdk:archivesType" />
+
+ <!-- The minimal revision of platform-tools required by this package.
+ Mandatory. Must be a revision element. -->
+ <xsd:element name="min-platform-tools-rev" type="sdk:revisionType" />
+
+ <!-- An optional element indicating the package is obsolete.
+ The string content is however currently not defined and ignored. -->
+ <xsd:element name="obsolete" type="xsd:string" minOccurs="0" />
+ </xsd:all>
+ </xsd:complexType>
+
+
+ <!-- The definition of an SDK platform-tool package. -->
+
+ <xsd:complexType name="platformToolType" >
+ <xsd:annotation>
+ <xsd:documentation>An SDK platform-tool package.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:all>
+ <!-- The full revision (major.minor.micro.preview), incremented each
+ time a new package is generated. -->
+ <xsd:element name="revision" type="sdk:revisionType" />
+
+ <!-- The optional display list item. When missing, it is auto-computed. -->
+ <xsd:element name="list-display" type="xsd:normalizedString" minOccurs="0" />
+
+ <!-- The optional license of this package. If present, users will have
+ to agree to it before downloading. -->
+ <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+ <!-- The optional description of this package. -->
+ <xsd:element name="description" type="xsd:string" minOccurs="0" />
+ <!-- The optional description URL of this package -->
+ <xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
+ <!-- The optional release note for this package. -->
+ <xsd:element name="release-note" type="xsd:string" minOccurs="0" />
+ <!-- The optional release note URL of this package -->
+ <xsd:element name="release-url" type="xsd:token" minOccurs="0" />
+ <!-- A list of file archives for this package. -->
+ <xsd:element name="archives" type="sdk:archivesType" />
+
+ <!-- An optional element indicating the package is obsolete.
+ The string content is however currently not defined and ignored. -->
+ <xsd:element name="obsolete" type="xsd:string" minOccurs="0" />
+ </xsd:all>
+ </xsd:complexType>
+
+ <!-- The definition of an SDK build-tool package. -->
+
+ <xsd:complexType name="buildToolType">
+ <xsd:annotation>
+ <xsd:documentation>An SDK build-tool package.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:all>
+ <!-- The full revision (major.minor.micro.preview), incremented each
+ time a new package is generated. -->
+ <xsd:element name="revision" type="sdk:revisionType" />
+
+ <!-- optional elements -->
+
+ <!-- The optional display list item. When missing, it is auto-computed. -->
+ <xsd:element name="list-display" type="xsd:normalizedString" minOccurs="0" />
+
+ <!-- The optional license of this package. If present, users will have
+ to agree to it before downloading. -->
+ <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+ <!-- The optional description of this package. -->
+ <xsd:element name="description" type="xsd:string" minOccurs="0" />
+ <!-- The optional description URL of this package -->
+ <xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
+ <!-- The optional release note for this package. -->
+ <xsd:element name="release-note" type="xsd:string" minOccurs="0" />
+ <!-- The optional release note URL of this package -->
+ <xsd:element name="release-url" type="xsd:token" minOccurs="0" />
+ <!-- A list of file archives for this package. -->
+ <xsd:element name="archives" type="sdk:archivesType" />
+
+ <!-- An optional element indicating the package is obsolete.
+ The string content is however currently not defined and ignored. -->
+ <xsd:element name="obsolete" type="xsd:string" minOccurs="0" />
+ </xsd:all>
+ </xsd:complexType>
+
+
+ <!-- The definition of an SDK doc package. -->
+
+ <xsd:complexType name="docType" >
+ <xsd:annotation>
+ <xsd:documentation>An SDK doc package.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:all>
+ <!-- The Android API Level for the documentation. An int > 0. -->
+ <xsd:element name="api-level" type="xsd:positiveInteger" />
+ <!-- The optional codename for this doc, if it's a preview. -->
+ <xsd:element name="codename" type="xsd:string" minOccurs="0" />
+
+ <!-- The optional display list item. When missing, it is auto-computed. -->
+ <xsd:element name="list-display" type="xsd:normalizedString" minOccurs="0" />
+
+ <!-- The revision, an int > 0, incremented each time a new
+ package is generated. -->
+ <xsd:element name="revision" type="xsd:positiveInteger" />
+ <!-- The optional license of this package. If present, users will have
+ to agree to it before downloading. -->
+ <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+ <!-- The optional description of this package. -->
+ <xsd:element name="description" type="xsd:string" minOccurs="0" />
+ <!-- The optional description URL of this package -->
+ <xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
+ <!-- The optional release note for this package. -->
+ <xsd:element name="release-note" type="xsd:string" minOccurs="0" />
+ <!-- The optional release note URL of this package -->
+ <xsd:element name="release-url" type="xsd:token" minOccurs="0" />
+ <!-- A list of file archives for this package. -->
+ <xsd:element name="archives" type="sdk:archivesType" />
+
+
+ <!-- An optional element indicating the package is obsolete.
+ The string content is however currently not defined and ignored. -->
+ <xsd:element name="obsolete" type="xsd:string" minOccurs="0" />
+ </xsd:all>
+ </xsd:complexType>
+
+
+ <!-- The definition of an SDK sample package. -->
+
+ <xsd:complexType name="sampleType" >
+ <xsd:annotation>
+ <xsd:documentation>An SDK sample package.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:all>
+ <!-- The Android API Level for the documentation. An int > 0. -->
+ <xsd:element name="api-level" type="xsd:positiveInteger" />
+ <!-- The optional codename for this doc, if it's a preview. -->
+ <xsd:element name="codename" type="xsd:string" minOccurs="0" />
+
+ <!-- The optional display list item. When missing, it is auto-computed. -->
+ <xsd:element name="list-display" type="xsd:normalizedString" minOccurs="0" />
+
+ <!-- The revision, an int > 0, incremented each time a new
+ package is generated. -->
+ <xsd:element name="revision" type="xsd:positiveInteger" />
+ <!-- The optional license of this package. If present, users will have
+ to agree to it before downloading. -->
+ <xsd:element name="uses-license" type="sdk:usesLicenseType" minOccurs="0" />
+ <!-- The optional description of this package. -->
+ <xsd:element name="description" type="xsd:string" minOccurs="0" />
+ <!-- The optional description URL of this package -->
+ <xsd:element name="desc-url" type="xsd:token" minOccurs="0" />
+ <!-- The optional release note for this package. -->
+ <xsd:element name="release-note" type="xsd:string" minOccurs="0" />
+ <!-- The optional release note URL of this package -->
+ <xsd:element name="release-url" type="xsd:token" minOccurs="0" />
+ <!-- A list of file archives for this package. -->
+ <xsd:element name="archives" type="sdk:archivesType" />
+ <!-- The minimal revision of tools required by this package.
+ Optional. If present, must be a revision element. -->
+ <xsd:element name="min-tools-rev" type="sdk:revisionType" minOccurs="0" />
+
+
+ <!-- An optional element indicating the package is obsolete.
+ The string content is however currently not defined and ignored. -->
+ <xsd:element name="obsolete" type="xsd:string" minOccurs="0" />
+ </xsd:all>
+ </xsd:complexType>
+
+
+ <!-- The definition of a path segment used by the extra element. -->
+
+ <xsd:simpleType name="segmentType">
+ <xsd:annotation>
+ <xsd:documentation>
+ One path segment for the install path of an extra element.
+ It must be a single-segment path.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:pattern value="[a-zA-Z0-9_]+"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="segmentListType">
+ <xsd:annotation>
+ <xsd:documentation>
+ A semi-colon separated list of a segmentTypes.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:pattern value="[a-zA-Z0-9_;]+"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+
+ <!-- The definition of a license to be referenced by the uses-license element. -->
+
+ <xsd:complexType name="licenseType">
+ <xsd:annotation>
+ <xsd:documentation>
+ A license definition. Such a license must be used later as a reference
+ using a uses-license element in one of the package elements.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <xsd:attribute name="id" type="xsd:ID" />
+ <xsd:attribute name="type" type="xsd:token" fixed="text" />
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+
+ <!-- Type describing the license used by a package.
+ The license MUST be defined using a license node and referenced
+ using the ref attribute of the license element inside a package.
+ -->
+
+ <xsd:complexType name="usesLicenseType">
+ <xsd:annotation>
+ <xsd:documentation>
+ Describes the license used by a package. The license MUST be defined
+ using a license node and referenced using the ref attribute of the
+ license element inside a package.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="ref" type="xsd:IDREF" />
+ </xsd:complexType>
+
+
+ <!-- A collection of files that can be downloaded for a given architecture.
+ The <archives> node is mandatory in the repository elements and the
+ collection must have at least one <archive> declared.
+ Each archive is a zip file that will be unzipped in a location that depends
+ on its package type.
+ -->
+
+ <xsd:complexType name="archivesType">
+ <xsd:annotation>
+ <xsd:documentation>
+ A collection of files that can be downloaded for a given architecture.
+ The <archives> node is mandatory in the repository packages and the
+ collection must have at least one <archive> declared.
+ Each archive is a zip file that will be unzipped in a location that depends
+ on its package type.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence minOccurs="1" maxOccurs="unbounded">
+ <!-- One archive file -->
+ <xsd:element name="archive">
+ <xsd:complexType>
+ <!-- Properties of the archive file -->
+ <xsd:all>
+ <!-- The size in bytes of the archive to download. -->
+ <xsd:element name="size" type="xsd:positiveInteger" />
+ <!-- The checksum of the archive file. -->
+ <xsd:element name="checksum" type="sdk:checksumType" />
+ <!-- The URL is an absolute URL if it starts with http://, https://
+ or ftp://. Otherwise it is relative to the parent directory that
+ contains this repository.xml -->
+ <xsd:element name="url" type="xsd:token" />
+
+ <xsd:element name="host-os" type="sdk:osType" minOccurs="0" />
+ <xsd:element name="host-bits" type="sdk:bitSizeType" minOccurs="0" />
+ <xsd:element name="jvm-bits" type="sdk:bitSizeType" minOccurs="0" />
+ <xsd:element name="min-jvm-version" type="sdk:jvmVersionType" minOccurs="0" />
+ </xsd:all>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:sequence>
+ </xsd:complexType>
+
+
+ <!-- A full revision, with a major.minor.micro and an optional preview number.
+ The major number is mandatory, the other elements are optional.
+ -->
+
+ <xsd:complexType name="revisionType">
+ <xsd:annotation>
+ <xsd:documentation>
+ A full revision, with a major.minor.micro and an
+ optional preview number. The major number is mandatory.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:all>
+ <!-- The major revision, an int > 0, incremented each time a new
+ package is generated. -->
+ <xsd:element name="major" type="xsd:positiveInteger" />
+ <!-- The minor revision, an int >= 0, incremented each time a new
+ minor package is generated. Assumed to be 0 if missing. -->
+ <xsd:element name="minor" type="xsd:nonNegativeInteger" minOccurs="0" />
+ <!-- The micro revision, an int >= 0, incremented each time a new
+ buf fix is generated. Assumed to be 0 if missing. -->
+ <xsd:element name="micro" type="xsd:nonNegativeInteger" minOccurs="0" />
+ <!-- The preview/release candidate revision, an int > 0,
+ incremented each time a new preview is generated.
+ Not present for final releases. -->
+ <xsd:element name="preview" type="xsd:positiveInteger" minOccurs="0" />
+ </xsd:all>
+ </xsd:complexType>
+
+
+ <!-- A collection of file paths available in an <extra> package
+ that can be installed in an Android project.
+ If present, the <project-files> collection must contain at least one path.
+ Each path is relative to the root directory of the package.
+ -->
+
+ <xsd:complexType name="projectFilesType">
+ <xsd:annotation>
+ <xsd:documentation>
+ A collection of file paths available in an <extra> package
+ that can be installed in an Android project.
+ If present, the <project-files> collection must contain at least one path.
+ Each path is relative to the root directory of the package.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence minOccurs="1" maxOccurs="unbounded">
+ <!-- One JAR Path, relative to the root folder of the package. -->
+ <xsd:element name="path" type="xsd:string" />
+ </xsd:sequence>
+ </xsd:complexType>
+
+
+ <!-- The definition of archive filters -->
+
+ <xsd:simpleType name="bitSizeType">
+ <xsd:annotation>
+ <xsd:documentation>A CPU bit size filter.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="32" />
+ <xsd:enumeration value="64" />
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="osType">
+ <xsd:annotation>
+ <xsd:documentation>A host OS filter.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="linux" />
+ <xsd:enumeration value="macosx" />
+ <xsd:enumeration value="windows" />
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="jvmVersionType">
+ <xsd:annotation>
+ <xsd:documentation>A JVM version number, e.g. "1" or "1.6" or "1.14.15".</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="([1-9](\.[1-9]{1,2}){0,2})"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+
+ <!-- The definition of a file checksum -->
+
+ <xsd:simpleType name="sha1Number">
+ <xsd:annotation>
+ <xsd:documentation>A SHA1 checksum.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="([0-9a-fA-F]){40}"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:complexType name="checksumType">
+ <xsd:annotation>
+ <xsd:documentation>A file checksum, currently only SHA1.</xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleContent>
+ <xsd:extension base="sdk:sha1Number">
+ <xsd:attribute name="type" type="xsd:token" fixed="sha1" />
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+</xsd:schema>
diff --git a/sdklib/src/main/java/com/android/sdklib/util/CommandLineParser.java b/sdklib/src/main/java/com/android/sdklib/util/CommandLineParser.java
index 7aaf224..28918da 100644
--- a/sdklib/src/main/java/com/android/sdklib/util/CommandLineParser.java
+++ b/sdklib/src/main/java/com/android/sdklib/util/CommandLineParser.java
@@ -510,7 +510,7 @@
*/
public void printHelpAndExitForAction(String verb, String directObject,
String errorFormat, Object... args) {
- if (errorFormat != null && errorFormat.length() > 0) {
+ if (errorFormat != null && !errorFormat.isEmpty()) {
stderr(errorFormat, args);
}
@@ -587,7 +587,7 @@
} else {
if (arg.getDefaultValue() instanceof String[]) {
for (String v : (String[]) arg.getDefaultValue()) {
- if (value.length() > 0) {
+ if (!value.isEmpty()) {
value += ", ";
}
value += v;
@@ -598,7 +598,7 @@
value = v.toString();
}
}
- if (value.length() > 0) {
+ if (!value.isEmpty()) {
value = " [Default: " + value + "]";
}
}
@@ -611,10 +611,10 @@
// where either the 1-letter arg or the long arg are optional.
String output = String.format(
" %1$-2s %2$-" + longArgWidth + "s: %3$s%4$s%5$s", //$NON-NLS-1$ //$NON-NLS-2$
- arg.getShortArg().length() > 0 ?
+ !arg.getShortArg().isEmpty() ?
"-" + arg.getShortArg() : //$NON-NLS-1$
"", //$NON-NLS-1$
- arg.getLongArg().length() > 0 ?
+ !arg.getLongArg().isEmpty() ?
"--" + arg.getLongArg() : //$NON-NLS-1$
"", //$NON-NLS-1$
arg.getDescription(),
@@ -923,7 +923,7 @@
// We should always have at least a short or long name, ideally both but never none.
assert shortName != null;
assert longName != null;
- assert shortName.length() > 0 || longName.length() > 0;
+ assert !shortName.isEmpty() || !longName.isEmpty();
if (directObject == null) {
directObject = NO_VERB_OBJECT;
diff --git a/sdklib/src/test/java/com/android/sdklib/SdkManagerTest.java b/sdklib/src/test/java/com/android/sdklib/SdkManagerTest.java
index 575f366..db90c56 100755
--- a/sdklib/src/test/java/com/android/sdklib/SdkManagerTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/SdkManagerTest.java
@@ -169,12 +169,12 @@
LocationType.IN_IMAGES_SUBFOLDER,
SystemImage.DEFAULT_TAG,
SdkConstants.ABI_ARMEABI_V7A,
- FileOp.EMPTY_FILE_ARRAY));
+ FileOp.EMPTY_FILE_ARRAY), null);
makeSystemImageFolder(new SystemImage(sdkman, t,
LocationType.IN_IMAGES_SUBFOLDER,
SystemImage.DEFAULT_TAG,
SdkConstants.ABI_INTEL_ATOM,
- FileOp.EMPTY_FILE_ARRAY));
+ FileOp.EMPTY_FILE_ARRAY), null);
sdkman.reloadSdk(getLog());
assertEquals("[PlatformTarget API 0 rev 1]", Arrays.toString(sdkman.getTargets()));
@@ -194,12 +194,12 @@
LocationType.IN_SYSTEM_IMAGE,
SystemImage.DEFAULT_TAG,
SdkConstants.ABI_ARMEABI,
- FileOp.EMPTY_FILE_ARRAY));
+ FileOp.EMPTY_FILE_ARRAY), null);
makeSystemImageFolder(new SystemImage(sdkman, t,
LocationType.IN_SYSTEM_IMAGE,
SystemImage.DEFAULT_TAG,
SdkConstants.ABI_ARMEABI_V7A,
- FileOp.EMPTY_FILE_ARRAY));
+ FileOp.EMPTY_FILE_ARRAY), null);
sdkman.reloadSdk(getLog());
assertEquals("[PlatformTarget API 0 rev 1]", Arrays.toString(sdkman.getTargets()));
@@ -217,7 +217,7 @@
LocationType.IN_SYSTEM_IMAGE,
new IdDisplay("tag-1", "My Tag 1"),
SdkConstants.ABI_ARMEABI_V7A,
- FileOp.EMPTY_FILE_ARRAY));
+ FileOp.EMPTY_FILE_ARRAY), null);
sdkman.reloadSdk(getLog());
assertEquals("[PlatformTarget API 0 rev 1]", Arrays.toString(sdkman.getTargets()));
@@ -251,7 +251,7 @@
LocationType.IN_SYSTEM_IMAGE,
SystemImage.DEFAULT_TAG,
SdkConstants.ABI_INTEL_ATOM,
- FileOp.EMPTY_FILE_ARRAY));
+ FileOp.EMPTY_FILE_ARRAY), null);
sdkman.reloadSdk(getLog());
assertEquals("[PlatformTarget API 0 rev 1]", Arrays.toString(sdkman.getTargets()));
@@ -269,7 +269,7 @@
LocationType.IN_SYSTEM_IMAGE,
SystemImage.DEFAULT_TAG,
SdkConstants.ABI_ARMEABI,
- FileOp.EMPTY_FILE_ARRAY));
+ FileOp.EMPTY_FILE_ARRAY), null);
sdkman.reloadSdk(getLog());
diff --git a/sdklib/src/test/java/com/android/sdklib/SdkManagerTestCase.java b/sdklib/src/test/java/com/android/sdklib/SdkManagerTestCase.java
index ae7f8cf..e3c945f 100755
--- a/sdklib/src/test/java/com/android/sdklib/SdkManagerTestCase.java
+++ b/sdklib/src/test/java/com/android/sdklib/SdkManagerTestCase.java
@@ -63,38 +63,48 @@
import java.util.List;
/**
- * Base Test case that allocates a temporary SDK, a temporary AVD base
- * folder with an SdkManager and an AvdManager that points to them.
- * <p/>
- * Also overrides the {@link AndroidLocation} to point to temp one.
+ * Base Test case that allocates a temporary SDK, a temporary AVD base folder with an SdkManager and
+ * an AvdManager that points to them. <p/> Also overrides the {@link AndroidLocation} to point to
+ * temp one.
*/
public abstract class SdkManagerTestCase extends AndroidLocationTestCase {
protected static final String TARGET_DIR_NAME_0 = "v0_0";
+
private File mFakeSdk;
+
private MockLog mLog;
+
private SdkManager mSdkManager;
+
private AvdManager mAvdManager;
+
private int mRepoXsdLevel;
- /** Returns the {@link MockLog} for this test case. */
+ /**
+ * Returns the {@link MockLog} for this test case.
+ */
public MockLog getLog() {
return mLog;
}
- /** Returns the {@link SdkManager} for this test case. */
+ /**
+ * Returns the {@link SdkManager} for this test case.
+ */
public SdkManager getSdkManager() {
return mSdkManager;
}
- /** Returns the {@link AvdManager} for this test case. */
+ /**
+ * Returns the {@link AvdManager} for this test case.
+ */
public AvdManager getAvdManager() {
return mAvdManager;
}
/**
- * Sets up a {@link MockLog}, a fake SDK in a temporary directory
- * and an AVD Manager pointing to an initially-empty AVD directory.
+ * Sets up a {@link MockLog}, a fake SDK in a temporary directory and an AVD Manager pointing to
+ * an initially-empty AVD directory.
*/
public void setUp(int repoXsdLevel) throws Exception {
super.setUp();
@@ -105,9 +115,9 @@
}
/**
- * Recreate the SDK and AVD Managers from scratch even if they already existed.
- * Useful for tests that want to reset their state without recreating the
- * android-home or the fake SDK. The SDK will be reparsed.
+ * Recreate the SDK and AVD Managers from scratch even if they already existed. Useful for tests
+ * that want to reset their state without recreating the android-home or the fake SDK. The SDK
+ * will be reparsed.
*/
protected void createSdkAvdManagers() throws AndroidLocationException {
mSdkManager = SdkManager.createManager(mFakeSdk.getAbsolutePath(), mLog);
@@ -131,13 +141,13 @@
return false;
}
- };
+ }
};
}
/**
- * Sets up a {@link MockLog}, a fake SDK in a temporary directory
- * and an AVD Manager pointing to an initially-empty AVD directory.
+ * Sets up a {@link MockLog}, a fake SDK in a temporary directory and an AVD Manager pointing to
+ * an initially-empty AVD directory.
*/
@Override
public void setUp() throws Exception {
@@ -154,10 +164,9 @@
}
/**
- * Build enough of a skeleton SDK to make the tests pass.
- * <p/>
- * Ideally this wouldn't touch the file system but the current
- * structure of the SdkManager and AvdManager makes this impossible.
+ * Build enough of a skeleton SDK to make the tests pass. <p/> Ideally this wouldn't touch the
+ * file system but the current structure of the SdkManager and AvdManager makes this
+ * impossible.
*/
private void makeFakeSdk() throws IOException {
// First we create a temp file to "reserve" the temp directory name we want to use.
@@ -204,42 +213,47 @@
* Creates the system image folder and places a fake userdata.img in it.
*
* @param systemImage A system image with a valid location.
- * @throws IOException if the file fails to be created.
*/
- protected void makeSystemImageFolder(ISystemImage systemImage) throws Exception {
+ protected void makeSystemImageFolder(ISystemImage systemImage, String deviceId)
+ throws Exception {
File sysImgDir = systemImage.getLocation();
-
+ String vendor = systemImage.getAddonVendor() == null ? null
+ : systemImage.getAddonVendor().getId();
if (systemImage.getLocationType() == LocationType.IN_LEGACY_FOLDER) {
// legacy mode. Path should look like SDK/platforms/platform-N/userdata.img
makeFakeLegacySysImg(sysImgDir.getParentFile(), systemImage.getAbiType());
- } else if (systemImage.getLocationType() == LocationType.IN_IMAGES_SUBFOLDER) {
+ } else if (systemImage.getLocationType() == LocationType.IN_IMAGES_SUBFOLDER) {
// not-so-legacy mode.
// Path should look like SDK/platforms/platform-N/images/userdata.img
makeFakeSysImgInternal(
sysImgDir,
systemImage.getTag().getId(),
- systemImage.getAbiType());
+ systemImage.getAbiType(),
+ deviceId,
+ vendor);
- } else if (systemImage.getLocationType() == LocationType.IN_SYSTEM_IMAGE) {
+ } else if (systemImage.getLocationType() == LocationType.IN_SYSTEM_IMAGE) {
// system-image folder mode.
// Path should like SDK/system-images/platform-N/tag/abi/userdata.img+source.properties
makeFakeSysImgInternal(
sysImgDir,
systemImage.getTag().getId(),
- systemImage.getAbiType());
+ systemImage.getAbiType(),
+ deviceId,
+ vendor);
}
}
/**
- * Creates the system image folder and places a fake userdata.img in it.
- * This must be called after {@link #setUp()} so that it can use the temp fake SDK folder,
- * and consequently you do not need to specify the SDK root.
+ * Creates the system image folder and places a fake userdata.img in it. This must be called
+ * after {@link #setUp()} so that it can use the temp fake SDK folder, and consequently you do
+ * not need to specify the SDK root.
*
- * @param targetDir The targetDir segment of the sys-image folder.
- * Use {@link #TARGET_DIR_NAME_0} to match the default single platform.
- * @param tagId An optional tag id. Use null for legacy no-tag system images.
- * @param abiType The abi for the system image.
+ * @param targetDir The targetDir segment of the sys-image folder. Use {@link
+ * #TARGET_DIR_NAME_0} to match the default single platform.
+ * @param tagId An optional tag id. Use null for legacy no-tag system images.
+ * @param abiType The abi for the system image.
* @return The directory of the system-image/tag/abi created.
* @throws IOException if the file fails to be created.
*/
@@ -255,13 +269,13 @@
}
sysImgDir = new File(sysImgDir, abiType);
- makeFakeSysImgInternal(sysImgDir, tagId, abiType);
+ makeFakeSysImgInternal(sysImgDir, tagId, abiType, null, null);
return sysImgDir;
}
//----
- private void createTextFile(File dir, String filepath, String...lines) throws IOException {
+ private void createTextFile(File dir, String filepath, String... lines) throws IOException {
File file = new File(dir, filepath);
File parent = file.getParentFile();
@@ -281,7 +295,9 @@
}
}
- /** Utility used by {@link #makeFakeSdk()} to create a fake target with API 0, rev 0. */
+ /**
+ * Utility used by {@link #makeFakeSdk()} to create a fake target with API 0, rev 0.
+ */
private File makeFakeTargetInternal(File platformsDir) throws IOException {
File targetDir = new File(platformsDir, TARGET_DIR_NAME_0);
targetDir.mkdirs();
@@ -296,17 +312,16 @@
PkgProps.LAYOUTLIB_REV, "2");
createFileProps(SdkConstants.FN_BUILD_PROP, targetDir,
- LocalPlatformPkgInfo.PROP_VERSION_RELEASE, "0.0",
- LocalPlatformPkgInfo.PROP_VERSION_SDK, "0",
+ LocalPlatformPkgInfo.PROP_VERSION_RELEASE, "0.0",
+ LocalPlatformPkgInfo.PROP_VERSION_SDK, "0",
LocalPlatformPkgInfo.PROP_VERSION_CODENAME, "REL");
return targetDir;
}
/**
- * Utility to create a fake *legacy* sys image in a platform folder.
- * Legacy system images follow that path pattern:
- * $SDK/platforms/platform-N/images/userdata.img
+ * Utility to create a fake *legacy* sys image in a platform folder. Legacy system images follow
+ * that path pattern: $SDK/platforms/platform-N/images/userdata.img
*
* They have no source.properties file in that directory.
*/
@@ -322,20 +337,18 @@
* Utility to create a fake sys image in the system-images folder.
*
* "modern" (as in "not legacy") system-images follow that path pattern:
- * $SDK/system-images/platform-N/abi/source.properties
- * $SDK/system-images/platform-N/abi/userdata.img
- * or
- * $SDK/system-images/platform-N/tag/abi/source.properties
- * $SDK/system-images/platform-N/tag/abi/userdata.img
+ * $SDK/system-images/platform-N/abi/source.properties $SDK/system-images/platform-N/abi/userdata.img
+ * or $SDK/system-images/platform-N/tag/abi/source.properties $SDK/system-images/platform-N/tag/abi/userdata.img
*
- * The tag id is optional and was only introduced in API 20 / Tools 22.6.
- * The platform-N and the tag folder names are irrelevant as the info from
- * source.properties matters most.
+ * The tag id is optional and was only introduced in API 20 / Tools 22.6. The platform-N and the
+ * tag folder names are irrelevant as the info from source.properties matters most.
*/
private void makeFakeSysImgInternal(
@NonNull File sysImgDir,
@Nullable String tagId,
- @NonNull String abiType) throws Exception {
+ @NonNull String abiType,
+ @Nullable String deviceId,
+ @Nullable String deviceMfg) throws Exception {
sysImgDir.mkdirs();
new File(sysImgDir, "userdata.img").createNewFile();
@@ -344,7 +357,7 @@
PkgProps.PKG_REVISION, "0",
PkgProps.VERSION_API_LEVEL, "0",
PkgProps.SYS_IMG_ABI, abiType);
- } else {
+ } else {
String tagDisplay = LocalSysImgPkgInfo.tagIdToDisplay(tagId);
createSourceProps(sysImgDir,
PkgProps.PKG_REVISION, "0",
@@ -352,14 +365,15 @@
PkgProps.SYS_IMG_TAG_ID, tagId,
PkgProps.SYS_IMG_TAG_DISPLAY, tagDisplay,
PkgProps.SYS_IMG_ABI, abiType,
- PkgProps.PKG_LIST_DISPLAY, "Sys-Img v0 for (" + tagDisplay + ", " + abiType + ")");
+ PkgProps.PKG_LIST_DISPLAY,
+ "Sys-Img v0 for (" + tagDisplay + ", " + abiType + ")");
// create a devices.xml file
List<Device> devices = new ArrayList<Device>();
Builder b = new Device.Builder();
b.setName("Mock " + tagDisplay + " Device Name");
- b.setId("MockDevice-" + tagId);
- b.setManufacturer("Mock " + tagDisplay + " OEM");
+ b.setId(deviceId == null ? "MockDevice-" + tagId : deviceId);
+ b.setManufacturer(deviceMfg == null ? "Mock " + tagDisplay + " OEM" : deviceMfg);
Software sw = new Software();
sw.setGlVersion("4.2");
@@ -410,10 +424,12 @@
FileOutputStream fos = new FileOutputStream(f);
DeviceWriter.writeToXml(fos, devices);
fos.close();
- }
+ }
}
- /** Utility to make a fake skin for the given target */
+ /**
+ * Utility to make a fake skin for the given target
+ */
protected void makeFakeSkin(File targetDir, String skinName) throws IOException {
File skinFolder = FileOp.append(targetDir, "skins", skinName);
skinFolder.mkdirs();
@@ -425,7 +441,9 @@
out.close();
}
- /** Utility to create a fake source with a few files in the given sdk folder. */
+ /**
+ * Utility to create a fake source with a few files in the given sdk folder.
+ */
private void makeFakeSourceInternal(File sdkDir) throws IOException {
File sourcesDir = FileOp.append(sdkDir, SdkConstants.FD_PKG_SOURCES, "android-0");
sourcesDir.mkdirs();
@@ -450,7 +468,7 @@
}
private void makeBuildTools(File sdkDir) throws IOException {
- for (String revision : new String[] { "3.0.0", "3.0.1", "18.3.4 rc5" }) {
+ for (String revision : new String[]{"3.0.0", "3.0.1", "18.3.4 rc5"}) {
createFakeBuildTools(sdkDir, "ANY", revision);
}
}
@@ -458,10 +476,9 @@
/**
* Adds a new fake build tools to the SDK In the given SDK/build-tools folder.
*
- * @param sdkDir The SDK top folder. Must already exist.
- * @param os The OS. One of HostOs#toString() or "ANY".
+ * @param sdkDir The SDK top folder. Must already exist.
+ * @param os The OS. One of HostOs#toString() or "ANY".
* @param revision The "x.y.z rc r" revision number from {@link FullRevision#toShortString()}.
- * @throws IOException
*/
protected void createFakeBuildTools(File sdkDir, String os, String revision)
throws IOException {
@@ -476,46 +493,47 @@
createFakeBuildToolsFile(
buildToolsDir, fullRevision,
- BuildToolInfo.PathId.AAPT, SdkConstants.FN_AAPT);
+ BuildToolInfo.PathId.AAPT, SdkConstants.FN_AAPT);
createFakeBuildToolsFile(
buildToolsDir, fullRevision,
- BuildToolInfo.PathId.AIDL, SdkConstants.FN_AIDL);
+ BuildToolInfo.PathId.AIDL, SdkConstants.FN_AIDL);
createFakeBuildToolsFile(
buildToolsDir, fullRevision,
- BuildToolInfo.PathId.DX, SdkConstants.FN_DX);
+ BuildToolInfo.PathId.DX, SdkConstants.FN_DX);
createFakeBuildToolsFile(
buildToolsDir, fullRevision,
- BuildToolInfo.PathId.DX_JAR, SdkConstants.FD_LIB + File.separator +
- SdkConstants.FN_DX_JAR);
+ BuildToolInfo.PathId.DX_JAR, SdkConstants.FD_LIB + File.separator +
+ SdkConstants.FN_DX_JAR);
createFakeBuildToolsFile(
buildToolsDir, fullRevision,
- BuildToolInfo.PathId.LLVM_RS_CC, SdkConstants.FN_RENDERSCRIPT);
+ BuildToolInfo.PathId.LLVM_RS_CC, SdkConstants.FN_RENDERSCRIPT);
createFakeBuildToolsFile(
buildToolsDir, fullRevision,
- BuildToolInfo.PathId.ANDROID_RS, SdkConstants.OS_FRAMEWORK_RS + File.separator +
- "placeholder.txt");
+ BuildToolInfo.PathId.ANDROID_RS, SdkConstants.OS_FRAMEWORK_RS + File.separator +
+ "placeholder.txt");
createFakeBuildToolsFile(
buildToolsDir, fullRevision,
- BuildToolInfo.PathId.ANDROID_RS_CLANG, SdkConstants.OS_FRAMEWORK_RS_CLANG + File.separator +
- "placeholder.txt");
+ BuildToolInfo.PathId.ANDROID_RS_CLANG,
+ SdkConstants.OS_FRAMEWORK_RS_CLANG + File.separator +
+ "placeholder.txt");
createFakeBuildToolsFile(
buildToolsDir, fullRevision,
- BuildToolInfo.PathId.BCC_COMPAT, SdkConstants.FN_BCC_COMPAT);
+ BuildToolInfo.PathId.BCC_COMPAT, SdkConstants.FN_BCC_COMPAT);
createFakeBuildToolsFile(
buildToolsDir, fullRevision,
- BuildToolInfo.PathId.LD_ARM, SdkConstants.FN_LD_ARM);
+ BuildToolInfo.PathId.LD_ARM, SdkConstants.FN_LD_ARM);
createFakeBuildToolsFile(
buildToolsDir, fullRevision,
- BuildToolInfo.PathId.LD_MIPS, SdkConstants.FN_LD_MIPS);
+ BuildToolInfo.PathId.LD_MIPS, SdkConstants.FN_LD_MIPS);
createFakeBuildToolsFile(
buildToolsDir, fullRevision,
- BuildToolInfo.PathId.LD_X86, SdkConstants.FN_LD_X86);
+ BuildToolInfo.PathId.LD_X86, SdkConstants.FN_LD_X86);
}
private void createFakeBuildToolsFile(@NonNull File dir,
- @NonNull FullRevision buildToolsRevision,
- @NonNull BuildToolInfo.PathId pathId,
- @NonNull String filepath)
+ @NonNull FullRevision buildToolsRevision,
+ @NonNull BuildToolInfo.PathId pathId,
+ @NonNull String filepath)
throws IOException {
if (pathId.isPresentIn(buildToolsRevision)) {
@@ -524,11 +542,12 @@
}
- protected void createSourceProps(File parentDir, String...paramValuePairs) throws IOException {
+ protected void createSourceProps(File parentDir, String... paramValuePairs) throws IOException {
createFileProps(SdkConstants.FN_SOURCE_PROP, parentDir, paramValuePairs);
}
- protected void createFileProps(String fileName, File parentDir, String...paramValuePairs) throws IOException {
+ protected void createFileProps(String fileName, File parentDir, String... paramValuePairs)
+ throws IOException {
File sourceProp = new File(parentDir, fileName);
parentDir = sourceProp.getParentFile();
if (!parentDir.isDirectory()) {
@@ -539,9 +558,9 @@
}
FileWriter out = new FileWriter(sourceProp);
int n = paramValuePairs.length;
- assertTrue("paramValuePairs must have an even length, format [param=value]+", n %2 == 0);
+ assertTrue("paramValuePairs must have an even length, format [param=value]+", n % 2 == 0);
for (int i = 0; i < n; i += 2) {
- out.write(paramValuePairs[i] + '=' + paramValuePairs[i+1] + '\n');
+ out.write(paramValuePairs[i] + '=' + paramValuePairs[i + 1] + '\n');
}
out.close();
diff --git a/sdklib/src/test/java/com/android/sdklib/SdkVersionInfoTest.java b/sdklib/src/test/java/com/android/sdklib/SdkVersionInfoTest.java
index 36595f5..5d919c6 100755
--- a/sdklib/src/test/java/com/android/sdklib/SdkVersionInfoTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/SdkVersionInfoTest.java
@@ -24,8 +24,6 @@
import static com.android.sdklib.SdkVersionInfo.getVersion;
import static com.android.sdklib.SdkVersionInfo.underlinesToCamelCase;
-import com.android.sdklib.SdkVersionInfo;
-
import junit.framework.TestCase;
public class SdkVersionInfoTest extends TestCase {
diff --git a/sdklib/src/test/java/com/android/sdklib/devices/DeviceManagerTest.java b/sdklib/src/test/java/com/android/sdklib/devices/DeviceManagerTest.java
index 22935f1..9bc9140 100755
--- a/sdklib/src/test/java/com/android/sdklib/devices/DeviceManagerTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/devices/DeviceManagerTest.java
@@ -18,20 +18,22 @@
import com.android.resources.Keyboard;
import com.android.resources.Navigation;
+import com.android.sdklib.ISystemImage;
import com.android.sdklib.SdkManagerTestCase;
+import com.android.sdklib.SystemImage;
import com.android.sdklib.devices.Device.Builder;
import com.android.sdklib.devices.DeviceManager.DeviceFilter;
import com.android.sdklib.devices.DeviceManager.DeviceStatus;
import com.android.sdklib.mock.MockLog;
+import com.android.sdklib.repository.descriptors.IdDisplay;
import java.io.File;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import java.util.*;
public class DeviceManagerTest extends SdkManagerTestCase {
private DeviceManager dm;
+
private MockLog log;
@Override
@@ -47,19 +49,28 @@
return DeviceManager.createInstance(sdkLocation, log);
}
- /** Returns a list of just the devices' display names, for unit test comparisons. */
+ /**
+ * Returns a list of just the devices' display names, for unit test comparisons.
+ */
private static String listDisplayName(Device device) {
- if (device == null) return null;
+ if (device == null) {
+ return null;
+ }
return device.getDisplayName();
}
- /** Returns a list of just the devices' display names, for unit test comparisons. */
+ /**
+ * Returns a list of just the devices' display names, for unit test comparisons.
+ */
private static List<String> listDisplayNames(Collection<Device> devices) {
- if (devices == null) return null;
+ if (devices == null) {
+ return null;
+ }
List<String> names = new ArrayList<String>();
for (Device d : devices) {
names.add(listDisplayName(d));
}
+ Collections.sort(names);
return names;
}
@@ -80,11 +91,12 @@
// this list comes from devices.xml bundled in the JAR
// cf /sdklib/src/main/java/com/android/sdklib/devices/devices.xml
assertEquals(
- "[2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), 3.2\" QVGA (ADP2), " +
- "3.3\" WQVGA, 3.4\" WQVGA, 3.7\" WVGA (Nexus One), 3.7\" FWVGA slider, " +
- "4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), 4.7\" WXGA, 5.1\" WVGA, " +
- "5.4\" FWVGA, 7\" WSVGA (Tablet), 10.1\" WXGA (Tablet)]",
- listDisplayNames(dm.getDevices(DeviceFilter.DEFAULT)).toString());
+ "[10.1\" WXGA (Tablet), 2.7\" QVGA, 2.7\" QVGA slider, " +
+ "3.2\" HVGA slider (ADP1), 3.2\" QVGA (ADP2), 3.3\" WQVGA, 3.4\" WQVGA, " +
+ "3.7\" FWVGA slider, 3.7\" WVGA (Nexus One), 4\" WVGA (Nexus S), " +
+ "4.65\" 720p (Galaxy Nexus), 4.7\" WXGA, 5.1\" WVGA, 5.4\" FWVGA, " +
+ "7\" WSVGA (Tablet)]",
+ listDisplayNames(dm.getDevices(DeviceFilter.DEFAULT)).toString());
assertEquals("", log.toString());
assertEquals("2.7\" QVGA",
@@ -92,25 +104,26 @@
// this list comes from the nexus.xml bundled in the JAR
// cf /sdklib/src/main/java/com/android/sdklib/devices/nexus.xml
- assertEquals(
- "[Nexus One, Nexus S, Galaxy Nexus, Nexus 7 (2012), " +
- "Nexus 4, Nexus 10, Nexus 7, Nexus 5, Nexus 6, Nexus 9, Android Wear Square, " +
- "Android Wear Round, Android TV (1080p), Android TV (720p)]",
- listDisplayNames(dm.getDevices(DeviceFilter.VENDOR)).toString());
+ assertEquals("[Android TV (1080p), Android TV (720p), Android Wear Round, " +
+ "Android Wear Round Chin, Android Wear Square, " +
+ "Galaxy Nexus, Nexus 10, Nexus 4, Nexus 5, Nexus 6, Nexus 7, " +
+ "Nexus 7 (2012), Nexus 9, Nexus One, Nexus S]",
+ listDisplayNames(dm.getDevices(DeviceFilter.VENDOR)).toString());
assertEquals("", log.toString());
assertEquals("Nexus One",
listDisplayName(dm.getDevice("Nexus One", "Google")));
assertEquals(
- "[2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), 3.2\" QVGA (ADP2), " +
- "3.3\" WQVGA, 3.4\" WQVGA, 3.7\" WVGA (Nexus One), 3.7\" FWVGA slider, " +
- "4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), 4.7\" WXGA, 5.1\" WVGA, " +
- "5.4\" FWVGA, 7\" WSVGA (Tablet), 10.1\" WXGA (Tablet), " +
- "Nexus One, Nexus S, Galaxy Nexus, Nexus 7 (2012), " +
- "Nexus 4, Nexus 10, Nexus 7, Nexus 5, Nexus 6, Nexus 9, Android Wear Square, Android Wear Round, " +
- "Android TV (1080p), Android TV (720p)]",
- listDisplayNames(dm.getDevices(DeviceManager.ALL_DEVICES)).toString());
+ "[10.1\" WXGA (Tablet), 2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), " +
+ "3.2\" QVGA (ADP2), 3.3\" WQVGA, 3.4\" WQVGA, 3.7\" FWVGA slider, " +
+ "3.7\" WVGA (Nexus One), 4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), " +
+ "4.7\" WXGA, 5.1\" WVGA, 5.4\" FWVGA, 7\" WSVGA (Tablet), Android TV (1080p), "
+ +
+ "Android TV (720p), Android Wear Round, Android Wear Round Chin, " +
+ "Android Wear Square, Galaxy Nexus, Nexus 10, Nexus 4, Nexus 5, " +
+ "Nexus 6, Nexus 7, Nexus 7 (2012), Nexus 9, Nexus One, Nexus S]",
+ listDisplayNames(dm.getDevices(DeviceManager.ALL_DEVICES)).toString());
assertEquals("", log.toString());
}
@@ -152,43 +165,43 @@
// 1 user device defined in the test's custom .android home folder
assertEquals("[My Custom Tablet]",
- listDisplayNames(dm2.getDevices(DeviceFilter.USER)).toString());
+ listDisplayNames(dm2.getDevices(DeviceFilter.USER)).toString());
assertEquals("", log.toString());
// no system-images devices defined in the SDK by default
assertEquals("[]",
- listDisplayNames(dm2.getDevices(DeviceFilter.SYSTEM_IMAGES)).toString());
+ listDisplayNames(dm2.getDevices(DeviceFilter.SYSTEM_IMAGES)).toString());
assertEquals("", log.toString());
// this list comes from devices.xml bundled in the JAR
// cf /sdklib/src/main/java/com/android/sdklib/devices/devices.xml
assertEquals(
- "[2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), 3.2\" QVGA (ADP2), " +
- "3.3\" WQVGA, 3.4\" WQVGA, 3.7\" WVGA (Nexus One), 3.7\" FWVGA slider, " +
- "4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), 4.7\" WXGA, 5.1\" WVGA, " +
- "5.4\" FWVGA, 7\" WSVGA (Tablet), 10.1\" WXGA (Tablet)]",
- listDisplayNames(dm2.getDevices(DeviceFilter.DEFAULT)).toString());
+ "[10.1\" WXGA (Tablet), 2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), " +
+ "3.2\" QVGA (ADP2), 3.3\" WQVGA, 3.4\" WQVGA, 3.7\" FWVGA slider, " +
+ "3.7\" WVGA (Nexus One), 4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), " +
+ "4.7\" WXGA, 5.1\" WVGA, 5.4\" FWVGA, 7\" WSVGA (Tablet)]",
+ listDisplayNames(dm2.getDevices(DeviceFilter.DEFAULT)).toString());
assertEquals("", log.toString());
// this list comes from the nexus.xml bundled in the JAR
// cf /sdklib/src/main/java/com/android/sdklib/devices/nexus.xml
- assertEquals(
- "[Nexus One, Nexus S, Galaxy Nexus, Nexus 7 (2012), " +
- "Nexus 4, Nexus 10, Nexus 7, Nexus 5, Nexus 6, Nexus 9, Android Wear Square, " +
- "Android Wear Round, Android TV (1080p), Android TV (720p)]",
- listDisplayNames(dm2.getDevices(DeviceFilter.VENDOR)).toString());
+ assertEquals("[Android TV (1080p), Android TV (720p), Android Wear Round, " +
+ "Android Wear Round Chin, Android Wear Square, Galaxy Nexus, " +
+ "Nexus 10, Nexus 4, Nexus 5, Nexus 6, Nexus 7, Nexus 7 (2012), " +
+ "Nexus 9, Nexus One, Nexus S]",
+ listDisplayNames(dm2.getDevices(DeviceFilter.VENDOR)).toString());
assertEquals("", log.toString());
assertEquals(
- "[My Custom Tablet, " +
- "2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), 3.2\" QVGA (ADP2), " +
- "3.3\" WQVGA, 3.4\" WQVGA, 3.7\" WVGA (Nexus One), 3.7\" FWVGA slider, " +
- "4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), 4.7\" WXGA, 5.1\" WVGA, " +
- "5.4\" FWVGA, 7\" WSVGA (Tablet), 10.1\" WXGA (Tablet), " +
- "Nexus One, Nexus S, Galaxy Nexus, Nexus 7 (2012), " +
- "Nexus 4, Nexus 10, Nexus 7, Nexus 5, Nexus 6, Nexus 9, Android Wear Square, Android Wear Round, " +
- "Android TV (1080p), Android TV (720p)]",
- listDisplayNames(dm2.getDevices(DeviceManager.ALL_DEVICES)).toString());
+ "[10.1\" WXGA (Tablet), 2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), " +
+ "3.2\" QVGA (ADP2), 3.3\" WQVGA, 3.4\" WQVGA, 3.7\" FWVGA slider, " +
+ "3.7\" WVGA (Nexus One), 4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), " +
+ "4.7\" WXGA, 5.1\" WVGA, 5.4\" FWVGA, 7\" WSVGA (Tablet), Android TV (1080p), "
+ +
+ "Android TV (720p), Android Wear Round, Android Wear Round Chin, " +
+ "Android Wear Square, Galaxy Nexus, My Custom Tablet, Nexus 10, " +
+ "Nexus 4, Nexus 5, Nexus 6, Nexus 7, Nexus 7 (2012), Nexus 9, Nexus One, Nexus S]",
+ listDisplayNames(dm2.getDevices(DeviceManager.ALL_DEVICES)).toString());
assertEquals("", log.toString());
}
@@ -212,47 +225,50 @@
// this list comes from devices.xml bundled in the JAR
// cf /sdklib/src/main/java/com/android/sdklib/devices/devices.xml
assertEquals(
- "[2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), 3.2\" QVGA (ADP2), " +
- "3.3\" WQVGA, 3.4\" WQVGA, 3.7\" WVGA (Nexus One), 3.7\" FWVGA slider, " +
- "4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), 4.7\" WXGA, 5.1\" WVGA, " +
- "5.4\" FWVGA, 7\" WSVGA (Tablet), 10.1\" WXGA (Tablet)]",
- listDisplayNames(dm.getDevices(DeviceFilter.DEFAULT)).toString());
+ "[10.1\" WXGA (Tablet), 2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), " +
+ "3.2\" QVGA (ADP2), 3.3\" WQVGA, 3.4\" WQVGA, 3.7\" FWVGA slider, " +
+ "3.7\" WVGA (Nexus One), 4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), " +
+ "4.7\" WXGA, 5.1\" WVGA, 5.4\" FWVGA, 7\" WSVGA (Tablet)]",
+ listDisplayNames(dm.getDevices(DeviceFilter.DEFAULT)).toString());
assertEquals("", log.toString());
// this list comes from the nexus.xml bundled in the JAR
// cf /sdklib/src/main/java/com/android/sdklib/devices/nexus.xml
assertEquals(
- "[Nexus One, Nexus S, Galaxy Nexus, Nexus 7 (2012), " +
- "Nexus 4, Nexus 10, Nexus 7, Nexus 5, Nexus 6, Nexus 9, Android Wear Square, " +
- "Android Wear Round, Android TV (1080p), Android TV (720p)]",
- listDisplayNames(dm.getDevices(DeviceFilter.VENDOR)).toString());
+ "[Android TV (1080p), Android TV (720p), Android Wear Round, Android Wear Round Chin, "
+ +
+ "Android Wear Square, Galaxy Nexus, Nexus 10, Nexus 4, Nexus 5, Nexus 6, Nexus 7, "
+ +
+ "Nexus 7 (2012), Nexus 9, Nexus One, Nexus S]",
+ listDisplayNames(dm.getDevices(DeviceFilter.VENDOR)).toString());
assertEquals("", log.toString());
assertEquals(
- "[2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), 3.2\" QVGA (ADP2), " +
- "3.3\" WQVGA, 3.4\" WQVGA, 3.7\" WVGA (Nexus One), 3.7\" FWVGA slider, " +
- "4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), 4.7\" WXGA, 5.1\" WVGA, " +
- "5.4\" FWVGA, 7\" WSVGA (Tablet), 10.1\" WXGA (Tablet), " +
- "Nexus One, Nexus S, Galaxy Nexus, Nexus 7 (2012), " +
- "Nexus 4, Nexus 10, Nexus 7, Nexus 5, Nexus 6, Nexus 9, Android Wear Square, Android Wear Round, " +
- "Android TV (1080p), Android TV (720p), " +
- "Mock Tag 1 Device Name]",
- listDisplayNames(dm.getDevices(DeviceManager.ALL_DEVICES)).toString());
+ "[10.1\" WXGA (Tablet), 2.7\" QVGA, 2.7\" QVGA slider, 3.2\" HVGA slider (ADP1), " +
+ "3.2\" QVGA (ADP2), 3.3\" WQVGA, 3.4\" WQVGA, 3.7\" FWVGA slider, " +
+ "3.7\" WVGA (Nexus One), 4\" WVGA (Nexus S), 4.65\" 720p (Galaxy Nexus), " +
+ "4.7\" WXGA, 5.1\" WVGA, 5.4\" FWVGA, 7\" WSVGA (Tablet), Android TV (1080p), "
+ +
+ "Android TV (720p), Android Wear Round, Android Wear Round Chin, " +
+ "Android Wear Square, Galaxy Nexus, Mock Tag 1 Device Name, Nexus 10, Nexus 4, "
+ +
+ "Nexus 5, Nexus 6, Nexus 7, Nexus 7 (2012), Nexus 9, Nexus One, Nexus S]",
+ listDisplayNames(dm.getDevices(DeviceManager.ALL_DEVICES)).toString());
assertEquals("", log.toString());
}
public final void testGetDeviceStatus() {
// get a definition from the bundled devices.xml file
assertEquals(DeviceStatus.EXISTS,
- dm.getDeviceStatus("7in WSVGA (Tablet)", "Generic"));
+ dm.getDeviceStatus("7in WSVGA (Tablet)", "Generic"));
// get a definition from the bundled oem file
assertEquals(DeviceStatus.EXISTS,
- dm.getDeviceStatus("Nexus One", "Google"));
+ dm.getDeviceStatus("Nexus One", "Google"));
// try a device that does not exist
assertEquals(DeviceStatus.MISSING,
- dm.getDeviceStatus("My Device", "Custom OEM"));
+ dm.getDeviceStatus("My Device", "Custom OEM"));
}
public final void testHasHardwarePropHashChanged_Generic() {
@@ -264,31 +280,25 @@
"invalid"));
assertEquals(null,
- DeviceManager.hasHardwarePropHashChanged(
- d1,
- "MD5:750a657019b49e621c42ce9a20c2cc30"));
+ DeviceManager.hasHardwarePropHashChanged(d1, "MD5:750a657019b49e621c42ce9a20c2cc30"));
// change the device hardware props, this should change the hash
d1.getDefaultHardware().setNav(Navigation.TRACKBALL);
assertEquals("MD5:9c4dd5018987da51f7166f139f4361a2",
- DeviceManager.hasHardwarePropHashChanged(
- d1,
- "MD5:750a657019b49e621c42ce9a20c2cc30"));
+ DeviceManager.hasHardwarePropHashChanged(d1, "MD5:750a657019b49e621c42ce9a20c2cc30"));
// change the property back, should revert its hash to the previous one
d1.getDefaultHardware().setNav(Navigation.NONAV);
assertEquals(null,
- DeviceManager.hasHardwarePropHashChanged(
- d1,
- "MD5:750a657019b49e621c42ce9a20c2cc30"));
+ DeviceManager.hasHardwarePropHashChanged(d1, "MD5:750a657019b49e621c42ce9a20c2cc30"));
}
public final void testHasHardwarePropHashChanged_Oem() {
final Device d2 = dm.getDevice("Nexus One", "Google");
- assertEquals("MD5:d886364fc30320c0518f51002d0ef22d",
+ assertEquals("MD5:36362a51e6c830c2ab515a312c9ecbff",
DeviceManager.hasHardwarePropHashChanged(
d2,
"invalid"));
@@ -296,15 +306,15 @@
assertEquals(null,
DeviceManager.hasHardwarePropHashChanged(
d2,
- "MD5:d886364fc30320c0518f51002d0ef22d"));
+ "MD5:36362a51e6c830c2ab515a312c9ecbff"));
// change the device hardware props, this should change the hash
d2.getDefaultHardware().setKeyboard(Keyboard.QWERTY);
- assertEquals("MD5:db682e0a58e74a8614e43e6e15c05176",
+ assertEquals("MD5:f8f4b390755f2f58dfeb7d3020cd87db",
DeviceManager.hasHardwarePropHashChanged(
d2,
- "MD5:d886364fc30320c0518f51002d0ef22d"));
+ "MD5:36362a51e6c830c2ab515a312c9ecbff"));
// change the property back, should revert its hash to the previous one
d2.getDefaultHardware().setKeyboard(Keyboard.NOKEY);
@@ -312,6 +322,44 @@
assertEquals(null,
DeviceManager.hasHardwarePropHashChanged(
d2,
- "MD5:d886364fc30320c0518f51002d0ef22d"));
+ "MD5:36362a51e6c830c2ab515a312c9ecbff"));
+ }
+
+ public final void testDeviceOverrides() throws Exception {
+ try {
+ File location = getSdkManager().getLocalSdk().getLocation();
+ SystemImage imageWithDevice = new SystemImage(
+ new File(location, "system-images/android-22/android-wear/x86"),
+ ISystemImage.LocationType.IN_SYSTEM_IMAGE,
+ new IdDisplay("android-wear", "android-wear"), new IdDisplay("Google", "Google1"),
+ "x86", new File[]{});
+ DeviceManager manager = DeviceManager.createInstance(location, log);
+ int count = manager.getDevices(EnumSet.allOf(DeviceFilter.class)).size();
+ Device d = manager.getDevice("wear_round", "Google");
+ assertEquals(d.getDisplayName(), "Android Wear Round");
+
+ makeSystemImageFolder(imageWithDevice, "wear_round");
+ manager = DeviceManager.createInstance(location, log);
+
+ d = manager.getDevice("wear_round", "Google");
+ assertEquals(d.getDisplayName(), "Mock Android wear Device Name");
+
+ Device d1 = dm.getDevice("wear_round", "Google");
+
+ Builder b = new Device.Builder(d1);
+ b.setName("Custom");
+
+ Device d2 = b.build();
+
+ manager.addUserDevice(d2);
+ manager.saveUserDevices();
+
+ d = manager.getDevice("wear_round", "Google");
+ assertEquals(d.getDisplayName(), "Custom");
+ assertEquals(count, manager.getDevices(EnumSet.allOf(DeviceFilter.class)).size());
+ }
+ finally {
+ super.setUp();
+ }
}
}
diff --git a/sdklib/src/test/java/com/android/sdklib/devices/DeviceParserTest.java b/sdklib/src/test/java/com/android/sdklib/devices/DeviceParserTest.java
index 263a8b5..2493f46 100644
--- a/sdklib/src/test/java/com/android/sdklib/devices/DeviceParserTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/devices/DeviceParserTest.java
@@ -24,10 +24,12 @@
import com.android.resources.NavigationState;
import com.android.resources.ScreenOrientation;
import com.android.resources.ScreenRatio;
+import com.android.resources.ScreenRound;
import com.android.resources.ScreenSize;
import com.android.resources.TouchScreen;
import com.android.sdklib.devices.Storage.Unit;
+import com.google.common.collect.Table;
import junit.framework.TestCase;
import org.xml.sax.SAXParseException;
@@ -44,11 +46,11 @@
public void testValidDevicesMinimal() throws Exception {
InputStream stream = DeviceSchemaTest.class.getResourceAsStream("devices_minimal.xml");
try {
- List<Device> devices = DeviceParser.parse(stream);
+ Table<String, String, Device> devices = DeviceParser.parse(stream);
assertEquals("Parsing devices_minimal.xml produces the wrong number of devices",
1, devices.size());
- Device device = devices.get(0);
+ Device device = devices.get("Galaxy Nexus", "Samsung");
assertEquals("Galaxy Nexus", device.getDisplayName());
assertEquals("Samsung", device.getManufacturer());
@@ -154,23 +156,23 @@
public void testValidDevicesFull_v1() throws Exception {
InputStream stream = DeviceSchemaTest.class.getResourceAsStream("devices.xml");
try {
- List<Device> devices = DeviceParser.parse(stream);
+ Table<String, String, Device> devices = DeviceParser.parse(stream);
assertEquals("Parsing devices.xml produces the wrong number of devices",
- 3, devices.size());
+ 4, devices.size());
- Device device0 = devices.get(0);
+ Device device0 = devices.get("galaxy_nexus", "Samsung");
assertEquals(null, device0.getTagId());
assertEquals("{}", device0.getBootProps().toString());
assertEquals("OMAP 4460", device0.getDefaultHardware().getCpu());
assertEquals("[armeabi, armeabi-v7a]", device0.getDefaultHardware().getSupportedAbis().toString());
- Device device1 = devices.get(1);
+ Device device1 = devices.get("Droid", "Motorola");
assertEquals(null, device1.getTagId());
assertEquals("{}", device1.getBootProps().toString());
assertEquals("OMAP 3430", device1.getDefaultHardware().getCpu());
assertEquals("[armeabi, armeabi-v7a]", device1.getDefaultHardware().getSupportedAbis().toString());
- Device device2 = devices.get(2);
+ Device device2 = devices.get("Nexus 5", "Google");
assertEquals("tag-1", device2.getTagId());
assertEquals("{ro-myservice-port=1234, " +
"ro.RAM.Size=1024 MiB, " +
@@ -178,6 +180,15 @@
device2.getBootProps().toString());
assertEquals("Snapdragon 800 (MSM8974)", device2.getDefaultHardware().getCpu());
assertEquals("[armeabi, armeabi-v7a]", device2.getDefaultHardware().getSupportedAbis().toString());
+ assertEquals(device2.getChinSize(), 0);
+ assertFalse(device2.isScreenRound());
+
+ Device device3 = devices.get("wear_round_chin", "Google");
+ assertEquals("android-wear", device3.getTagId());
+ assertEquals(device3.getDefaultHardware().getScreen().getChin(), 30);
+ assertEquals(device3.getChinSize(), 30);
+ assertTrue(device3.isScreenRound());
+ assertEquals(device3.getDefaultHardware().getScreen().getScreenRound(), ScreenRound.ROUND);
} finally {
stream.close();
}
@@ -186,17 +197,17 @@
public void testValidDevicesFull_v2() throws Exception {
InputStream stream = DeviceSchemaTest.class.getResourceAsStream("devices_v2.xml");
try {
- List<Device> devices = DeviceParser.parse(stream);
+ Table<String, String, Device> devices = DeviceParser.parse(stream);
assertEquals("Parsing devices.xml produces the wrong number of devices",
- 3, devices.size());
+ 4, devices.size());
- Device device0 = devices.get(0);
+ Device device0 = devices.get("galaxy_64", "Gnusmas");
assertEquals(null, device0.getTagId());
assertEquals("{}", device0.getBootProps().toString());
assertEquals("arm64", device0.getDefaultHardware().getCpu());
assertEquals("[arm64-v8a]", device0.getDefaultHardware().getSupportedAbis().toString());
- Device device1 = devices.get(1);
+ Device device1 = devices.get("Droid X86", "Letni");
assertEquals("tag-1", device1.getTagId());
assertEquals("{ro-myservice-port=1234, " +
"ro.RAM.Size=1024 MiB, " +
@@ -205,7 +216,7 @@
assertEquals("Intel Atom 64", device1.getDefaultHardware().getCpu());
assertEquals("[x86_64]", device1.getDefaultHardware().getSupportedAbis().toString());
- Device device2 = devices.get(2);
+ Device device2 = devices.get("Mips 64", "Mips");
assertEquals("tag-2", device2.getTagId());
assertEquals("{ro-myservice-port=1234, " +
"ro.RAM.Size=1024 MiB, " +
@@ -213,6 +224,16 @@
device2.getBootProps().toString());
assertEquals("MIPS32+64", device2.getDefaultHardware().getCpu());
assertEquals("[mips, mips64]", device2.getDefaultHardware().getSupportedAbis().toString());
+
+ assertEquals(device2.getChinSize(), 0);
+ assertFalse(device2.isScreenRound());
+
+ Device device3 = devices.get("wear_round_chin", "Google");
+ assertEquals("android-wear", device3.getTagId());
+ assertEquals(device3.getDefaultHardware().getScreen().getChin(), 30);
+ assertEquals(device3.getChinSize(), 30);
+ assertTrue(device3.isScreenRound());
+ assertEquals(device3.getDefaultHardware().getScreen().getScreenRound(), ScreenRound.ROUND);
} finally {
stream.close();
}
@@ -223,27 +244,27 @@
replacements.put("api-level", "1-");
InputStream stream = DeviceSchemaTest.getReplacedStream(replacements);
try {
- List<Device> devices = DeviceParser.parse(stream);
+ Table<String, String, Device> devices = DeviceParser.parse(stream);
assertEquals(1, devices.size());
- Device device = devices.get(0);
+ Device device = devices.get("Galaxy Nexus", "Samsung");
assertTrue(device.getSoftware(1) != null);
assertTrue(device.getSoftware(2) != null);
assertTrue(device.getSoftware(0) == null);
replacements.put("api-level", "-2");
stream = DeviceSchemaTest.getReplacedStream(replacements);
- device = DeviceParser.parse(stream).get(0);
+ device = DeviceParser.parse(stream).get("Galaxy Nexus", "Samsung");
assertTrue(device.getSoftware(2) != null);
assertTrue(device.getSoftware(3) == null);
replacements.put("api-level", "1-2");
stream = DeviceSchemaTest.getReplacedStream(replacements);
- device = DeviceParser.parse(stream).get(0);
+ device = DeviceParser.parse(stream).get("Galaxy Nexus", "Samsung");
assertTrue(device.getSoftware(0) == null);
assertTrue(device.getSoftware(1) != null);
assertTrue(device.getSoftware(2) != null);
assertTrue(device.getSoftware(3) == null);
replacements.put("api-level", "-");
stream = DeviceSchemaTest.getReplacedStream(replacements);
- device = DeviceParser.parse(stream).get(0);
+ device = DeviceParser.parse(stream).get("Galaxy Nexus", "Samsung");
assertTrue(device.getSoftware(0) != null);
assertTrue(device.getSoftware(15) != null);
} finally {
@@ -256,9 +277,9 @@
replacements.put("networking", "NFD");
InputStream stream = DeviceSchemaTest.getReplacedStream(replacements);
try {
- List<Device> devices = DeviceParser.parse(stream);
+ Table<String, String, Device> devices = DeviceParser.parse(stream);
assertEquals(1, devices.size());
- assertEquals(0, devices.get(0).getDefaultHardware().getNetworking().size());
+ assertEquals(0, devices.get("Galaxy Nexus", "Samsung").getDefaultHardware().getNetworking().size());
fail();
} catch (SAXParseException e) {
assertTrue(e.getMessage().startsWith("cvc-enumeration-valid: Value 'NFD'"));
@@ -271,11 +292,11 @@
InputStream stream = DeviceSchemaTest.class.getResourceAsStream(
"devices_minimal.xml");
try {
- List<Device> devices = DeviceParser.parse(stream);
+ Table<String, String, Device> devices = DeviceParser.parse(stream);
assertEquals("Parsing devices_minimal.xml produces the wrong number of devices", 1,
devices.size());
- Device device = devices.get(0);
+ Device device = devices.get("Galaxy Nexus", "Samsung");
assertEquals("Galaxy Nexus", device.getDisplayName());
assertEquals(new Dimension(1280, 720), device.getScreenSize(ScreenOrientation.LANDSCAPE));
@@ -284,5 +305,4 @@
stream.close();
}
}
-
}
diff --git a/sdklib/src/test/java/com/android/sdklib/devices/DeviceWriterTest.java b/sdklib/src/test/java/com/android/sdklib/devices/DeviceWriterTest.java
index e0bcf66..285ef9a 100644
--- a/sdklib/src/test/java/com/android/sdklib/devices/DeviceWriterTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/devices/DeviceWriterTest.java
@@ -18,13 +18,13 @@
import com.android.dvlib.DeviceSchemaTest;
+import com.google.common.collect.Table;
import junit.framework.TestCase;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
-import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -33,41 +33,29 @@
public void testWriteIsValid_Minimal() throws Exception {
InputStream devicesFile =
DeviceSchemaTest.class.getResourceAsStream("devices_minimal.xml");
- List<Device> devices = DeviceParser.parse(devicesFile);
+ Table<String, String, Device> devices = DeviceParser.parse(devicesFile);
assertEquals("Parsed devices contained an un expected number of devices",
1, devices.size());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- DeviceWriter.writeToXml(baos, devices);
+ DeviceWriter.writeToXml(baos, devices.values());
String written = baos.toString();
- List<Device> writtenDevices = DeviceParser.parse(
+ Table<String, String, Device> writtenDevices = DeviceParser.parse(
new ByteArrayInputStream(written.getBytes()));
- assertEquals("Writing and reparsing returns a different number of devices",
- devices.size(), writtenDevices.size());
- for (int i = 0; i < devices.size(); i++) {
- assertEquals(
- "Device " + i + " differs in XML " + written,
- "\n" + devices.get(i), "\n" + writtenDevices.get(i));
- }
+ assertEquals(devices, writtenDevices);
}
public void testWriteIsValid_Full() throws Exception {
InputStream devicesFile =
DeviceSchemaTest.class.getResourceAsStream("devices.xml");
- List<Device> devices = DeviceParser.parse(devicesFile);
+ Table<String, String, Device> devices = DeviceParser.parse(devicesFile);
assertEquals("Parsed devices contained an unexpected number of devices",
- 3, devices.size());
+ 4, devices.size());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- DeviceWriter.writeToXml(baos, devices);
+ DeviceWriter.writeToXml(baos, devices.values());
String written = baos.toString();
- List<Device> writtenDevices = DeviceParser.parse(
- new ByteArrayInputStream(written.getBytes()));
- assertEquals("Writing and reparsing returns a different number of devices",
- devices.size(), writtenDevices.size());
- for (int i = 0; i < devices.size(); i++) {
- assertEquals(
- "Device " + i + " differs in XML " + written,
- "\n" + devices.get(i), "\n" + writtenDevices.get(i));
- }
+ Table<String, String, Device> writtenDevices = DeviceParser.parse(
+ new ByteArrayInputStream(written.getBytes()));
+ assertEquals(devices, writtenDevices);
}
public void testLocale() throws Exception {
@@ -76,11 +64,11 @@
Locale.setDefault(Locale.FRANCE);
InputStream devicesFile =
DeviceSchemaTest.class.getResourceAsStream("devices.xml");
- List<Device> devices = DeviceParser.parse(devicesFile);
+ Table<String, String, Device> devices = DeviceParser.parse(devicesFile);
assertEquals("Parsed devices contained an unexpected number of devices",
- 3, devices.size());
+ 4, devices.size());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- DeviceWriter.writeToXml(baos, devices);
+ DeviceWriter.writeToXml(baos, devices.values());
String xml = baos.toString();
assertTrue(xml.contains(".00"));
assertFalse(xml.contains(",00"));
@@ -93,78 +81,58 @@
Map<String, String> replacements = new HashMap<String, String>();
replacements.put("api-level", "1-");
InputStream stream = DeviceSchemaTest.getReplacedStream(replacements);
- List<Device> devices = DeviceParser.parse(stream);
+ Table<String, String, Device> devices = DeviceParser.parse(stream);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- DeviceWriter.writeToXml(baos, devices);
- List<Device> writtenDevices = DeviceParser.parse(
+ DeviceWriter.writeToXml(baos, devices.values());
+ Table<String, String, Device> writtenDevices = DeviceParser.parse(
new ByteArrayInputStream(baos.toString().getBytes()));
- assertEquals("Writing and reparsing returns a different number of devices",
- devices.size(), writtenDevices.size());
- for (int i = 0; i < devices.size(); i++) {
- assertEquals(devices.get(i), writtenDevices.get(i));
- }
+ assertEquals(devices, writtenDevices);
}
public void testApiUpperBound() throws Exception {
Map<String, String> replacements = new HashMap<String, String>();
replacements.put("api-level", "-10");
InputStream stream = DeviceSchemaTest.getReplacedStream(replacements);
- List<Device> devices = DeviceParser.parse(stream);
+ Table<String, String, Device> devices = DeviceParser.parse(stream);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- DeviceWriter.writeToXml(baos, devices);
- List<Device> writtenDevices = DeviceParser.parse(
+ DeviceWriter.writeToXml(baos, devices.values());
+ Table<String, String, Device> writtenDevices = DeviceParser.parse(
new ByteArrayInputStream(baos.toString().getBytes()));
- assertEquals("Writing and reparsing returns a different number of devices",
- devices.size(), writtenDevices.size());
- for (int i = 0; i < devices.size(); i++) {
- assertEquals(devices.get(i), writtenDevices.get(i));
- }
+ assertEquals(devices, writtenDevices);
}
public void testApiNeitherBound() throws Exception {
Map<String, String> replacements = new HashMap<String, String>();
replacements.put("api-level", "-");
InputStream stream = DeviceSchemaTest.getReplacedStream(replacements);
- List<Device> devices = DeviceParser.parse(stream);
+ Table<String, String, Device> devices = DeviceParser.parse(stream);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- DeviceWriter.writeToXml(baos, devices);
- List<Device> writtenDevices = DeviceParser.parse(
+ DeviceWriter.writeToXml(baos, devices.values());
+ Table<String, String, Device> writtenDevices = DeviceParser.parse(
new ByteArrayInputStream(baos.toString().getBytes()));
- assertEquals("Writing and reparsing returns a different number of devices",
- devices.size(), writtenDevices.size());
- for (int i = 0; i < devices.size(); i++) {
- assertEquals(devices.get(i), writtenDevices.get(i));
- }
+ assertEquals(devices, writtenDevices);
}
public void testApiBothBound() throws Exception {
Map<String, String> replacements = new HashMap<String, String>();
replacements.put("api-level", "9-10");
InputStream stream = DeviceSchemaTest.getReplacedStream(replacements);
- List<Device> devices = DeviceParser.parse(stream);
+ Table<String, String, Device> devices = DeviceParser.parse(stream);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- DeviceWriter.writeToXml(baos, devices);
- List<Device> writtenDevices = DeviceParser.parse(
+ DeviceWriter.writeToXml(baos, devices.values());
+ Table<String, String, Device> writtenDevices = DeviceParser.parse(
new ByteArrayInputStream(baos.toString().getBytes()));
- assertEquals("Writing and reparsing returns a different number of devices",
- devices.size(), writtenDevices.size());
- for (int i = 0; i < devices.size(); i++) {
- assertEquals(devices.get(i), writtenDevices.get(i));
- }
+ assertEquals(devices, writtenDevices);
}
public void testApiSingle() throws Exception {
Map<String, String> replacements = new HashMap<String, String>();
replacements.put("api-level", "10");
InputStream stream = DeviceSchemaTest.getReplacedStream(replacements);
- List<Device> devices = DeviceParser.parse(stream);
+ Table<String, String, Device> devices = DeviceParser.parse(stream);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- DeviceWriter.writeToXml(baos, devices);
- List<Device> writtenDevices = DeviceParser.parse(
+ DeviceWriter.writeToXml(baos, devices.values());
+ Table<String, String, Device> writtenDevices = DeviceParser.parse(
new ByteArrayInputStream(baos.toString().getBytes()));
- assertEquals("Writing and reparsing returns a different number of devices",
- devices.size(), writtenDevices.size());
- for (int i = 0; i < devices.size(); i++) {
- assertEquals(devices.get(i), writtenDevices.get(i));
- }
+ assertEquals(devices, writtenDevices);
}
}
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockAddonTarget.java b/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockAddonTarget.java
index 0184d48..d10bb4f 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockAddonTarget.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockAddonTarget.java
@@ -27,6 +27,7 @@
import com.android.sdklib.SystemImage;
import com.android.sdklib.io.FileOp;
import com.android.sdklib.repository.descriptors.IdDisplay;
+import com.google.common.collect.ImmutableList;
import java.io.File;
import java.util.List;
@@ -42,7 +43,7 @@
private final int mRevision;
private final String mName;
private ISystemImage[] mSystemImages;
- private IOptionalLibrary[] mOptionalLibraries;
+ private ImmutableList<OptionalLibrary> mOptionalLibraries = ImmutableList.of();
public MockAddonTarget(String name, IAndroidTarget parentTarget, int revision) {
mName = name;
@@ -103,12 +104,19 @@
return "/sdk/add-ons/addon-" + mName;
}
+ @NonNull
@Override
- public IOptionalLibrary[] getOptionalLibraries() {
+ public List<OptionalLibrary> getAdditionalLibraries() {
return mOptionalLibraries;
}
- public void setOptionalLibraries(IOptionalLibrary[] libraries) {
+ @NonNull
+ @Override
+ public List<OptionalLibrary> getOptionalLibraries() {
+ return ImmutableList.of();
+ }
+
+ public void setOptionalLibraries(ImmutableList<OptionalLibrary> libraries) {
mOptionalLibraries = libraries;
}
@@ -167,6 +175,7 @@
return mRevision;
}
+ @NonNull
@Override
public File[] getSkins() {
return FileOp.EMPTY_FILE_ARRAY;
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockPlatformTarget.java b/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockPlatformTarget.java
index b3319d0..37cd184 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockPlatformTarget.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/androidTarget/MockPlatformTarget.java
@@ -27,6 +27,7 @@
import com.android.sdklib.SystemImage;
import com.android.sdklib.io.FileOp;
import com.android.sdklib.repository.descriptors.IdDisplay;
+import com.google.common.collect.ImmutableList;
import java.io.File;
import java.util.List;
@@ -100,9 +101,16 @@
return "/sdk/platforms/android-" + getVersion().getApiString();
}
+ @NonNull
@Override
- public IOptionalLibrary[] getOptionalLibraries() {
- return null;
+ public List<OptionalLibrary> getOptionalLibraries() {
+ return ImmutableList.of();
+ }
+
+ @NonNull
+ @Override
+ public List<OptionalLibrary> getAdditionalLibraries() {
+ return ImmutableList.of();
}
@Override
@@ -160,6 +168,7 @@
return mRevision;
}
+ @NonNull
@Override
public File[] getSkins() {
return FileOp.EMPTY_FILE_ARRAY;
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/LocalSdkParserTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/LocalSdkParserTest.java
index 7e95e78..672d69d 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/LocalSdkParserTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/LocalSdkParserTest.java
@@ -114,12 +114,12 @@
LocationType.IN_IMAGES_SUBFOLDER,
SystemImage.DEFAULT_TAG,
SdkConstants.ABI_ARMEABI_V7A,
- FileOp.EMPTY_FILE_ARRAY));
+ FileOp.EMPTY_FILE_ARRAY), null);
makeSystemImageFolder(new SystemImage(mSdkMan, t,
LocationType.IN_IMAGES_SUBFOLDER,
SystemImage.DEFAULT_TAG,
SdkConstants.ABI_INTEL_ATOM,
- FileOp.EMPTY_FILE_ARRAY));
+ FileOp.EMPTY_FILE_ARRAY), null);
mSdkMan.reloadSdk(getLog());
t = mSdkMan.getTargets()[0];
@@ -142,12 +142,12 @@
LocationType.IN_SYSTEM_IMAGE,
SystemImage.DEFAULT_TAG,
SdkConstants.ABI_ARMEABI,
- FileOp.EMPTY_FILE_ARRAY));
+ FileOp.EMPTY_FILE_ARRAY), null);
makeSystemImageFolder(new SystemImage(mSdkMan, t,
LocationType.IN_SYSTEM_IMAGE,
SystemImage.DEFAULT_TAG,
SdkConstants.ABI_ARMEABI_V7A,
- FileOp.EMPTY_FILE_ARRAY));
+ FileOp.EMPTY_FILE_ARRAY), null);
mSdkMan.reloadSdk(getLog());
@@ -171,7 +171,7 @@
LocationType.IN_SYSTEM_IMAGE,
SystemImage.DEFAULT_TAG,
SdkConstants.ABI_INTEL_ATOM,
- FileOp.EMPTY_FILE_ARRAY));
+ FileOp.EMPTY_FILE_ARRAY), null);
assertEquals(
"[Android SDK Tools, revision 1.0.1, " +
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/MockDownloadCache.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/MockDownloadCache.java
index 78e5b85..eb832f2 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/MockDownloadCache.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/MockDownloadCache.java
@@ -18,9 +18,6 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
-import com.android.sdklib.internal.repository.CanceledByUserException;
-import com.android.sdklib.internal.repository.DownloadCache;
-import com.android.sdklib.internal.repository.ITaskMonitor;
import com.android.utils.Pair;
import org.apache.http.Header;
@@ -57,7 +54,7 @@
private Strategy mOverrideStrategy;
- public final static int THROW_FNF = -1;
+ public static final int THROW_FNF = -1;
/**
* Creates a download cache with a {@code DIRECT} strategy and
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/AddonSystemImagePackageTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/AddonSystemImagePackageTest.java
index 9074769..134dc7d 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/AddonSystemImagePackageTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/AddonSystemImagePackageTest.java
@@ -17,7 +17,6 @@
package com.android.sdklib.internal.repository.packages;
import com.android.sdklib.AndroidVersion;
-import com.android.sdklib.SystemImage;
import com.android.sdklib.AndroidVersion.AndroidVersionException;
import com.android.sdklib.internal.repository.archives.Archive;
import com.android.sdklib.repository.PkgProps;
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BrokenPackageTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BrokenPackageTest.java
index 05e0198..052292f 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BrokenPackageTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BrokenPackageTest.java
@@ -16,7 +16,6 @@
package com.android.sdklib.internal.repository.packages;
-import com.android.sdklib.internal.repository.packages.BrokenPackage;
import com.android.sdklib.repository.FullRevision;
import com.android.sdklib.repository.descriptors.PkgDesc;
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BuildToolPackageTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BuildToolPackageTest.java
index 998c578..b3233ea 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BuildToolPackageTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/BuildToolPackageTest.java
@@ -60,7 +60,7 @@
public void testInstallId() throws Exception {
Properties props1 = createExpectedProps(true);
BuildToolPackage p1 = new BuildToolPackageFakeArchive(props1);
- assertEquals("build-tools-1.2.3_rc4", p1.installId());
+ assertEquals("build-tools-1.2.3-preview", p1.installId());
Properties props2 = createExpectedProps(false);
BuildToolPackage p2 = new BuildToolPackageFakeArchive(props2);
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockBrokenPackage.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockBrokenPackage.java
index 51f9e35..37173b6 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockBrokenPackage.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockBrokenPackage.java
@@ -16,7 +16,6 @@
package com.android.sdklib.internal.repository.packages;
-import com.android.sdklib.internal.repository.packages.BrokenPackage;
import com.android.sdklib.repository.FullRevision;
import com.android.sdklib.repository.descriptors.PkgDesc;
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSourcePackage.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSourcePackage.java
index d5785ce..115b1a1 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSourcePackage.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSourcePackage.java
@@ -17,8 +17,6 @@
package com.android.sdklib.internal.repository.packages;
import com.android.sdklib.AndroidVersion;
-import com.android.sdklib.internal.repository.packages.SourcePackage;
-import com.android.sdklib.internal.repository.packages.SystemImagePackage;
import com.android.sdklib.internal.repository.sources.SdkSource;
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSystemImagePackage.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSystemImagePackage.java
index 3c7baec..c09c081 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSystemImagePackage.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/MockSystemImagePackage.java
@@ -16,7 +16,6 @@
package com.android.sdklib.internal.repository.packages;
-import com.android.sdklib.internal.repository.packages.SystemImagePackage;
import com.android.sdklib.internal.repository.sources.SdkSource;
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/PreciseRevisionTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/PreciseRevisionTest.java
index 5c4b5a1..b07d422 100644
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/PreciseRevisionTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/packages/PreciseRevisionTest.java
@@ -18,7 +18,7 @@
import com.android.sdklib.repository.PreciseRevision;
-import junit.framework.Assert;
+import org.junit.Assert;
import junit.framework.TestCase;
import java.util.Arrays;
@@ -37,7 +37,7 @@
assertEquals(PreciseRevision.IMPLICIT_MINOR_REV, p.getMinor());
assertEquals(PreciseRevision.IMPLICIT_MICRO_REV, p.getMicro());
assertEquals(PreciseRevision.NOT_A_PREVIEW, p.getPreview());
- assertFalse (p.isPreview());
+ assertFalse(p.isPreview());
assertEquals("5", p.toShortString());
assertEquals(p, PreciseRevision.parseRevision("5"));
assertEquals("5", p.toString());
@@ -106,6 +106,8 @@
assertFalse(p.isPreview());
assertEquals("10.11.12", p.toShortString());
assertEquals("10.11.12", p.toString());
+ assertEquals("[10, 11, 12]", Arrays.toString(p.toIntArray(false /*includePreview*/)));
+ assertEquals("[10, 11, 12, 0]", Arrays.toString(p.toIntArray(true /*includePreview*/)));
p = new PreciseRevision(10, 11, 12, 13);
assertEquals(10, p.getMajor());
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkAddonSourceTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkAddonSourceTest.java
index f03aed9..d05c6ee 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkAddonSourceTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkAddonSourceTest.java
@@ -24,7 +24,6 @@
import com.android.sdklib.internal.repository.packages.ExtraPackage;
import com.android.sdklib.internal.repository.packages.IMinToolsDependency;
import com.android.sdklib.internal.repository.packages.Package;
-import com.android.sdklib.internal.repository.sources.SdkAddonSource;
import com.android.sdklib.repository.SdkAddonConstants;
import com.android.utils.Pair;
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkRepoSourceTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkRepoSourceTest.java
index 596e2d5..b8190db 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkRepoSourceTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkRepoSourceTest.java
@@ -29,7 +29,6 @@
import com.android.sdklib.internal.repository.packages.SourcePackage;
import com.android.sdklib.internal.repository.packages.SystemImagePackage;
import com.android.sdklib.internal.repository.packages.ToolPackage;
-import com.android.sdklib.internal.repository.sources.SdkRepoSource;
import com.android.sdklib.repository.SdkRepoConstants;
import com.android.utils.Pair;
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkSourcePropertiesTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkSourcePropertiesTest.java
index 6313e69..1336bfc 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkSourcePropertiesTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/sources/SdkSourcePropertiesTest.java
@@ -17,8 +17,6 @@
package com.android.sdklib.internal.repository.sources;
-import com.android.sdklib.internal.repository.sources.SdkSourceProperties;
-
import junit.framework.TestCase;
public class SdkSourcePropertiesTest extends TestCase {
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/SettingsControllerTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/SettingsControllerTest.java
index adfb0e9..63b774a 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/SettingsControllerTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/SettingsControllerTest.java
@@ -51,7 +51,7 @@
m.loadSettings();
Settings s = m.getSettings();
assertFalse(s.getAskBeforeAdbRestart());
- assertFalse(s.getEnablePreviews());
+ assertTrue(s.getEnablePreviews());
assertFalse(s.getForceHttp());
assertTrue (s.getShowUpdateOnly());
assertTrue (s.getUseDownloadCache());
@@ -63,14 +63,14 @@
Settings s1 = m.getSettings();
assertFalse(s1.getAskBeforeAdbRestart());
- assertFalse(s1.getEnablePreviews());
+ assertTrue (s1.getEnablePreviews());
assertFalse(s1.getForceHttp());
assertTrue (s1.getShowUpdateOnly());
assertTrue (s1.getUseDownloadCache());
assertEquals(-1, s1.getMonitorDensity());
m.setSetting(ISettingsPage.KEY_ASK_ADB_RESTART, true);
- m.setSetting(ISettingsPage.KEY_ENABLE_PREVIEWS, true);
+ m.setSetting(ISettingsPage.KEY_ENABLE_PREVIEWS, false);
m.setSetting(ISettingsPage.KEY_FORCE_HTTP, true);
m.setShowUpdateOnly(false);
m.setSetting(ISettingsPage.KEY_USE_DOWNLOAD_CACHE, false);
@@ -79,7 +79,7 @@
Settings s2 = m.getSettings();
assertSame(s2, s1);
assertTrue (s2.getAskBeforeAdbRestart());
- assertTrue (s2.getEnablePreviews());
+ assertFalse(s2.getEnablePreviews());
assertTrue (s2.getForceHttp());
assertFalse(s2.getShowUpdateOnly());
assertFalse(s2.getUseDownloadCache());
@@ -94,7 +94,7 @@
Settings s3 = m3.getSettings();
assertNotSame(s3, s1);
assertTrue (s3.getAskBeforeAdbRestart());
- assertTrue (s3.getEnablePreviews());
+ assertFalse(s3.getEnablePreviews());
assertTrue (s3.getForceHttp());
assertFalse(s3.getShowUpdateOnly());
assertFalse(s3.getUseDownloadCache());
diff --git a/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/UpdaterDataTest.java b/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/UpdaterDataTest.java
index 4a2bf9b..9dfe12e 100755
--- a/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/UpdaterDataTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/internal/repository/updater/UpdaterDataTest.java
@@ -100,7 +100,7 @@
public void clear() {
mLog.clear();
}
- };
+ }
public final void testAcceptLicenses_Empty() {
SdkManager sdkman = getSdkManager();
diff --git a/sdklib/src/test/java/com/android/sdklib/repository/CaptureErrorHandler.java b/sdklib/src/test/java/com/android/sdklib/repository/CaptureErrorHandler.java
index dccec59..0cc7a48 100755
--- a/sdklib/src/test/java/com/android/sdklib/repository/CaptureErrorHandler.java
+++ b/sdklib/src/test/java/com/android/sdklib/repository/CaptureErrorHandler.java
@@ -16,6 +16,8 @@
package com.android.sdklib.repository;
+import static org.junit.Assert.fail;
+
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
@@ -43,13 +45,13 @@
* Also fails the unit test if any error was generated.
*/
public void verify() {
- if (mWarnings.length() > 0) {
+ if (!mWarnings.isEmpty()) {
System.err.println(mWarnings);
}
- if (mErrors.length() > 0) {
+ if (!mErrors.isEmpty()) {
System.err.println(mErrors);
- junit.framework.Assert.fail(mErrors);
+ fail(mErrors);
}
}
diff --git a/sdklib/src/test/java/com/android/sdklib/repository/descriptors/PkgDescTest.java b/sdklib/src/test/java/com/android/sdklib/repository/descriptors/PkgDescTest.java
index 42557c3..e01b81a 100755
--- a/sdklib/src/test/java/com/android/sdklib/repository/descriptors/PkgDescTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/repository/descriptors/PkgDescTest.java
@@ -139,6 +139,11 @@
PkgDesc.Builder.newTool(new FullRevision(1, 2, 3, 1), min5671).create();
assertFalse(p1231.isUpdateFor(f122));
assertFalse(f122 .isUpdateFor(p1231));
+ assertFalse(p1231.isUpdateFor(f122, FullRevision.PreviewComparison.COMPARE_NUMBER));
+ assertFalse(p1231.isUpdateFor(f122, FullRevision.PreviewComparison.COMPARE_TYPE));
+ // ...unless we ignore them explicitly
+ assertTrue(p1231.isUpdateFor(f122, FullRevision.PreviewComparison.IGNORE));
+
// but previews are used for comparisons
assertTrue (p1231.compareTo(f122 ) > 0);
assertTrue (f123 .compareTo(p1231) > 0);
@@ -283,12 +288,12 @@
assertFalse(p.hasMinPlatformToolsRev());
assertNull(p.getMinPlatformToolsRev());
- assertEquals("doc-19", p.getInstallId());
+ assertEquals("doc", p.getInstallId());
assertEquals(FileOp.append(mRoot, "docs"),
p.getCanonicalInstallFolder(mRoot));
assertEquals("<PkgDesc Type=doc Android=API 19 MajorRev=1>", p.toString());
- assertEquals("Documentation for Android SDK 19", p.getListDescription());
+ assertEquals("Documentation for Android SDK", p.getListDescription());
}
public final void testPkgDescDoc_Update() throws Exception {
@@ -371,8 +376,8 @@
assertFalse(p.hasMinPlatformToolsRev());
assertNull (p.getMinPlatformToolsRev());
- assertEquals("build-tools-1.2.3_rc4", p.getInstallId());
- assertEquals(FileOp.append(mRoot, "build-tools", "build-tools-1.2.3_rc4"),
+ assertEquals("build-tools-1.2.3-preview", p.getInstallId());
+ assertEquals(FileOp.append(mRoot, "build-tools", "build-tools-1.2.3-preview"),
p.getCanonicalInstallFolder(mRoot));
assertEquals("<PkgDesc Type=build_tools FullRev=1.2.3 rc4>", p.toString());
diff --git a/sdklib/src/test/java/com/android/sdklib/repository/local/LocalPlatformPkgInfoTest.java b/sdklib/src/test/java/com/android/sdklib/repository/local/LocalPlatformPkgInfoTest.java
new file mode 100644
index 0000000..c161e40
--- /dev/null
+++ b/sdklib/src/test/java/com/android/sdklib/repository/local/LocalPlatformPkgInfoTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdklib.repository.local;
+
+import static org.junit.Assert.*;
+
+import com.android.annotations.NonNull;
+import com.android.sdklib.IAndroidTarget;
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+public class LocalPlatformPkgInfoTest {
+
+ @Test
+ public void testOptionalLibWith1() throws Exception {
+ String content =
+ "[\n" +
+ " {\n" +
+ " \"name\": \"org.apache.http.legacy\",\n" +
+ " \"jar\": \"org.apache.http.legacy.jar\",\n" +
+ " \"manifest\": false\n" +
+ " }\n" +
+ "]\n";
+
+ File json = getJsonFile(content);
+
+ List<IAndroidTarget.OptionalLibrary> libs = LocalPlatformPkgInfo.getLibsFromJson(json);
+
+ assertEquals(1, libs.size());
+ IAndroidTarget.OptionalLibrary lib = libs.get(0);
+ assertEquals("org.apache.http.legacy", lib.getName());
+ assertEquals(new File(json.getParentFile(), "org.apache.http.legacy.jar"), lib.getJar());
+ assertEquals(false, lib.isManifestEntryRequired());
+ }
+
+ @NonNull
+ private static File getJsonFile(String content) throws IOException {
+ File json = File.createTempFile("testGetLibsFromJson", "");
+ json.deleteOnExit();
+
+ Files.write(content, json, Charsets.UTF_8);
+ return json;
+ }
+
+
+}
\ No newline at end of file
diff --git a/sdklib/src/test/java/com/android/sdklib/repository/local/LocalSdkTest.java b/sdklib/src/test/java/com/android/sdklib/repository/local/LocalSdkTest.java
index b2edf28..15c6b37 100755
--- a/sdklib/src/test/java/com/android/sdklib/repository/local/LocalSdkTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/repository/local/LocalSdkTest.java
@@ -147,7 +147,7 @@
assertEquals(null, pi.getLoadError());
assertEquals(new MajorRevision(2), pi.getDesc().getMajorRevision());
assertEquals("<LocalDocPkgInfo <PkgDesc Type=doc Android=API 18 MajorRev=2>>", pi.toString());
- assertEquals("Documentation for Android SDK 18, rev 2", pi.getListDescription());
+ assertEquals("Documentation for Android SDK", pi.getListDescription());
assertSame(pi, mLS.getPkgInfo(pi.getDesc()));
}
diff --git a/sdklib/src/test/java/com/android/sdklib/util/BSPatchTest.java b/sdklib/src/test/java/com/android/sdklib/util/BSPatchTest.java
index 6c6c405..3fd1fa5 100755
--- a/sdklib/src/test/java/com/android/sdklib/util/BSPatchTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/util/BSPatchTest.java
@@ -413,7 +413,7 @@
throw new PatchException("Failed to read control data") ;
}
ctrl[i] = offtin(buf, 0);
- };
+ }
/* Sanity-check */
if (newpos + ctrl[0] > newsize) {
diff --git a/sdklib/src/test/java/com/android/sdklib/util/LineUtilTest.java b/sdklib/src/test/java/com/android/sdklib/util/LineUtilTest.java
index 213efc2..414c74d 100755
--- a/sdklib/src/test/java/com/android/sdklib/util/LineUtilTest.java
+++ b/sdklib/src/test/java/com/android/sdklib/util/LineUtilTest.java
@@ -16,8 +16,6 @@
package com.android.sdklib.util;
-import com.android.sdklib.util.LineUtil;
-
import junit.framework.TestCase;
diff --git a/templates/activities/AlwaysOnWearActivity/globals.xml.ftl b/templates/activities/AlwaysOnWearActivity/globals.xml.ftl
new file mode 100644
index 0000000..4bf836f
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/globals.xml.ftl
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<globals>
+ <global id="manifestOut" value="${manifestDir}" />
+ <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+ <global id="resOut" value="${resDir}" />
+ <global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
+</globals>
diff --git a/templates/activities/AlwaysOnWearActivity/recipe.xml.ftl b/templates/activities/AlwaysOnWearActivity/recipe.xml.ftl
new file mode 100644
index 0000000..278df15
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/recipe.xml.ftl
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<recipe>
+
+ <merge from="AndroidManifest.xml.ftl"
+ to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
+
+ <merge from="res/values/strings.xml.ftl"
+ to="${escapeXmlAttribute(resOut)}/values/strings.xml" />
+
+ <merge from="build.gradle.ftl"
+ to="${escapeXmlAttribute(projectOut)}/build.gradle" />
+
+ <instantiate from="res/layout/blank_activity.xml.ftl"
+ to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
+
+ <instantiate from="src/app_package/BlankActivity.java.ftl"
+ to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
+
+ <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
+ <open file="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
+</recipe>
diff --git a/templates/activities/AlwaysOnWearActivity/root/AndroidManifest.xml.ftl b/templates/activities/AlwaysOnWearActivity/root/AndroidManifest.xml.ftl
new file mode 100644
index 0000000..a0e3c80
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/root/AndroidManifest.xml.ftl
@@ -0,0 +1,24 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+ <application>
+ <uses-library android:name="com.google.android.wearable" android:required="false" />
+ <activity android:name="${relativePackage}.${activityClass}"
+ <#if isNewProject>
+ android:label="@string/app_name"
+ <#else>
+ android:label="@string/title_${activityToLayout(activityClass)}"
+ </#if>
+ android:theme="@android:style/Theme.DeviceDefault.Light"
+ >
+ <#if isLauncher>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </#if>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/templates/activities/AlwaysOnWearActivity/root/build.gradle.ftl b/templates/activities/AlwaysOnWearActivity/root/build.gradle.ftl
new file mode 100644
index 0000000..d62125c
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/root/build.gradle.ftl
@@ -0,0 +1,3 @@
+dependencies {
+ provided 'com.google.android.wearable:wearable:1.0.0'
+}
diff --git a/templates/activities/AlwaysOnWearActivity/root/res/layout/blank_activity.xml.ftl b/templates/activities/AlwaysOnWearActivity/root/res/layout/blank_activity.xml.ftl
new file mode 100644
index 0000000..7ae5edf
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/root/res/layout/blank_activity.xml.ftl
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.wearable.view.BoxInsetLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/container"
+ tools:context="${relativePackage}.${activityClass}"
+ tools:deviceIds="wear">
+
+ <TextView
+ android:id="@+id/text"
+ app:layout_box="all"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@string/hello_world" />
+
+ <TextView
+ android:id="@+id/clock"
+ app:layout_box="all"
+ android:layout_gravity="bottom|start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/white" />
+
+</android.support.wearable.view.BoxInsetLayout>
diff --git a/templates/activities/AlwaysOnWearActivity/root/res/values/strings.xml.ftl b/templates/activities/AlwaysOnWearActivity/root/res/values/strings.xml.ftl
new file mode 100644
index 0000000..304117e
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/root/res/values/strings.xml.ftl
@@ -0,0 +1,6 @@
+<resources>
+ <#if !isNewProject>
+ <string name="title_${activityToLayout(activityClass)}">${escapeXmlString(activityClass)}</string>
+ </#if>
+ <string name="hello_world">Hello World!</string>
+</resources>
diff --git a/templates/activities/AlwaysOnWearActivity/root/src/app_package/BlankActivity.java.ftl b/templates/activities/AlwaysOnWearActivity/root/src/app_package/BlankActivity.java.ftl
new file mode 100644
index 0000000..7e6673c
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/root/src/app_package/BlankActivity.java.ftl
@@ -0,0 +1,64 @@
+package ${packageName};
+
+import android.os.Bundle;
+import android.support.wearable.activity.WearableActivity;
+import android.support.wearable.view.BoxInsetLayout;
+import android.view.View;
+import android.widget.TextView;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+public class ${activityClass} extends WearableActivity {
+
+ private static final SimpleDateFormat AMBIENT_DATE_FORMAT =
+ new SimpleDateFormat("HH:mm", Locale.US);
+
+ private BoxInsetLayout mContainerView;
+ private TextView mTextView;
+ private TextView mClockView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.${layoutName});
+ setAmbientEnabled();
+
+ mContainerView = (BoxInsetLayout) findViewById(R.id.container);
+ mTextView = (TextView) findViewById(R.id.text);
+ mClockView = (TextView) findViewById(R.id.clock);
+ }
+
+ @Override
+ public void onEnterAmbient(Bundle ambientDetails) {
+ super.onEnterAmbient(ambientDetails);
+ updateDisplay();
+ }
+
+ @Override
+ public void onUpdateAmbient() {
+ super.onUpdateAmbient();
+ updateDisplay();
+ }
+
+ @Override
+ public void onExitAmbient() {
+ updateDisplay();
+ super.onExitAmbient();
+ }
+
+ private void updateDisplay() {
+ if (isAmbient()) {
+ mContainerView.setBackgroundColor(getResources().getColor(android.R.color.black));
+ mTextView.setTextColor(getResources().getColor(android.R.color.white));
+ mClockView.setVisibility(View.VISIBLE);
+
+ mClockView.setText(AMBIENT_DATE_FORMAT.format(new Date()));
+ } else {
+ mContainerView.setBackground(null);
+ mTextView.setTextColor(getResources().getColor(android.R.color.black));
+ mClockView.setVisibility(View.GONE);
+ }
+ }
+}
diff --git a/templates/activities/AlwaysOnWearActivity/template.xml b/templates/activities/AlwaysOnWearActivity/template.xml
new file mode 100644
index 0000000..d3aeeb1
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/template.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<template
+ format="4"
+ revision="1"
+ name="Always On Wear Activity"
+ minApi="20"
+ minBuildApi="20"
+ description="Creates an always on activity for Android Wear">
+
+ <category value="Activity" />
+ <formfactor value="Wear" />
+
+ <parameter
+ id="activityClass"
+ name="Activity Name"
+ type="string"
+ constraints="class|unique|nonempty"
+ suggest="${layoutToActivity(layoutName)}"
+ default="MainActivity"
+ help="The name of the activity class to create" />
+
+ <parameter
+ id="layoutName"
+ name="Layout Name"
+ type="string"
+ constraints="layout|unique|nonempty"
+ suggest="${activityToLayout(activityClass)}"
+ default="activity_main"
+ help="The name of the layout to create for the activity" />
+
+ <parameter
+ id="isLauncher"
+ name="Launcher Activity"
+ type="boolean"
+ default="true"
+ help="If true, this activity will have a CATEGORY_LAUNCHER intent filter, making it the default launchable activity" />
+
+ <parameter
+ id="packageName"
+ name="Package name"
+ type="string"
+ constraints="package"
+ default="com.mycompany.myapp" />
+
+ <!-- 128x128 thumbnails relative to template.xml -->
+ <thumbs>
+ <!-- default thumbnail is required -->
+ <thumb>template_thumb.png</thumb>
+ </thumbs>
+
+ <globals file="globals.xml.ftl" />
+ <execute file="recipe.xml.ftl" />
+
+</template>
diff --git a/templates/activities/AlwaysOnWearActivity/template_thumb.png b/templates/activities/AlwaysOnWearActivity/template_thumb.png
new file mode 100644
index 0000000..42ebcf2
--- /dev/null
+++ b/templates/activities/AlwaysOnWearActivity/template_thumb.png
Binary files differ
diff --git a/templates/activities/AndroidTVActivity/root/src/app_package/MainFragment.java.ftl b/templates/activities/AndroidTVActivity/root/src/app_package/MainFragment.java.ftl
index 3b1d90a..3cae7ae 100644
--- a/templates/activities/AndroidTVActivity/root/src/app_package/MainFragment.java.ftl
+++ b/templates/activities/AndroidTVActivity/root/src/app_package/MainFragment.java.ftl
@@ -106,11 +106,19 @@
for (int j = 0; j < NUM_COLS; j++) {
listRowAdapter.add(list.get(j % 5));
}
- HeaderItem header = new HeaderItem(i, MovieList.MOVIE_CATEGORY[i], null);
+ <#if buildApi gte 22>
+ HeaderItem header = new HeaderItem(i, MovieList.MOVIE_CATEGORY[i]);
+ <#else>
+ HeaderItem header = new HeaderItem(i, MovieList.MOVIE_CATEGORY[i], null);
+ </#if>
mRowsAdapter.add(new ListRow(header, listRowAdapter));
}
- HeaderItem gridHeader = new HeaderItem(i, "PREFERENCES", null);
+ <#if buildApi gte 22>
+ HeaderItem gridHeader = new HeaderItem(i, "PREFERENCES");
+ <#else>
+ HeaderItem gridHeader = new HeaderItem(i, "PREFERENCES", null);
+ </#if>
GridItemPresenter mGridPresenter = new GridItemPresenter();
ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(mGridPresenter);
diff --git a/templates/activities/AndroidTVActivity/root/src/app_package/PlaybackOverlayFragment.java.ftl b/templates/activities/AndroidTVActivity/root/src/app_package/PlaybackOverlayFragment.java.ftl
index eb7adf1..31ef35c 100644
--- a/templates/activities/AndroidTVActivity/root/src/app_package/PlaybackOverlayFragment.java.ftl
+++ b/templates/activities/AndroidTVActivity/root/src/app_package/PlaybackOverlayFragment.java.ftl
@@ -313,7 +313,11 @@
for (Movie movie : mItems) {
listRowAdapter.add(movie);
}
- HeaderItem header = new HeaderItem(0, getString(R.string.related_movies), null);
+ <#if buildApi gte 22>
+ HeaderItem header = new HeaderItem(0, getString(R.string.related_movies));
+ <#else>
+ HeaderItem header = new HeaderItem(0, getString(R.string.related_movies), null);
+ </#if>
mRowsAdapter.add(new ListRow(header, listRowAdapter));
}
diff --git a/templates/activities/AndroidTVActivity/root/src/app_package/VideoDetailsFragment.java.ftl b/templates/activities/AndroidTVActivity/root/src/app_package/VideoDetailsFragment.java.ftl
index 0f90a45..1d3d2a7 100644
--- a/templates/activities/AndroidTVActivity/root/src/app_package/VideoDetailsFragment.java.ftl
+++ b/templates/activities/AndroidTVActivity/root/src/app_package/VideoDetailsFragment.java.ftl
@@ -154,7 +154,12 @@
listRowAdapter.add(list.get(j % 5));
}
- HeaderItem header = new HeaderItem(0, subcategories[0], null);
+ <#if buildApi gte 22>
+ HeaderItem header = new HeaderItem(0, subcategories[0]);
+ <#else>
+ HeaderItem header = new HeaderItem(0, subcategories[0], null);
+ </#if>
+
adapter.add(new ListRow(header, listRowAdapter));
setAdapter(adapter);
diff --git a/templates/activities/BlankActivity/globals.xml.ftl b/templates/activities/BlankActivity/globals.xml.ftl
index 5f60e5c..09e5c5e 100644
--- a/templates/activities/BlankActivity/globals.xml.ftl
+++ b/templates/activities/BlankActivity/globals.xml.ftl
@@ -1,7 +1,15 @@
<?xml version="1.0"?>
<globals>
<global id="manifestOut" value="${manifestDir}" />
- <global id="appCompat" type="boolean" value="${(hasDependency('com.android.support:appcompat-v7'))?string}" />
+<#if hasDependency('com.android.support:appcompat-v7')>
+ <global id="appCompat" type="boolean" value="true" />
+ <global id="superClass" type="string" value="<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+ <global id="superClassFqcn" type="string" value="android.support.v7.app.<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+<#else>
+ <global id="appCompat" type="boolean" value="false" />
+ <global id="superClass" type="string" value="Activity"/>
+ <global id="superClassFqcn" type="string" value="android.app.Activity"/>
+</#if>
<global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
<global id="resOut" value="${resDir}" />
<global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
diff --git a/templates/activities/BlankActivity/root/src/app_package/SimpleActivity.java.ftl b/templates/activities/BlankActivity/root/src/app_package/SimpleActivity.java.ftl
index 1b8d938..d6aeee3 100644
--- a/templates/activities/BlankActivity/root/src/app_package/SimpleActivity.java.ftl
+++ b/templates/activities/BlankActivity/root/src/app_package/SimpleActivity.java.ftl
@@ -1,12 +1,14 @@
package ${packageName};
-import <#if appCompat>android.support.v7.app.ActionBarActivity<#else>android.app.Activity</#if>;
+import ${superClassFqcn};
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
-public class ${activityClass} extends ${(appCompat)?string('ActionBar','')}Activity {
+public class ${activityClass} extends ${superClass} {
@Override
protected void onCreate(Bundle savedInstanceState) {
diff --git a/templates/activities/BlankActivityWithFragment/globals.xml.ftl b/templates/activities/BlankActivityWithFragment/globals.xml.ftl
index 7678b9b..06453f7 100644
--- a/templates/activities/BlankActivityWithFragment/globals.xml.ftl
+++ b/templates/activities/BlankActivityWithFragment/globals.xml.ftl
@@ -2,7 +2,15 @@
<globals>
<global id="fragmentClass" value="${activityClass}Fragment" />
<global id="manifestOut" value="${manifestDir}" />
- <global id="appCompat" type="boolean" value="${(hasDependency('com.android.support:appcompat-v7'))?string}" />
+<#if hasDependency('com.android.support:appcompat-v7')>
+ <global id="appCompat" type="boolean" value="true" />
+ <global id="superClass" type="string" value="<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+ <global id="superClassFqcn" type="string" value="android.support.v7.app.<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+<#else>
+ <global id="appCompat" type="boolean" value="false" />
+ <global id="superClass" type="string" value="Activity"/>
+ <global id="superClassFqcn" type="string" value="android.app.Activity"/>
+</#if>
<global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
<global id="resOut" value="${resDir}" />
<global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
diff --git a/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivity.java.ftl b/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivity.java.ftl
index e18ad91..ec2c685 100644
--- a/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivity.java.ftl
+++ b/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivity.java.ftl
@@ -1,13 +1,14 @@
package ${packageName};
-import <#if appCompat>android.support.v7.app.ActionBarActivity<#else>android.app.Activity</#if>;
+import ${superClassFqcn};
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
-
-public class ${activityClass} extends ${appCompat?string('ActionBar','')}Activity {
+public class ${activityClass} extends ${superClass} {
@Override
protected void onCreate(Bundle savedInstanceState) {
diff --git a/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivityFragment.ftl b/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivityFragment.ftl
index bacabb4..b45f76d 100644
--- a/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivityFragment.ftl
+++ b/templates/activities/BlankActivityWithFragment/root/src/app_package/SimpleActivityFragment.ftl
@@ -5,8 +5,9 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
/**
* A placeholder fragment containing a simple view.
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 d8db3c2..4364a41 100644
--- a/templates/activities/FullscreenActivity/root/src/app_package/FullscreenActivity.java.ftl
+++ b/templates/activities/FullscreenActivity/root/src/app_package/FullscreenActivity.java.ftl
@@ -13,7 +13,9 @@
import android.view.MenuItem;
import android.support.v4.app.NavUtils;
</#if>
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
/**
* An example full-screen activity that shows and hides the system UI (i.e.
diff --git a/templates/activities/GoogleAdMobAdsActivity/globals.xml.ftl b/templates/activities/GoogleAdMobAdsActivity/globals.xml.ftl
index 5f60e5c..09e5c5e 100644
--- a/templates/activities/GoogleAdMobAdsActivity/globals.xml.ftl
+++ b/templates/activities/GoogleAdMobAdsActivity/globals.xml.ftl
@@ -1,7 +1,15 @@
<?xml version="1.0"?>
<globals>
<global id="manifestOut" value="${manifestDir}" />
- <global id="appCompat" type="boolean" value="${(hasDependency('com.android.support:appcompat-v7'))?string}" />
+<#if hasDependency('com.android.support:appcompat-v7')>
+ <global id="appCompat" type="boolean" value="true" />
+ <global id="superClass" type="string" value="<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+ <global id="superClassFqcn" type="string" value="android.support.v7.app.<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+<#else>
+ <global id="appCompat" type="boolean" value="false" />
+ <global id="superClass" type="string" value="Activity"/>
+ <global id="superClassFqcn" type="string" value="android.app.Activity"/>
+</#if>
<global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
<global id="resOut" value="${resDir}" />
<global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
diff --git a/templates/activities/GoogleAdMobAdsActivity/recipe.xml.ftl b/templates/activities/GoogleAdMobAdsActivity/recipe.xml.ftl
index 5256fc7..12b954d 100644
--- a/templates/activities/GoogleAdMobAdsActivity/recipe.xml.ftl
+++ b/templates/activities/GoogleAdMobAdsActivity/recipe.xml.ftl
@@ -22,11 +22,6 @@
<instantiate from="src/app_package/SimpleActivity.java.ftl"
to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
- <#if adFormat == "interstitial">
- <instantiate from="res/layout/fragment_interstitial.xml.ftl"
- to="${escapeXmlAttribute(resOut)}/layout/fragment_interstitial.xml" />
- </#if>
-
<open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
<open file="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
</recipe>
diff --git a/templates/activities/GoogleAdMobAdsActivity/root/src/app_package/SimpleActivity.java.ftl b/templates/activities/GoogleAdMobAdsActivity/root/src/app_package/SimpleActivity.java.ftl
index 3c81789..14246ed 100644
--- a/templates/activities/GoogleAdMobAdsActivity/root/src/app_package/SimpleActivity.java.ftl
+++ b/templates/activities/GoogleAdMobAdsActivity/root/src/app_package/SimpleActivity.java.ftl
@@ -10,7 +10,7 @@
</#if>
import android.os.Bundle;
-import <#if appCompat>android.support.v7.app.ActionBarActivity<#else>android.app.Activity</#if>;
+import ${superClassFqcn};
import android.view.Menu;
import android.view.MenuItem;
<#if adFormat == "interstitial">
@@ -19,10 +19,11 @@
import android.widget.TextView;
</#if>
import android.widget.Toast;
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
-
-public class ${activityClass} extends ${(appCompat)?string('ActionBar','')}Activity {
+public class ${activityClass} extends ${superClass} {
// Remove the below line after defining your own ad unit ID.
private static final String TOAST_TEXT = "Test ads are being shown. "
+ "To show live ads, replace the ad unit ID in res/values/strings.xml with your own ad unit ID.";
@@ -62,25 +63,7 @@
mLevel = START_LEVEL;
// Create the InterstitialAd and set the adUnitId (defined in values/strings.xml).
- mInterstitialAd = new InterstitialAd(this);
- mInterstitialAd.setAdUnitId(getString(R.string.interstitial_ad_unit_id));
- mInterstitialAd.setAdListener(new AdListener() {
- @Override
- public void onAdLoaded() {
- mNextLevelButton.setEnabled(true);
- }
-
- @Override
- public void onAdFailedToLoad(int errorCode) {
- mNextLevelButton.setEnabled(true);
- }
-
- @Override
- public void onAdClosed() {
- // Proceed to the next level.
- goToNextLevel();
- }
- });
+ mInterstitialAd = newInterstitialAd();
loadInterstitial();
</#if>
@@ -111,6 +94,29 @@
}
<#if adFormat == "interstitial">
+ private InterstitialAd newInterstitialAd() {
+ InterstitialAd interstitialAd = new InterstitialAd(this);
+ interstitialAd.setAdUnitId(getString(R.string.interstitial_ad_unit_id));
+ interstitialAd.setAdListener(new AdListener() {
+ @Override
+ public void onAdLoaded() {
+ mNextLevelButton.setEnabled(true);
+ }
+
+ @Override
+ public void onAdFailedToLoad(int errorCode) {
+ mNextLevelButton.setEnabled(true);
+ }
+
+ @Override
+ public void onAdClosed() {
+ // Proceed to the next level.
+ goToNextLevel();
+ }
+ });
+ return interstitialAd;
+ }
+
private void showInterstitial() {
// Show the ad if it's ready. Otherwise toast and reload the ad.
if (mInterstitialAd != null && mInterstitialAd.isLoaded()) {
@@ -132,6 +138,7 @@
private void goToNextLevel() {
// Show the next level and reload the ad to prepare for the level after.
mLevelTextView.setText("Level " + (++mLevel));
+ mInterstitialAd = newInterstitialAd();
loadInterstitial();
}
</#if>
diff --git a/templates/activities/GoogleMapsActivity/root/AndroidManifest.xml.ftl b/templates/activities/GoogleMapsActivity/root/AndroidManifest.xml.ftl
index 6041dc2..7c3e9e8 100644
--- a/templates/activities/GoogleMapsActivity/root/AndroidManifest.xml.ftl
+++ b/templates/activities/GoogleMapsActivity/root/AndroidManifest.xml.ftl
@@ -1,16 +1,22 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
- <!-- The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
- Google Maps Android API v2, but are recommended. -->
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <!--
+ The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
+ Google Maps Android API v2, but you must specify either coarse or fine
+ location permissions for the 'MyLocation' functionality.
+ -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<application>
- <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
- <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="@string/google_maps_key"/>
+
+ <!--
+ The API key for Google Maps-based APIs is defined as a string resource.
+ (See the file "res/values/google_maps_api.xml").
+ Note that the API key is linked to the encryption key used to sign the APK.
+ You need a different API key for each encryption key, including the release key that is used to
+ sign the APK for publishing.
+ You can define the keys for the debug and release targets in src/debug/ and src/release/.
+ -->
+ <meta-data android:name="com.google.android.geo.API_KEY" android:value="@string/google_maps_key"/>
<activity android:name="${relativePackage}.${activityClass}"
android:label="@string/title_${simpleName}">
diff --git a/templates/activities/GoogleMapsActivity/root/debugRes/values/google_maps_api.xml.ftl b/templates/activities/GoogleMapsActivity/root/debugRes/values/google_maps_api.xml.ftl
index cff17a8..59df0e3 100644
--- a/templates/activities/GoogleMapsActivity/root/debugRes/values/google_maps_api.xml.ftl
+++ b/templates/activities/GoogleMapsActivity/root/debugRes/values/google_maps_api.xml.ftl
@@ -4,11 +4,14 @@
To get one, follow this link, follow the directions and press "Create" at the end:
-https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=${debugKeystoreSha1}%3B${packageName}
+ https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=${debugKeystoreSha1}%3B${packageName}
You can also add your credentials to an existing key, using this line:
${debugKeystoreSha1};${packageName}
+ Alternatively, follow the directions here:
+ https://developers.google.com/maps/documentation/android/start#get-key
+
Once you have your key (it starts with "AIza"), replace the "google_maps_key"
string in this file.
-->
diff --git a/templates/activities/GoogleMapsActivity/root/releaseRes/values/google_maps_api.xml.ftl b/templates/activities/GoogleMapsActivity/root/releaseRes/values/google_maps_api.xml.ftl
index a16ec96..f960b28 100644
--- a/templates/activities/GoogleMapsActivity/root/releaseRes/values/google_maps_api.xml.ftl
+++ b/templates/activities/GoogleMapsActivity/root/releaseRes/values/google_maps_api.xml.ftl
@@ -5,9 +5,13 @@
To do this, you can either add your release key credentials to your existing
key, or create a new key.
+ Note that this file specifies the API key for the release build target.
+ If you have previously set up a key for the debug target with the debug signing certificate,
+ you will also need to set up a key for your release certificate.
+
Follow the directions here:
-https://developers.google.com/maps/documentation/android/start#get_an_android_certificate_and_the_google_maps_api_key
+ https://developers.google.com/maps/documentation/android/signup
Once you have your key (it starts with "AIza"), replace the "google_maps_key"
string in this file.
diff --git a/templates/activities/GoogleMapsActivity/root/res/layout/activity_map.xml.ftl b/templates/activities/GoogleMapsActivity/root/res/layout/activity_map.xml.ftl
index 3b6c9b8..a19166b 100644
--- a/templates/activities/GoogleMapsActivity/root/res/layout/activity_map.xml.ftl
+++ b/templates/activities/GoogleMapsActivity/root/res/layout/activity_map.xml.ftl
@@ -1,5 +1,6 @@
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ xmlns:map="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/map"
diff --git a/templates/activities/GoogleMapsActivity/root/src/app_package/MapActivity.java.ftl b/templates/activities/GoogleMapsActivity/root/src/app_package/MapActivity.java.ftl
index ce90b52..518c0dc 100644
--- a/templates/activities/GoogleMapsActivity/root/src/app_package/MapActivity.java.ftl
+++ b/templates/activities/GoogleMapsActivity/root/src/app_package/MapActivity.java.ftl
@@ -3,63 +3,44 @@
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
+import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
-public class ${activityClass} extends FragmentActivity {
+public class ${activityClass} extends FragmentActivity implements OnMapReadyCallback {
- private GoogleMap mMap; // Might be null if Google Play services APK is not available.
+ private GoogleMap mMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.${layoutName});
- setUpMapIfNeeded();
+ // Obtain the SupportMapFragment and get notified when the map is ready to be used.
+ SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
+ .findFragmentById(R.id.map);
+ mapFragment.getMapAsync(this);
}
+
+ /**
+ * Manipulates the map once available.
+ * This callback is triggered when the map is ready to be used.
+ * This is where we can add markers or lines, add listeners or move the camera. In this case,
+ * we just add a marker near Sydney, Australia.
+ * If Google Play services is not installed on the device, the user will be prompted to install
+ * it inside the SupportMapFragment. This method will only be triggered once the user has
+ * installed Google Play services and returned to the app.
+ */
@Override
- protected void onResume() {
- super.onResume();
- setUpMapIfNeeded();
- }
+ public void onMapReady(GoogleMap googleMap) {
+ mMap = googleMap;
- /**
- * Sets up the map if it is possible to do so (i.e., the Google Play services APK is correctly
- * installed) and the map has not already been instantiated.. This will ensure that we only ever
- * call {@link #setUpMap()} once when {@link #mMap} is not null.
- * <p>
- * If it isn't installed {@link SupportMapFragment} (and
- * {@link com.google.android.gms.maps.MapView MapView}) will show a prompt for the user to
- * install/update the Google Play services APK on their device.
- * <p>
- * A user can return to this FragmentActivity after following the prompt and correctly
- * installing/updating/enabling the Google Play services. Since the FragmentActivity may not
- * have been completely destroyed during this process (it is likely that it would only be
- * stopped or paused), {@link #onCreate(Bundle)} may not be called again so we should call this
- * method in {@link #onResume()} to guarantee that it will be called.
- */
- private void setUpMapIfNeeded() {
- // Do a null check to confirm that we have not already instantiated the map.
- if (mMap == null) {
- // Try to obtain the map from the SupportMapFragment.
- mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
- .getMap();
- // Check if we were successful in obtaining the map.
- if (mMap != null) {
- setUpMap();
- }
- }
- }
-
- /**
- * This is where we can add markers or lines, add listeners or move the camera. In this case, we
- * just add a marker near Africa.
- * <p>
- * This should only be called once and when we are sure that {@link #mMap} is not null.
- */
- private void setUpMap() {
- mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
+ // Add a marker in Sydney and move the camera
+ LatLng sydney = new LatLng(-34, 151);
+ mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
+ mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
}
}
diff --git a/templates/activities/GoogleMapsWearActivity/globals.xml.ftl b/templates/activities/GoogleMapsWearActivity/globals.xml.ftl
new file mode 100644
index 0000000..58b383e
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/globals.xml.ftl
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<globals>
+ <global id="projectOut" value="." />
+ <global id="manifestOut" value="${manifestDir}" />
+ <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+ <global id="debugResOut" value="${projectOut}/src/debug/res" />
+ <global id="releaseResOut" value="${projectOut}/src/release/res" />
+ <global id="resOut" value="${resDir}" />
+ <global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
+</globals>
diff --git a/templates/activities/GoogleMapsWearActivity/recipe.xml.ftl b/templates/activities/GoogleMapsWearActivity/recipe.xml.ftl
new file mode 100644
index 0000000..b02fd4a
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/recipe.xml.ftl
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<recipe>
+
+ <dependency mavenUrl="com.google.android.gms:play-services-wearable:7.5.0" />
+ <dependency mavenUrl="com.google.android.gms:play-services-maps:7.5.0" />
+ <dependency mavenUrl="com.google.android.support:wearable:1.2.0" />
+
+
+ <merge from="AndroidManifest.xml.ftl"
+ to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
+
+ <instantiate from="res/layout/activity_map.xml.ftl"
+ to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
+
+ <merge from="res/values/strings.xml.ftl"
+ to="${escapeXmlAttribute(resOut)}/values/strings.xml" />
+
+ <instantiate from="res/layout/activity_map.xml.ftl"
+ to="${escapeXmlAttribute(resOut)}/layout/${layoutName}.xml" />
+
+ <instantiate from="src/app_package/MapActivity.java.ftl"
+ to="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
+
+ <open file="${escapeXmlAttribute(srcOut)}/${activityClass}.java" />
+
+ <merge from="debugRes/values/google_maps_api.xml.ftl"
+ to="${escapeXmlAttribute(debugResOut)}/values/google_maps_api.xml" />
+
+ <merge from="releaseRes/values/google_maps_api.xml.ftl"
+ to="${escapeXmlAttribute(releaseResOut)}/values/google_maps_api.xml" />
+
+ <!-- Display the API key instructions. -->
+ <open file="${escapeXmlAttribute(debugResOut)}/values/google_maps_api.xml" />
+</recipe>
diff --git a/templates/activities/GoogleMapsWearActivity/root/AndroidManifest.xml.ftl b/templates/activities/GoogleMapsWearActivity/root/AndroidManifest.xml.ftl
new file mode 100644
index 0000000..64e82fe
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/root/AndroidManifest.xml.ftl
@@ -0,0 +1,38 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <!--
+ The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
+ Google Maps Android API v2, but you must specify either coarse or fine
+ location permissions for the 'MyLocation' functionality.
+ -->
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+
+ <application>
+
+ <!--
+ The API key for Google Maps-based APIs is defined as a string resource.
+ (See the file "res/values/google_maps_api.xml").
+ Note that the API key is linked to the encryption key used to sign the APK.
+ You need a different API key for each encryption key, including the release key that is used to
+ sign the APK for publishing.
+ You can define the keys for the debug and release targets in src/debug/ and src/release/.
+ -->
+ <meta-data android:name="com.google.android.geo.API_KEY" android:value="@string/google_maps_key"/>
+
+ <activity android:name="${relativePackage}.${activityClass}"
+ <#if isNewProject>
+ android:label="@string/app_name"
+ <#else>
+ android:label="@string/title_${activityToLayout(activityClass)}"
+ </#if>
+ >
+ <#if isLauncher>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </#if>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/templates/activities/GoogleMapsWearActivity/root/debugRes/values/google_maps_api.xml.ftl b/templates/activities/GoogleMapsWearActivity/root/debugRes/values/google_maps_api.xml.ftl
new file mode 100644
index 0000000..59df0e3
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/root/debugRes/values/google_maps_api.xml.ftl
@@ -0,0 +1,20 @@
+<resources>
+ <!--
+ TODO: Before you run your application, you need a Google Maps API key.
+
+ To get one, follow this link, follow the directions and press "Create" at the end:
+
+ https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=${debugKeystoreSha1}%3B${packageName}
+
+ You can also add your credentials to an existing key, using this line:
+ ${debugKeystoreSha1};${packageName}
+
+ Alternatively, follow the directions here:
+ https://developers.google.com/maps/documentation/android/start#get-key
+
+ Once you have your key (it starts with "AIza"), replace the "google_maps_key"
+ string in this file.
+ -->
+ <#-- Always preserve the existing key. -->
+ <string name="google_maps_key" translatable="false" templateMergeStrategy="preserve">YOUR_KEY_HERE</string>
+</resources>
diff --git a/templates/activities/GoogleMapsWearActivity/root/releaseRes/values/google_maps_api.xml.ftl b/templates/activities/GoogleMapsWearActivity/root/releaseRes/values/google_maps_api.xml.ftl
new file mode 100644
index 0000000..f44f789
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/root/releaseRes/values/google_maps_api.xml.ftl
@@ -0,0 +1,21 @@
+<resources>
+ <!--
+ TODO: Before you release your application, you need a Google Maps API key.
+
+ To do this, you can either add your release key credentials to your existing
+ key, or create a new key.
+
+ Note that this file specifies the API key for the release build target.
+ If you have previously set up a key for the debug target with the debug signing certificate,
+ you will also need to set up a key for your release certificate.
+
+ Follow the directions here under 'release certificate':
+
+ https://developers.google.com/maps/documentation/android/signup
+
+ Once you have your key (it starts with "AIza"), replace the "google_maps_key"
+ string in this file.
+ -->
+ <#-- Always preserve the existing key. -->
+ <string name="google_maps_key" translatable="false" templateMergeStrategy="preserve">YOUR_KEY_HERE</string>
+</resources>
diff --git a/templates/activities/GoogleMapsWearActivity/root/res/layout/activity_map.xml.ftl b/templates/activities/GoogleMapsWearActivity/root/res/layout/activity_map.xml.ftl
new file mode 100644
index 0000000..dbe2d7c
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/root/res/layout/activity_map.xml.ftl
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:map="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/root_container"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ tools:context="${relativePackage}.${activityClass}">
+
+ <FrameLayout
+ android:id="@+id/map_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <fragment
+ android:id="@+id/map"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:name="com.google.android.gms.maps.MapFragment"/>
+
+ </FrameLayout>
+
+ <android.support.wearable.view.DismissOverlayView
+ android:id="@+id/dismiss_overlay"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"/>
+</FrameLayout>
diff --git a/templates/activities/GoogleMapsWearActivity/root/res/values/strings.xml.ftl b/templates/activities/GoogleMapsWearActivity/root/res/values/strings.xml.ftl
new file mode 100644
index 0000000..68d93fb
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/root/res/values/strings.xml.ftl
@@ -0,0 +1,6 @@
+<resources>
+ <#if !isNewProject>
+ <string name="title_${activityToLayout(activityClass)}">${escapeXmlString(activityClass)}</string>
+ </#if>
+ <string name="intro_text">Long click to exit the map.</string>
+</resources>
diff --git a/templates/activities/GoogleMapsWearActivity/root/src/app_package/MapActivity.java.ftl b/templates/activities/GoogleMapsWearActivity/root/src/app_package/MapActivity.java.ftl
new file mode 100644
index 0000000..4247d59
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/root/src/app_package/MapActivity.java.ftl
@@ -0,0 +1,97 @@
+package ${packageName};
+
+import com.google.android.gms.maps.CameraUpdateFactory;
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.MapFragment;
+import com.google.android.gms.maps.OnMapReadyCallback;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.MarkerOptions;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.wearable.view.DismissOverlayView;
+import android.view.View;
+import android.view.WindowInsets;
+import android.widget.FrameLayout;
+
+public class ${activityClass} extends Activity implements OnMapReadyCallback,
+ GoogleMap.OnMapLongClickListener {
+
+ /**
+ * Overlay that shows a short help text when first launched. It also provides an option to
+ * exit the app.
+ */
+ private DismissOverlayView mDismissOverlay;
+
+ /**
+ * The map. It is initialized when the map has been fully loaded and is ready to be used.
+ *
+ * @see #onMapReady(com.google.android.gms.maps.GoogleMap)
+ */
+ private GoogleMap mMap;
+
+ public void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+
+ // Set the layout. It only contains a MapFragment and a DismissOverlay.
+ setContentView(R.layout.${layoutName});
+
+ // Retrieve the containers for the root of the layout and the map. Margins will need to be
+ // set on them to account for the system window insets.
+ final FrameLayout topFrameLayout = (FrameLayout) findViewById(R.id.root_container);
+ final FrameLayout mapFrameLayout = (FrameLayout) findViewById(R.id.map_container);
+
+ // Set the system view insets on the containers when they become available.
+ topFrameLayout.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+ @Override
+ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ // Call through to super implementation and apply insets
+ insets = topFrameLayout.onApplyWindowInsets(insets);
+
+ FrameLayout.LayoutParams params =
+ (FrameLayout.LayoutParams) mapFrameLayout.getLayoutParams();
+
+ // Add Wearable insets to FrameLayout container holding map as margins
+ params.setMargins(
+ insets.getSystemWindowInsetLeft(),
+ insets.getSystemWindowInsetTop(),
+ insets.getSystemWindowInsetRight(),
+ insets.getSystemWindowInsetBottom());
+ mapFrameLayout.setLayoutParams(params);
+
+ return insets;
+ }
+ });
+
+ // Obtain the DismissOverlayView and display the introductory help text.
+ mDismissOverlay = (DismissOverlayView) findViewById(R.id.dismiss_overlay);
+ mDismissOverlay.setIntroText(R.string.intro_text);
+ mDismissOverlay.showIntroIfNecessary();
+
+ // Obtain the MapFragment and set the async listener to be notified when the map is ready.
+ MapFragment mapFragment =
+ (MapFragment) getFragmentManager().findFragmentById(R.id.map);
+ mapFragment.getMapAsync(this);
+
+ }
+
+ @Override
+ public void onMapReady(GoogleMap googleMap) {
+ // Map is ready to be used.
+ mMap = googleMap;
+
+ // Set the long click listener as a way to exit the map.
+ mMap.setOnMapLongClickListener(this);
+
+ // Add a marker in Sydney, Australia and move the camera.
+ LatLng sydney = new LatLng(-34, 151);
+ mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
+ mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
+ }
+
+ @Override
+ public void onMapLongClick(LatLng latLng) {
+ // Display the dismiss overlay with a button to exit this activity.
+ mDismissOverlay.show();
+ }
+}
diff --git a/templates/activities/GoogleMapsWearActivity/template.xml b/templates/activities/GoogleMapsWearActivity/template.xml
new file mode 100644
index 0000000..0c30f2b
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/template.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<template
+ format="4"
+ revision="1"
+ name="Google Maps Wear Activity"
+ minApi="20"
+ minBuildApi="20"
+ description="Creates an new activity with a Google Map for Android Wear">
+
+ <category value="Google" />
+ <formfactor value="Wear" />
+
+ <parameter
+ id="activityClass"
+ name="Activity Name"
+ type="string"
+ constraints="class|unique|nonempty"
+ default="MapsActivity"
+ help="The name of the activity class to create" />
+
+ <parameter
+ id="layoutName"
+ name="Layout Name"
+ type="string"
+ constraints="layout|unique|nonempty"
+ suggest="${activityToLayout(activityClass)}"
+ default="activity_map"
+ help="The name of the layout to create for the activity" />
+
+ <parameter
+ id="isLauncher"
+ name="Launcher Activity"
+ type="boolean"
+ default="true"
+ help="If true, this activity will have a CATEGORY_LAUNCHER intent filter, making it the default launchable activity" />
+
+ <parameter
+ id="packageName"
+ name="Package name"
+ type="string"
+ constraints="package"
+ default="com.mycompany.myapp" />
+
+ <!-- 128x128 thumbnails relative to template.xml -->
+ <thumbs>
+ <!-- default thumbnail is required -->
+ <thumb>template_thumb.png</thumb>
+ </thumbs>
+
+ <globals file="globals.xml.ftl" />
+ <execute file="recipe.xml.ftl" />
+
+</template>
diff --git a/templates/activities/GoogleMapsWearActivity/template_thumb.png b/templates/activities/GoogleMapsWearActivity/template_thumb.png
new file mode 100644
index 0000000..4153368
--- /dev/null
+++ b/templates/activities/GoogleMapsWearActivity/template_thumb.png
Binary files differ
diff --git a/templates/activities/LoginActivity/globals.xml.ftl b/templates/activities/LoginActivity/globals.xml.ftl
index f60259b..c77e9ac 100644
--- a/templates/activities/LoginActivity/globals.xml.ftl
+++ b/templates/activities/LoginActivity/globals.xml.ftl
@@ -1,5 +1,14 @@
<?xml version="1.0"?>
<globals>
+<#if hasDependency('com.android.support:appcompat-v7')>
+ <global id="appCompat" type="boolean" value="true" />
+ <global id="superClass" type="string" value="<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+ <global id="superClassFqcn" type="string" value="android.support.v7.app.<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+<#else>
+ <global id="appCompat" type="boolean" value="false" />
+ <global id="superClass" type="string" value="Activity"/>
+ <global id="superClassFqcn" type="string" value="android.app.Activity"/>
+</#if>
<global id="manifestOut" value="${manifestDir}" />
<global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
<global id="resOut" value="${resDir}" />
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 0bcfb4b..0571d4b 100644
--- a/templates/activities/LoginActivity/root/src/app_package/LoginActivity.java.ftl
+++ b/templates/activities/LoginActivity/root/src/app_package/LoginActivity.java.ftl
@@ -32,7 +32,9 @@
</#if>
import java.util.ArrayList;
import java.util.List;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
/**
* A login screen that offers login via email/password<#if includeGooglePlus> and via Google+ sign in</#if>.
diff --git a/templates/activities/LoginActivity/root/src/app_package/PlusBaseActivity.java.ftl b/templates/activities/LoginActivity/root/src/app_package/PlusBaseActivity.java.ftl
index 45ea9a9..943d562 100644
--- a/templates/activities/LoginActivity/root/src/app_package/PlusBaseActivity.java.ftl
+++ b/templates/activities/LoginActivity/root/src/app_package/PlusBaseActivity.java.ftl
@@ -1,22 +1,23 @@
package ${packageName};
+import ${superClassFqcn};
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
-<#if minApiLevel lt 14>import android.support.v7.app.ActionBarActivity;</#if>
-<#if minApiLevel gte 14>import android.app.Activity;</#if>
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.common.Scopes;
import com.google.android.gms.plus.PlusClient;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
/**
* A base class to wrap communication with the Google Play Services PlusClient.
*/
-public abstract class PlusBaseActivity extends <#if minApiLevel lt 14>ActionBar</#if>Activity
+public abstract class PlusBaseActivity extends ${superClass}
implements GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener {
diff --git a/templates/activities/MasterDetailFlow/globals.xml.ftl b/templates/activities/MasterDetailFlow/globals.xml.ftl
index 9031f64..3ab716f 100644
--- a/templates/activities/MasterDetailFlow/globals.xml.ftl
+++ b/templates/activities/MasterDetailFlow/globals.xml.ftl
@@ -1,6 +1,14 @@
<?xml version="1.0"?>
<globals>
- <global id="appCompat" type="boolean" value="${(hasDependency('com.android.support:appcompat-v7'))?string}" />
+<#if hasDependency('com.android.support:appcompat-v7')>
+ <global id="appCompat" type="boolean" value="true" />
+ <global id="superClass" type="string" value="<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+ <global id="superClassFqcn" type="string" value="android.support.v7.app.<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+<#else>
+ <global id="appCompat" type="boolean" value="false" />
+ <global id="superClass" type="string" value="Activity"/>
+ <global id="superClassFqcn" type="string" value="android.app.Activity"/>
+</#if>
<global id="Support" value="${(hasDependency('com.android.support:appcompat-v7'))?string('Support','')}" />
<global id="manifestOut" value="${manifestDir}" />
<global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
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 6dc3409..7eca5ce 100644
--- a/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailActivity.java.ftl
+++ b/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailActivity.java.ftl
@@ -1,11 +1,13 @@
package ${packageName};
+import ${superClassFqcn};
import android.content.Intent;
import android.os.Bundle;
-import <#if appCompat>android.support.v7.app.ActionBarActivity<#else>android.app.Activity</#if>;
<#if minApiLevel lt 16>import android.support.v4.app.NavUtils;</#if>
import android.view.MenuItem;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
/**
* An activity representing a single ${objectKind} detail screen. This
@@ -16,7 +18,7 @@
* This activity is mostly just a 'shell' activity containing nothing
* more than a {@link ${DetailName}Fragment}.
*/
-public class ${DetailName}Activity extends ${appCompat?string('ActionBar','')}Activity {
+public class ${DetailName}Activity extends ${superClass} {
@Override
protected void onCreate(Bundle savedInstanceState) {
diff --git a/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailFragment.java.ftl b/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailFragment.java.ftl
index 27331cd..fd4dbf9 100644
--- a/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailFragment.java.ftl
+++ b/templates/activities/MasterDetailFlow/root/src/app_package/ContentDetailFragment.java.ftl
@@ -6,8 +6,9 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
-
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
import ${packageName}.dummy.DummyContent;
/**
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 8e8ac58..477d718 100644
--- a/templates/activities/MasterDetailFlow/root/src/app_package/ContentListActivity.java.ftl
+++ b/templates/activities/MasterDetailFlow/root/src/app_package/ContentListActivity.java.ftl
@@ -5,7 +5,9 @@
import <#if appCompat>android.support.v4.app.FragmentActivity<#else>android.app.Activity</#if>;
<#if (parentActivityClass != "" && minApiLevel lt 16)>import android.support.v4.app.NavUtils;</#if>
<#if parentActivityClass != "">import android.view.MenuItem;</#if>
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
/**
* An activity representing a list of ${objectKindPlural}. This activity
diff --git a/templates/activities/MasterDetailFlow/root/src/app_package/ContentListFragment.java.ftl b/templates/activities/MasterDetailFlow/root/src/app_package/ContentListFragment.java.ftl
index 9e34f3e..93fd1ac 100644
--- a/templates/activities/MasterDetailFlow/root/src/app_package/ContentListFragment.java.ftl
+++ b/templates/activities/MasterDetailFlow/root/src/app_package/ContentListFragment.java.ftl
@@ -6,8 +6,9 @@
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
-
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
import ${packageName}.dummy.DummyContent;
/**
diff --git a/templates/activities/NavigationDrawerActivity/globals.xml.ftl b/templates/activities/NavigationDrawerActivity/globals.xml.ftl
index 8dd7ff2..f9e6aa8 100644
--- a/templates/activities/NavigationDrawerActivity/globals.xml.ftl
+++ b/templates/activities/NavigationDrawerActivity/globals.xml.ftl
@@ -1,7 +1,15 @@
<?xml version="1.0"?>
<globals>
<global id="manifestOut" value="${manifestDir}" />
- <global id="appCompat" type="boolean" value="${(hasDependency('com.android.support:appcompat-v7'))?string}" />
+<#if hasDependency('com.android.support:appcompat-v7')>
+ <global id="appCompat" type="boolean" value="true" />
+ <global id="superClass" type="string" value="<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+ <global id="superClassFqcn" type="string" value="android.support.v7.app.<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+<#else>
+ <global id="appCompat" type="boolean" value="false" />
+ <global id="superClass" type="string" value="Activity"/>
+ <global id="superClassFqcn" type="string" value="android.app.Activity"/>
+</#if>
<!-- e.g. getSupportActionBar vs. getActionBar -->
<global id="Support" value="${(hasDependency('com.android.support:appcompat-v7'))?string('Support','')}" />
<global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
diff --git a/templates/activities/NavigationDrawerActivity/root/src/app_package/DrawerActivity.java.ftl b/templates/activities/NavigationDrawerActivity/root/src/app_package/DrawerActivity.java.ftl
index 261d090..0ee2754 100644
--- a/templates/activities/NavigationDrawerActivity/root/src/app_package/DrawerActivity.java.ftl
+++ b/templates/activities/NavigationDrawerActivity/root/src/app_package/DrawerActivity.java.ftl
@@ -1,7 +1,7 @@
package ${packageName};
import android.app.Activity;
-<#if appCompat>import android.support.v7.app.ActionBarActivity;</#if>
+<#if appCompat>import ${superClassFqcn};</#if>
import android.<#if appCompat>support.v7.</#if>app.ActionBar;
import android.<#if appCompat>support.v4.</#if>app.Fragment;
import android.<#if appCompat>support.v4.</#if>app.FragmentManager;
@@ -17,9 +17,11 @@
import android.support.v4.widget.DrawerLayout;
import android.widget.ArrayAdapter;
import android.widget.TextView;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
-public class ${activityClass} extends ${(appCompat)?string('ActionBar','')}Activity
+public class ${activityClass} extends ${superClass}
implements NavigationDrawerFragment.NavigationDrawerCallbacks {
/**
diff --git a/templates/activities/NavigationDrawerActivity/root/src/app_package/NavigationDrawerFragment.java.ftl b/templates/activities/NavigationDrawerActivity/root/src/app_package/NavigationDrawerFragment.java.ftl
index cb0f2c5..8485499 100644
--- a/templates/activities/NavigationDrawerActivity/root/src/app_package/NavigationDrawerFragment.java.ftl
+++ b/templates/activities/NavigationDrawerActivity/root/src/app_package/NavigationDrawerFragment.java.ftl
@@ -1,6 +1,6 @@
package ${packageName};
-<#if appCompat>import android.support.v7.app.ActionBarActivity;</#if>
+<#if appCompat>import ${superClassFqcn};</#if>
import android.app.Activity;
import android.<#if appCompat>support.v7.</#if>app.ActionBar;
import android.<#if appCompat>support.v4.</#if>app.Fragment;
@@ -267,7 +267,7 @@
}
private ActionBar getActionBar() {
- return <#if appCompat>((ActionBarActivity) getActivity()).getSupportActionBar();<#else>getActivity().getActionBar();</#if>
+ return <#if appCompat>((${superClass}) getActivity()).getSupportActionBar();<#else>getActivity().getActionBar();</#if>
}
/**
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 e562f92..8e8ced6 100644
--- a/templates/activities/SettingsActivity/root/src/app_package/SettingsActivity.java.ftl
+++ b/templates/activities/SettingsActivity/root/src/app_package/SettingsActivity.java.ftl
@@ -20,8 +20,9 @@
import android.view.MenuItem;
import android.support.v4.app.NavUtils;
</#if>
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
-
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
import java.util.List;
<#assign includeSimple=(minApiLevel?? && minApiLevel lt 10)>
diff --git a/templates/activities/TabbedActivity/globals.xml.ftl b/templates/activities/TabbedActivity/globals.xml.ftl
index bb93c2c..ca4effb 100644
--- a/templates/activities/TabbedActivity/globals.xml.ftl
+++ b/templates/activities/TabbedActivity/globals.xml.ftl
@@ -1,7 +1,15 @@
<?xml version="1.0"?>
<globals>
<global id="manifestOut" value="${manifestDir}" />
- <global id="appCompat" type="boolean" value="${(hasDependency('com.android.support:appcompat-v7'))?string}" />
+<#if hasDependency('com.android.support:appcompat-v7')>
+ <global id="appCompat" type="boolean" value="true" />
+ <global id="superClass" type="string" value="<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+ <global id="superClassFqcn" type="string" value="android.support.v7.app.<#if buildApi gte 22>AppCompat<#else>ActionBar</#if>Activity"/>
+<#else>
+ <global id="appCompat" type="boolean" value="false" />
+ <global id="superClass" type="string" value="Activity"/>
+ <global id="superClassFqcn" type="string" value="android.app.Activity"/>
+</#if>
<!-- e.g. getSupportActionBar vs. getActionBar -->
<global id="Support" value="${(hasDependency('com.android.support:appcompat-v7'))?string('Support','')}" />
<global id="hasViewPager" type="boolean" value="${(features == 'pager' || features == 'tabs')?string}" />
diff --git a/templates/activities/TabbedActivity/root/src/app_package/DropdownActivity.java.ftl b/templates/activities/TabbedActivity/root/src/app_package/DropdownActivity.java.ftl
index 8f579a4..050457c 100644
--- a/templates/activities/TabbedActivity/root/src/app_package/DropdownActivity.java.ftl
+++ b/templates/activities/TabbedActivity/root/src/app_package/DropdownActivity.java.ftl
@@ -1,6 +1,6 @@
package ${packageName};
-import <#if appCompat>android.support.v7.app.ActionBarActivity<#else>android.app.Activity</#if>;
+import ${superClassFqcn};
import android.<#if appCompat>support.v7.</#if>app.ActionBar;
import android.<#if appCompat>support.v4.</#if>app.Fragment;
import android.content.Context;
@@ -14,9 +14,11 @@
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
-public class ${activityClass} extends ${(appCompat)?string('ActionBar','')}Activity implements ActionBar.OnNavigationListener {
+public class ${activityClass} extends ${superClass} implements ActionBar.OnNavigationListener {
/**
* The serialization (saved instance state) Bundle key representing the
diff --git a/templates/activities/TabbedActivity/root/src/app_package/TabsAndPagerActivity.java.ftl b/templates/activities/TabbedActivity/root/src/app_package/TabsAndPagerActivity.java.ftl
index dfa4786..a135605 100644
--- a/templates/activities/TabbedActivity/root/src/app_package/TabsAndPagerActivity.java.ftl
+++ b/templates/activities/TabbedActivity/root/src/app_package/TabsAndPagerActivity.java.ftl
@@ -2,7 +2,7 @@
import java.util.Locale;
-import <#if appCompat>android.support.v7.app.ActionBarActivity<#else>android.app.Activity</#if>;
+import ${superClassFqcn};
import android.<#if appCompat>support.v7.</#if>app.ActionBar;
import android.<#if appCompat>support.v4.</#if>app.Fragment;
import android.<#if appCompat>support.v4.</#if>app.FragmentManager;
@@ -17,9 +17,11 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
-public class ${activityClass} extends ${(appCompat)?string('ActionBar','')}Activity<#if features == 'tabs'> implements ActionBar.TabListener</#if> {
+public class ${activityClass} extends ${superClass}<#if features == 'tabs'> implements ActionBar.TabListener</#if> {
/**
* The {@link android.support.v4.view.PagerAdapter} that will provide
diff --git a/templates/gradle-projects/NewAndroidAutoProject/globals.xml.ftl b/templates/gradle-projects/NewAndroidAutoProject/globals.xml.ftl
new file mode 100644
index 0000000..da1c39a
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/globals.xml.ftl
@@ -0,0 +1,5 @@
+<?xml version="1.0"?>
+<globals>
+ <global id="topOut" value="." />
+ <global id="mavenUrl" value="mavenCentral" />
+</globals>
diff --git a/templates/gradle-projects/NewAndroidAutoProject/recipe.xml.ftl b/templates/gradle-projects/NewAndroidAutoProject/recipe.xml.ftl
new file mode 100644
index 0000000..316065f
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/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="${templateRoot}/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/NewAndroidAutoProject/root/build.gradle.ftl b/templates/gradle-projects/NewAndroidAutoProject/root/build.gradle.ftl
new file mode 100644
index 0000000..5ac9a20
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/root/build.gradle.ftl
@@ -0,0 +1,29 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+<#if mavenUrl != "mavenCentral">
+ maven {
+ url '${mavenUrl}'
+ }
+</#if>
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:${gradlePluginVersion}'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+<#if mavenUrl != "mavenCentral">
+ maven {
+ url '${mavenUrl}'
+ }
+</#if>
+ }
+}
diff --git a/templates/gradle-projects/NewAndroidAutoProject/root/gradle.properties.ftl b/templates/gradle-projects/NewAndroidAutoProject/root/gradle.properties.ftl
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/root/gradle.properties.ftl
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# 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/NewAndroidAutoProject/root/local.properties.ftl b/templates/gradle-projects/NewAndroidAutoProject/root/local.properties.ftl
new file mode 100644
index 0000000..9dcbf9b
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/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/NewAndroidAutoProject/root/project_ignore b/templates/gradle-projects/NewAndroidAutoProject/root/project_ignore
new file mode 100644
index 0000000..9c4de58
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/root/project_ignore
@@ -0,0 +1,7 @@
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
diff --git a/templates/gradle-projects/NewAndroidAutoProject/root/settings.gradle.ftl b/templates/gradle-projects/NewAndroidAutoProject/root/settings.gradle.ftl
new file mode 100644
index 0000000..b12004b
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/root/settings.gradle.ftl
@@ -0,0 +1 @@
+include ':${projectName}'
diff --git a/templates/gradle-projects/NewAndroidAutoProject/template.xml b/templates/gradle-projects/NewAndroidAutoProject/template.xml
new file mode 100644
index 0000000..1bf59ca
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/template.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<template
+ format="4"
+ revision="1"
+ name="Android Auto Project"
+ description="Creates a new Android Auto project.">
+
+ <category value="Application" />
+ <formfactor value="Car" />
+
+ <thumbs>
+ <thumb>template_new_project.png</thumb>
+ </thumbs>
+
+ <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/NewAndroidAutoProject/template_new_project.png b/templates/gradle-projects/NewAndroidAutoProject/template_new_project.png
new file mode 100644
index 0000000..92e8556
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidAutoProject/template_new_project.png
Binary files differ
diff --git a/templates/gradle-projects/NewAndroidModule/globals.xml.ftl b/templates/gradle-projects/NewAndroidModule/globals.xml.ftl
index ba085ec..32a9164 100644
--- a/templates/gradle-projects/NewAndroidModule/globals.xml.ftl
+++ b/templates/gradle-projects/NewAndroidModule/globals.xml.ftl
@@ -12,4 +12,6 @@
<global id="mavenUrl" value="mavenCentral" />
<global id="buildToolsVersion" value="18.0.1" />
<global id="gradlePluginVersion" value="0.6.+" />
+ <global id="junitVersion" value="4.12" />
+ <global id="unitTestsSupported" type="boolean" value="${(compareVersions(gradlePluginVersion, '1.1.0') >= 0)?string}" />
</globals>
diff --git a/templates/gradle-projects/NewAndroidModule/recipe.xml.ftl b/templates/gradle-projects/NewAndroidModule/recipe.xml.ftl
index 6ce95fd..d960f16 100644
--- a/templates/gradle-projects/NewAndroidModule/recipe.xml.ftl
+++ b/templates/gradle-projects/NewAndroidModule/recipe.xml.ftl
@@ -50,6 +50,9 @@
<instantiate from="test/app_package/ApplicationTest.java.ftl"
to="${testOut}/ApplicationTest.java" />
+<#if unitTestsSupported>
<instantiate from="test/app_package/ExampleUnitTest.java.ftl"
to="${unitTestOut}/ExampleUnitTest.java" />
+</#if>
+
</recipe>
diff --git a/templates/gradle-projects/NewAndroidModule/root/build.gradle.ftl b/templates/gradle-projects/NewAndroidModule/root/build.gradle.ftl
index 07a96fe..e59f426 100644
--- a/templates/gradle-projects/NewAndroidModule/root/build.gradle.ftl
+++ b/templates/gradle-projects/NewAndroidModule/root/build.gradle.ftl
@@ -72,5 +72,7 @@
wearApp project(':${WearprojectName}')
compile 'com.google.android.gms:play-services:+'
</#if>
- testCompile 'junit:junit:4.12'
+<#if unitTestsSupported>
+ testCompile 'junit:junit:${junitVersion}'
+</#if>
}
diff --git a/templates/gradle-projects/NewAndroidModule/root/res/mipmap-anydpi/test.svg b/templates/gradle-projects/NewAndroidModule/root/res/mipmap-anydpi/test.svg
new file mode 100644
index 0000000..b89a0d0
--- /dev/null
+++ b/templates/gradle-projects/NewAndroidModule/root/res/mipmap-anydpi/test.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M9.64 7.64c.23-.5.36-1.05.36-1.64 0-2.21-1.79-4-4-4S2 3.79 2 6s1.79 4 4 4c.59 0 1.14-.13 1.64-.36L10 12l-2.36 2.36C7.14 14.13 6.59 14 6 14c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4c0-.59-.13-1.14-.36-1.64L12 14l7 7h3v-1L9.64 7.64zM6 8c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm0 12c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2zm6-7.5c-.28 0-.5-.22-.5-.5s.22-.5.5-.5.5.22.5.5-.22.5-.5.5zM19 3l-6 6 2 2 7-7V3z"/></svg>
\ No newline at end of file
diff --git a/templates/other/AndroidAutoMediaService/globals.xml.ftl b/templates/other/AndroidAutoMediaService/globals.xml.ftl
new file mode 100644
index 0000000..4bf836f
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/globals.xml.ftl
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<globals>
+ <global id="manifestOut" value="${manifestDir}" />
+ <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+ <global id="resOut" value="${resDir}" />
+ <global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
+</globals>
diff --git a/templates/other/AndroidAutoMediaService/recipe.xml.ftl b/templates/other/AndroidAutoMediaService/recipe.xml.ftl
new file mode 100644
index 0000000..061d7ba
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/recipe.xml.ftl
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<recipe>
+ <merge from="AndroidManifest.xml.ftl"
+ to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
+
+ <#if useCustomTheme>
+ <merge from="res/values-v21/styles.xml.ftl"
+ to="${escapeXmlAttribute(resOut)}/values-v21/styles.xml" />
+ </#if>
+
+ <copy from="res/xml/automotive_app_desc.xml"
+ to="${escapeXmlAttribute(resOut)}/xml/automotive_app_desc.xml" />
+
+ <instantiate from="src/app_package/MusicService.java.ftl"
+ to="${escapeXmlAttribute(srcOut)}/${mediaBrowserServiceName}.java" />
+
+ <open file="${escapeXmlAttribute(srcOut)}/${mediaBrowserServiceName}.java" />
+
+ <#if useCustomTheme>
+ <open file="${escapeXmlAttribute(resOut)}/values-v21/styles.xml" />
+ </#if>
+</recipe>
diff --git a/templates/other/AndroidAutoMediaService/root/AndroidManifest.xml.ftl b/templates/other/AndroidAutoMediaService/root/AndroidManifest.xml.ftl
new file mode 100644
index 0000000..c26e543
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/root/AndroidManifest.xml.ftl
@@ -0,0 +1,34 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <application>
+
+ <meta-data
+ android:name="com.google.android.gms.car.application"
+ android:resource="@xml/automotive_app_desc" />
+
+ <#if useCustomTheme>
+ <!--
+ Use this meta data to override the theme from which Android Auto will
+ look for colors. If you don't set this, Android Auto will look
+ for color attributes in your application theme.
+ -->
+ <meta-data
+ android:name="com.google.android.gms.car.application.theme"
+ android:resource="@style/${escapeXmlAttribute(customThemeName)}" />
+
+ </#if>
+
+ <!-- Main music service, provides media browsing and media playback services to
+ consumers through MediaBrowserService and MediaSession. Consumers connect to it through
+ MediaBrowser (for browsing) and MediaController (for playback control) -->
+ <service
+ android:name="${relativePackage}.${mediaBrowserServiceName}"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.media.browse.MediaBrowserService" />
+ </intent-filter>
+ </service>
+
+ </application>
+
+</manifest>
diff --git a/templates/other/AndroidAutoMediaService/root/res/values-v21/styles.xml.ftl b/templates/other/AndroidAutoMediaService/root/res/values-v21/styles.xml.ftl
new file mode 100644
index 0000000..6ba61a3
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/root/res/values-v21/styles.xml.ftl
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="${customThemeName}" parent="android:Theme.Material.Light">
+ <!-- colorPrimaryDark is currently used in Android Auto for:
+ - App background
+ - Drawer right side ("more" custom actions) background
+ - Notification icon badge tinting
+ - Overview “now playing” icon tinting
+ -->
+ <item name="android:colorPrimaryDark">#ffbf360c</item>
+
+ <!-- colorAccent is used in Android Auto for:
+ - Spinner
+ - progress bar
+ - floating action button background (Play/Pause in media apps)
+ -->
+ <item name="android:colorAccent">#00B8D4</item>
+ </style>
+</resources>
diff --git a/templates/other/AndroidAutoMediaService/root/res/xml/automotive_app_desc.xml b/templates/other/AndroidAutoMediaService/root/res/xml/automotive_app_desc.xml
new file mode 100644
index 0000000..a3d9fda
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/root/res/xml/automotive_app_desc.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<automotiveApp>
+ <uses name="media"/>
+</automotiveApp>
diff --git a/templates/other/AndroidAutoMediaService/root/src/app_package/MusicService.java.ftl b/templates/other/AndroidAutoMediaService/root/src/app_package/MusicService.java.ftl
new file mode 100644
index 0000000..2d79289
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/root/src/app_package/MusicService.java.ftl
@@ -0,0 +1,138 @@
+package ${packageName};
+
+import android.media.browse.MediaBrowser.MediaItem;
+import android.media.session.MediaSession;
+import android.os.Bundle;
+import android.service.media.MediaBrowserService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides a MediaBrowser through a service. It exposes the media library to a browsing
+ * client, through the onGetRoot and onLoadChildren methods. It also creates a MediaSession and
+ * exposes it through its MediaSession.Token, which allows the client to create a MediaController
+ * that connects to and send control commands to the MediaSession remotely. This is useful for
+ * user interfaces that need to interact with your media session, like Android Auto. You can
+ * (should) also use the same service from your app's UI, which gives a seamless playback
+ * experience to the user.
+ *
+ * To implement a MediaBrowserService, you need to:
+ *
+ * <ul>
+ *
+ * <li> Extend {@link android.service.media.MediaBrowserService}, implementing the media browsing
+ * related methods {@link android.service.media.MediaBrowserService#onGetRoot} and
+ * {@link android.service.media.MediaBrowserService#onLoadChildren};
+ * <li> In onCreate, start a new {@link android.media.session.MediaSession} and notify its parent
+ * with the session's token {@link android.service.media.MediaBrowserService#setSessionToken};
+ *
+ * <li> Set a callback on the
+ * {@link android.media.session.MediaSession#setCallback(android.media.session.MediaSession.Callback)}.
+ * The callback will receive all the user's actions, like play, pause, etc;
+ *
+ * <li> Handle all the actual music playing using any method your app prefers (for example,
+ * {@link android.media.MediaPlayer})
+ *
+ * <li> Update playbackState, "now playing" metadata and queue, using MediaSession proper methods
+ * {@link android.media.session.MediaSession#setPlaybackState(android.media.session.PlaybackState)}
+ * {@link android.media.session.MediaSession#setMetadata(android.media.MediaMetadata)} and
+ * {@link android.media.session.MediaSession#setQueue(java.util.List)})
+ *
+ * <li> Declare and export the service in AndroidManifest with an intent receiver for the action
+ * android.media.browse.MediaBrowserService
+ *
+ * </ul>
+ *
+ * To make your app compatible with Android Auto, you also need to:
+ *
+ * <ul>
+ *
+ * <li> Declare a meta-data tag in AndroidManifest.xml linking to a xml resource
+ * with a <automotiveApp> root element. For a media app, this must include
+ * an <uses name="media"/> element as a child.
+ * For example, in AndroidManifest.xml:
+ * <meta-data android:name="com.google.android.gms.car.application"
+ * android:resource="@xml/automotive_app_desc"/>
+ * And in res/values/automotive_app_desc.xml:
+ * <automotiveApp>
+ * <uses name="media"/>
+ * </automotiveApp>
+ *
+ * </ul>
+
+ * @see <a href="README.md">README.md</a> for more details.
+ *
+ */
+public class ${mediaBrowserServiceName} extends MediaBrowserService {
+
+ private MediaSession mSession;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ mSession = new MediaSession(this, "${mediaBrowserServiceName}");
+ setSessionToken(mSession.getSessionToken());
+ mSession.setCallback(new MediaSessionCallback());
+ mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
+ MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
+ }
+
+ @Override
+ public void onDestroy() {
+ mSession.release();
+ }
+
+ @Override
+ public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
+ return new BrowserRoot("root", null);
+ }
+
+ @Override
+ public void onLoadChildren(final String parentMediaId, final Result<List<MediaItem>> result) {
+ result.sendResult(new ArrayList<MediaItem>());
+ }
+
+ private final class MediaSessionCallback extends MediaSession.Callback {
+ @Override
+ public void onPlay() {
+ }
+
+ @Override
+ public void onSkipToQueueItem(long queueId) {
+ }
+
+ @Override
+ public void onSeekTo(long position) {
+ }
+
+ @Override
+ public void onPlayFromMediaId(String mediaId, Bundle extras) {
+ }
+
+ @Override
+ public void onPause() {
+ }
+
+ @Override
+ public void onStop() {
+ }
+
+ @Override
+ public void onSkipToNext() {
+ }
+
+ @Override
+ public void onSkipToPrevious() {
+ }
+
+ @Override
+ public void onCustomAction(String action, Bundle extras) {
+ }
+
+ @Override
+ public void onPlayFromSearch(final String query, final Bundle extras) {
+ }
+ }
+}
diff --git a/templates/other/AndroidAutoMediaService/template.xml b/templates/other/AndroidAutoMediaService/template.xml
new file mode 100644
index 0000000..08d6fa3
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/template.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<template
+ format="4"
+ revision="1"
+ name="Media service"
+ description="Create a MediaBrowserService and adds the required metadata for Android Auto"
+ minApi="21">
+
+ <category value="Android Auto" />
+ <formfactor value="Car" />
+
+ <parameter
+ id="mediaBrowserServiceName"
+ name="Class name"
+ type="string"
+ constraints="class|unique|nonempty"
+ default="MyMusicService"
+ help="The name of the service that will extend MediaBrowserService and contain the logic to browse and playback media" />
+
+ <parameter
+ id="packageName"
+ name="Package name"
+ type="string"
+ constraints="package"
+ help="The package where the media service will be created" />
+
+ <parameter
+ id="useCustomTheme"
+ name="Use a custom theme for Android Auto colors?"
+ type="boolean"
+ constraints="package"
+ help="Android Auto apps can define a different set of colors that will be used exclusively when running on Android Auto" />
+
+ <parameter
+ id="customThemeName"
+ name="Android Auto custom theme name"
+ type="String"
+ default="CarTheme"
+ visibility="useCustomTheme"
+ constraints="nonempty"/>
+
+ <globals file="globals.xml.ftl" />
+ <execute file="recipe.xml.ftl" />
+
+ <thumbs>
+ <thumb>templates-mediaService-Auto.png</thumb>
+ </thumbs>
+
+</template>
diff --git a/templates/other/AndroidAutoMediaService/templates-mediaService-Auto.png b/templates/other/AndroidAutoMediaService/templates-mediaService-Auto.png
new file mode 100644
index 0000000..48c4ef5
--- /dev/null
+++ b/templates/other/AndroidAutoMediaService/templates-mediaService-Auto.png
Binary files differ
diff --git a/templates/other/AndroidAutoMessagingService/globals.xml.ftl b/templates/other/AndroidAutoMessagingService/globals.xml.ftl
new file mode 100644
index 0000000..4bf836f
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/globals.xml.ftl
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<globals>
+ <global id="manifestOut" value="${manifestDir}" />
+ <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
+ <global id="resOut" value="${resDir}" />
+ <global id="relativePackage" value="<#if relativePackage?has_content>${relativePackage}<#else>${packageName}</#if>" />
+</globals>
diff --git a/templates/other/AndroidAutoMessagingService/recipe.xml.ftl b/templates/other/AndroidAutoMessagingService/recipe.xml.ftl
new file mode 100644
index 0000000..dddb1e5
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/recipe.xml.ftl
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<recipe>
+ <dependency mavenUrl="com.android.support:support-v4:21.0.2"/>
+ <dependency mavenUrl="com.android.support:support-v13:21.0.2"/>
+ <merge from="AndroidManifest.xml.ftl"
+ to="${escapeXmlAttribute(manifestOut)}/AndroidManifest.xml" />
+
+ <copy from="res/xml/automotive_app_desc.xml"
+ to="${escapeXmlAttribute(resOut)}/xml/automotive_app_desc.xml" />
+
+ <instantiate from="src/app_package/MessagingService.java.ftl"
+ to="${escapeXmlAttribute(srcOut)}/${serviceName}.java" />
+
+ <instantiate from="src/app_package/MessageReadReceiver.java.ftl"
+ to="${escapeXmlAttribute(srcOut)}/${readReceiverName}.java" />
+
+ <instantiate from="src/app_package/MessageReplyReceiver.java.ftl"
+ to="${escapeXmlAttribute(srcOut)}/${replyReceiverName}.java" />
+
+ <open file="${escapeXmlAttribute(srcOut)}/${serviceName}.java" />
+ <open file="${escapeXmlAttribute(srcOut)}/${readReceiverName}.java" />
+ <open file="${escapeXmlAttribute(srcOut)}/${replyReceiverName}.java" />
+</recipe>
diff --git a/templates/other/AndroidAutoMessagingService/root/AndroidManifest.xml.ftl b/templates/other/AndroidAutoMessagingService/root/AndroidManifest.xml.ftl
new file mode 100644
index 0000000..e6c133d
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/root/AndroidManifest.xml.ftl
@@ -0,0 +1,26 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <application>
+
+ <meta-data
+ android:name="com.google.android.gms.car.application"
+ android:resource="@xml/automotive_app_desc" />
+
+ <service android:name="${relativePackage}.${serviceName}">
+ </service>
+
+ <receiver android:name="${relativePackage}.${readReceiverName}">
+ <intent-filter>
+ <action android:name="${packageName}.ACTION_MESSAGE_READ"/>
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="${relativePackage}.${replyReceiverName}">
+ <intent-filter>
+ <action android:name="${packageName}.ACTION_MESSAGE_REPLY"/>
+ </intent-filter>
+ </receiver>
+
+ </application>
+
+</manifest>
diff --git a/templates/other/AndroidAutoMessagingService/root/res/xml/automotive_app_desc.xml b/templates/other/AndroidAutoMessagingService/root/res/xml/automotive_app_desc.xml
new file mode 100644
index 0000000..6d31de8
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/root/res/xml/automotive_app_desc.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<automotiveApp>
+ <uses name="notification"/>
+</automotiveApp>
diff --git a/templates/other/AndroidAutoMessagingService/root/src/app_package/MessageReadReceiver.java.ftl b/templates/other/AndroidAutoMessagingService/root/src/app_package/MessageReadReceiver.java.ftl
new file mode 100644
index 0000000..a175baa
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/root/src/app_package/MessageReadReceiver.java.ftl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ${packageName};
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.NotificationManagerCompat;
+import android.util.Log;
+
+public class ${readReceiverName} extends BroadcastReceiver {
+ private static final String TAG = ${readReceiverName}.class.getSimpleName();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (${serviceName}.READ_ACTION.equals(intent.getAction())) {
+ int conversationId = intent.getIntExtra(${serviceName}.CONVERSATION_ID, -1);
+ if (conversationId != -1) {
+ Log.d(TAG, "Conversation " + conversationId + " was read");
+ NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
+ notificationManager.cancel(conversationId);
+ }
+ }
+ }
+}
diff --git a/templates/other/AndroidAutoMessagingService/root/src/app_package/MessageReplyReceiver.java.ftl b/templates/other/AndroidAutoMessagingService/root/src/app_package/MessageReplyReceiver.java.ftl
new file mode 100644
index 0000000..021e4e4
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/root/src/app_package/MessageReplyReceiver.java.ftl
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ${packageName};
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.RemoteInput;
+import android.util.Log;
+
+/**
+ * A receiver that gets called when a reply is sent to a given conversationId
+ */
+public class ${replyReceiverName} extends BroadcastReceiver {
+
+ private static final String TAG = ${replyReceiverName}.class.getSimpleName();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (${serviceName}.REPLY_ACTION.equals(intent.getAction())) {
+ int conversationId = intent.getIntExtra(${serviceName}.CONVERSATION_ID, -1);
+ CharSequence reply = getMessageText(intent);
+ Log.d(TAG, "Got reply (" + reply + ") for ConversationId " + conversationId);
+ }
+ }
+
+ /**
+ * Get the message text from the intent.
+ * Note that you should call {@code RemoteInput#getResultsFromIntent(intent)} to process
+ * the RemoteInput.
+ */
+ private CharSequence getMessageText(Intent intent) {
+ Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
+ if (remoteInput != null) {
+ return remoteInput.getCharSequence(${serviceName}.EXTRA_VOICE_REPLY);
+ }
+ return null;
+ }
+}
diff --git a/templates/other/AndroidAutoMessagingService/root/src/app_package/MessagingService.java.ftl b/templates/other/AndroidAutoMessagingService/root/src/app_package/MessagingService.java.ftl
new file mode 100644
index 0000000..bcdebed
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/root/src/app_package/MessagingService.java.ftl
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ${packageName};
+
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationCompat.CarExtender;
+import android.support.v4.app.NotificationCompat.CarExtender.UnreadConversation;
+import android.support.v4.app.NotificationManagerCompat;
+import android.support.v4.app.RemoteInput;
+
+public class ${serviceName} extends Service {
+ private static final String TAG = ${serviceName}.class.getSimpleName();
+
+ public static final String READ_ACTION =
+ "${packageName}.ACTION_MESSAGE_READ";
+ public static final String REPLY_ACTION =
+ "${packageName}.ACTION_MESSAGE_REPLY";
+ public static final String CONVERSATION_ID = "conversation_id";
+ public static final String EXTRA_VOICE_REPLY = "extra_voice_reply";
+
+ private NotificationManagerCompat mNotificationManager;
+
+ private final Messenger mMessenger = new Messenger(new IncomingHandler());
+
+ /**
+ * Handler of incoming messages from clients.
+ */
+ class IncomingHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ sendNotification(1, "This is a sample message", "John Doe",
+ System.currentTimeMillis());
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ mNotificationManager = NotificationManagerCompat.from(getApplicationContext());
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mMessenger.getBinder();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ return START_STICKY;
+ }
+
+ private Intent createIntent(int conversationId, String action) {
+ return new Intent()
+ .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
+ .setAction(action)
+ .putExtra(CONVERSATION_ID, conversationId);
+ }
+
+ private void sendNotification(int conversationId, String message,
+ String participant, long timestamp) {
+ // A pending Intent for reads
+ PendingIntent readPendingIntent = PendingIntent.getBroadcast(getApplicationContext(),
+ conversationId,
+ createIntent(conversationId, READ_ACTION),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ // Build a RemoteInput for receiving voice input in a Car Notification
+ RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
+ .setLabel("Reply by voice")
+ .build();
+
+ // Building a Pending Intent for the reply action to trigger
+ PendingIntent replyIntent = PendingIntent.getBroadcast(getApplicationContext(),
+ conversationId,
+ createIntent(conversationId, REPLY_ACTION),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ // Create the UnreadConversation and populate it with the participant name,
+ // read and reply intents.
+ UnreadConversation.Builder unreadConvBuilder =
+ new UnreadConversation.Builder(participant)
+ .setLatestTimestamp(timestamp)
+ .setReadPendingIntent(readPendingIntent)
+ .setReplyAction(replyIntent, remoteInput);
+
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext())
+ // Set the application notification icon:
+ //.setSmallIcon(R.drawable.notification_icon)
+
+ // Set the large icon, for example a picture of the other recipient of the message
+ //.setLargeIcon(personBitmap)
+
+ .setContentText(message)
+ .setWhen(timestamp)
+ .setContentTitle(participant)
+ .setContentIntent(readPendingIntent)
+ .extend(new CarExtender()
+ .setUnreadConversation(unreadConvBuilder.build()));
+
+ mNotificationManager.notify(conversationId, builder.build());
+ }
+}
diff --git a/templates/other/AndroidAutoMessagingService/template.xml b/templates/other/AndroidAutoMessagingService/template.xml
new file mode 100644
index 0000000..3412a8e
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/template.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<template
+ format="4"
+ revision="1"
+ name="Messaging service"
+ description="Create a service that sends notifications compatible with Android Auto"
+ minApi="21">
+
+ <category value="Android Auto" />
+ <formfactor value="Car" />
+
+ <parameter
+ id="serviceName"
+ name="Service class name"
+ type="string"
+ constraints="class|unique|nonempty"
+ default="MyMessagingService"
+ help="The name of the service that will handle incoming messages and send corresponding notifications." />
+
+ <parameter
+ id="readReceiverName"
+ name="Read receiver class name"
+ type="string"
+ constraints="class|unique|nonempty"
+ default="MessageReadReceiver"
+ help="The broadcast receiver that will handle Read notifications." />
+
+ <parameter
+ id="replyReceiverName"
+ name="Reply receiver class name"
+ type="string"
+ constraints="class|unique|nonempty"
+ default="MessageReplyReceiver"
+ help="The broadcast receiver that will handle Reply notifications." />
+
+ <parameter
+ id="packageName"
+ name="Package name"
+ type="string"
+ constraints="package"
+ help="The package where the messaging service will be created." />
+
+ <globals file="globals.xml.ftl" />
+ <execute file="recipe.xml.ftl" />
+
+ <thumbs>
+ <thumb>templates-messagingService-Auto.png</thumb>
+ </thumbs>
+
+</template>
diff --git a/templates/other/AndroidAutoMessagingService/templates-messagingService-Auto.png b/templates/other/AndroidAutoMessagingService/templates-messagingService-Auto.png
new file mode 100644
index 0000000..02f611a
--- /dev/null
+++ b/templates/other/AndroidAutoMessagingService/templates-messagingService-Auto.png
Binary files differ
diff --git a/templates/other/AppWidget/root/src/app_package/AppWidget.java.ftl b/templates/other/AppWidget/root/src/app_package/AppWidget.java.ftl
index 60a1e08..d273f19 100644
--- a/templates/other/AppWidget/root/src/app_package/AppWidget.java.ftl
+++ b/templates/other/AppWidget/root/src/app_package/AppWidget.java.ftl
@@ -4,7 +4,9 @@
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.widget.RemoteViews;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
/**
* Implementation of App Widget functionality.
diff --git a/templates/other/AppWidget/root/src/app_package/AppWidgetConfigureActivity.java.ftl b/templates/other/AppWidget/root/src/app_package/AppWidgetConfigureActivity.java.ftl
index eed95b5..c51f367 100644
--- a/templates/other/AppWidget/root/src/app_package/AppWidgetConfigureActivity.java.ftl
+++ b/templates/other/AppWidget/root/src/app_package/AppWidgetConfigureActivity.java.ftl
@@ -8,7 +8,9 @@
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
/**
* The configuration screen for the {@link ${className} ${className}} AppWidget.
diff --git a/templates/other/BlankFragment/root/src/app_package/BlankFragment.java.ftl b/templates/other/BlankFragment/root/src/app_package/BlankFragment.java.ftl
index 6e33197..322f37f 100644
--- a/templates/other/BlankFragment/root/src/app_package/BlankFragment.java.ftl
+++ b/templates/other/BlankFragment/root/src/app_package/BlankFragment.java.ftl
@@ -8,7 +8,9 @@
import android.view.View;
import android.view.ViewGroup;
<#if !includeLayout>import android.widget.TextView;</#if>
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
/**
* A simple {@link Fragment} subclass.
diff --git a/templates/other/CustomView/root/src/app_package/CustomView.java.ftl b/templates/other/CustomView/root/src/app_package/CustomView.java.ftl
index a989a30..fe5c9ec 100644
--- a/templates/other/CustomView/root/src/app_package/CustomView.java.ftl
+++ b/templates/other/CustomView/root/src/app_package/CustomView.java.ftl
@@ -9,7 +9,9 @@
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
/**
* TODO: document your custom view class.
diff --git a/templates/other/Daydream/root/src/app_package/DreamService.java.ftl b/templates/other/Daydream/root/src/app_package/DreamService.java.ftl
index 0dc51ca..81738bd 100644
--- a/templates/other/Daydream/root/src/app_package/DreamService.java.ftl
+++ b/templates/other/Daydream/root/src/app_package/DreamService.java.ftl
@@ -15,7 +15,9 @@
import android.view.ViewPropertyAnimator;
import android.view.animation.LinearInterpolator;
import android.widget.TextView;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
/**
* This class is a sample implementation of a DreamService. When activated, a
diff --git a/templates/other/Daydream/root/src/app_package/SettingsActivity.java.ftl b/templates/other/Daydream/root/src/app_package/SettingsActivity.java.ftl
index cd1b4c8..a7bc420 100644
--- a/templates/other/Daydream/root/src/app_package/SettingsActivity.java.ftl
+++ b/templates/other/Daydream/root/src/app_package/SettingsActivity.java.ftl
@@ -5,7 +5,9 @@
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
/**
* A settings Activity for {@link ${className}}.
diff --git a/templates/other/DisplayNotification/root/src/app_package/BroadcastReceiver.java.ftl b/templates/other/DisplayNotification/root/src/app_package/BroadcastReceiver.java.ftl
index d5a4615..18e750e 100644
--- a/templates/other/DisplayNotification/root/src/app_package/BroadcastReceiver.java.ftl
+++ b/templates/other/DisplayNotification/root/src/app_package/BroadcastReceiver.java.ftl
@@ -7,7 +7,9 @@
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
public class ${receiverClass} extends BroadcastReceiver {
public static final String CONTENT_KEY = "contentText";
@@ -20,7 +22,7 @@
Intent displayIntent = new Intent(context, ${displayActivityClass}.class);
String text = intent.getStringExtra(CONTENT_KEY);
Notification notification = new Notification.Builder(context)
- .setSmallIcon(R.drawable.ic_launcher)
+ .setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(text)
.extend(new Notification.WearableExtender()
.setDisplayIntent(PendingIntent.getActivity(context, 0, displayIntent,
@@ -30,4 +32,4 @@
Toast.makeText(context, context.getString(R.string.notification_posted), Toast.LENGTH_SHORT).show();
}
-}
\ No newline at end of file
+}
diff --git a/templates/other/DisplayNotification/root/src/app_package/DisplayActivity.java.ftl b/templates/other/DisplayNotification/root/src/app_package/DisplayActivity.java.ftl
index dad7cdd..13ac120 100644
--- a/templates/other/DisplayNotification/root/src/app_package/DisplayActivity.java.ftl
+++ b/templates/other/DisplayNotification/root/src/app_package/DisplayActivity.java.ftl
@@ -3,7 +3,9 @@
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
public class ${displayActivityClass} extends Activity {
@@ -15,4 +17,4 @@
setContentView(R.layout.${displayActivityLayout});
mTextView = (TextView) findViewById(R.id.text);
}
-}
\ No newline at end of file
+}
diff --git a/templates/other/DisplayNotification/root/src/app_package/StubActivity.java.ftl b/templates/other/DisplayNotification/root/src/app_package/StubActivity.java.ftl
index 9a1f5b5..7367ce2 100644
--- a/templates/other/DisplayNotification/root/src/app_package/StubActivity.java.ftl
+++ b/templates/other/DisplayNotification/root/src/app_package/StubActivity.java.ftl
@@ -3,7 +3,9 @@
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
/**
* Example shell activity which simply broadcasts to our receiver and exits.
diff --git a/templates/other/ListFragment/root/src/app_package/ListFragment.java.ftl b/templates/other/ListFragment/root/src/app_package/ListFragment.java.ftl
index 9c11374..01379d2 100644
--- a/templates/other/ListFragment/root/src/app_package/ListFragment.java.ftl
+++ b/templates/other/ListFragment/root/src/app_package/ListFragment.java.ftl
@@ -21,8 +21,9 @@
<#else>
import android.widget.ListView;
</#if>
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
-
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
import ${packageName}.dummy.DummyContent;
/**
diff --git a/templates/other/Notification/root/src/app_package/NotificationHelper.java.ftl b/templates/other/Notification/root/src/app_package/NotificationHelper.java.ftl
index 6c39bfe..28074f1 100644
--- a/templates/other/Notification/root/src/app_package/NotificationHelper.java.ftl
+++ b/templates/other/Notification/root/src/app_package/NotificationHelper.java.ftl
@@ -17,7 +17,9 @@
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
</#if>
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
/**
* Helper class for showing and canceling ${display_title?lower_case}
@@ -203,4 +205,4 @@
nm.cancel(NOTIFICATION_TAG.hashCode());
}
}
-}
\ No newline at end of file
+}
diff --git a/templates/other/PlusOneFragment/root/src/app_package/PlusOneFragment.java.ftl b/templates/other/PlusOneFragment/root/src/app_package/PlusOneFragment.java.ftl
index 659bc45..8351bcb 100644
--- a/templates/other/PlusOneFragment/root/src/app_package/PlusOneFragment.java.ftl
+++ b/templates/other/PlusOneFragment/root/src/app_package/PlusOneFragment.java.ftl
@@ -7,8 +7,9 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-<#if applicationPackage??>import ${applicationPackage}.R;</#if>
-
+<#if applicationPackage??>
+import ${applicationPackage}.R;
+</#if>
import com.google.android.gms.plus.PlusOneButton;
/**
diff --git a/templates/other/WatchFaceService/root/src/app_package/MyAnalogWatchFaceService.java.ftl b/templates/other/WatchFaceService/root/src/app_package/MyAnalogWatchFaceService.java.ftl
index 32d4e8d..81395cb 100644
--- a/templates/other/WatchFaceService/root/src/app_package/MyAnalogWatchFaceService.java.ftl
+++ b/templates/other/WatchFaceService/root/src/app_package/MyAnalogWatchFaceService.java.ftl
@@ -32,6 +32,7 @@
import android.text.format.Time;
import android.view.SurfaceHolder;
+import java.lang.ref.WeakReference;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@@ -46,36 +47,23 @@
*/
private static final long INTERACTIVE_UPDATE_RATE_MS = TimeUnit.SECONDS.toMillis(1);
+ /**
+ * Handler message id for updating the time periodically in interactive mode.
+ */
+ private static final int MSG_UPDATE_TIME = 0;
+
@Override
public Engine onCreateEngine() {
return new Engine();
}
private class Engine extends CanvasWatchFaceService.Engine {
- static final int MSG_UPDATE_TIME = 0;
-
Paint mBackgroundPaint;
Paint mHandPaint;
boolean mAmbient;
Time mTime;
- /** Handler to update the time once a second in interactive mode. */
- final Handler mUpdateTimeHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MSG_UPDATE_TIME:
- invalidate();
- if (shouldTimerBeRunning()) {
- long timeMs = System.currentTimeMillis();
- long delayMs = INTERACTIVE_UPDATE_RATE_MS
- - (timeMs % INTERACTIVE_UPDATE_RATE_MS);
- mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
- }
- break;
- }
- }
- };
+ final Handler mUpdateTimeHandler = new EngineHandler(this);
final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
@Override
@@ -244,5 +232,38 @@
private boolean shouldTimerBeRunning() {
return isVisible() && !isInAmbientMode();
}
+
+ /**
+ * Handle updating the time periodically in interactive mode.
+ */
+ private void handleUpdateTimeMessage() {
+ invalidate();
+ if (shouldTimerBeRunning()) {
+ long timeMs = System.currentTimeMillis();
+ long delayMs = INTERACTIVE_UPDATE_RATE_MS
+ - (timeMs % INTERACTIVE_UPDATE_RATE_MS);
+ mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
+ }
+ }
+ }
+
+ private static class EngineHandler extends Handler {
+ private final WeakReference<${serviceClass}.Engine> mWeakReference;
+
+ public EngineHandler(${serviceClass}.Engine reference) {
+ mWeakReference = new WeakReference<>(reference);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ ${serviceClass}.Engine engine = mWeakReference.get();
+ if (engine != null) {
+ switch (msg.what) {
+ case MSG_UPDATE_TIME:
+ engine.handleUpdateTimeMessage();
+ break;
+ }
+ }
+ }
}
}
diff --git a/templates/other/WatchFaceService/root/src/app_package/MyDigitalWatchFaceService.java.ftl b/templates/other/WatchFaceService/root/src/app_package/MyDigitalWatchFaceService.java.ftl
index 4835d37..ac00af2 100644
--- a/templates/other/WatchFaceService/root/src/app_package/MyDigitalWatchFaceService.java.ftl
+++ b/templates/other/WatchFaceService/root/src/app_package/MyDigitalWatchFaceService.java.ftl
@@ -34,6 +34,7 @@
import android.view.SurfaceHolder;
import android.view.WindowInsets;
+import java.lang.ref.WeakReference;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@@ -51,33 +52,18 @@
*/
private static final long INTERACTIVE_UPDATE_RATE_MS = TimeUnit.SECONDS.toMillis(1);
+ /**
+ * Handler message id for updating the time periodically in interactive mode.
+ */
+ private static final int MSG_UPDATE_TIME = 0;
+
@Override
public Engine onCreateEngine() {
return new Engine();
}
private class Engine extends CanvasWatchFaceService.Engine {
- static final int MSG_UPDATE_TIME = 0;
-
- /**
- * Handler to update the time periodically in interactive mode.
- */
- final Handler mUpdateTimeHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MSG_UPDATE_TIME:
- invalidate();
- if (shouldTimerBeRunning()) {
- long timeMs = System.currentTimeMillis();
- long delayMs = INTERACTIVE_UPDATE_RATE_MS
- - (timeMs % INTERACTIVE_UPDATE_RATE_MS);
- mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
- }
- break;
- }
- }
- };
+ final Handler mUpdateTimeHandler = new EngineHandler(this);
final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() {
@Override
@@ -250,5 +236,38 @@
private boolean shouldTimerBeRunning() {
return isVisible() && !isInAmbientMode();
}
+
+ /**
+ * Handle updating the time periodically in interactive mode.
+ */
+ private void handleUpdateTimeMessage() {
+ invalidate();
+ if (shouldTimerBeRunning()) {
+ long timeMs = System.currentTimeMillis();
+ long delayMs = INTERACTIVE_UPDATE_RATE_MS
+ - (timeMs % INTERACTIVE_UPDATE_RATE_MS);
+ mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs);
+ }
+ }
+ }
+
+ private static class EngineHandler extends Handler {
+ private final WeakReference<${serviceClass}.Engine> mWeakReference;
+
+ public EngineHandler(${serviceClass}.Engine reference) {
+ mWeakReference = new WeakReference<>(reference);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ ${serviceClass}.Engine engine = mWeakReference.get();
+ if (engine != null) {
+ switch (msg.what) {
+ case MSG_UPDATE_TIME:
+ engine.handleUpdateTimeMessage();
+ break;
+ }
+ }
+ }
}
}
diff --git a/testutils/build.gradle b/testutils/build.gradle
index 67ec14d..b69a4fb 100644
--- a/testutils/build.gradle
+++ b/testutils/build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'java'
+apply plugin: 'jacoco'
group = 'com.android.tools'
archivesBaseName = 'testutils'
@@ -14,3 +15,10 @@
main.resources.srcDir 'src/main/java'
test.resources.srcDir 'src/test/java'
}
+
+project.ext.pomName = 'Android Tools Test Utilities'
+project.ext.pomDesc = 'API used by lint testing infrastructure'
+
+apply from: "$rootDir/buildSrc/base/publish.gradle"
+apply from: "$rootDir/buildSrc/base/bintray.gradle"
+apply from: "$rootDir/buildSrc/base/javadoc.gradle"
diff --git a/testutils/src/main/java/com/android/testutils/SdkTestCase.java b/testutils/src/main/java/com/android/testutils/SdkTestCase.java
index f85873f..75877a5 100644
--- a/testutils/src/main/java/com/android/testutils/SdkTestCase.java
+++ b/testutils/src/main/java/com/android/testutils/SdkTestCase.java
@@ -17,7 +17,6 @@
import com.android.SdkConstants;
import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
import com.google.common.base.Charsets;
import com.google.common.collect.Sets;
import com.google.common.io.ByteStreams;
@@ -184,7 +183,7 @@
// cannot happen
}
- assertTrue(xml.length() > 0);
+ assertTrue(!xml.isEmpty());
// Remove any references to the project name such that we are isolated from
// that in golden file.