aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java38
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/Main.java115
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java43
-rw-r--r--sdkmanager/app/tests/com/android/sdkmanager/MainTest.java93
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java2
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java12
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateNoWindow.java26
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java202
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java66
9 files changed, 430 insertions, 167 deletions
diff --git a/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java b/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
index 8f5dec4..50fc496 100644
--- a/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
+++ b/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
@@ -47,10 +47,10 @@ class CommandLineProcessor {
*/
/** Internal verb name for internally hidden flags. */
- public final static String GLOBAL_FLAG_VERB = "@@internal@@";
+ public final static String GLOBAL_FLAG_VERB = "@@internal@@"; //$NON-NLS-1$
/** String to use when the verb doesn't need any object. */
- public final static String NO_VERB_OBJECT = "";
+ public final static String NO_VERB_OBJECT = ""; //$NON-NLS-1$
/** The global help flag. */
public static final String KEY_HELP = "help";
@@ -183,7 +183,7 @@ class CommandLineProcessor {
public Object getValue(String verb, String directObject, String longFlagName) {
if (verb != null && directObject != null) {
- String key = verb + "/" + directObject + "/" + longFlagName;
+ String key = verb + '/' + directObject + '/' + longFlagName;
Arg arg = mArguments.get(key);
return arg.getCurrentValue();
}
@@ -216,7 +216,7 @@ class CommandLineProcessor {
* argument mode.
*/
protected void setValue(String verb, String directObject, String longFlagName, Object value) {
- String key = verb + "/" + directObject + "/" + longFlagName;
+ String key = verb + '/' + directObject + '/' + longFlagName;
Arg arg = mArguments.get(key);
arg.setCurrentValue(value);
}
@@ -238,16 +238,16 @@ class CommandLineProcessor {
for (int i = 0; i < n; i++) {
Arg arg = null;
String a = args[i];
- if (a.startsWith("--")) {
+ if (a.startsWith("--")) { //$NON-NLS-1$
arg = findLongArg(verb, directObject, a.substring(2));
- } else if (a.startsWith("-")) {
+ } else if (a.startsWith("-")) { //$NON-NLS-1$
arg = findShortArg(verb, directObject, a.substring(1));
}
// No matching argument name found
if (arg == null) {
// Does it looks like a dashed parameter?
- if (a.startsWith("-")) {
+ if (a.startsWith("-")) { //$NON-NLS-1$
if (verb == null || directObject == null) {
// It looks like a dashed parameter and we don't have a a verb/object
// set yet, the parameter was just given too early.
@@ -330,9 +330,9 @@ class CommandLineProcessor {
String b = args[i];
Arg dummyArg = null;
- if (b.startsWith("--")) {
+ if (b.startsWith("--")) { //$NON-NLS-1$
dummyArg = findLongArg(verb, directObject, b.substring(2));
- } else if (b.startsWith("-")) {
+ } else if (b.startsWith("-")) { //$NON-NLS-1$
dummyArg = findShortArg(verb, directObject, b.substring(1));
}
if (dummyArg != null) {
@@ -352,7 +352,7 @@ class CommandLineProcessor {
// used to print specific help.
// Setting a non-null error message triggers printing the help, however
// there is no specific error to print.
- errorMsg = "";
+ errorMsg = ""; //$NON-NLS-1$
}
}
@@ -392,9 +392,9 @@ class CommandLineProcessor {
arg.getDirectObject().equals(directObject)) {
if (arg.isMandatory() && arg.getCurrentValue() == null) {
if (missing == null) {
- missing = "--" + arg.getLongArg();
+ missing = "--" + arg.getLongArg(); //$NON-NLS-1$
} else {
- missing += ", --" + arg.getLongArg();
+ missing += ", --" + arg.getLongArg(); //$NON-NLS-1$
plural = true;
}
}
@@ -432,7 +432,7 @@ class CommandLineProcessor {
if (directObject == null) {
directObject = NO_VERB_OBJECT;
}
- String key = verb + "/" + directObject + "/" + longName;
+ String key = verb + '/' + directObject + '/' + longName; //$NON-NLS-1$
return mArguments.get(key);
}
@@ -497,7 +497,7 @@ class CommandLineProcessor {
"\n" +
"Global options:",
verb == null ? "action" :
- verb + (directObject == null ? "" : " " + directObject));
+ verb + (directObject == null ? "" : " " + directObject)); //$NON-NLS-1$
listOptions(GLOBAL_FLAG_VERB, NO_VERB_OBJECT);
if (verb == null || directObject == null) {
@@ -552,8 +552,8 @@ class CommandLineProcessor {
Arg arg = entry.getValue();
if (arg.getVerb().equals(verb) && arg.getDirectObject().equals(directObject)) {
- String value = "";
- String required = "";
+ String value = ""; //$NON-NLS-1$
+ String required = ""; //$NON-NLS-1$
if (arg.isMandatory()) {
required = " [required]";
@@ -828,7 +828,7 @@ class CommandLineProcessor {
* Internal helper to define a new argument for a give action.
*
* @param mode The {@link Mode} for the argument.
- * @param mandatory The argument is required (never if {@link Mode.BOOLEAN})
+ * @param mandatory The argument is required (never if {@link Mode#BOOLEAN})
* @param verb The verb name. Can be #INTERNAL_VERB.
* @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG.
* @param shortName The one-letter short argument name. Can be empty but not null.
@@ -853,7 +853,7 @@ class CommandLineProcessor {
directObject = NO_VERB_OBJECT;
}
- String key = verb + "/" + directObject + "/" + longName;
+ String key = verb + '/' + directObject + '/' + longName;
mArguments.put(key, new Arg(mode, mandatory,
verb, directObject, shortName, longName, description, defaultValue));
}
@@ -874,7 +874,7 @@ class CommandLineProcessor {
* @param args Format arguments.
*/
protected void stdout(String format, Object...args) {
- mLog.printf(format + "\n", args);
+ mLog.printf(format + '\n', args);
}
/**
diff --git a/sdkmanager/app/src/com/android/sdkmanager/Main.java b/sdkmanager/app/src/com/android/sdkmanager/Main.java
index bbefa99..3936286 100644
--- a/sdkmanager/app/src/com/android/sdkmanager/Main.java
+++ b/sdkmanager/app/src/com/android/sdkmanager/Main.java
@@ -16,6 +16,8 @@
package com.android.sdkmanager;
+import com.android.annotations.VisibleForTesting;
+import com.android.annotations.VisibleForTesting.Visibility;
import com.android.io.FileWrapper;
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
@@ -32,6 +34,7 @@ import com.android.sdklib.internal.project.ProjectCreator;
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.internal.project.ProjectCreator.OutputLevel;
import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
+import com.android.sdklib.repository.SdkAddonConstants;
import com.android.sdklib.repository.SdkRepoConstants;
import com.android.sdklib.xml.AndroidXPathFactory;
import com.android.sdkmanager.internal.repository.AboutPage;
@@ -41,6 +44,7 @@ import com.android.sdkuilib.internal.repository.UpdateNoWindow;
import com.android.sdkuilib.internal.widgets.MessageBoxLog;
import com.android.sdkuilib.repository.IUpdaterWindow;
import com.android.sdkuilib.repository.UpdaterWindow;
+import com.android.util.Pair;
import org.eclipse.swt.widgets.Display;
import org.xml.sax.InputSource;
@@ -53,6 +57,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
@@ -221,6 +227,15 @@ public class Main {
} else if (SdkCommandLine.OBJECT_AVD.equals(directObject)) {
displayAvdList();
+ } else if (SdkCommandLine.OBJECT_SDK.equals(directObject)) {
+ // We don't support a specific GUI for this.
+ // If the user forces a gui mode to see this list, simply launch the regular GUI.
+ if (!mSdkCommandLine.getFlagNoUI(verb)) {
+ showMainWindow(false /*autoUpdate*/);
+ } else {
+ displayRemoteSdkListNoUI();
+ }
+
} else {
displayTargetList();
displayAvdList();
@@ -260,7 +275,7 @@ public class Main {
updateExportProject();
} else if (SdkCommandLine.OBJECT_SDK.equals(directObject)) {
- if (mSdkCommandLine.getFlagNoUI()) {
+ if (mSdkCommandLine.getFlagNoUI(verb)) {
updateSdkNoUI();
} else {
showMainWindow(true /*autoUpdate*/);
@@ -319,6 +334,18 @@ public class Main {
}
}
+ private void displayRemoteSdkListNoUI() {
+ boolean force = mSdkCommandLine.getFlagForce();
+ boolean useHttp = mSdkCommandLine.getFlagNoHttps();
+ boolean obsolete = mSdkCommandLine.getFlagObsolete();
+ String proxyHost = mSdkCommandLine.getParamProxyHost();
+ String proxyPort = mSdkCommandLine.getParamProxyPort();
+
+ UpdateNoWindow upd = new UpdateNoWindow(mOsSdkFolder, mSdkManager, mSdkLog,
+ force, useHttp, proxyHost, proxyPort);
+ upd.listRemotePackages(obsolete);
+ }
+
/**
* Updates the whole SDK without any UI, just using console output.
*/
@@ -327,40 +354,76 @@ public class Main {
boolean useHttp = mSdkCommandLine.getFlagNoHttps();
boolean dryMode = mSdkCommandLine.getFlagDryMode();
boolean obsolete = mSdkCommandLine.getFlagObsolete();
- String proxyHost = mSdkCommandLine.getProxyHost();
- String proxyPort = mSdkCommandLine.getProxyPort();
+ String proxyHost = mSdkCommandLine.getParamProxyHost();
+ String proxyPort = mSdkCommandLine.getParamProxyPort();
// Check filter types.
+ Pair<String, ArrayList<String>> filterResult =
+ checkFilterValues(mSdkCommandLine.getParamFilter());
+ if (filterResult.getFirst() != null) {
+ // We got an error.
+ errorAndExit(filterResult.getFirst());
+ }
+
+ UpdateNoWindow upd = new UpdateNoWindow(mOsSdkFolder, mSdkManager, mSdkLog,
+ force, useHttp, proxyHost, proxyPort);
+ upd.updateAll(filterResult.getSecond(), obsolete, dryMode);
+ }
+
+ /**
+ * Checks the values from the filter parameter and returns a tuple
+ * (error , accepted values). Either error is null and accepted values is not,
+ * or the reverse.
+ * <p/>
+ * Note that this is a quick sanity check of the --filter parameter *before* we
+ * start loading the remote repository sources. Loading the remotes takes a while
+ * so it's worth doing a quick sanity check before hand.
+ *
+ * @param filter A comma-separated list of keywords
+ * @return A pair <error string, usable values>, only one must be null and the other non-null.
+ */
+ @VisibleForTesting(visibility=Visibility.PRIVATE)
+ Pair<String, ArrayList<String>> checkFilterValues(String filter) {
ArrayList<String> pkgFilter = new ArrayList<String>();
- String filter = mSdkCommandLine.getParamFilter();
+
if (filter != null && filter.length() > 0) {
+ // Available types
+ Set<String> filterTypes = new TreeSet<String>();
+ filterTypes.addAll(Arrays.asList(SdkRepoConstants.NODES));
+ filterTypes.addAll(Arrays.asList(SdkAddonConstants.NODES));
+
for (String t : filter.split(",")) { //$NON-NLS-1$
- if (t != null) {
- t = t.trim();
- if (t.length() > 0) {
- boolean found = false;
- for (String t2 : SdkRepoConstants.NODES) {
- if (t2.equals(t)) {
- pkgFilter.add(t2);
- found = true;
- break;
- }
- }
- if (!found) {
- errorAndExit(
- "Unknown package filter type '%1$s'.\nAccepted values are: %2$s",
- t,
- Arrays.toString(SdkRepoConstants.NODES));
- return;
- }
- }
+ if (t == null) {
+ continue;
+ }
+ t = t.trim();
+ if (t.length() <= 0) {
+ continue;
}
+
+ if (t.replaceAll("[0-9]+", "").length() == 0) { //$NON-NLS-1$ //$NON-NLS-2$
+ // If the filter argument *only* contains digits, accept it.
+ // It's probably an index for the remote repository list,
+ // which we can't validate yet.
+ pkgFilter.add(t);
+ continue;
+ }
+
+ if (filterTypes.contains(t)) {
+ pkgFilter.add(t);
+ continue;
+ }
+
+ return Pair.of(
+ String.format(
+ "Unknown package filter type '%1$s'.\nAccepted values are: %2$s",
+ t,
+ Arrays.toString(filterTypes.toArray())),
+ null);
}
}
- UpdateNoWindow upd = new UpdateNoWindow(mOsSdkFolder, mSdkManager, mSdkLog,
- force, useHttp, proxyHost, proxyPort);
- upd.updateAll(pkgFilter, obsolete, dryMode);
+ return Pair.of(null, pkgFilter);
}
/**
diff --git a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
index 5bb6c4e..157e896 100644
--- a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
+++ b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
@@ -104,6 +104,8 @@ class SdkCommandLine extends CommandLineProcessor {
{ VERB_LIST, OBJECT_TARGET,
"Lists existing targets.",
OBJECT_TARGETS },
+ { VERB_LIST, OBJECT_SDK,
+ "Lists remote SDK repository." },
{ VERB_CREATE, OBJECT_AVD,
"Creates a new Android Virtual Device." },
@@ -195,6 +197,31 @@ class SdkCommandLine extends CommandLineProcessor {
VERB_UPDATE, OBJECT_AVD, "n", KEY_NAME,
"Name of the AVD to update", null);
+ // --- list sdk ---
+
+ define(Mode.BOOLEAN, false,
+ VERB_LIST, OBJECT_SDK, "u", KEY_NO_UI,
+ "Displays list result on console (no GUI)", true);
+
+ define(Mode.BOOLEAN, false,
+ VERB_LIST, OBJECT_SDK, "s", KEY_NO_HTTPS,
+ "Uses HTTP instead of HTTPS (the default) for downloads", false);
+
+ define(Mode.STRING, false,
+ VERB_LIST, OBJECT_SDK, "", KEY_PROXY_PORT,
+ "HTTP/HTTPS proxy port (overrides settings if defined)",
+ null);
+
+ define(Mode.STRING, false,
+ VERB_LIST, OBJECT_SDK, "", KEY_PROXY_HOST,
+ "HTTP/HTTPS proxy host (overrides settings if defined)",
+ null);
+
+ define(Mode.BOOLEAN, false,
+ VERB_LIST, OBJECT_SDK, "o", KEY_OBSOLETE,
+ "Installs obsolete packages",
+ false);
+
// --- update sdk ---
define(Mode.BOOLEAN, false,
@@ -273,7 +300,8 @@ class SdkCommandLine extends CommandLineProcessor {
"Project name", null);
define(Mode.STRING, true,
VERB_CREATE, OBJECT_TEST_PROJECT, "m", KEY_MAIN_PROJECT,
- "Path to directory of the app under test, relative to the test project directory", null);
+ "Path to directory of the app under test, relative to the test project directory",
+ null);
// --- create lib-project ---
@@ -327,7 +355,8 @@ class SdkCommandLine extends CommandLineProcessor {
define(Mode.STRING, false,
VERB_UPDATE, OBJECT_PROJECT,
"l", KEY_LIBRARY,
- "Directory of an Android library to add, relative to this project's directory", null);
+ "Directory of an Android library to add, relative to this project's directory",
+ null);
// --- update test project ---
@@ -464,9 +493,9 @@ class SdkCommandLine extends CommandLineProcessor {
// -- some helpers for update sdk flags
- /** Helper to retrieve the --force flag. */
- public boolean getFlagNoUI() {
- return ((Boolean) getValue(null, null, KEY_NO_UI)).booleanValue();
+ /** Helper to retrieve the --no-ui flag. */
+ public boolean getFlagNoUI(String verb) {
+ return ((Boolean) getValue(verb, null, KEY_NO_UI)).booleanValue();
}
/** Helper to retrieve the --no-https flag. */
@@ -490,12 +519,12 @@ class SdkCommandLine extends CommandLineProcessor {
}
/** Helper to retrieve the --proxy-host value. */
- public String getProxyHost() {
+ public String getParamProxyHost() {
return ((String) getValue(null, null, KEY_PROXY_HOST));
}
/** Helper to retrieve the --proxy-port value. */
- public String getProxyPort() {
+ public String getParamProxyPort() {
return ((String) getValue(null, null, KEY_PROXY_PORT));
}
}
diff --git a/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java b/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java
index c4a7a53..3acb01a 100644
--- a/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java
+++ b/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java
@@ -17,15 +17,20 @@
package com.android.sdkmanager;
-import static java.io.File.createTempFile;
-
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdklib.mock.MockLog;
-import com.android.sdklib.SdkConstants;
+import com.android.sdklib.repository.SdkAddonConstants;
+import com.android.sdklib.repository.SdkRepoConstants;
+import com.android.util.Pair;
import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.TreeSet;
import junit.framework.TestCase;
@@ -42,7 +47,8 @@ public class MainTest extends TestCase {
@Override
public void setUp() throws Exception {
mLog = new MockLog();
- fakeSdkDir = createTempFile(this.getClass().getSimpleName() + "_" + this.getName(), null);
+ fakeSdkDir = File.createTempFile(
+ this.getClass().getSimpleName() + '_' + this.getName(), null);
mFakeSdk = SdkManagerTestUtil.makeFakeSdk(fakeSdkDir);
mSdkManager = SdkManager.createManager(mFakeSdk.getAbsolutePath(), mLog);
assertNotNull("sdkManager location was invalid", mSdkManager);
@@ -57,7 +63,7 @@ public class MainTest extends TestCase {
SdkManagerTestUtil.deleteDir(mFakeSdk);
}
- public void txestDisplayEmptyAvdList() {
+ public void testDisplayEmptyAvdList() {
Main main = new Main();
main.setLogger(mLog);
mLog.clear();
@@ -122,4 +128,81 @@ public class MainTest extends TestCase {
+ "]",
mLog.toString());
}
+
+ public void testCheckFilterValues() {
+ // These are the values we expect checkFilterValues() to match.
+ String[] expectedValues = {
+ "platform",
+ "tool",
+ "platform-tool",
+ "doc",
+ "sample",
+ "add-on",
+ "extra"
+ };
+
+ Set<String> expectedSet = new TreeSet<String>(Arrays.asList(expectedValues));
+
+ // First check the values are actually defined in the proper arrays
+ // in the Sdk*Constants.NODES
+ for (String node : SdkRepoConstants.NODES) {
+ assertTrue(
+ String.format(
+ "Error: value '%1$s' from SdkRepoConstants.NODES should be used in unit-test",
+ node),
+ expectedSet.contains(node));
+ }
+ for (String node : SdkAddonConstants.NODES) {
+ assertTrue(
+ String.format(
+ "Error: value '%1$s' from SdkAddonConstants.NODES should be used in unit-test",
+ node),
+ expectedSet.contains(node));
+ }
+
+ // Now check none of these values are NOT present in the NODES arrays
+ for (String node : SdkRepoConstants.NODES) {
+ expectedSet.remove(node);
+ }
+ for (String node : SdkAddonConstants.NODES) {
+ expectedSet.remove(node);
+ }
+ assertTrue(
+ String.format(
+ "Error: values %1$s are missing from Sdk[Repo|Addons]Constants.NODES",
+ Arrays.toString(expectedSet.toArray())),
+ expectedSet.isEmpty());
+
+ // We're done with expectedSet now
+ expectedSet = null;
+
+ // Finally check that checkFilterValues accepts all these values, one by one.
+ Main main = new Main();
+ main.setLogger(mLog);
+
+ for (int step = 0; step < 3; step++) {
+ for (String value : expectedValues) {
+ switch(step) {
+ // step 0: use value as-is
+ case 1:
+ // add some whitespace before and after
+ value = " " + value + " ";
+ break;
+ case 2:
+ // same with some empty arguments that should get ignored
+ value = " ," + value + " , ";
+ break;
+ }
+
+ Pair<String, ArrayList<String>> result = main.checkFilterValues(value);
+ assertNull(
+ String.format("Expected error to be null for value '%1$s', got: %2$s",
+ value, result.getFirst()),
+ result.getFirst());
+ assertEquals(
+ String.format("[%1$s]", value.replace(',', ' ').trim()),
+ Arrays.toString(result.getSecond().toArray()));
+ }
+ }
+ }
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
index f3fd347..4d5d616 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
@@ -161,7 +161,7 @@ public class ArchiveInstaller {
// if the file exists, check its checksum & size. Use it if complete
if (tmpFile.exists()) {
if (tmpFile.length() == archive.getSize()) {
- String chksum = "";
+ String chksum = ""; //$NON-NLS-1$
try {
chksum = fileChecksum(archive.getChecksumType().getMessageDigest(),
tmpFile,
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java
index 1a8a9f4..6fc26e3 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java
@@ -258,7 +258,7 @@ public abstract class SdkSource implements IDescription, Comparable<SdkSource> {
url = url.replaceAll("https://", "http://"); //$NON-NLS-1$ //$NON-NLS-2$
}
- monitor.setDescription("Fetching %1$s", url);
+ monitor.setDescription("Fetching URL: %1$s", url);
monitor.incProgress(1);
mFetchError = null;
@@ -284,7 +284,7 @@ public abstract class SdkSource implements IDescription, Comparable<SdkSource> {
}
if (xml != null) {
- monitor.setDescription("Validate XML");
+ monitor.setDescription(String.format("Validate XML: %1$s", url));
for (int tryOtherUrl = 0; tryOtherUrl < 2; tryOtherUrl++) {
// Explore the XML to find the potential XML schema version
@@ -432,7 +432,7 @@ public abstract class SdkSource implements IDescription, Comparable<SdkSource> {
monitor.incProgress(1);
if (xml != null) {
- monitor.setDescription("Parse XML");
+ monitor.setDescription(String.format("Parse XML: %1$s", url));
monitor.incProgress(1);
parsePackages(validatedDoc, validatedUri, monitor);
if (mPackages == null || mPackages.length == 0) {
@@ -748,7 +748,11 @@ public abstract class SdkSource implements IDescription, Comparable<SdkSource> {
if (p != null) {
packages.add(p);
- monitor.setDescription("Found %1$s", p.getShortDescription());
+ // TODO: change this to a monitor.print() in sdkman2. In between
+ // simply remove this which isn't very useful since it hides
+ // which source is being loaded in the progress dialog.
+ // Note that this breaks unit tests that depend on this output.
+ // monitor.setDescription("Found %1$s", p.getShortDescription());
}
} catch (Exception e) {
// Ignore invalid packages
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateNoWindow.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateNoWindow.java
index 2279b2d..ddf1e2e 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateNoWindow.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateNoWindow.java
@@ -28,11 +28,6 @@ import java.util.Properties;
/**
* Performs an update using only a non-interactive console output with no GUI.
- * <p/>
- * TODO: It may be useful in the future to let the filter specify packages names
- * rather than package types, typically to let the user upgrade to a new platform.
- * This can be achieved easily by simply allowing package names in the pkgFilter
- * argument.
*/
public class UpdateNoWindow {
@@ -111,6 +106,15 @@ public class UpdateNoWindow {
mUpdaterData.updateOrInstallAll_NoGUI(pkgFilter, includeObsoletes, dryMode);
}
+ /**
+ * Lists remote packages available for install using 'android update sdk --no-ui'.
+ *
+ * @param includeObsoletes True to also list and install obsolete packages.
+ */
+ public void listRemotePackages(boolean includeObsoletes) {
+ mUpdaterData.listRemotePackages_NoGUI(includeObsoletes);
+ }
+
// -----
/**
@@ -173,18 +177,18 @@ public class UpdateNoWindow {
public void setDescription(String descriptionFormat, Object...args) {
String last = mLastDesc;
- String line = String.format(" " + descriptionFormat, args);
+ String line = String.format(" " + descriptionFormat, args); //$NON-NLS-1$
// If the description contains a %, it generally indicates a recurring
// progress so we want a \r at the end.
if (line.indexOf('%') > -1) {
if (mLastProgressBase != null && line.startsWith(mLastProgressBase)) {
- line = " " + line.substring(mLastProgressBase.length());
+ line = " " + line.substring(mLastProgressBase.length()); //$NON-NLS-1$
}
- line += "\r";
+ line += '\r';
} else {
mLastProgressBase = line;
- line += "\n";
+ line += '\n';
}
// Skip line if it's the same as the last one.
@@ -198,10 +202,10 @@ public class UpdateNoWindow {
if (last != null &&
last.endsWith("\r") &&
!line.endsWith("\r")) {
- line = "\n" + line;
+ line = '\n' + line;
}
- mSdkLog.printf("%s", line);
+ mSdkLog.printf("%s", line); //$NON-NLS-1$
}
/**
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java
index 20ceab7..04cc947 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java
@@ -44,6 +44,7 @@ import com.android.sdklib.internal.repository.AddonsListFetcher.Site;
import com.android.sdklib.repository.SdkAddonConstants;
import com.android.sdklib.repository.SdkAddonsListConstants;
import com.android.sdklib.repository.SdkRepoConstants;
+import com.android.sdklib.util.SparseIntArray;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import com.android.sdkuilib.repository.ISdkChangeListener;
@@ -709,23 +710,17 @@ class UpdaterData implements IUpdaterData {
}
/**
- * Tries to update all the *existing* local packages.
- * This version is intended to run without a GUI and
- * only outputs to the current {@link ISdkLog}.
+ * Fetches all archives available on the known remote sources.
*
- * @param pkgFilter A list of {@link SdkRepoConstants#NODES} to limit the type of packages
- * we can update. A null or empty list means to update everything possible.
- * @param includeObsoletes True to also list and install obsolete packages.
- * @param dryMode True to check what would be updated/installed but do not actually
- * download or install anything.
+ * Used by {@link UpdaterData#listRemotePackages_NoGUI} and
+ * {@link UpdaterData#updateOrInstallAll_NoGUI}.
+ *
+ * @param includeObsoletes True to also list obsolete packages.
+ * @return A list of potential {@link ArchiveInfo} to install.
*/
- @SuppressWarnings("unchecked")
- public void updateOrInstallAll_NoGUI(
- Collection<String> pkgFilter,
- boolean includeObsoletes,
- boolean dryMode) {
-
+ private List<ArchiveInfo> getRemoteArchives_NoGUI(boolean includeObsoletes) {
refreshSources(true);
+ loadRemoteAddonsList();
UpdaterLogic ul = new UpdaterLogic(this);
List<ArchiveInfo> archives = ul.computeUpdates(
@@ -734,7 +729,6 @@ class UpdaterData implements IUpdaterData {
getLocalSdkParser().getPackages(),
includeObsoletes);
- loadRemoteAddonsList();
ul.addNewPlatforms(
archives,
getSources(),
@@ -742,66 +736,81 @@ class UpdaterData implements IUpdaterData {
includeObsoletes);
Collections.sort(archives);
+ return archives;
+ }
+
+ /**
+ * Lists remote packages available for install using
+ * {@link UpdaterData#updateOrInstallAll_NoGUI}.
+ *
+ * @param includeObsoletes True to also list obsolete packages.
+ */
+ public void listRemotePackages_NoGUI(boolean includeObsoletes) {
+
+ List<ArchiveInfo> archives = getRemoteArchives_NoGUI(includeObsoletes);
+
+ mSdkLog.printf("Packages available for installation or update: %1$d\n", archives.size());
+
+ int index = 1;
+ for (ArchiveInfo ai : archives) {
+ Archive a = ai.getNewArchive();
+ if (a != null) {
+ Package p = a.getParentPackage();
+ if (p != null) {
+ mSdkLog.printf("%1$ 4d- %2$s\n",
+ index,
+ p.getShortDescription());
+ index++;
+ }
+ }
+ }
+ }
+
+ /**
+ * Tries to update all the *existing* local packages.
+ * This version is intended to run without a GUI and
+ * only outputs to the current {@link ISdkLog}.
+ *
+ * @param pkgFilter A list of {@link SdkRepoConstants#NODES} to limit the type of packages
+ * we can update. A null or empty list means to update everything possible.
+ * @param includeObsoletes True to also list and install obsolete packages.
+ * @param dryMode True to check what would be updated/installed but do not actually
+ * download or install anything.
+ */
+ public void updateOrInstallAll_NoGUI(
+ Collection<String> pkgFilter,
+ boolean includeObsoletes,
+ boolean dryMode) {
+
+ List<ArchiveInfo> archives = getRemoteArchives_NoGUI(includeObsoletes);
// Filter the selected archives to only keep the ones matching the filter
if (pkgFilter != null && pkgFilter.size() > 0 && archives != null && archives.size() > 0) {
- // Map filter types to an SdkRepository Package type.
+ // Map filter types to an SdkRepository Package type,
+ // e.g. create a map "platform" => PlatformPackage.class
HashMap<String, Class<? extends Package>> pkgMap =
new HashMap<String, Class<? extends Package>>();
- // Automatically find the classes matching the node names
- ClassLoader classLoader = getClass().getClassLoader();
- String basePackage = Package.class.getPackage().getName();
- for (String node : SdkRepoConstants.NODES) {
- // Capitalize the name
- String name = node.substring(0, 1).toUpperCase() + node.substring(1);
-
- // We can have one dash at most in a name. If it's present, we'll try
- // with the dash or with the next letter capitalized.
- int dash = name.indexOf('-');
- if (dash > 0) {
- name = name.replaceFirst("-", ""); //$NON-NLS-1$ //$NON-NLS-2$
- }
+ mapFilterToPackageClass(pkgMap, SdkRepoConstants.NODES);
+ mapFilterToPackageClass(pkgMap, SdkAddonConstants.NODES);
- for (int alternatives = 0; alternatives < 2; alternatives++) {
+ // Now intersect this with the pkgFilter requested by the user, in order to
+ // only keep the classes that the user wants to install.
+ // We also create a set with the package indices requested by the user.
- String fqcn = basePackage + "." + name + "Package"; //$NON-NLS-1$ //$NON-NLS-2$
- try {
- Class<? extends Package> clazz =
- (Class<? extends Package>) classLoader.loadClass(fqcn);
- if (clazz != null) {
- pkgMap.put(node, clazz);
- continue;
- }
- } catch (Throwable ignore) {
- }
+ HashSet<Class<? extends Package>> userFilteredClasses =
+ new HashSet<Class<? extends Package>>();
+ SparseIntArray userFilteredIndices = new SparseIntArray();
- if (alternatives == 0 && dash > 0) {
- // Try an alternative where the next letter after the dash
- // is converted to an upper case.
- name = name.substring(0, dash) +
- name.substring(dash, dash + 1).toUpperCase() +
- name.substring(dash + 1);
- } else {
- break;
- }
- }
- }
+ for (String type : pkgFilter) {
+ if (type.replaceAll("[0-9]+", "").length() == 0) { //$NON-NLS-1$ //$NON-NLS-2$
+ // An all-digit number is a package index requested by the user.
+ int index = Integer.parseInt(type);
+ userFilteredIndices.put(index, index);
- if (SdkRepoConstants.NODES.length != pkgMap.size()) {
- // Sanity check in case we forget to update this node array.
- // We don't cancel the operation though.
- mSdkLog.printf(
- "*** Filter Mismatch! ***\n" +
- "*** The package filter list has changed. Please report this.");
- }
+ } else if (pkgMap.containsKey(type)) {
+ userFilteredClasses.add(pkgMap.get(type));
- // Now make a set of the types that are allowed by the filter.
- HashSet<Class<? extends Package>> allowedPkgSet =
- new HashSet<Class<? extends Package>>();
- for (String type : pkgFilter) {
- if (pkgMap.containsKey(type)) {
- allowedPkgSet.add(pkgMap.get(type));
} else {
// This should not happen unless there's a mismatch in the package map.
mSdkLog.error(null, "Ignoring unknown package filter '%1$s'", type);
@@ -811,15 +820,24 @@ class UpdaterData implements IUpdaterData {
// we don't need the map anymore
pkgMap = null;
- Iterator<ArchiveInfo> it = archives.iterator();
- while (it.hasNext()) {
+ // Now filter the remote archives list to keep:
+ // - any package which class matches userFilteredClasses
+ // - any package index which matches userFilteredIndices
+
+ int index = 1;
+ for (Iterator<ArchiveInfo> it = archives.iterator(); it.hasNext(); ) {
boolean keep = false;
ArchiveInfo ai = it.next();
Archive a = ai.getNewArchive();
if (a != null) {
Package p = a.getParentPackage();
- if (p != null && allowedPkgSet.contains(p.getClass())) {
- keep = true;
+ if (p != null) {
+ if (userFilteredClasses.contains(p.getClass()) ||
+ userFilteredIndices.get(index) > 0) {
+ keep = true;
+ }
+
+ index++;
}
}
@@ -856,6 +874,52 @@ class UpdaterData implements IUpdaterData {
}
}
+ @SuppressWarnings("unchecked")
+ private void mapFilterToPackageClass(
+ HashMap<String, Class<? extends Package>> inOutPkgMap,
+ String[] nodes) {
+
+ // Automatically find the classes matching the node names
+ ClassLoader classLoader = getClass().getClassLoader();
+ String basePackage = Package.class.getPackage().getName();
+
+ for (String node : nodes) {
+ // Capitalize the name
+ String name = node.substring(0, 1).toUpperCase() + node.substring(1);
+
+ // We can have one dash at most in a name. If it's present, we'll try
+ // with the dash or with the next letter capitalized.
+ int dash = name.indexOf('-');
+ if (dash > 0) {
+ name = name.replaceFirst("-", "");
+ }
+
+ for (int alternatives = 0; alternatives < 2; alternatives++) {
+
+ String fqcn = basePackage + '.' + name + "Package"; //$NON-NLS-1$
+ try {
+ Class<? extends Package> clazz =
+ (Class<? extends Package>) classLoader.loadClass(fqcn);
+ if (clazz != null) {
+ inOutPkgMap.put(node, clazz);
+ continue;
+ }
+ } catch (Throwable ignore) {
+ }
+
+ if (alternatives == 0 && dash > 0) {
+ // Try an alternative where the next letter after the dash
+ // is converted to an upper case.
+ name = name.substring(0, dash) +
+ name.substring(dash, dash + 1).toUpperCase() +
+ name.substring(dash + 1);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
/**
* Refresh all sources. This is invoked either internally (reusing an existing monitor)
* or as a UI callback on the remote page "Refresh" button (in which case the monitor is
@@ -943,7 +1007,7 @@ class UpdaterData implements IUpdaterData {
if (url != null) {
if (getSettingsController().getForceHttp()) {
- url = url.replaceAll("https://", "http://"); //$NON-NLS-1$ //$NON-NLS-2$
+ url = url.replaceAll("https://", "http://"); //$NON-NLS-1$ //$NON-NLS-2$
}
AddonsListFetcher fetcher = new AddonsListFetcher();
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java
index 91afed6..aa2be48 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java
@@ -120,6 +120,20 @@ class UpdaterLogic {
/**
* Finds new packages that the user does not have in his/her local SDK
* and adds them to the list of archives to install.
+ * <p/>
+ * The default is to only find "new" platforms, that is anything more
+ * recent than the highest platform currently installed.
+ * A side effect is that for an empty SDK install this will list *all*
+ * platforms available (since there's no "highest" installed platform.)
+ *
+ * @param archives The in-out list of archives to install. Typically the
+ * list is not empty at first as it should contain any archives that is
+ * already scheduled for install. This method will add to the list.
+ * @param sources The list of all sources, to fetch them as necessary.
+ * @param localPkgs The list of all currently installed packages.
+ * @param includeObsoletes When true, this will list all platform
+ * (included these lower than the highest installed one) as well as
+ * all obsolete packages of these platforms.
*/
public void addNewPlatforms(
Collection<ArchiveInfo> archives,
@@ -136,32 +150,34 @@ class UpdaterLogic {
float currentAddonScore = 0;
float currentDocScore = 0;
HashMap<String, Float> currentExtraScore = new HashMap<String, Float>();
- if (localPkgs != null) {
- for (Package p : localPkgs) {
- int rev = p.getRevision();
- int api = 0;
- boolean isPreview = false;
- if (p instanceof IPackageVersion) {
- AndroidVersion vers = ((IPackageVersion) p).getVersion();
- api = vers.getApiLevel();
- isPreview = vers.isPreview();
- }
-
- // The score is 10*api + (1 if preview) + rev/100
- // This allows previews to rank above a non-preview and
- // allows revisions to rank appropriately.
- float score = api * 10 + (isPreview ? 1 : 0) + rev/100.f;
+ if (!includeObsoletes) {
+ if (localPkgs != null) {
+ for (Package p : localPkgs) {
+ int rev = p.getRevision();
+ int api = 0;
+ boolean isPreview = false;
+ if (p instanceof IPackageVersion) {
+ AndroidVersion vers = ((IPackageVersion) p).getVersion();
+ api = vers.getApiLevel();
+ isPreview = vers.isPreview();
+ }
- if (p instanceof PlatformPackage) {
- currentPlatformScore = Math.max(currentPlatformScore, score);
- } else if (p instanceof SamplePackage) {
- currentSampleScore = Math.max(currentSampleScore, score);
- } else if (p instanceof AddonPackage) {
- currentAddonScore = Math.max(currentAddonScore, score);
- } else if (p instanceof ExtraPackage) {
- currentExtraScore.put(((ExtraPackage) p).getPath(), score);
- } else if (p instanceof DocPackage) {
- currentDocScore = Math.max(currentDocScore, score);
+ // The score is 10*api + (1 if preview) + rev/100
+ // This allows previews to rank above a non-preview and
+ // allows revisions to rank appropriately.
+ float score = api * 10 + (isPreview ? 1 : 0) + rev/100.f;
+
+ if (p instanceof PlatformPackage) {
+ currentPlatformScore = Math.max(currentPlatformScore, score);
+ } else if (p instanceof SamplePackage) {
+ currentSampleScore = Math.max(currentSampleScore, score);
+ } else if (p instanceof AddonPackage) {
+ currentAddonScore = Math.max(currentAddonScore, score);
+ } else if (p instanceof ExtraPackage) {
+ currentExtraScore.put(((ExtraPackage) p).getPath(), score);
+ } else if (p instanceof DocPackage) {
+ currentDocScore = Math.max(currentDocScore, score);
+ }
}
}
}