aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--anttasks/src/com/android/ant/MultiApkExportTask.java2
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java45
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java73
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/xml/ManifestData.java142
-rw-r--r--sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/SupportsScreensTest.java86
5 files changed, 313 insertions, 35 deletions
diff --git a/anttasks/src/com/android/ant/MultiApkExportTask.java b/anttasks/src/com/android/ant/MultiApkExportTask.java
index 151b81b..8c9ee3e 100644
--- a/anttasks/src/com/android/ant/MultiApkExportTask.java
+++ b/anttasks/src/com/android/ant/MultiApkExportTask.java
@@ -73,7 +73,7 @@ public class MultiApkExportTask extends Task {
String appPackage = getValidatedProperty(antProject, "package");
System.out.println("Multi APK export for: " + appPackage);
- String version = getValidatedProperty(antProject, "version");
+ String version = getValidatedProperty(antProject, "versionCode");
int versionCode;
try {
versionCode = Integer.parseInt(version);
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java
index 88f28f6..f2cd97f 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/ApkData.java
@@ -39,7 +39,6 @@ public class ApkData implements Comparable<ApkData> {
private static final String PROP_PROJECT = "project";
private static final String PROP_MINOR = "minor";
private static final String PROP_BUILDINFO = "buildinfo";
- private static final String PROP_OUTPUTNAME = "outputname";
private String mOutputName;
private String mRelativePath;
@@ -149,30 +148,26 @@ public class ApkData implements Comparable<ApkData> {
@Override
public String toString() {
- StringBuilder sb = new StringBuilder();
- write(sb, PROP_OUTPUTNAME, mOutputName);
- write(sb, PROP_BUILDINFO, mBuildInfo);
- sb.append(getLogLine());
-
- return sb.toString();
+ return getLogLine();
}
public String getLogLine() {
StringBuilder sb = new StringBuilder();
- sb.append(mBuildInfo).append(':');
+ sb.append(mOutputName).append(':');
+ write(sb, PROP_BUILDINFO, mBuildInfo);
write(sb, PROP_MINOR, mMinor);
write(sb, PROP_PROJECT, mRelativePath);
write(sb, PROP_API, mMinSdkVersion);
if (mGlVersion != ManifestData.GL_ES_VERSION_NOT_SET) {
- write(sb, PROP_GL, mGlVersion);
+ write(sb, PROP_GL, "0x" + Integer.toHexString(mGlVersion));
}
if (mAbi != null) {
write(sb, PROP_ABI, mAbi);
}
- write(sb, PROP_SCREENS, mSupportsScreens);
+ write(sb, PROP_SCREENS, mSupportsScreens.getEncodedValues());
return sb.toString();
}
@@ -195,7 +190,7 @@ public class ApkData implements Comparable<ApkData> {
return 1;
}
- comp = mSupportsScreens.compareTo(o.mSupportsScreens);
+ comp = mSupportsScreens.compareScreenSizesWith(o.mSupportsScreens);
if (comp != 0) return comp;
if (mGlVersion != ManifestData.GL_ES_VERSION_NOT_SET) {
@@ -212,6 +207,24 @@ public class ApkData implements Comparable<ApkData> {
return 0;
}
+ public boolean hasSameApkProperties(ApkData apk) {
+ if (mMinSdkVersion != apk.mMinSdkVersion ||
+ mSupportsScreens.equals(apk.mSupportsScreens) == false ||
+ mGlVersion != apk.mGlVersion) {
+ return false;
+ }
+
+ if (mAbi != null) {
+ if (mAbi.equals(apk.mAbi) == false) {
+ return false;
+ }
+ } else if (apk.mAbi != null) {
+ return false;
+ }
+
+ return true;
+ }
+
/**
* reads the apk description from a log line.
* @param line The fields to read, comma-separated.
@@ -220,7 +233,7 @@ public class ApkData implements Comparable<ApkData> {
*/
public void initFromLogLine(String line) {
int colon = line.indexOf(':');
- mBuildInfo = Integer.parseInt(line.substring(0, colon));
+ mOutputName = line.substring(0, colon);
String[] properties = line.substring(colon+1).split(";");
HashMap<String, String> map = new HashMap<String, String>();
for (String prop : properties) {
@@ -231,13 +244,19 @@ public class ApkData implements Comparable<ApkData> {
}
private void setValues(Map<String, String> values) {
+ mBuildInfo = Integer.parseInt(values.get(PROP_BUILDINFO));
mMinor = Integer.parseInt(values.get(PROP_MINOR));
mRelativePath = values.get(PROP_PROJECT);
mMinSdkVersion = Integer.parseInt(values.get(PROP_API));
String tmp = values.get(PROP_GL);
if (tmp != null) {
- mGlVersion = Integer.parseInt(tmp);
+ try {
+ mGlVersion = Integer.decode(tmp);
+ } catch (NumberFormatException e) {
+ // pass. This is probably due to a manual edit, and it'll most likely
+ // generate an error when matching the log to the current setup.
+ }
}
tmp = values.get(PROP_ABI);
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java
index 693a5f8..ae70e6f 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java
@@ -28,6 +28,7 @@ import com.android.sdklib.io.StreamException;
import com.android.sdklib.io.IAbstractFolder.FilenameFilter;
import com.android.sdklib.xml.AndroidManifestParser;
import com.android.sdklib.xml.ManifestData;
+import com.android.sdklib.xml.ManifestData.SupportsScreens;
import org.xml.sax.SAXException;
@@ -126,14 +127,14 @@ public class MultiApkExportHelper {
if (previousApks.length != apks.length) {
throw new ExportException(String.format(
"Project export is setup differently from previous export at versionCode %d.\n" +
- "Any change in the multi-apk configuration requires an increment of the versionCode.",
+ "Any change in the multi-apk configuration requires an increment of the versionCode in export.properties.",
mVersionCode));
}
for (int i = 0 ; i < previousApks.length ; i++) {
// update the minor value from what is in the log file.
apks[i].setMinor(previousApks[i].getMinor());
- if (apks[i].compareTo(previousApks[i]) != 0) {
+ if (apks[i].hasSameApkProperties(previousApks[i]) == false) {
throw new ExportException(String.format(
"Project export is setup differently from previous export at versionCode %d.\n" +
"Any change in the multi-apk configuration requires an increment of the versionCode.",
@@ -143,7 +144,6 @@ public class MultiApkExportHelper {
}
return apks;
-
}
/**
@@ -161,7 +161,7 @@ public class MultiApkExportHelper {
"# Multi-APK BUILD LOG.\n" +
"# This file serves two purpose:\n" +
"# - A log of what was built, showing what went in each APK and their properties.\n" +
- "# You can refer to this if you get a bug report for a specific versionCode." +
+ "# You can refer to this if you get a bug report for a specific versionCode.\n" +
"# - A way to update builds through minor revisions for specific APKs.\n" +
"# Only edit manually to change the minor properties for build you wish to respin.\n" +
"# Note that all APKs will be regenerated all the time.\n");
@@ -171,7 +171,7 @@ public class MultiApkExportHelper {
writer.append(
"# The format of the following lines is:\n" +
- "# <build number>:<property1>;<property2>;<property3>;...\n" +
+ "# <filename>:<property1>;<property2>;<property3>;...\n" +
"# Properties are written as <name>=<value>\n");
for (ApkData apk : apks) {
@@ -309,19 +309,58 @@ public class MultiApkExportHelper {
// - GL version
// - ABI (not managed at the Manifest level).
// if those values are the same between 2 manifest, then it's an error.
- if (minSdkVersion == previousManifest.data.getMinSdkVersion() &&
- manifestData.getSupportsScreensValues().equals(
- previousManifest.data.getSupportsScreensValues()) &&
- manifestData.getGlEsVersion() == previousManifest.data.getGlEsVersion()) {
- throw new ExportException(String.format(
- "Android manifests must differ in at least one of the following values:\n" +
- "- minSdkVersion\n" +
- "- SupportsScreen\n" +
- "- GL ES version.\n" +
- "%1$s and %2$s are considered identical for multi-apk export.",
- androidManifest.getOsLocation(),
- previousManifest.file.getOsLocation()));
+ // first the minSdkVersion.
+ if (minSdkVersion == previousManifest.data.getMinSdkVersion()) {
+ // if it's the same compare the rest.
+ SupportsScreens currentSS = manifestData.getSupportsScreensValues();
+ SupportsScreens previousSS = previousManifest.data.getSupportsScreensValues();
+ boolean sameSupportsScreens = currentSS.hasSameScreenSupportAs(previousSS);
+
+ // if it's the same, then it's an error. Can't export 2 projects that have the
+ // same approved (for multi-apk export) hard-properties.
+ if (manifestData.getGlEsVersion() == previousManifest.data.getGlEsVersion() &&
+ sameSupportsScreens) {
+
+ throw new ExportException(String.format(
+ "Android manifests must differ in at least one of the following values:\n" +
+ "- minSdkVersion\n" +
+ "- SupportsScreen (screen sizes only)\n" +
+ "- GL ES version.\n" +
+ "%1$s and %2$s are considered identical for multi-apk export.",
+ androidManifest.getOsLocation(),
+ previousManifest.file.getOsLocation()));
+ }
+
+ // At this point, either supports-screens or GL are different.
+ // Because supports-screens is the highest priority properties to be
+ // (potentially) different, we must do some extra checks on it.
+ // It must either be the same in both projects (difference is only on GL value),
+ // or follow theses rules:
+ // - Property in each projects must be strictly different, ie both projects
+ // cannot support the same screen size(s).
+ // - Property in each projects cannot overlap, ie a projects cannot support
+ // both a lower and a higher screen size than the other project.
+ // (ie APK1 supports small/large and APK2 supports normal).
+ if (sameSupportsScreens == false) {
+ if (currentSS.hasStrictlyDifferentScreenSupportAs(previousSS) == false) {
+ throw new ExportException(String.format(
+ "APK differentiation by Supports-Screens cannot support different APKs supporting the same screen size.\n" +
+ "%1$s supports %2$s\n" +
+ "%3$s supports %4$s\n",
+ androidManifest.getOsLocation(), currentSS.toString(),
+ previousManifest.file.getOsLocation(), previousSS.toString()));
+ }
+
+ if (currentSS.overlapWith(previousSS)) {
+ throw new ExportException(String.format(
+ "Unable to compute APK priority due to incompatible difference in Supports-Screens values.\n" +
+ "%1$s supports %2$s\n" +
+ "%3$s supports %4$s\n",
+ androidManifest.getOsLocation(), currentSS.toString(),
+ previousManifest.file.getOsLocation(), previousSS.toString()));
+ }
+ }
}
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/ManifestData.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/ManifestData.java
index da96cd4..cc1f3dd 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/ManifestData.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/ManifestData.java
@@ -158,7 +158,7 @@ public final class ManifestData {
*
* To get an instance with all the actual values, use {@link #resolveSupportsScreensValues(int)}
*/
- public final static class SupportsScreens implements Comparable<SupportsScreens> {
+ public final static class SupportsScreens {
private Boolean mResizeable;
private Boolean mAnyDensity;
private Boolean mSmallScreens;
@@ -168,6 +168,11 @@ public final class ManifestData {
public SupportsScreens() {
}
+ /**
+ * Instantiate an instance from a string. The string must have been created with
+ * {@link #getEncodedValues()}.
+ * @param value the string.
+ */
public SupportsScreens(String value) {
String[] values = value.split("\\|");
@@ -303,15 +308,144 @@ public final class ManifestData {
return false;
}
- public int compareTo(SupportsScreens o) {
+ /**
+ * Returns true if the two instances support the same screen sizes.
+ * This is similar to {@link #equals(Object)} except that it ignores the values of
+ * {@link #getAnyDensity()} and {@link #getResizeable()}.
+ * @param support the other instance to compare to.
+ * @return true if the two instances support the same screen sizes.
+ */
+ public boolean hasSameScreenSupportAs(SupportsScreens support) {
+ // since all the fields are guaranteed to be either Boolean.TRUE or Boolean.FALSE
+ // (or null), we can simply check they are identical and not bother with
+ // calling equals (which would require to check != null.
+ // see #getConstanntBoolean(Boolean)
+
+ // This only checks that matter here are the screen sizes. resizeable and anyDensity
+ // are not checked.
+ return mSmallScreens == support.mSmallScreens &&
+ mNormalScreens == support.mNormalScreens &&
+ mLargeScreens == support.mLargeScreens;
+ }
+
+ /**
+ * Returns true if the two instances have strictly different screen size support.
+ * This means that there is no screen size that they both support.
+ * @param support the other instance to compare to.
+ * @return true if they are stricly different.
+ */
+ public boolean hasStrictlyDifferentScreenSupportAs(SupportsScreens support) {
+ // since all the fields are guaranteed to be either Boolean.TRUE or Boolean.FALSE
+ // (or null), we can simply check they are identical and not bother with
+ // calling equals (which would require to check != null.
+ // see #getConstanntBoolean(Boolean)
+
+ // This only checks that matter here are the screen sizes. resizeable and anyDensity
+ // are not checked.
+ return (mSmallScreens != Boolean.TRUE || support.mSmallScreens != Boolean.TRUE) &&
+ (mNormalScreens != Boolean.TRUE || support.mNormalScreens != Boolean.TRUE) &&
+ (mLargeScreens != Boolean.TRUE || support.mLargeScreens != Boolean.TRUE);
+ }
+
+ /**
+ * Comparison of 2 Supports-screens. This only uses screen sizes (ignores resizeable and
+ * anyDensity), and considers that
+ * {@link #hasStrictlyDifferentScreenSupportAs(SupportsScreens)} returns true and
+ * {@link #overlapWith(SupportsScreens)} returns false.
+ * @throws IllegalArgumentException if the two instanced are not strictly different or
+ * overlap each other
+ * @see #hasStrictlyDifferentScreenSupportAs(SupportsScreens)
+ * @see #overlapWith(SupportsScreens)
+ */
+ public int compareScreenSizesWith(SupportsScreens o) {
+ if (hasStrictlyDifferentScreenSupportAs(o) == false) {
+ throw new IllegalArgumentException("The two instances are not strictly different.");
+ }
+ if (overlapWith(o)) {
+ throw new IllegalArgumentException("The two instances overlap each other.");
+ }
+
+ int comp = mLargeScreens.compareTo(o.mLargeScreens);
+ if (comp != 0) return comp;
+
+ comp = mNormalScreens.compareTo(o.mNormalScreens);
+ if (comp != 0) return comp;
+
+ comp = mSmallScreens.compareTo(o.mSmallScreens);
+ if (comp != 0) return comp;
+
return 0;
}
- @Override
- public String toString() {
+ /**
+ * Returns a string encoding of the content of the instance. This string can be used to
+ * instantiate a {@link SupportsScreens} object through
+ * {@link #SupportsScreens(String)}.
+ */
+ public String getEncodedValues() {
return String.format("%1$s|%2$s|%3$s|%4$s|%5$s",
mAnyDensity, mResizeable, mSmallScreens, mNormalScreens, mLargeScreens);
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ boolean alreadyOutputSomething = false;
+
+ if (Boolean.TRUE.equals(mSmallScreens)) {
+ alreadyOutputSomething = true;
+ sb.append("small");
+ }
+
+ if (Boolean.TRUE.equals(mNormalScreens)) {
+ if (alreadyOutputSomething) {
+ sb.append(", ");
+ }
+ alreadyOutputSomething = true;
+ sb.append("normal");
+ }
+
+ if (Boolean.TRUE.equals(mLargeScreens)) {
+ if (alreadyOutputSomething) {
+ sb.append(", ");
+ }
+ alreadyOutputSomething = true;
+ sb.append("large");
+ }
+
+ if (alreadyOutputSomething == false) {
+ sb.append("<none>");
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns true if the two instance overlap with each other.
+ * This can happen if one instances supports a size, when the other instance doesn't while
+ * supporting a size above and a size below.
+ * @param otherSS the other supports-screens to compare to.
+ */
+ public boolean overlapWith(SupportsScreens otherSS) {
+ if (mSmallScreens == null || mNormalScreens == null || mLargeScreens == null ||
+ otherSS.mSmallScreens == null || otherSS.mNormalScreens == null ||
+ otherSS.mLargeScreens == null) {
+ throw new IllegalArgumentException("Some screen sizes Boolean are not initialized");
+ }
+
+ if (mSmallScreens == Boolean.TRUE && mNormalScreens == Boolean.FALSE &&
+ mLargeScreens == Boolean.TRUE) {
+ return otherSS.mNormalScreens == Boolean.TRUE;
+ }
+
+ if (otherSS.mSmallScreens == Boolean.TRUE && otherSS.mNormalScreens == Boolean.FALSE &&
+ otherSS.mLargeScreens == Boolean.TRUE) {
+ return mNormalScreens == Boolean.TRUE;
+ }
+
+ return false;
+ }
}
/**
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/SupportsScreensTest.java b/sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/SupportsScreensTest.java
index c0cb12f..baf13b1 100644
--- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/SupportsScreensTest.java
+++ b/sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/SupportsScreensTest.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.sdklib.xml;
import com.android.sdklib.xml.ManifestData.SupportsScreens;
@@ -57,4 +73,74 @@ public class SupportsScreensTest extends TestCase {
assertEquals(Boolean.TRUE, supportsScreens.getNormalScreens());
assertEquals(Boolean.FALSE, supportsScreens.getLargeScreens());
}
+
+ public void testOverlapWith() {
+ SupportsScreens supportsN = new SupportsScreens("false|false|false|true|false");
+ SupportsScreens supportsSL = new SupportsScreens("false|false|true|false|true");
+
+ assertTrue(supportsN.overlapWith(supportsSL));
+ assertTrue(supportsSL.overlapWith(supportsN));
+ }
+
+ public void testCompareScreenSizesWith() {
+ // set instance that support all combo of the three sizes.
+ SupportsScreens supportsS = new SupportsScreens("false|false|true|false|false");
+ SupportsScreens supportsN = new SupportsScreens("false|false|false|true|false");
+ SupportsScreens supportsL = new SupportsScreens("false|false|false|false|true");
+ SupportsScreens supportsSL = new SupportsScreens("false|false|true|false|true");
+
+ assertEquals(-1, supportsS.compareScreenSizesWith(supportsN));
+ assertEquals( 1, supportsN.compareScreenSizesWith(supportsS));
+ assertEquals(-1, supportsN.compareScreenSizesWith(supportsL));
+ assertEquals(-1, supportsS.compareScreenSizesWith(supportsL));
+
+ // test thrown exception for overlapWith == true
+ try {
+ supportsSL.compareScreenSizesWith(supportsN);
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // success!
+ }
+
+ // test thrown exception for hasStrictlyDifferentScreenSupportAs == false
+ try {
+ supportsSL.compareScreenSizesWith(supportsS);
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // success!
+ }
+
+ }
+
+ public void testHasStrictlyDifferentScreenSupportAs() {
+ SupportsScreens supportsS = new SupportsScreens("false|false|true|false|false");
+ SupportsScreens supportsN = new SupportsScreens("false|false|false|true|false");
+ SupportsScreens supportsL = new SupportsScreens("false|false|false|false|true");
+
+ SupportsScreens supportsSN = new SupportsScreens("false|false|true|true|false");
+ SupportsScreens supportsNL = new SupportsScreens("false|false|false|true|true");
+ SupportsScreens supportsSL = new SupportsScreens("false|false|true|false|true");
+ SupportsScreens supportsSNL = new SupportsScreens("false|false|true|true|true");
+
+ assertTrue(supportsS.hasStrictlyDifferentScreenSupportAs(supportsN));
+ assertTrue(supportsS.hasStrictlyDifferentScreenSupportAs(supportsL));
+ assertTrue(supportsN.hasStrictlyDifferentScreenSupportAs(supportsS));
+ assertTrue(supportsN.hasStrictlyDifferentScreenSupportAs(supportsL));
+ assertTrue(supportsL.hasStrictlyDifferentScreenSupportAs(supportsS));
+ assertTrue(supportsL.hasStrictlyDifferentScreenSupportAs(supportsN));
+
+
+ assertFalse(supportsSN.hasStrictlyDifferentScreenSupportAs(supportsS));
+ assertFalse(supportsSN.hasStrictlyDifferentScreenSupportAs(supportsN));
+ assertTrue(supportsSN.hasStrictlyDifferentScreenSupportAs(supportsL));
+ assertFalse(supportsSL.hasStrictlyDifferentScreenSupportAs(supportsS));
+ assertTrue(supportsSL.hasStrictlyDifferentScreenSupportAs(supportsN));
+ assertFalse(supportsSL.hasStrictlyDifferentScreenSupportAs(supportsL));
+ assertTrue(supportsNL.hasStrictlyDifferentScreenSupportAs(supportsS));
+ assertFalse(supportsNL.hasStrictlyDifferentScreenSupportAs(supportsN));
+ assertFalse(supportsNL.hasStrictlyDifferentScreenSupportAs(supportsL));
+ assertFalse(supportsSNL.hasStrictlyDifferentScreenSupportAs(supportsS));
+ assertFalse(supportsSNL.hasStrictlyDifferentScreenSupportAs(supportsN));
+ assertFalse(supportsSNL.hasStrictlyDifferentScreenSupportAs(supportsL));
+ }
}