diff options
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);
+ }
}
}
}
|