diff options
Diffstat (limited to 'sdkmanager')
130 files changed, 7583 insertions, 2537 deletions
diff --git a/sdkmanager/app/.classpath b/sdkmanager/app/.classpath index 3380ba1..50576bb 100644 --- a/sdkmanager/app/.classpath +++ b/sdkmanager/app/.classpath @@ -1,12 +1,14 @@ -<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="tests"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
- <classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
- <classpathentry combineaccessrules="false" kind="src" path="/SdkUiLib"/>
- <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
- <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry excluding="**/Android.mk" kind="src" path="src"/> + <classpathentry excluding="**/Android.mk" kind="src" path="tests"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/> + <classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/> + <classpathentry combineaccessrules="false" kind="src" path="/SdkUiLib"/> + <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> + <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/> + <classpathentry combineaccessrules="false" kind="src" path="/common"/> + <classpathentry kind="output" path="bin"/> +</classpath> + diff --git a/sdkmanager/app/.settings/org.eclipse.jdt.core.prefs b/sdkmanager/app/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..a363b10 --- /dev/null +++ b/sdkmanager/app/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,64 @@ +#Wed Mar 16 15:11:09 PDT 2011 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning diff --git a/sdkmanager/app/Android.mk b/sdkmanager/app/Android.mk index 24ba61f..d7b630e 100644 --- a/sdkmanager/app/Android.mk +++ b/sdkmanager/app/Android.mk @@ -1,5 +1,33 @@ # Copyright 2007 The Android Open Source Project # -SDKMANAGERAPP_LOCAL_DIR := $(call my-dir) -include $(SDKMANAGERAPP_LOCAL_DIR)/etc/Android.mk -include $(SDKMANAGERAPP_LOCAL_DIR)/src/Android.mk +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_JAVA_RESOURCE_DIRS := src + +LOCAL_JAR_MANIFEST := etc/manifest.txt + +# IMPORTANT: if you add a new dependency here, please make sure +# to also check the following files: +# sdkmanager/app/etc/manifest.txt +# sdkmanager/app/etc/android.bat +# (Note that we don't reference swt.jar in these files since +# it is dynamically added by android.bat/.sh based on whether the +# current VM is 32 or 64 bit.) +LOCAL_JAVA_LIBRARIES := \ + androidprefs \ + sdklib \ + sdkuilib \ + swt \ + org.eclipse.jface_3.4.2.M20090107-0800 \ + org.eclipse.equinox.common_3.4.0.v20080421-2006 \ + org.eclipse.core.commands_3.4.0.I20080509-2000 + +LOCAL_MODULE := sdkmanager + +include $(BUILD_HOST_JAVA_LIBRARY) + +# Build all sub-directories +include $(call all-makefiles-under,$(LOCAL_PATH)) + diff --git a/sdkmanager/app/NOTICE b/sdkmanager/app/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/sdkmanager/app/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/sdkmanager/app/etc/android b/sdkmanager/app/etc/android index 2e17009..555e071 100755 --- a/sdkmanager/app/etc/android +++ b/sdkmanager/app/etc/android @@ -74,9 +74,10 @@ fi if [ "$OSTYPE" = "cygwin" ] ; then jarpath=`cygpath -w "$frameworkdir/$jarfile"` + jarpath="$jarpath;"`cygpath -w "$frameworkdir/swtmenubar.jar"` progdir=`cygpath -w "$progdir"` else - jarpath="$frameworkdir/$jarfile" + jarpath="$frameworkdir/$jarfile:$frameworkdir/swtmenubar.jar" fi # Get the current content of java.ext.dirs so that we can add to it instead of replacing @@ -105,9 +106,9 @@ if [ ! -d "$swtpath" ]; then exit 1 fi -if [ -z "$1" ]; then - echo "Starting Android SDK and AVD Manager" -fi - # need to use "java.ext.dirs" because "-jar" causes classpath to be ignored -exec "$java_cmd" -Xmx256M $os_opts $java_debug -Dcom.android.sdkmanager.toolsdir="$progdir" -classpath "$jarpath:$swtpath/swt.jar" com.android.sdkmanager.Main "$@" +exec "$java_cmd" \ + -Xmx256M $os_opts $java_debug \ + -Dcom.android.sdkmanager.toolsdir="$progdir" \ + -classpath "$jarpath:$swtpath/swt.jar" \ + com.android.sdkmanager.Main "$@" diff --git a/sdkmanager/app/etc/android.bat b/sdkmanager/app/etc/android.bat index daa6b8a..0d83734 100755 --- a/sdkmanager/app/etc/android.bat +++ b/sdkmanager/app/etc/android.bat @@ -37,7 +37,7 @@ set java_exe= call lib\find_java.bat
if not defined java_exe goto :EOF
-set jar_path=lib\sdkmanager.jar
+set jar_path=lib\sdkmanager.jar;lib\swtmenubar.jar
rem Set SWT.Jar path based on current architecture (x86 or x86_64)
for /f %%a in ('%java_exe% -jar lib\archquery.jar') do set swt_path=lib\%%a
@@ -45,7 +45,7 @@ for /f %%a in ('%java_exe% -jar lib\archquery.jar') do set swt_path=lib\%%a if "%1 %2"=="update sdk" goto StartUi
if not "%1"=="" goto EndTempCopy
:StartUi
- echo [INFO] Starting Android SDK and AVD Manager
+ rem Starting Android SDK and AVD Manager UI
rem We're now going to create a temp dir to hold all the Jar files needed
rem to run the android tool, copy them in the temp dir and finally execute
diff --git a/sdkmanager/app/etc/manifest.txt b/sdkmanager/app/etc/manifest.txt index 51845c7..bb2e8c4 100644 --- a/sdkmanager/app/etc/manifest.txt +++ b/sdkmanager/app/etc/manifest.txt @@ -1,2 +1,2 @@ Main-Class: com.android.sdkmanager.Main -Class-Path: androidprefs.jar common.jar sdklib.jar sdkuilib.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar +Class-Path: androidprefs.jar common.jar sdklib.jar sdkuilib.jar swtmenubar.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar diff --git a/sdkmanager/app/src/Android.mk b/sdkmanager/app/src/Android.mk deleted file mode 100644 index 7520f96..0000000 --- a/sdkmanager/app/src/Android.mk +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2007 The Android Open Source Project -# -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_JAVA_RESOURCE_DIRS := . - -LOCAL_JAR_MANIFEST := ../etc/manifest.txt - -# If the dependency list is changed, etc/manifest.txt -# MUST be updated as well (Except for swt.jar which is dynamically -# added based on whether the VM is 32 or 64 bit) -LOCAL_JAVA_LIBRARIES := \ - androidprefs \ - sdklib \ - sdkuilib \ - swt \ - org.eclipse.jface_3.4.2.M20090107-0800 \ - org.eclipse.equinox.common_3.4.0.v20080421-2006 \ - org.eclipse.core.commands_3.4.0.I20080509-2000 - -LOCAL_MODULE := sdkmanager - -include $(BUILD_HOST_JAVA_LIBRARY) - 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 1fe6d97..5ac5f4c 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/Main.java +++ b/sdkmanager/app/src/com/android/sdkmanager/Main.java @@ -16,30 +16,35 @@ 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; import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.IAndroidTarget.IOptionalLibrary; import com.android.sdklib.ISdkLog; import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkManager; +import com.android.sdklib.IAndroidTarget.IOptionalLibrary; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.internal.avd.AvdManager; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.HardwareProperties; import com.android.sdklib.internal.avd.HardwareProperties.HardwareProperty; import com.android.sdklib.internal.project.ProjectCreator; -import com.android.sdklib.internal.project.ProjectCreator.OutputLevel; 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.io.FileWrapper; +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; import com.android.sdkmanager.internal.repository.SettingsPage; -import com.android.sdkuilib.internal.repository.LocalPackagesPage; +import com.android.sdkuilib.internal.repository.PackagesPage; 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; @@ -52,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; @@ -220,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(); @@ -259,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*/); @@ -290,23 +306,19 @@ public class Main { */ private void showMainWindow(boolean autoUpdate) { try { - // display a message talking about the command line version - System.out.printf("No command line parameters provided, launching UI.\n" + - "See 'android --help' for operations from the command line.\n"); - MessageBoxLog errorLogger = new MessageBoxLog( "SDK Manager", Display.getCurrent(), true /*logErrorsOnly*/); - UpdaterWindow window = new UpdaterWindow( + IUpdaterWindow window = new UpdaterWindow( null /* parentShell */, errorLogger, mOsSdkFolder); window.registerPage("Settings", SettingsPage.class); window.registerPage("About", AboutPage.class); if (autoUpdate) { - window.setInitialPage(LocalPackagesPage.class); + window.setInitialPage(PackagesPage.class); window.setRequestAutoUpdate(true); } window.open(); @@ -318,6 +330,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. */ @@ -326,40 +350,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); } /** @@ -720,6 +780,18 @@ public class Main { * Displays the list of available Targets (Platforms and Add-ons) */ private void displayTargetList() { + + // Compact output, suitable for scripts. + if (mSdkCommandLine != null && mSdkCommandLine.getFlagCompact()) { + char eol = mSdkCommandLine.getFlagEolNull() ? '\0' : '\n'; + + for (IAndroidTarget target : mSdkManager.getTargets()) { + mSdkLog.printf("%1$s%2$c", target.hashString(), eol); + } + + return; + } + mSdkLog.printf("Available Android targets:\n"); int index = 1; @@ -798,16 +870,30 @@ public class Main { * @param avdManager */ public void displayAvdList(AvdManager avdManager) { - mSdkLog.printf("Available Android Virtual Devices:\n"); AvdInfo[] avds = avdManager.getValidAvds(); + + // Compact output, suitable for scripts. + if (mSdkCommandLine != null && mSdkCommandLine.getFlagCompact()) { + char eol = mSdkCommandLine.getFlagEolNull() ? '\0' : '\n'; + + for (int index = 0 ; index < avds.length ; index++) { + AvdInfo info = avds[index]; + mSdkLog.printf("%1$s%2$c", info.getName(), eol); + } + + return; + } + + mSdkLog.printf("Available Android Virtual Devices:\n"); + for (int index = 0 ; index < avds.length ; index++) { AvdInfo info = avds[index]; if (index > 0) { mSdkLog.printf("---------\n"); } mSdkLog.printf(" Name: %s\n", info.getName()); - mSdkLog.printf(" Path: %s\n", info.getPath()); + mSdkLog.printf(" Path: %s\n", info.getDataFolderPath()); // get the target of the AVD IAndroidTarget target = info.getTarget(); @@ -856,7 +942,8 @@ public class Main { mSdkLog.printf("---------\n"); } mSdkLog.printf(" Name: %s\n", info.getName() == null ? "--" : info.getName()); - mSdkLog.printf(" Path: %s\n", info.getPath() == null ? "--" : info.getPath()); + mSdkLog.printf(" Path: %s\n", + info.getDataFolderPath() == null ? "--" : info.getDataFolderPath()); String error = info.getErrorMessage(); mSdkLog.printf(" Error: %s\n", error == null ? "Uknown error" : error); @@ -923,8 +1010,7 @@ public class Main { if (paramFolderPath != null) { avdFolder = new File(paramFolderPath); } else { - avdFolder = new File(AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD, - avdName + AvdManager.AVD_FOLDER_EXTENSION); + avdFolder = AvdInfo.getDefaultAvdFolder(avdManager, avdName); } // Validate skin is either default (empty) or NNNxMMM or a valid skin name. @@ -981,15 +1067,20 @@ public class Main { oldAvdInfo = avdManager.getAvd(avdName, false /*validAvdOnly*/); } + // NOTE: need to update with command line processor selectivity + + String preferredAbi = SdkConstants.ABI_ARMEABI; @SuppressWarnings("unused") // newAvdInfo is never read, yet useful for debugging AvdInfo newAvdInfo = avdManager.createAvd(avdFolder, avdName, target, + preferredAbi, skin, mSdkCommandLine.getParamSdCard(), hardwareConfig, - removePrevious, mSdkCommandLine.getFlagSnapshot(), + removePrevious, + false, //edit existing mSdkLog); } catch (AndroidLocationException e) { @@ -1045,7 +1136,7 @@ public class Main { // check if paths are the same. Use File methods to account for OS idiosyncrasies. try { File f1 = new File(paramFolderPath).getCanonicalFile(); - File f2 = new File(info.getPath()).getCanonicalFile(); + File f2 = new File(info.getDataFolderPath()).getCanonicalFile(); if (f1.equals(f2)) { // same canonical path, so not actually a move paramFolderPath = null; @@ -1071,7 +1162,7 @@ public class Main { File originalFolder = new File( AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD, info.getName() + AvdManager.AVD_FOLDER_EXTENSION); - if (originalFolder.equals(info.getPath())) { + if (originalFolder.equals(info.getDataFolderPath())) { try { // The AVD is using the default data folder path based on the AVD name. // That folder needs to be adjusted to use the new name. @@ -1093,7 +1184,7 @@ public class Main { } File ini = info.getIniFile(); - if (ini.equals(AvdInfo.getIniFile(newName))) { + if (ini.equals(AvdInfo.getDefaultIniFile(avdManager, newName))) { errorAndExit("The AVD file '%s' is in the way.", ini.getCanonicalPath()); return; } diff --git a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java index 5bb6c4e..fb15cb5 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java +++ b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java @@ -38,47 +38,49 @@ class SdkCommandLine extends CommandLineProcessor { * or optional) for the given action. */ - public final static String VERB_LIST = "list"; - public final static String VERB_CREATE = "create"; - public final static String VERB_MOVE = "move"; - public final static String VERB_DELETE = "delete"; - public final static String VERB_UPDATE = "update"; - - public static final String OBJECT_SDK = "sdk"; - public static final String OBJECT_AVD = "avd"; - public static final String OBJECT_AVDS = "avds"; - public static final String OBJECT_TARGET = "target"; - public static final String OBJECT_TARGETS = "targets"; - public static final String OBJECT_PROJECT = "project"; - public static final String OBJECT_TEST_PROJECT = "test-project"; - public static final String OBJECT_LIB_PROJECT = "lib-project"; - public static final String OBJECT_EXPORT_PROJECT = "export-project"; - public static final String OBJECT_ADB = "adb"; - - public static final String ARG_ALIAS = "alias"; - public static final String ARG_ACTIVITY = "activity"; + public final static String VERB_LIST = "list"; //$NON-NLS-1$ + public final static String VERB_CREATE = "create"; //$NON-NLS-1$ + public final static String VERB_MOVE = "move"; //$NON-NLS-1$ + public final static String VERB_DELETE = "delete"; //$NON-NLS-1$ + public final static String VERB_UPDATE = "update"; //$NON-NLS-1$ + + public static final String OBJECT_SDK = "sdk"; //$NON-NLS-1$ + public static final String OBJECT_AVD = "avd"; //$NON-NLS-1$ + public static final String OBJECT_AVDS = "avds"; //$NON-NLS-1$ + public static final String OBJECT_TARGET = "target"; //$NON-NLS-1$ + public static final String OBJECT_TARGETS = "targets"; //$NON-NLS-1$ + public static final String OBJECT_PROJECT = "project"; //$NON-NLS-1$ + public static final String OBJECT_TEST_PROJECT = "test-project"; //$NON-NLS-1$ + public static final String OBJECT_LIB_PROJECT = "lib-project"; //$NON-NLS-1$ + public static final String OBJECT_EXPORT_PROJECT = "export-project"; //$NON-NLS-1$ + public static final String OBJECT_ADB = "adb"; //$NON-NLS-1$ + + public static final String ARG_ALIAS = "alias"; //$NON-NLS-1$ + public static final String ARG_ACTIVITY = "activity"; //$NON-NLS-1$ public static final String KEY_ACTIVITY = ARG_ACTIVITY; - public static final String KEY_PACKAGE = "package"; - public static final String KEY_MODE = "mode"; + public static final String KEY_PACKAGE = "package"; //$NON-NLS-1$ + public static final String KEY_MODE = "mode"; //$NON-NLS-1$ public static final String KEY_TARGET_ID = OBJECT_TARGET; - public static final String KEY_NAME = "name"; - public static final String KEY_LIBRARY = "library"; - public static final String KEY_PATH = "path"; - public static final String KEY_FILTER = "filter"; - public static final String KEY_SKIN = "skin"; - public static final String KEY_SDCARD = "sdcard"; - public static final String KEY_FORCE = "force"; - public static final String KEY_RENAME = "rename"; - public static final String KEY_SUBPROJECTS = "subprojects"; - public static final String KEY_MAIN_PROJECT = "main"; - public static final String KEY_NO_UI = "no-ui"; - public static final String KEY_NO_HTTPS = "no-https"; - public static final String KEY_PROXY_PORT = "proxy-port"; - public static final String KEY_PROXY_HOST = "proxy-host"; - public static final String KEY_DRY_MODE = "dry-mode"; - public static final String KEY_OBSOLETE = "obsolete"; - public static final String KEY_SNAPSHOT = "snapshot"; + public static final String KEY_NAME = "name"; //$NON-NLS-1$ + public static final String KEY_LIBRARY = "library"; //$NON-NLS-1$ + public static final String KEY_PATH = "path"; //$NON-NLS-1$ + public static final String KEY_FILTER = "filter"; //$NON-NLS-1$ + public static final String KEY_SKIN = "skin"; //$NON-NLS-1$ + public static final String KEY_SDCARD = "sdcard"; //$NON-NLS-1$ + public static final String KEY_FORCE = "force"; //$NON-NLS-1$ + public static final String KEY_RENAME = "rename"; //$NON-NLS-1$ + public static final String KEY_SUBPROJECTS = "subprojects"; //$NON-NLS-1$ + public static final String KEY_MAIN_PROJECT = "main"; //$NON-NLS-1$ + public static final String KEY_NO_UI = "no-ui"; //$NON-NLS-1$ + public static final String KEY_NO_HTTPS = "no-https"; //$NON-NLS-1$ + public static final String KEY_PROXY_PORT = "proxy-port"; //$NON-NLS-1$ + public static final String KEY_PROXY_HOST = "proxy-host"; //$NON-NLS-1$ + public static final String KEY_DRY_MODE = "dry-mode"; //$NON-NLS-1$ + public static final String KEY_OBSOLETE = "obsolete"; //$NON-NLS-1$ + public static final String KEY_SNAPSHOT = "snapshot"; //$NON-NLS-1$ + public static final String KEY_COMPACT = "compact"; //$NON-NLS-1$ + public static final String KEY_EOL_NULL = "null"; //$NON-NLS-1$ /** * Action definitions for SdkManager command line. @@ -104,6 +106,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." }, @@ -147,92 +151,139 @@ class SdkCommandLine extends CommandLineProcessor { // The following defines the parameters of the actions defined in mAction. + // --- list avds --- + + define(Mode.BOOLEAN, false, + VERB_LIST, OBJECT_AVD, "c", KEY_COMPACT, //$NON-NLS-1$ + "Compact output (suitable for scripts)", false); + + define(Mode.BOOLEAN, false, + VERB_LIST, OBJECT_AVD, "0", KEY_EOL_NULL, //$NON-NLS-1$ + "Terminates lines with \\0 instead of \\n (e.g. for xargs -0). Only used by --" + KEY_COMPACT + ".", + false); + + // --- list targets --- + + define(Mode.BOOLEAN, false, + VERB_LIST, OBJECT_TARGET, "c", KEY_COMPACT, //$NON-NLS-1$ + "Compact output (suitable for scripts)", false); + + define(Mode.BOOLEAN, false, + VERB_LIST, OBJECT_TARGET, "0", KEY_EOL_NULL, //$NON-NLS-1$ + "Terminates lines with \\0 instead of \\n (e.g. for xargs -0) Only used by --" + KEY_COMPACT + ".", + false); + // --- create avd --- define(Mode.STRING, false, - VERB_CREATE, OBJECT_AVD, "p", KEY_PATH, + VERB_CREATE, OBJECT_AVD, "p", KEY_PATH, //$NON-NLS-1$ "Directory where the new AVD will be created", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_AVD, "n", KEY_NAME, + VERB_CREATE, OBJECT_AVD, "n", KEY_NAME, //$NON-NLS-1$ "Name of the new AVD", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_AVD, "t", KEY_TARGET_ID, + VERB_CREATE, OBJECT_AVD, "t", KEY_TARGET_ID, //$NON-NLS-1$ "Target ID of the new AVD", null); define(Mode.STRING, false, - VERB_CREATE, OBJECT_AVD, "s", KEY_SKIN, + VERB_CREATE, OBJECT_AVD, "s", KEY_SKIN, //$NON-NLS-1$ "Skin for the new AVD", null); define(Mode.STRING, false, - VERB_CREATE, OBJECT_AVD, "c", KEY_SDCARD, + VERB_CREATE, OBJECT_AVD, "c", KEY_SDCARD, //$NON-NLS-1$ "Path to a shared SD card image, or size of a new sdcard for the new AVD", null); define(Mode.BOOLEAN, false, - VERB_CREATE, OBJECT_AVD, "f", KEY_FORCE, + VERB_CREATE, OBJECT_AVD, "f", KEY_FORCE, //$NON-NLS-1$ "Forces creation (overwrites an existing AVD)", false); define(Mode.BOOLEAN, false, - VERB_CREATE, OBJECT_AVD, "a", KEY_SNAPSHOT, + VERB_CREATE, OBJECT_AVD, "a", KEY_SNAPSHOT, //$NON-NLS-1$ "Place a snapshots file in the AVD, to enable persistence.", false); // --- delete avd --- define(Mode.STRING, true, - VERB_DELETE, OBJECT_AVD, "n", KEY_NAME, + VERB_DELETE, OBJECT_AVD, "n", KEY_NAME, //$NON-NLS-1$ "Name of the AVD to delete", null); // --- move avd --- define(Mode.STRING, true, - VERB_MOVE, OBJECT_AVD, "n", KEY_NAME, + VERB_MOVE, OBJECT_AVD, "n", KEY_NAME, //$NON-NLS-1$ "Name of the AVD to move or rename", null); define(Mode.STRING, false, - VERB_MOVE, OBJECT_AVD, "r", KEY_RENAME, + VERB_MOVE, OBJECT_AVD, "r", KEY_RENAME, //$NON-NLS-1$ "New name of the AVD", null); define(Mode.STRING, false, - VERB_MOVE, OBJECT_AVD, "p", KEY_PATH, + VERB_MOVE, OBJECT_AVD, "p", KEY_PATH, //$NON-NLS-1$ "Path to the AVD's new directory", null); // --- update avd --- define(Mode.STRING, true, - VERB_UPDATE, OBJECT_AVD, "n", KEY_NAME, + VERB_UPDATE, OBJECT_AVD, "n", KEY_NAME, //$NON-NLS-1$ "Name of the AVD to update", null); + // --- list sdk --- + + define(Mode.BOOLEAN, false, + VERB_LIST, OBJECT_SDK, "u", KEY_NO_UI, //$NON-NLS-1$ + "Displays list result on console (no GUI)", true); + + define(Mode.BOOLEAN, false, + VERB_LIST, OBJECT_SDK, "s", KEY_NO_HTTPS, //$NON-NLS-1$ + "Uses HTTP instead of HTTPS (the default) for downloads", false); + + define(Mode.STRING, false, + VERB_LIST, OBJECT_SDK, "", KEY_PROXY_PORT, //$NON-NLS-1$ + "HTTP/HTTPS proxy port (overrides settings if defined)", + null); + + define(Mode.STRING, false, + VERB_LIST, OBJECT_SDK, "", KEY_PROXY_HOST, //$NON-NLS-1$ + "HTTP/HTTPS proxy host (overrides settings if defined)", + null); + + define(Mode.BOOLEAN, false, + VERB_LIST, OBJECT_SDK, "o", KEY_OBSOLETE, //$NON-NLS-1$ + "Installs obsolete packages", + false); + // --- update sdk --- define(Mode.BOOLEAN, false, - VERB_UPDATE, OBJECT_SDK, "u", KEY_NO_UI, + VERB_UPDATE, OBJECT_SDK, "u", KEY_NO_UI, //$NON-NLS-1$ "Updates from command-line (does not display the GUI)", false); define(Mode.BOOLEAN, false, - VERB_UPDATE, OBJECT_SDK, "s", KEY_NO_HTTPS, + VERB_UPDATE, OBJECT_SDK, "s", KEY_NO_HTTPS, //$NON-NLS-1$ "Uses HTTP instead of HTTPS (the default) for downloads", false); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_SDK, "", KEY_PROXY_PORT, + VERB_UPDATE, OBJECT_SDK, "", KEY_PROXY_PORT, //$NON-NLS-1$ "HTTP/HTTPS proxy port (overrides settings if defined)", null); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_SDK, "", KEY_PROXY_HOST, + VERB_UPDATE, OBJECT_SDK, "", KEY_PROXY_HOST, //$NON-NLS-1$ "HTTP/HTTPS proxy host (overrides settings if defined)", null); define(Mode.BOOLEAN, false, - VERB_UPDATE, OBJECT_SDK, "f", KEY_FORCE, + VERB_UPDATE, OBJECT_SDK, "f", KEY_FORCE, //$NON-NLS-1$ "Forces replacement of a package or its parts, even if something has been modified", false); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_SDK, "t", KEY_FILTER, + VERB_UPDATE, OBJECT_SDK, "t", KEY_FILTER, //$NON-NLS-1$ "A filter that limits the update to the specified types of packages in the form of\n" + "a comma-separated list of " + Arrays.toString(SdkRepoConstants.NODES), null); define(Mode.BOOLEAN, false, - VERB_UPDATE, OBJECT_SDK, "o", KEY_OBSOLETE, + VERB_UPDATE, OBJECT_SDK, "o", KEY_OBSOLETE, //$NON-NLS-1$ "Installs obsolete packages", false); define(Mode.BOOLEAN, false, - VERB_UPDATE, OBJECT_SDK, "n", KEY_DRY_MODE, + VERB_UPDATE, OBJECT_SDK, "n", KEY_DRY_MODE, //$NON-NLS-1$ "Simulates the update but does not download or install anything", false); @@ -242,7 +293,7 @@ class SdkCommandLine extends CommandLineProcessor { This currently does not work, the alias build rules need to be fixed. define(Mode.ENUM, true, - VERB_CREATE, OBJECT_PROJECT, "m", KEY_MODE, + VERB_CREATE, OBJECT_PROJECT, "m", KEY_MODE, //$NON-NLS-1$ "Project mode", new String[] { ARG_ACTIVITY, ARG_ALIAS }); */ define(Mode.STRING, true, @@ -250,45 +301,44 @@ class SdkCommandLine extends CommandLineProcessor { "p", KEY_PATH, "The new project's directory", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_PROJECT, "t", KEY_TARGET_ID, + VERB_CREATE, OBJECT_PROJECT, "t", KEY_TARGET_ID, //$NON-NLS-1$ "Target ID of the new project", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_PROJECT, "k", KEY_PACKAGE, + VERB_CREATE, OBJECT_PROJECT, "k", KEY_PACKAGE, //$NON-NLS-1$ "Android package name for the application", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_PROJECT, "a", KEY_ACTIVITY, + VERB_CREATE, OBJECT_PROJECT, "a", KEY_ACTIVITY, //$NON-NLS-1$ "Name of the default Activity that is created", null); define(Mode.STRING, false, - VERB_CREATE, OBJECT_PROJECT, "n", KEY_NAME, + VERB_CREATE, OBJECT_PROJECT, "n", KEY_NAME, //$NON-NLS-1$ "Project name", null); // --- create test-project --- define(Mode.STRING, true, - VERB_CREATE, OBJECT_TEST_PROJECT, - "p", KEY_PATH, + VERB_CREATE, OBJECT_TEST_PROJECT, "p", KEY_PATH, //$NON-NLS-1$ "The new project's directory", null); define(Mode.STRING, false, - VERB_CREATE, OBJECT_TEST_PROJECT, "n", KEY_NAME, + VERB_CREATE, OBJECT_TEST_PROJECT, "n", KEY_NAME, //$NON-NLS-1$ "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); + VERB_CREATE, OBJECT_TEST_PROJECT, "m", KEY_MAIN_PROJECT, //$NON-NLS-1$ + "Path to directory of the app under test, relative to the test project directory", + null); // --- create lib-project --- define(Mode.STRING, true, - VERB_CREATE, OBJECT_LIB_PROJECT, - "p", KEY_PATH, + VERB_CREATE, OBJECT_LIB_PROJECT, "p", KEY_PATH, //$NON-NLS-1$ "The new project's directory", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_LIB_PROJECT, "t", KEY_TARGET_ID, + VERB_CREATE, OBJECT_LIB_PROJECT, "t", KEY_TARGET_ID, //$NON-NLS-1$ "Target ID of the new project", null); define(Mode.STRING, false, - VERB_CREATE, OBJECT_LIB_PROJECT, "n", KEY_NAME, + VERB_CREATE, OBJECT_LIB_PROJECT, "n", KEY_NAME, //$NON-NLS-1$ "Project name", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_LIB_PROJECT, "k", KEY_PACKAGE, + VERB_CREATE, OBJECT_LIB_PROJECT, "k", KEY_PACKAGE, //$NON-NLS-1$ "Android package name for the library", null); // --- create export-project --- @@ -296,74 +346,63 @@ class SdkCommandLine extends CommandLineProcessor { * disabled until the feature is officially supported. define(Mode.STRING, true, - VERB_CREATE, OBJECT_EXPORT_PROJECT, - "p", KEY_PATH, + VERB_CREATE, OBJECT_EXPORT_PROJECT, "p", KEY_PATH, //$NON-NLS-1$ "Location path of new project", null); define(Mode.STRING, false, - VERB_CREATE, OBJECT_EXPORT_PROJECT, "n", KEY_NAME, + VERB_CREATE, OBJECT_EXPORT_PROJECT, "n", KEY_NAME, //$NON-NLS-1$ "Project name", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_EXPORT_PROJECT, "k", KEY_PACKAGE, + VERB_CREATE, OBJECT_EXPORT_PROJECT, "k", KEY_PACKAGE, //$NON-NLS-1$ "Package name", null); */ // --- update project --- define(Mode.STRING, true, - VERB_UPDATE, OBJECT_PROJECT, - "p", KEY_PATH, + VERB_UPDATE, OBJECT_PROJECT, "p", KEY_PATH, //$NON-NLS-1$ "The project's directory", null); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_PROJECT, - "t", KEY_TARGET_ID, + VERB_UPDATE, OBJECT_PROJECT, "t", KEY_TARGET_ID, //$NON-NLS-1$ "Target ID to set for the project", null); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_PROJECT, - "n", KEY_NAME, + VERB_UPDATE, OBJECT_PROJECT, "n", KEY_NAME, //$NON-NLS-1$ "Project name", null); define(Mode.BOOLEAN, false, - VERB_UPDATE, OBJECT_PROJECT, - "s", KEY_SUBPROJECTS, + VERB_UPDATE, OBJECT_PROJECT, "s", KEY_SUBPROJECTS, //$NON-NLS-1$ "Also updates any projects in sub-folders, such as test projects.", false); 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); + VERB_UPDATE, OBJECT_PROJECT, "l", KEY_LIBRARY, //$NON-NLS-1$ + "Directory of an Android library to add, relative to this project's directory", + null); // --- update test project --- define(Mode.STRING, true, - VERB_UPDATE, OBJECT_TEST_PROJECT, - "p", KEY_PATH, + VERB_UPDATE, OBJECT_TEST_PROJECT, "p", KEY_PATH, //$NON-NLS-1$ "The project's directory", null); define(Mode.STRING, true, - VERB_UPDATE, OBJECT_TEST_PROJECT, - "m", KEY_MAIN_PROJECT, + VERB_UPDATE, OBJECT_TEST_PROJECT, "m", KEY_MAIN_PROJECT, //$NON-NLS-1$ "Directory of the app under test, relative to the test project directory", null); // --- update lib project --- define(Mode.STRING, true, - VERB_UPDATE, OBJECT_LIB_PROJECT, - "p", KEY_PATH, + VERB_UPDATE, OBJECT_LIB_PROJECT, "p", KEY_PATH, //$NON-NLS-1$ "The project's directory", null); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_LIB_PROJECT, - "t", KEY_TARGET_ID, + VERB_UPDATE, OBJECT_LIB_PROJECT, "t", KEY_TARGET_ID, //$NON-NLS-1$ "Target ID to set for the project", null); // --- update export project --- /* * disabled until the feature is officially supported. define(Mode.STRING, true, - VERB_UPDATE, OBJECT_EXPORT_PROJECT, - "p", KEY_PATH, + VERB_UPDATE, OBJECT_EXPORT_PROJECT, "p", KEY_PATH, //$NON-NLS-1$ "Location path of the project", null); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_EXPORT_PROJECT, - "n", KEY_NAME, + VERB_UPDATE, OBJECT_EXPORT_PROJECT, "n", KEY_NAME, //$NON-NLS-1$ "Project name", null); define(Mode.BOOLEAN, false, - VERB_UPDATE, OBJECT_EXPORT_PROJECT, "f", KEY_FORCE, + VERB_UPDATE, OBJECT_EXPORT_PROJECT, "f", KEY_FORCE, //$NON-NLS-1$ "Force replacing the build.xml file", false); */ } @@ -464,9 +503,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 +529,24 @@ 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)); } + + // -- some helpers for list avds and list targets flags + + /** Helper to retrieve the --compact value. */ + public boolean getFlagCompact() { + return ((Boolean) getValue(null, null, KEY_COMPACT)).booleanValue(); + } + + /** Helper to retrieve the --null value. */ + public boolean getFlagEolNull() { + return ((Boolean) getValue(null, null, KEY_EOL_NULL)).booleanValue(); + } } diff --git a/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java b/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java index cb2f981..4c72e1e 100755 --- a/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java +++ b/sdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java @@ -70,7 +70,7 @@ public class AboutPage extends Composite { "Revision %1$s\n" +
"Add-on XML Schema #%2$d\n" +
"Repository XML Schema #%3$d\n" +
- "Copyright (C) 2009-2010 The Android Open Source Project.",
+ "Copyright (C) 2009-2011 The Android Open Source Project.",
getRevision(),
SdkAddonConstants.NS_LATEST_VERSION,
SdkRepoConstants.NS_LATEST_VERSION));
diff --git a/sdkmanager/app/tests/Android.mk b/sdkmanager/app/tests/Android.mk new file mode 100644 index 0000000..4f67370 --- /dev/null +++ b/sdkmanager/app/tests/Android.mk @@ -0,0 +1,28 @@ +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + + +include $(CLEAR_VARS) + +# Only compile source java files in this lib. +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_MODULE := sdkmanager-tests +LOCAL_MODULE_TAGS := optional + +LOCAL_JAVA_LIBRARIES := sdkmanager sdklib-tests junit + +include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java b/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java index 4ae107c..a5a8289 100644 --- a/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java +++ b/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java @@ -16,59 +16,57 @@ package com.android.sdkmanager; -import static java.io.File.createTempFile; - +import com.android.io.FileWrapper; import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.SdkManager; -import com.android.sdklib.internal.avd.AvdManager; +import com.android.sdklib.SdkConstants; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.internal.project.ProjectProperties; -import com.android.sdklib.io.FileWrapper; -import com.android.sdklib.mock.MockLog; import java.io.File; import java.util.Map; -import junit.framework.TestCase; - -public class AvdManagerTest extends TestCase { +public class AvdManagerTest extends SdkManagerTestCase { - private AvdManager mAvdManager; - private SdkManager mSdkManager; - private MockLog mLog; - private File mFakeSdk; - private File mAvdFolder; private IAndroidTarget mTarget; + private File mAvdFolder; @Override public void setUp() throws Exception { - mLog = new MockLog(); - mFakeSdk = SdkManagerTestUtil.makeFakeSdk(createTempFile(this.getClass().getSimpleName(), null)); - mSdkManager = SdkManager.createManager(mFakeSdk.getAbsolutePath(), mLog); - assertNotNull("sdkManager location was invalid", mSdkManager); + super.setUp(); - mAvdManager = new AvdManager(mSdkManager, mLog); - mAvdFolder = new File(mFakeSdk, "avdData"); - mTarget = mSdkManager.getTargets()[0]; + mTarget = getSdkManager().getTargets()[0]; + mAvdFolder = AvdInfo.getDefaultAvdFolder(getAvdManager(), getName()); } @Override public void tearDown() throws Exception { - SdkManagerTestUtil.deleteDir(mFakeSdk); + super.tearDown(); } public void testCreateAvdWithoutSnapshot() { - mAvdManager.createAvd( - mAvdFolder, this.getName(), mTarget, null, null, null, false, false, mLog); - assertEquals("[P Created AVD '" + this.getName() + "' based on Android 0.0\n]", - mLog.toString()); + getAvdManager().createAvd( + mAvdFolder, + this.getName(), + mTarget, + SdkConstants.ABI_ARMEABI, + null, // skinName + null, // sdName + null, // properties + false, // createSnapshot + false, // removePrevious + false, // editExisting + getLog()); + + assertEquals("[P Created AVD '" + this.getName() + "' based on Android 0.0, ARM (armeabi) processor\n]", + getLog().toString()); assertTrue("Expected config.ini in " + mAvdFolder, new File(mAvdFolder, "config.ini").exists()); Map<String, String> map = ProjectProperties.parsePropertyFile( - new FileWrapper(mAvdFolder, "config.ini"), mLog); + new FileWrapper(mAvdFolder, "config.ini"), getLog()); assertEquals("HVGA", map.get("skin.name")); - assertEquals("platforms/v0_0/skins/HVGA", map.get("skin.path")); - assertEquals("platforms/v0_0/images/", map.get("image.sysdir.1")); + assertEquals("platforms/v0_0/skins/HVGA", map.get("skin.path").replace(File.separatorChar, '/')); + assertEquals("platforms/v0_0/images/", map.get("image.sysdir.1").replace(File.separatorChar, '/')); assertEquals(null, map.get("snapshot.present")); assertTrue("Expected userdata.img in " + mAvdFolder, new File(mAvdFolder, "userdata.img").exists()); @@ -77,15 +75,26 @@ public class AvdManagerTest extends TestCase { } public void testCreateAvdWithSnapshot() { - mAvdManager.createAvd( - mAvdFolder, this.getName(), mTarget, null, null, null, false, true, mLog); - assertEquals("[P Created AVD '" + this.getName() + "' based on Android 0.0\n]", - mLog.toString()); + getAvdManager().createAvd( + mAvdFolder, + this.getName(), + mTarget, + SdkConstants.ABI_ARMEABI, + null, // skinName + null, // sdName + null, // properties + true, // createSnapshot + false, // removePrevious + false, // editExisting + getLog()); + + assertEquals("[P Created AVD '" + this.getName() + "' based on Android 0.0, ARM (armeabi) processor\n]", + getLog().toString()); assertTrue("Expected snapshots.img in " + mAvdFolder, new File(mAvdFolder, "snapshots.img").exists()); Map<String, String> map = ProjectProperties.parsePropertyFile( - new FileWrapper(mAvdFolder, "config.ini"), mLog); + new FileWrapper(mAvdFolder, "config.ini"), getLog()); assertEquals("true", map.get("snapshot.present")); } } diff --git a/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java b/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java index 29516e3..4a17e32 100644 --- a/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java +++ b/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java @@ -17,60 +17,63 @@ package com.android.sdkmanager; -import static java.io.File.createTempFile; - import com.android.sdklib.IAndroidTarget; -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.internal.avd.AvdInfo; +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; - -public class MainTest extends TestCase { +public class MainTest extends SdkManagerTestCase { - private File mFakeSdk; - private MockLog mLog; - private SdkManager mSdkManager; - private AvdManager mAvdManager; - private File mAvdFolder; private IAndroidTarget mTarget; - private File fakeSdkDir; + private File mAvdFolder; @Override public void setUp() throws Exception { - mLog = new MockLog(); - fakeSdkDir = createTempFile(this.getClass().getSimpleName() + "_" + this.getName(), null); - mFakeSdk = SdkManagerTestUtil.makeFakeSdk(fakeSdkDir); - mSdkManager = SdkManager.createManager(mFakeSdk.getAbsolutePath(), mLog); - assertNotNull("sdkManager location was invalid", mSdkManager); - - mAvdManager = new AvdManager(mSdkManager, mLog); - mAvdFolder = new File(mFakeSdk, "avdData"); - mTarget = mSdkManager.getTargets()[0]; + super.setUp(); + + mTarget = getSdkManager().getTargets()[0]; + mAvdFolder = AvdInfo.getDefaultAvdFolder(getAvdManager(), getName()); } @Override public void tearDown() throws Exception { - SdkManagerTestUtil.deleteDir(mFakeSdk); + super.tearDown(); } - public void txestDisplayEmptyAvdList() { + public void testDisplayEmptyAvdList() { Main main = new Main(); - main.setLogger(mLog); - mLog.clear(); - main.displayAvdList(mAvdManager); - assertEquals("P Available Android Virtual Devices:\n", mLog.toString()); + main.setLogger(getLog()); + getLog().clear(); + main.displayAvdList(getAvdManager()); + assertEquals("[P Available Android Virtual Devices:\n]", getLog().toString()); } public void testDisplayAvdListOfOneNonSnapshot() { Main main = new Main(); - main.setLogger(mLog); - mAvdManager.createAvd( - mAvdFolder, this.getName(), mTarget, null, null, null, false, false, mLog); - mLog.clear(); - main.displayAvdList(mAvdManager); + main.setLogger(getLog()); + getAvdManager().createAvd( + mAvdFolder, + this.getName(), + mTarget, + SdkConstants.ABI_ARMEABI, + null, // skinName + null, // sdName + null, // properties + false, // createSnapshot + false, // removePrevious + false, // editExisting + getLog()); + + getLog().clear(); + main.displayAvdList(getAvdManager()); assertEquals( "[P Available Android Virtual Devices:\n" + ", P Name: " + this.getName() + "\n" @@ -78,16 +81,28 @@ public class MainTest extends TestCase { + ", P Target: Android 0.0 (API level 0)\n" + ", P Skin: HVGA\n" + "]", - mLog.toString()); + getLog().toString()); } public void testDisplayAvdListOfOneSnapshot() { Main main = new Main(); - main.setLogger(mLog); - mAvdManager.createAvd( - mAvdFolder, this.getName(), mTarget, null, null, null, false, true, mLog); - mLog.clear(); - main.displayAvdList(mAvdManager); + main.setLogger(getLog()); + + getAvdManager().createAvd( + mAvdFolder, + this.getName(), + mTarget, + SdkConstants.ABI_ARMEABI, + null, // skinName + null, // sdName + null, // properties + true, // createSnapshot + false, // removePrevious + false, // editExisting + getLog()); + + getLog().clear(); + main.displayAvdList(getAvdManager()); assertEquals( "[P Available Android Virtual Devices:\n" + ", P Name: " + this.getName() + "\n" @@ -96,6 +111,83 @@ public class MainTest extends TestCase { + ", P Skin: HVGA\n" + ", P Snapshot: true\n" + "]", - mLog.toString()); + getLog().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(getLog()); + + 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/app/tests/com/android/sdkmanager/SdkManagerTestCase.java b/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestCase.java new file mode 100755 index 0000000..9fdd852 --- /dev/null +++ b/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestCase.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdkmanager; + + +import com.android.prefs.AndroidLocation; +import com.android.prefs.AndroidLocation.AndroidLocationException; +import com.android.sdklib.ISdkLog; +import com.android.sdklib.SdkConstants; +import com.android.sdklib.SdkManager; +import com.android.sdklib.internal.avd.AvdManager; +import com.android.sdklib.mock.MockLog; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import junit.framework.TestCase; + +/** + * Test case that allocates a temporary SDK, a temporary AVD base folder + * with an SdkManager and an AvdManager that points to them. + */ +public abstract class SdkManagerTestCase extends TestCase { + + private File mFakeSdk; + private MockLog mLog; + private SdkManager mSdkManager; + private TmpAvdManager mAvdManager; + + /** Returns the {@link MockLog} for this test case. */ + public MockLog getLog() { + return mLog; + } + + /** Returns the {@link SdkManager} for this test case. */ + public SdkManager getSdkManager() { + return mSdkManager; + } + + /** Returns the {@link AvdManager} for this test case. */ + public TmpAvdManager getAvdManager() { + return mAvdManager; + } + + /** + * Sets up a {@link MockLog}, a fake SDK in a temporary directory + * and an AVD Manager pointing to an initially-empty AVD directory. + */ + @Override + public void setUp() throws Exception { + mLog = new MockLog(); + mFakeSdk = makeFakeSdk(); + mSdkManager = SdkManager.createManager(mFakeSdk.getAbsolutePath(), mLog); + assertNotNull("SdkManager location was invalid", mSdkManager); + + mAvdManager = new TmpAvdManager(mSdkManager, mLog); + } + + /** + * Removes the temporary SDK and AVD directories. + */ + @Override + public void tearDown() throws Exception { + deleteDir(mFakeSdk); + } + + /** + * An {@link AvdManager} that uses a temporary directory + * located <em>inside</em> the SDK directory for testing. + * The AVD list should be initially empty. + */ + protected static class TmpAvdManager extends AvdManager { + + /* + * Implementation detail: + * - When the super.AvdManager constructor is invoked, it will invoke + * the buildAvdFilesList() to fill the initial AVD list, which will in + * turn call getBaseAvdFolder(). + * - That's why mTmpAvdRoot is initialized in getAvdRoot() rather than + * in the constructor, since we can't initialize fields before the super() + * call. + */ + + /** + * AVD Root, initialized "lazily" when the AVD root is first requested. + */ + private File mTmpAvdRoot; + + public TmpAvdManager(SdkManager sdkManager, ISdkLog log) throws AndroidLocationException { + super(sdkManager, log); + } + + @Override + public String getBaseAvdFolder() throws AndroidLocationException { + if (mTmpAvdRoot == null) { + mTmpAvdRoot = new File(getSdkManager().getLocation(), "tmp_avds"); + mTmpAvdRoot.mkdirs(); + } + return mTmpAvdRoot.getAbsolutePath(); + } + } + + /** + * Build enough of a skeleton SDK to make the tests pass. + * <p/> + * Ideally this wouldn't touch the file system but the current + * structure of the SdkManager and AvdManager makes this difficult. + * + * @return Path to the temporary SDK root + * @throws IOException + */ + private File makeFakeSdk() throws IOException { + + File tmpFile = File.createTempFile( + this.getClass().getSimpleName() + '_' + this.getName(), null); + tmpFile.delete(); + tmpFile.mkdirs(); + + AndroidLocation.resetFolder(); + System.setProperty("user.home", tmpFile.getAbsolutePath()); + File addonsDir = new File(tmpFile, SdkConstants.FD_ADDONS); + addonsDir.mkdir(); + File toolsLibEmuDir = new File(tmpFile, SdkConstants.OS_SDK_TOOLS_LIB_FOLDER + "emulator"); + toolsLibEmuDir.mkdirs(); + new File(toolsLibEmuDir, "snapshots.img").createNewFile(); + File platformsDir = new File(tmpFile, SdkConstants.FD_PLATFORMS); + + // Creating a fake target here on down + File targetDir = new File(platformsDir, "v0_0"); + targetDir.mkdirs(); + new File(targetDir, SdkConstants.FN_FRAMEWORK_LIBRARY).createNewFile(); + new File(targetDir, SdkConstants.FN_FRAMEWORK_AIDL).createNewFile(); + new File(targetDir, SdkConstants.FN_SOURCE_PROP).createNewFile(); + File buildProp = new File(targetDir, SdkConstants.FN_BUILD_PROP); + FileWriter out = new FileWriter(buildProp); + out.write(SdkManager.PROP_VERSION_RELEASE + "=0.0\n"); + out.write(SdkManager.PROP_VERSION_SDK + "=0\n"); + out.write(SdkManager.PROP_VERSION_CODENAME + "=REL\n"); + out.close(); + File imagesDir = new File(targetDir, "images"); + imagesDir.mkdirs(); + new File(imagesDir, "userdata.img").createNewFile(); + File skinsDir = new File(targetDir, "skins"); + File hvgaDir = new File(skinsDir, "HVGA"); + hvgaDir.mkdirs(); + return tmpFile; + } + + /** + * Recursive delete directory. Mostly for fake SDKs. + * + * @param root directory to delete + */ + private void deleteDir(File root) { + if (root.exists()) { + for (File file : root.listFiles()) { + if (file.isDirectory()) { + deleteDir(file); + } else { + file.delete(); + } + } + root.delete(); + } + } + +} diff --git a/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestUtil.java b/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestUtil.java deleted file mode 100644 index 96efb5c..0000000 --- a/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestUtil.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.sdkmanager; - -import com.android.prefs.AndroidLocation; -import com.android.sdklib.SdkConstants; -import com.android.sdklib.SdkManager; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; - -public class SdkManagerTestUtil { - /** - * Build enough of a skeleton SDK to make the tests pass. - *<p> - * Ideally this wouldn't touch the file system, but I'm not inclined to - * fiddle around with mock file systems just at the moment. - * - * @return an sdk manager to a fake sdk - * @throws IOException - */ - public static File makeFakeSdk(File fakeSdk) throws IOException { - fakeSdk.delete(); - fakeSdk.mkdirs(); - AndroidLocation.resetFolder(); - System.setProperty("user.home", fakeSdk.getAbsolutePath()); - File addonsDir = new File(fakeSdk, SdkConstants.FD_ADDONS); - addonsDir.mkdir(); - File toolsLibEmuDir = new File(fakeSdk, SdkConstants.OS_SDK_TOOLS_LIB_FOLDER + "emulator"); - toolsLibEmuDir.mkdirs(); - new File(toolsLibEmuDir, "snapshots.img").createNewFile(); - File platformsDir = new File(fakeSdk, SdkConstants.FD_PLATFORMS); - - // Creating a fake target here on down - File targetDir = new File(platformsDir, "v0_0"); - targetDir.mkdirs(); - new File(targetDir, SdkConstants.FN_FRAMEWORK_LIBRARY).createNewFile(); - new File(targetDir, SdkConstants.FN_FRAMEWORK_AIDL).createNewFile(); - new File(targetDir, SdkConstants.FN_SOURCE_PROP).createNewFile(); - File buildProp = new File(targetDir, SdkConstants.FN_BUILD_PROP); - FileWriter out = new FileWriter(buildProp); - out.write(SdkManager.PROP_VERSION_RELEASE + "=0.0\n"); - out.write(SdkManager.PROP_VERSION_SDK + "=0\n"); - out.write(SdkManager.PROP_VERSION_CODENAME + "=REL\n"); - out.close(); - File imagesDir = new File(targetDir, "images"); - imagesDir.mkdirs(); - new File(imagesDir, "userdata.img").createNewFile(); - File skinsDir = new File(targetDir, "skins"); - File hvgaDir = new File(skinsDir, "HVGA"); - hvgaDir.mkdirs(); - return fakeSdk; - } - - /** - * Recursive delete directory. Mostly for fake SDKs. - * - * @param root directory to delete - */ - public static void deleteDir(File root) { - if (root.exists()) { - for (File file : root.listFiles()) { - if (file.isDirectory()) { - deleteDir(file); - } else { - file.delete(); - } - } - root.delete(); - } - } - -} diff --git a/sdkmanager/libs/sdklib/.classpath b/sdkmanager/libs/sdklib/.classpath index 174a804..7cabaa0 100644 --- a/sdkmanager/libs/sdklib/.classpath +++ b/sdkmanager/libs/sdklib/.classpath @@ -1,11 +1,11 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src"/> - <classpathentry kind="src" path="tests"/> + <classpathentry kind="src" path="tests/src"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/> <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> <classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/commons-compress/commons-compress-1.0.jar"/> <classpathentry combineaccessrules="false" kind="src" path="/common"/> <classpathentry kind="output" path="bin"/> -</classpath> +</classpath>
\ No newline at end of file diff --git a/sdkmanager/libs/sdklib/.settings/org.eclipse.jdt.core.prefs b/sdkmanager/libs/sdklib/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..cba2e98 --- /dev/null +++ b/sdkmanager/libs/sdklib/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,64 @@ +#Wed Mar 16 15:10:56 PDT 2011 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning diff --git a/sdkmanager/libs/sdklib/Android.mk b/sdkmanager/libs/sdklib/Android.mk index 509c573..7ed009c 100644 --- a/sdkmanager/libs/sdklib/Android.mk +++ b/sdkmanager/libs/sdklib/Android.mk @@ -13,5 +13,27 @@ # See the License for the specific language governing permissions and # limitations under the License. # -SDKLIB_LOCAL_DIR := $(call my-dir) -include $(SDKLIB_LOCAL_DIR)/src/Android.mk +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_JAVA_RESOURCE_DIRS := src + +LOCAL_JAR_MANIFEST := manifest.txt + +# IMPORTANT: if you add a new dependency here, please make sure +# to also check the following files: +# sdkmanager/sdklib/manifest.txt +# sdkmanager/app/etc/android.bat +LOCAL_JAVA_LIBRARIES := \ + androidprefs \ + common \ + commons-compress-1.0 + +LOCAL_MODULE := sdklib + +include $(BUILD_HOST_JAVA_LIBRARY) + + +# Build all sub-directories +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/sdkmanager/libs/sdklib/NOTICE b/sdkmanager/libs/sdklib/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/sdkmanager/libs/sdklib/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java index f3da39c..866d5b6 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; +import java.util.ArrayList; /** * Represents an add-on target in the SDK. @@ -67,6 +68,7 @@ final class AddOnTarget implements IAndroidTarget { private final String mLocation; private final PlatformTarget mBasePlatform; private final String mName; + private String[] mAbis; private final String mVendor; private final int mRevision; private final String mDescription; @@ -74,6 +76,7 @@ final class AddOnTarget implements IAndroidTarget { private String mDefaultSkin; private IOptionalLibrary[] mLibraries; private int mVendorId = NO_USB_ID; + private boolean mAbiCompatibilityMode; /** * Creates a new add-on @@ -82,12 +85,13 @@ final class AddOnTarget implements IAndroidTarget { * @param vendor the vendor name of the add-on * @param revision the revision of the add-on * @param description the add-on description + * @param abis list of supported abis * @param libMap A map containing the optional libraries. The map key is the fully-qualified * library name. The value is a 2 string array with the .jar filename, and the description. * @param basePlatform the platform the add-on is extending. */ AddOnTarget(String location, String name, String vendor, int revision, String description, - Map<String, String[]> libMap, PlatformTarget basePlatform) { + String[] abis, Map<String, String[]> libMap, PlatformTarget basePlatform) { if (location.endsWith(File.separator) == false) { location = location + File.separator; } @@ -99,6 +103,14 @@ final class AddOnTarget implements IAndroidTarget { mDescription = description; mBasePlatform = basePlatform; + //set compatibility mode + if (abis.length > 0) { + mAbis = abis; + } else { + mAbiCompatibilityMode = true; + mAbis = new String[] { SdkConstants.ABI_ARMEABI }; + } + // handle the optional libraries. if (libMap != null) { mLibraries = new IOptionalLibrary[libMap.size()]; @@ -121,6 +133,25 @@ final class AddOnTarget implements IAndroidTarget { return mName; } + /** + * Return the full path for images + * @param abiType type of the abi + * @return complete path where the image files are located + */ + public String getImagePath(String abiType) { + + if (mAbiCompatibilityMode) { + // Use legacy directory structure if only arm + return mLocation + SdkConstants.OS_IMAGES_FOLDER; + } else { + return mLocation + SdkConstants.OS_IMAGES_FOLDER + abiType + File.separator; + } + } + + public String[] getAbiList() { + return mAbis; + } + public String getVendor() { return mVendor; } @@ -160,8 +191,6 @@ final class AddOnTarget implements IAndroidTarget { public String getPath(int pathId) { switch (pathId) { - case IMAGES: - return mLocation + SdkConstants.OS_IMAGES_FOLDER; case SKINS: return mLocation + SdkConstants.OS_SKINS_FOLDER; case DOCS: diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java index c0dcaa7..2ca7763 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java @@ -29,8 +29,6 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> { public final static int ANDROID_JAR = 1; /** OS Path to the "framework.aidl" file. */ public final static int ANDROID_AIDL = 2; - /** OS Path to "images" folder which contains the emulator system images. */ - public final static int IMAGES = 3; /** OS Path to the "samples" folder which contains sample projects. */ public final static int SAMPLES = 4; /** OS Path to the "skins" folder which contains the emulator skins. */ @@ -227,6 +225,16 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> { int getUsbVendorId(); /** + * Returns array of permitted processor architectures + */ + public String[] getAbiList(); + + /** + * Returns string to append to images directory for current ProcessorType + */ + public String getImagePath(String abiType); + + /** * Returns whether the given target is compatible with the receiver. * <p/> * This means that a project using the receiver's target can run on the given target. diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java index 6aeeade..62720e7 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java @@ -21,6 +21,7 @@ import com.android.sdklib.util.SparseArray; import java.io.File; import java.util.Collections; import java.util.Map; +import java.util.ArrayList; /** * Represents a platform target in the SDK. @@ -43,21 +44,25 @@ final class PlatformTarget implements IAndroidTarget { private final Map<String, String> mProperties; private final SparseArray<String> mPaths = new SparseArray<String>(); private String[] mSkins; + private String[] mAbis; + private boolean mAbiCompatibilityMode; /** * Creates a Platform target. * @param sdkOsPath the root folder of the SDK * @param platformOSPath the root folder of the platform component - * @param properties the platform properties * @param apiLevel the API Level * @param codeName the codename. can be null. * @param versionName the version name of the platform. * @param revision the revision of the platform component. + * @param abis the list of supported abis + * @param properties the platform properties */ @SuppressWarnings("deprecation") - PlatformTarget(String sdkOsPath, String platformOSPath, Map<String, String> properties, - int apiLevel, String codeName, String versionName, int revision) { + PlatformTarget(String sdkOsPath, String platformOSPath, int apiLevel, + String codeName, String versionName, int revision, String[] abis, + Map<String, String> properties) { if (platformOSPath.endsWith(File.separator) == false) { platformOSPath = platformOSPath + File.separator; } @@ -79,7 +84,6 @@ final class PlatformTarget implements IAndroidTarget { mPaths.put(ANDROID_AIDL, mRootFolderOsPath + SdkConstants.FN_FRAMEWORK_AIDL); mPaths.put(ANDROID_RS, mRootFolderOsPath + SdkConstants.OS_FRAMEWORK_RS); mPaths.put(ANDROID_RS_CLANG, mRootFolderOsPath + SdkConstants.OS_FRAMEWORK_RS_CLANG); - mPaths.put(IMAGES, mRootFolderOsPath + SdkConstants.OS_IMAGES_FOLDER); mPaths.put(SAMPLES, mRootFolderOsPath + SdkConstants.OS_PLATFORM_SAMPLES_FOLDER); mPaths.put(SKINS, mRootFolderOsPath + SdkConstants.OS_SKINS_FOLDER); mPaths.put(TEMPLATES, mRootFolderOsPath + SdkConstants.OS_PLATFORM_TEMPLATES_FOLDER); @@ -112,6 +116,36 @@ final class PlatformTarget implements IAndroidTarget { SdkConstants.FN_DX); mPaths.put(DX_JAR, sdkOsPath + SdkConstants.OS_SDK_PLATFORM_TOOLS_LIB_FOLDER + SdkConstants.FN_DX_JAR); + + //set compatibility mode, abis length would be 0 for older APIs + if (abis.length > 0) { + mAbis = abis; + } else { + mAbiCompatibilityMode = true; + mAbis = new String[] { SdkConstants.ABI_ARMEABI }; + } + + } + + /** + * Return the full path for images + * @param abiType type of the abi + * @return complete path where the image files are located + */ + public String getImagePath(String abiType) { + if (mAbiCompatibilityMode) { + // Use legacy directory structure if only arm is supported + return mRootFolderOsPath + SdkConstants.OS_IMAGES_FOLDER; + } else { + return mRootFolderOsPath + SdkConstants.OS_IMAGES_FOLDER + abiType + File.separator; + } + } + + /** + * Retrieve and return the list of abis + */ + public String[] getAbiList() { + return mAbis; } public String getLocation() { diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java index 37265b1..f75d3a9 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java @@ -16,6 +16,8 @@ package com.android.sdklib; +import com.android.AndroidConstants; + import java.io.File; /** @@ -130,9 +132,13 @@ public final class SdkConstants { public final static String FN_ADB = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? "adb.exe" : "adb"; //$NON-NLS-1$ //$NON-NLS-2$ - /** emulator executable (with extension for the current OS) */ - public final static String FN_EMULATOR = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? - "emulator.exe" : "emulator"; //$NON-NLS-1$ //$NON-NLS-2$ + /** emulator executable (_WITHOUT_ extension for the current OS) */ + public final static String FN_EMULATOR = + "emulator"; //$NON-NLS-1$ //$NON-NLS-2$ + + /** emulator executable extension for the current OS */ + public final static String FN_EMULATOR_EXTENSION = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? + ".exe" : ""; //$NON-NLS-1$ //$NON-NLS-2$ /** zipalign executable (with extension for the current OS) */ public final static String FN_ZIPALIGN = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? @@ -180,28 +186,6 @@ public final class SdkConstants { public final static String FD_APK_NATIVE_LIBS = "lib"; //$NON-NLS-1$ /** Default output folder name, i.e. "bin" */ public final static String FD_OUTPUT = "bin"; //$NON-NLS-1$ - /** Default anim resource folder name, i.e. "anim" */ - public final static String FD_ANIM = "anim"; //$NON-NLS-1$ - /** Default animator resource folder name, i.e. "animator" */ - public final static String FD_ANIMATOR = "animator"; //$NON-NLS-1$ - /** Default color resource folder name, i.e. "color" */ - public final static String FD_COLOR = "color"; //$NON-NLS-1$ - /** Default drawable resource folder name, i.e. "drawable" */ - public final static String FD_DRAWABLE = "drawable"; //$NON-NLS-1$ - /** Default interpolator resource folder name, i.e. "interpolator" */ - public final static String FD_INTERPOLATOR = "interpolator"; //$NON-NLS-1$ - /** Default layout resource folder name, i.e. "layout" */ - public final static String FD_LAYOUT = "layout"; //$NON-NLS-1$ - /** Default menu resource folder name, i.e. "menu" */ - public final static String FD_MENU = "menu"; //$NON-NLS-1$ - /** Default menu resource folder name, i.e. "mipmap" */ - public final static String FD_MIPMAP = "mipmap"; //$NON-NLS-1$ - /** Default values resource folder name, i.e. "values" */ - public final static String FD_VALUES = "values"; //$NON-NLS-1$ - /** Default xml resource folder name, i.e. "xml" */ - public final static String FD_XML = "xml"; //$NON-NLS-1$ - /** Default raw resource folder name, i.e. "raw" */ - public final static String FD_RAW = "raw"; //$NON-NLS-1$ /** proguard output folder for mapping, etc.. files */ public final static String FD_PROGUARD = "proguard"; //$NON-NLS-1$ @@ -223,6 +207,10 @@ public final class SdkConstants { public static final String FD_DOCS_REFERENCE = "reference"; /** Name of the SDK images folder. */ public final static String FD_IMAGES = "images"; + /** Name of the processors to support. */ + public final static String ABI_ARMEABI = "armeabi"; + public final static String ABI_INTEL_ATOM = "x86"; + /** Name of the SDK skins folder. */ public final static String FD_SKINS = "skins"; /** Name of the SDK samples folder. */ @@ -336,11 +324,13 @@ public final class SdkConstants { /** Path of the attrs.xml file relative to a platform folder. */ public final static String OS_PLATFORM_ATTRS_XML = - OS_PLATFORM_RESOURCES_FOLDER + FD_VALUES + File.separator + FN_ATTRS_XML; + OS_PLATFORM_RESOURCES_FOLDER + AndroidConstants.FD_RES_VALUES + File.separator + + FN_ATTRS_XML; /** Path of the attrs_manifest.xml file relative to a platform folder. */ public final static String OS_PLATFORM_ATTRS_MANIFEST_XML = - OS_PLATFORM_RESOURCES_FOLDER + FD_VALUES + File.separator + FN_ATTRS_MANIFEST_XML; + OS_PLATFORM_RESOURCES_FOLDER + AndroidConstants.FD_RES_VALUES + File.separator + + FN_ATTRS_MANIFEST_XML; /** Path of the layoutlib.jar file relative to a platform folder. */ public final static String OS_PLATFORM_LAYOUTLIB_JAR = diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java index 3e6e23e..d924d3d 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java @@ -18,11 +18,11 @@ package com.android.sdklib; 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; import com.android.sdklib.AndroidVersion.AndroidVersionException; import com.android.sdklib.internal.project.ProjectProperties; -import com.android.sdklib.io.FileWrapper; import com.android.util.Pair; import java.io.File; @@ -233,13 +233,17 @@ public class SdkManager { /** * Loads the Platforms from the SDK. + * Creates the "platforms" folder if necessary. + * * @param sdkOsPath Location of the SDK * @param list the list to fill with the platforms. * @param log the ISdkLog object receiving warning/error from the parsing. Cannot be null. + * @throws RuntimeException when the "platforms" folder is missing and cannot be created. */ private static void loadPlatforms(String sdkOsPath, ArrayList<IAndroidTarget> list, ISdkLog log) { File platformFolder = new File(sdkOsPath, SdkConstants.FD_PLATFORMS); + if (platformFolder.isDirectory()) { File[] platforms = platformFolder.listFiles(); @@ -257,15 +261,18 @@ public class SdkManager { return; } - String message = null; - if (platformFolder.exists() == false) { - message = "%s is missing."; + // Try to create it or complain if something else is in the way. + if (!platformFolder.exists()) { + if (!platformFolder.mkdir()) { + throw new RuntimeException( + String.format("Failed to create %1$s.", + platformFolder.getAbsolutePath())); + } } else { - message = "%s is not a folder."; + throw new RuntimeException( + String.format("%1$s is not a folder.", + platformFolder.getAbsolutePath())); } - - throw new IllegalArgumentException(String.format(message, - platformFolder.getAbsolutePath())); } /** @@ -355,15 +362,17 @@ public class SdkManager { return null; } + String[] abiList = getAbiList(platformFolder.getAbsolutePath()); // create the target. PlatformTarget target = new PlatformTarget( sdkOsPath, platformFolder.getAbsolutePath(), - map, apiNumber, apiCodename, apiName, - revision); + revision, + abiList, + map); // need to parse the skins. String[] skins = parseSkinFolder(target.getPath(IAndroidTarget.SKINS)); @@ -380,15 +389,43 @@ public class SdkManager { return null; } + /** + * Get all the abi types supported for a given target + * @param path Path where the images folder for a target is located + * @return an array of strings containing all the abi names for the target + */ + private static String[] getAbiList(String path) { + ArrayList<String> list = new ArrayList<String>(); + + File imagesFolder = new File(path + File.separator + SdkConstants.OS_IMAGES_FOLDER); + File[] files = imagesFolder.listFiles(); + + if (files != null) { + // Loop through Images directory. If subdirectories exist, set multiprocessor mode + for (File file : files) { + if (file.isDirectory()) { + list.add(file.getName()); + } + } + } + String[] abis = new String[list.size()]; + list.toArray(abis); + + return abis; + } /** * Loads the Add-on from the SDK. + * Creates the "add-ons" folder if necessary. + * * @param osSdkPath Location of the SDK * @param list the list to fill with the add-ons. * @param log the ISdkLog object receiving warning/error from the parsing. Cannot be null. + * @throws RuntimeException when the "add-ons" folder is missing and cannot be created. */ private static void loadAddOns(String osSdkPath, ArrayList<IAndroidTarget> list, ISdkLog log) { File addonFolder = new File(osSdkPath, SdkConstants.FD_ADDONS); + if (addonFolder.isDirectory()) { File[] addons = addonFolder.listFiles(); @@ -407,15 +444,18 @@ public class SdkManager { return; } - String message = null; - if (addonFolder.exists() == false) { - message = "%s is missing."; + // Try to create it or complain if something else is in the way. + if (!addonFolder.exists()) { + if (!addonFolder.mkdir()) { + throw new RuntimeException( + String.format("Failed to create %1$s.", + addonFolder.getAbsolutePath())); + } } else { - message = "%s is not a folder."; + throw new RuntimeException( + String.format("%1$s is not a folder.", + addonFolder.getAbsolutePath())); } - - throw new IllegalArgumentException(String.format(message, - addonFolder.getAbsolutePath())); } /** @@ -514,8 +554,9 @@ public class SdkManager { } } + String[] abiList = getAbiList(addonDir.getAbsolutePath()); AddOnTarget target = new AddOnTarget(addonDir.getAbsolutePath(), name, vendor, - revisionValue, description, libMap, baseTarget); + revisionValue, description, abiList, libMap, baseTarget); // need to parse the skins. String[] skins = parseSkinFolder(target.getPath(IAndroidTarget.SKINS)); diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdInfo.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdInfo.java new file mode 100755 index 0000000..81ffa5d --- /dev/null +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdInfo.java @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdklib.internal.avd; + +import com.android.prefs.AndroidLocation.AndroidLocationException; +import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.SdkConstants; + +import java.io.File; +import java.util.Collections; +import java.util.Map; + +/** + * An immutable structure describing an Android Virtual Device. + */ +public final class AvdInfo implements Comparable<AvdInfo> { + + /** + * Status for an {@link AvdInfo}. Indicates whether or not this AVD is valid. + */ + public static enum AvdStatus { + /** No error */ + OK, + /** Missing 'path' property in the ini file */ + ERROR_PATH, + /** Missing config.ini file in the AVD data folder */ + ERROR_CONFIG, + /** Missing 'target' property in the ini file */ + ERROR_TARGET_HASH, + /** Target was not resolved from its hash */ + ERROR_TARGET, + /** Unable to parse config.ini */ + ERROR_PROPERTIES, + /** System Image folder in config.ini doesn't exist */ + ERROR_IMAGE_DIR; + } + + private final String mName; + private final File mIniFile; + private final String mFolderPath; + private final String mTargetHash; + private final IAndroidTarget mTarget; + private final String mAbiType; + private final Map<String, String> mProperties; + private final AvdStatus mStatus; + + /** + * Creates a new valid AVD info. Values are immutable. + * <p/> + * Such an AVD is available and can be used. + * The error string is set to null. + * + * @param name The name of the AVD (for display or reference) + * @param iniFile The path to the config.ini file + * @param folderPath The path to the data directory + * @param targetHash the target hash + * @param target The target. Can be null, if the target was not resolved. + * @param abiType Name of the abi. + * @param properties The property map. Cannot be null. + */ + public AvdInfo(String name, + File iniFile, + String folderPath, + String targetHash, + IAndroidTarget target, + String abiType, + Map<String, String> properties) { + this(name, iniFile, folderPath, targetHash, target, abiType, properties, AvdStatus.OK); + } + + /** + * Creates a new <em>invalid</em> AVD info. Values are immutable. + * <p/> + * Such an AVD is not complete and cannot be used. + * The error string must be non-null. + * + * @param name The name of the AVD (for display or reference) + * @param iniFile The path to the config.ini file + * @param folderPath The path to the data directory + * @param targetHash the target hash + * @param target The target. Can be null, if the target was not resolved. + * @param abiType Name of the abi. + * @param properties The property map. Can be null. + * @param status The {@link AvdStatus} of this AVD. Cannot be null. + */ + public AvdInfo(String name, + File iniFile, + String folderPath, + String targetHash, + IAndroidTarget target, + String abiType, + Map<String, String> properties, + AvdStatus status) { + mName = name; + mIniFile = iniFile; + mFolderPath = folderPath; + mTargetHash = targetHash; + mTarget = target; + mAbiType = abiType; + mProperties = properties == null ? null : Collections.unmodifiableMap(properties); + mStatus = status; + } + + /** Returns the name of the AVD. */ + public String getName() { + return mName; + } + + /** Returns the path of the AVD data directory. */ + public String getDataFolderPath() { + return mFolderPath; + } + + /** Returns the processor type of the AVD. */ + public String getAbiType() { + return mAbiType; + } + + /** Convenience function to return a more user friendly name of the abi type. */ + public static String getPrettyAbiType(String raw) { + String s = null; + if (raw.equalsIgnoreCase(SdkConstants.ABI_ARMEABI)) { + s = "ARM (" + SdkConstants.ABI_ARMEABI + ")"; + } + else if (raw.equalsIgnoreCase(SdkConstants.ABI_INTEL_ATOM)) { + s = "Intel Atom (" + SdkConstants.ABI_INTEL_ATOM + ")"; + } + else { + s = raw + " (" + raw + ")"; + } + return s; + } + + /** + * Returns the emulator executable path + * @param sdkPath path of the sdk + * @return path of the emulator executable + */ + public String getEmulatorPath(String sdkPath) { + String path = sdkPath + SdkConstants.OS_SDK_TOOLS_FOLDER; + + // Start with base name of the emulator + path = path + SdkConstants.FN_EMULATOR; + + // If not using ARM, add processor type to emulator command line + boolean useAbi = !getAbiType().equalsIgnoreCase(SdkConstants.ABI_ARMEABI); + + if (useAbi) { + path = path + "-" + getAbiType(); //$NON-NLS-1$ + } + // Add OS appropriate emulator extension (e.g., .exe on windows) + path = path + SdkConstants.FN_EMULATOR_EXTENSION; + + // HACK: The AVD manager should look for "emulator" or for "emulator-abi" (if not arm). + // However this is a transition period and we don't have that unified "emulator" binary + // in AOSP so if we can't find the generic one, look for an abi-specific one with the + // special case that the armeabi one is actually named emulator-arm. + // TODO remove this kludge once no longer necessary. + if (!useAbi && !(new File(path).isFile())) { + path = sdkPath + SdkConstants.OS_SDK_TOOLS_FOLDER; + path = path + SdkConstants.FN_EMULATOR; + path = path + "-" //$NON-NLS-1$ + + getAbiType().replace(SdkConstants.ABI_ARMEABI, "arm"); //$NON-NLS-1$ + path = path + SdkConstants.FN_EMULATOR_EXTENSION; + } + + return path; + } + + /** + * Returns the target hash string. + */ + public String getTargetHash() { + return mTargetHash; + } + + /** Returns the target of the AVD, or <code>null</code> if it has not been resolved. */ + public IAndroidTarget getTarget() { + return mTarget; + } + + /** Returns the {@link AvdStatus} of the receiver. */ + public AvdStatus getStatus() { + return mStatus; + } + + /** + * Helper method that returns the default AVD folder that would be used for a given + * AVD name <em>if and only if</em> the AVD was created with the default choice. + * <p/> + * Callers must NOT use this to "guess" the actual folder from an actual AVD since + * the purpose of the AVD .ini file is to be able to change this folder. Callers + * should however use this to create a new {@link AvdInfo} to setup its data folder + * to the default. + * <p/> + * The default is {@code getDefaultAvdFolder()/avdname.avd/}. + * <p/> + * For an actual existing AVD, callers must use {@link #getDataFolderPath()} instead. + * + * @param manager The AVD Manager, used to get the AVD storage path. + * @param avdName The name of the desired AVD. + * @throws AndroidLocationException if there's a problem getting android root directory. + */ + public static File getDefaultAvdFolder(AvdManager manager, String avdName) + throws AndroidLocationException { + return new File(manager.getBaseAvdFolder(), + avdName + AvdManager.AVD_FOLDER_EXTENSION); + } + + /** + * Helper method that returns the .ini {@link File} for a given AVD name. + * <p/> + * The default is {@code getDefaultAvdFolder()/avdname.ini}. + * + * @param manager The AVD Manager, used to get the AVD storage path. + * @param avdName The name of the desired AVD. + * @throws AndroidLocationException if there's a problem getting android root directory. + */ + public static File getDefaultIniFile(AvdManager manager, String avdName) + throws AndroidLocationException { + String avdRoot = manager.getBaseAvdFolder(); + return new File(avdRoot, avdName + AvdManager.INI_EXTENSION); + } + + /** + * Returns the .ini {@link File} for this AVD. + */ + public File getIniFile() { + return mIniFile; + } + + /** + * Helper method that returns the Config {@link File} for a given AVD name. + */ + public static File getConfigFile(String path) { + return new File(path, AvdManager.CONFIG_INI); + } + + /** + * Returns the Config {@link File} for this AVD. + */ + public File getConfigFile() { + return getConfigFile(mFolderPath); + } + + /** + * Returns an unmodifiable map of properties for the AVD. This can be null. + */ + public Map<String, String> getProperties() { + return mProperties; + } + + /** + * Returns the error message for the AVD or <code>null</code> if {@link #getStatus()} + * returns {@link AvdStatus#OK} + */ + public String getErrorMessage() { + switch (mStatus) { + case ERROR_PATH: + return String.format("Missing AVD 'path' property in %1$s", getIniFile()); + case ERROR_CONFIG: + return String.format("Missing config.ini file in %1$s", mFolderPath); + case ERROR_TARGET_HASH: + return String.format("Missing 'target' property in %1$s", getIniFile()); + case ERROR_TARGET: + return String.format("Unknown target '%1$s' in %2$s", + mTargetHash, getIniFile()); + case ERROR_PROPERTIES: + return String.format("Failed to parse properties from %1$s", + getConfigFile()); + case ERROR_IMAGE_DIR: + return String.format( + "Invalid value in image.sysdir. Run 'android update avd -n %1$s'", + mName); + case OK: + assert false; + return null; + } + + return null; + } + + /** + * Returns whether an emulator is currently running the AVD. + */ + public boolean isRunning() { + File f = new File(mFolderPath, "userdata-qemu.img.lock"); //$NON-NLS-1$ + return f.isFile(); + } + + /** + * Compares this object with the specified object for order. Returns a + * negative integer, zero, or a positive integer as this object is less + * than, equal to, or greater than the specified object. + * + * @param o the Object to be compared. + * @return a negative integer, zero, or a positive integer as this object is + * less than, equal to, or greater than the specified object. + */ + public int compareTo(AvdInfo o) { + // first handle possible missing targets (if the AVD failed to load for unresolved targets) + if (mTarget == null && o != null && o.mTarget == null) { + return 0; + } if (mTarget == null) { + return +1; + } else if (o == null || o.mTarget == null) { + return -1; + } + + // then compare the targets + int targetDiff = mTarget.compareTo(o.mTarget); + + if (targetDiff == 0) { + // same target? compare on the avd name + return mName.compareTo(o.mName); + } + + return targetDiff; + } +} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java index eba8e07..c9a3561 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java @@ -16,15 +16,16 @@ package com.android.sdklib.internal.avd; +import com.android.io.FileWrapper; import com.android.prefs.AndroidLocation; import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.ISdkLog; import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkManager; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus; +import com.android.sdklib.internal.avd.AvdInfo.AvdStatus; import com.android.sdklib.internal.project.ProjectProperties; -import com.android.sdklib.io.FileWrapper; +import com.android.util.Pair; import java.io.BufferedReader; import java.io.File; @@ -37,7 +38,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -47,7 +47,7 @@ import java.util.regex.Pattern; /** * Android Virtual Device Manager to manage AVDs. */ -public final class AvdManager { +public class AvdManager { /** * Exception thrown when something is wrong with a target path. @@ -66,6 +66,13 @@ public final class AvdManager { public final static String AVD_INFO_TARGET = "target"; //$NON-NLS-1$ /** + * AVD/config.ini key name representing the abi type of the specific avd + * + */ + public final static String AVD_INI_ABI_TYPE = "abi.type"; //$NON-NLS-1$ + + + /** * AVD/config.ini key name representing the SDK-relative path of the skin folder, if any, * or a 320x480 like constant for a numeric skin size. * @@ -92,6 +99,7 @@ public final class AvdManager { * This property is for UI purposes only. It is not used by the emulator. * * @see #SDCARD_SIZE_PATTERN + * @see #parseSdcardSize(String, String[]) */ public final static String AVD_INI_SDCARD_SIZE = "sdcard.size"; //$NON-NLS-1$ /** @@ -123,11 +131,11 @@ public final class AvdManager { public final static Pattern NUMERIC_SKIN_SIZE = Pattern.compile("([0-9]{2,})x([0-9]{2,})"); //$NON-NLS-1$ private final static String USERDATA_IMG = "userdata.img"; //$NON-NLS-1$ - private final static String CONFIG_INI = "config.ini"; //$NON-NLS-1$ + final static String CONFIG_INI = "config.ini"; //$NON-NLS-1$ private final static String SDCARD_IMG = "sdcard.img"; //$NON-NLS-1$ private final static String SNAPSHOTS_IMG = "snapshots.img"; //$NON-NLS-1$ - private final static String INI_EXTENSION = ".ini"; //$NON-NLS-1$ + final static String INI_EXTENSION = ".ini"; //$NON-NLS-1$ private final static Pattern INI_NAME_PATTERN = Pattern.compile("(.+)\\" + //$NON-NLS-1$ INI_EXTENSION + "$", //$NON-NLS-1$ Pattern.CASE_INSENSITIVE); @@ -137,8 +145,26 @@ public final class AvdManager { /** * Pattern for matching SD Card sizes, e.g. "4K" or "16M". + * Callers should use {@link #parseSdcardSize(String, String[])} instead of using this directly. + */ + private final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("(\\d+)([KMG])"); //$NON-NLS-1$ + + /** + * Minimal size of an SDCard image file in bytes. Currently 9 MiB. + */ + + public static final long SDCARD_MIN_BYTE_SIZE = 9<<20; + /** + * Maximal size of an SDCard image file in bytes. Currently 1023 GiB. */ - public final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("(\\d+)([MK])"); //$NON-NLS-1$ + public static final long SDCARD_MAX_BYTE_SIZE = 1023L<<30; + + /** The sdcard string represents a valid number but the size is outside of the allowed range. */ + public final static int SDCARD_SIZE_NOT_IN_RANGE = 0; + /** The sdcard string looks like a size number+suffix but the number failed to decode. */ + public final static int SDCARD_SIZE_INVALID = -1; + /** The sdcard string doesn't look like a size, it might be a path instead. */ + public final static int SDCARD_NOT_SIZE_PATTERN = -2; /** Regex used to validate characters that compose an AVD name. */ public final static Pattern RE_AVD_NAME = Pattern.compile("[a-zA-Z0-9._-]+"); //$NON-NLS-1$ @@ -148,212 +174,21 @@ public final class AvdManager { public final static String HARDWARE_INI = "hardware.ini"; //$NON-NLS-1$ - /** An immutable structure describing an Android Virtual Device. */ - public static final class AvdInfo implements Comparable<AvdInfo> { - - /** - * Status for an {@link AvdInfo}. Indicates whether or not this AVD is valid. - */ - public static enum AvdStatus { - /** No error */ - OK, - /** Missing 'path' property in the ini file */ - ERROR_PATH, - /** Missing config.ini file in the AVD data folder */ - ERROR_CONFIG, - /** Missing 'target' property in the ini file */ - ERROR_TARGET_HASH, - /** Target was not resolved from its hash */ - ERROR_TARGET, - /** Unable to parse config.ini */ - ERROR_PROPERTIES, - /** System Image folder in config.ini doesn't exist */ - ERROR_IMAGE_DIR; - } - - private final String mName; - private final String mPath; - private final String mTargetHash; - private final IAndroidTarget mTarget; - private final Map<String, String> mProperties; - private final AvdStatus mStatus; - - /** - * Creates a new valid AVD info. Values are immutable. - * <p/> - * Such an AVD is available and can be used. - * The error string is set to null. - * - * @param name The name of the AVD (for display or reference) - * @param path The path to the config.ini file - * @param targetHash the target hash - * @param target The target. Can be null, if the target was not resolved. - * @param properties The property map. Cannot be null. - */ - public AvdInfo(String name, String path, String targetHash, IAndroidTarget target, - Map<String, String> properties) { - this(name, path, targetHash, target, properties, AvdStatus.OK); - } - - /** - * Creates a new <em>invalid</em> AVD info. Values are immutable. - * <p/> - * Such an AVD is not complete and cannot be used. - * The error string must be non-null. - * - * @param name The name of the AVD (for display or reference) - * @param path The path to the config.ini file - * @param targetHash the target hash - * @param target The target. Can be null, if the target was not resolved. - * @param properties The property map. Can be null. - * @param status The {@link AvdStatus} of this AVD. Cannot be null. - */ - public AvdInfo(String name, String path, String targetHash, IAndroidTarget target, - Map<String, String> properties, AvdStatus status) { - mName = name; - mPath = path; - mTargetHash = targetHash; - mTarget = target; - mProperties = properties == null ? null : Collections.unmodifiableMap(properties); - mStatus = status; - } - - /** Returns the name of the AVD. */ - public String getName() { - return mName; - } - - /** Returns the path of the AVD data directory. */ - public String getPath() { - return mPath; - } - - /** - * Returns the target hash string. - */ - public String getTargetHash() { - return mTargetHash; - } - - /** Returns the target of the AVD, or <code>null</code> if it has not been resolved. */ - public IAndroidTarget getTarget() { - return mTarget; - } - - /** Returns the {@link AvdStatus} of the receiver. */ - public AvdStatus getStatus() { - return mStatus; - } - - /** - * Helper method that returns the .ini {@link File} for a given AVD name. - * @throws AndroidLocationException if there's a problem getting android root directory. - */ - public static File getIniFile(String name) throws AndroidLocationException { - String avdRoot; - avdRoot = getBaseAvdFolder(); - return new File(avdRoot, name + INI_EXTENSION); - } - - /** - * Returns the .ini {@link File} for this AVD. - * @throws AndroidLocationException if there's a problem getting android root directory. - */ - public File getIniFile() throws AndroidLocationException { - return getIniFile(mName); - } - - /** - * Helper method that returns the Config {@link File} for a given AVD name. - */ - public static File getConfigFile(String path) { - return new File(path, CONFIG_INI); - } - - /** - * Returns the Config {@link File} for this AVD. - */ - public File getConfigFile() { - return getConfigFile(mPath); - } - - /** - * Returns an unmodifiable map of properties for the AVD. This can be null. - */ - public Map<String, String> getProperties() { - return mProperties; - } - - /** - * Returns the error message for the AVD or <code>null</code> if {@link #getStatus()} - * returns {@link AvdStatus#OK} - */ - public String getErrorMessage() { - try { - switch (mStatus) { - case ERROR_PATH: - return String.format("Missing AVD 'path' property in %1$s", getIniFile()); - case ERROR_CONFIG: - return String.format("Missing config.ini file in %1$s", mPath); - case ERROR_TARGET_HASH: - return String.format("Missing 'target' property in %1$s", getIniFile()); - case ERROR_TARGET: - return String.format("Unknown target '%1$s' in %2$s", - mTargetHash, getIniFile()); - case ERROR_PROPERTIES: - return String.format("Failed to parse properties from %1$s", - getConfigFile()); - case ERROR_IMAGE_DIR: - return String.format( - "Invalid value in image.sysdir. Run 'android update avd -n %1$s'", - mName); - case OK: - assert false; - return null; - } - } catch (AndroidLocationException e) { - return "Unable to get HOME folder."; - } - - return null; - } - - /** - * Returns whether an emulator is currently running the AVD. - */ - public boolean isRunning() { - File f = new File(mPath, "userdata-qemu.img.lock"); - return f.isFile(); - } - + /** + * Status returned by {@link AvdManager#isAvdNameConflicting(String)}. + */ + public static enum AvdConflict { + /** There is no known conflict for the given AVD name. */ + NO_CONFLICT, + /** The AVD name conflicts with an existing valid AVD. */ + CONFLICT_EXISTING_AVD, + /** The AVD name conflicts with an existing invalid AVD. */ + CONFLICT_INVALID_AVD, /** - * Compares this object with the specified object for order. Returns a - * negative integer, zero, or a positive integer as this object is less - * than, equal to, or greater than the specified object. - * - * @param o the Object to be compared. - * @return a negative integer, zero, or a positive integer as this object is - * less than, equal to, or greater than the specified object. + * The AVD name does not conflict with any known AVD however there are + * files or directory that would cause a conflict if this were to be created. */ - public int compareTo(AvdInfo o) { - // first handle possible missing targets (if the AVD failed to load for - // unresolved targets. - if (mTarget == null) { - return +1; - } else if (o.mTarget == null) { - return -1; - } - - // then compare the targets - int targetDiff = mTarget.compareTo(o.mTarget); - - if (targetDiff == 0) { - // same target? compare on the avd name - return mName.compareTo(o.mName); - } - - return targetDiff; - } + CONFLICT_EXISTING_PATH, } private final ArrayList<AvdInfo> mAllAvdList = new ArrayList<AvdInfo>(); @@ -362,15 +197,6 @@ public final class AvdManager { private final SdkManager mSdkManager; /** - * Returns the base folder where AVDs are created. - * - * @throws AndroidLocationException - */ - public static String getBaseAvdFolder() throws AndroidLocationException { - return AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD; - } - - /** * Creates an AVD Manager for a given SDK represented by a {@link SdkManager}. * @param sdkManager The SDK. * @param log The log object to receive the log of the initial loading of the AVDs. @@ -386,6 +212,16 @@ public final class AvdManager { } /** + * Returns the base folder where AVDs are created. + * + * @throws AndroidLocationException + */ + public String getBaseAvdFolder() throws AndroidLocationException { + assert AndroidLocation.getFolder().endsWith(File.separator); + return AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD; + } + + /** * Returns the {@link SdkManager} associated with the {@link AvdManager}. */ public SdkManager getSdkManager() { @@ -393,6 +229,71 @@ public final class AvdManager { } /** + * Parse the sdcard string to decode the size. + * Returns: + * <ul> + * <li> The size in bytes > 0 if the sdcard string is a valid size in the allowed range. + * <li> {@link #SDCARD_SIZE_NOT_IN_RANGE} (0) + * if the sdcard string is a valid size NOT in the allowed range. + * <li> {@link #SDCARD_SIZE_INVALID} (-1) + * if the sdcard string is number that fails to parse correctly. + * <li> {@link #SDCARD_NOT_SIZE_PATTERN} (-2) + * if the sdcard string is not a number, in which case it's probably a file path. + * </ul> + * + * @param sdcard The sdcard string, which can be a file path, a size string or something else. + * @param parsedStrings If non-null, an array of 2 strings. The first string will be + * filled with the parsed numeric size and the second one will be filled with the + * parsed suffix. This is filled even if the returned size is deemed out of range or + * failed to parse. The values are null if the sdcard is not a size pattern. + * @return A size in byte if > 0, or {@link #SDCARD_SIZE_NOT_IN_RANGE}, + * {@link #SDCARD_SIZE_INVALID} or {@link #SDCARD_NOT_SIZE_PATTERN} as error codes. + */ + public static long parseSdcardSize(String sdcard, String[] parsedStrings) { + + if (parsedStrings != null) { + assert parsedStrings.length == 2; + parsedStrings[0] = null; + parsedStrings[1] = null; + } + + Matcher m = SDCARD_SIZE_PATTERN.matcher(sdcard); + if (m.matches()) { + if (parsedStrings != null) { + assert parsedStrings.length == 2; + parsedStrings[0] = m.group(1); + parsedStrings[1] = m.group(2); + } + + // get the sdcard values for checks + try { + long sdcardSize = Long.parseLong(m.group(1)); + + String sdcardSizeModifier = m.group(2); + if ("K".equals(sdcardSizeModifier)) { //$NON-NLS-1$ + sdcardSize <<= 10; + } else if ("M".equals(sdcardSizeModifier)) { //$NON-NLS-1$ + sdcardSize <<= 20; + } else if ("G".equals(sdcardSizeModifier)) { //$NON-NLS-1$ + sdcardSize <<= 30; + } + + if (sdcardSize < SDCARD_MIN_BYTE_SIZE || + sdcardSize > SDCARD_MAX_BYTE_SIZE) { + return SDCARD_SIZE_NOT_IN_RANGE; + } + + return sdcardSize; + } catch (NumberFormatException e) { + // This could happen if the number is too large to fit in a long. + return SDCARD_SIZE_INVALID; + } + } + + return SDCARD_NOT_SIZE_PATTERN; + } + + /** * Returns all the existing AVDs. * @return a newly allocated array containing all the AVDs. */ @@ -476,6 +377,53 @@ public final class AvdManager { } /** + * Returns whether this AVD name would generate a conflict. + * + * @param name the name of the AVD to return + * @return A pair of {@link AvdConflict} and the path or AVD name that conflicts. + */ + public Pair<AvdConflict, String> isAvdNameConflicting(String name) { + + boolean ignoreCase = SdkConstants.currentPlatform() == SdkConstants.PLATFORM_WINDOWS; + + // Check whether we have a conflict with an existing or invalid AVD + // known to the manager. + synchronized (mAllAvdList) { + for (AvdInfo info : mAllAvdList) { + String name2 = info.getName(); + if (name2.equals(name) || (ignoreCase && name2.equalsIgnoreCase(name))) { + if (info.getStatus() == AvdStatus.OK) { + return Pair.of(AvdConflict.CONFLICT_EXISTING_AVD, name2); + } else { + return Pair.of(AvdConflict.CONFLICT_INVALID_AVD, name2); + } + } + } + } + + // No conflict with known AVDs. + // Are some existing files/folders in the way of creating this AVD? + + try { + File file = AvdInfo.getDefaultIniFile(this, name); + if (file.exists()) { + return Pair.of(AvdConflict.CONFLICT_EXISTING_PATH, file.getPath()); + } + + file = AvdInfo.getDefaultAvdFolder(this, name); + if (file.exists()) { + return Pair.of(AvdConflict.CONFLICT_EXISTING_PATH, file.getPath()); + } + + } catch (AndroidLocationException e) { + // ignore + } + + + return Pair.of(AvdConflict.NO_CONFLICT, null); + } + + /** * Reloads the AVD list. * @param log the log object to receive action logs. Cannot be null. * @throws AndroidLocationException if there was an error finding the location of the @@ -495,37 +443,38 @@ public final class AvdManager { } /** - * Creates a new AVD, but with no snapshot. - * - * See {@link #createAvd(File, String, IAndroidTarget, String, String, Map, boolean, boolean, ISdkLog)} - **/ - @Deprecated - public AvdInfo createAvd(File avdFolder, String name, IAndroidTarget target, String skinName, - String sdcard, Map<String, String> hardwareConfig, boolean removePrevious, - ISdkLog log) { - return createAvd(avdFolder, name, target, skinName, sdcard, hardwareConfig, removePrevious, - false, log); - } - - /** * Creates a new AVD. It is expected that there is no existing AVD with this name already. * * @param avdFolder the data folder for the AVD. It will be created as needed. - * @param name the name of the AVD + * Unless you want to locate it in a specific directory, the ideal default is + * {@code AvdManager.AvdInfo.getAvdFolder}. + * @param avdName the name of the AVD * @param target the target of the AVD + * @param abiType the abi type of the AVD * @param skinName the name of the skin. Can be null. Must have been verified by caller. * @param sdcard the parameter value for the sdCard. Can be null. This is either a path to * an existing sdcard image or a sdcard size (\d+, \d+K, \dM). * @param hardwareConfig the hardware setup for the AVD. Can be null to use defaults. - * @param removePrevious If true remove any previous files. * @param createSnapshot If true copy a blank snapshot image into the AVD. + * @param removePrevious If true remove any previous files. + * @param editExisting If true, edit an existing AVD, changing only the minimum required. + * This won't remove files unless required or unless {@code removePrevious} is set. * @param log the log object to receive action logs. Cannot be null. * @return The new {@link AvdInfo} in case of success (which has just been added to the * internal list) or null in case of failure. */ - public AvdInfo createAvd(File avdFolder, String name, IAndroidTarget target, - String skinName, String sdcard, Map<String,String> hardwareConfig, - boolean removePrevious, boolean createSnapshot, ISdkLog log) { + public AvdInfo createAvd( + File avdFolder, + String avdName, + IAndroidTarget target, + String abiType, + String skinName, + String sdcard, + Map<String,String> hardwareConfig, + boolean createSnapshot, + boolean removePrevious, + boolean editExisting, + ISdkLog log) { if (log == null) { throw new IllegalArgumentException("log cannot be null"); } @@ -542,8 +491,9 @@ public final class AvdManager { } catch (SecurityException e) { log.error(e, "Failed to delete %1$s", avdFolder.getAbsolutePath()); } - } else { - // AVD shouldn't already exist if removePrevious is false. + } else if (!editExisting) { + // AVD shouldn't already exist if removePrevious is false and + // we're not editing an existing AVD. log.error(null, "Folder %1$s is in the way. Use --force if you want to overwrite.", avdFolder.getAbsolutePath()); @@ -552,23 +502,28 @@ public final class AvdManager { } else { // create the AVD folder. avdFolder.mkdir(); + // We're not editing an existing AVD. + editExisting = false; } // actually write the ini file - iniFile = createAvdIniFile(name, avdFolder, target); + iniFile = createAvdIniFile(avdName, avdFolder, target, removePrevious); // writes the userdata.img in it. - String imagePath = target.getPath(IAndroidTarget.IMAGES); + String imagePath = target.getImagePath(abiType); + File userdataSrc = new File(imagePath, USERDATA_IMG); if (userdataSrc.exists() == false && target.isPlatform() == false) { - imagePath = target.getParent().getPath(IAndroidTarget.IMAGES); + imagePath = + target.getParent().getImagePath(abiType); userdataSrc = new File(imagePath, USERDATA_IMG); } if (userdataSrc.exists() == false) { - log.error(null, "Unable to find a '%1$s' file to copy into the AVD folder.", - USERDATA_IMG); + log.error(null, + "Unable to find a '%1$s' file of '%2$s' to copy into the AVD folder.", + USERDATA_IMG, imagePath); needCleanup = true; return null; } @@ -576,30 +531,48 @@ public final class AvdManager { copyImageFile(userdataSrc, userdataDest); + if (userdataDest.exists() == false) { + log.error(null, "Unable to create '%1$s' file in the AVD folder.", + userdataDest); + needCleanup = true; + return null; + } + // Config file. HashMap<String, String> values = new HashMap<String, String>(); - if (setImagePathProperties(target, values, log) == false) { - needCleanup = true; - return null; + if (setImagePathProperties(target, abiType, values, log) == false) { + log.error(null, "Failed to set image path properties in the AVD folder."); + needCleanup = true; + return null; } // Create the snapshot file if (createSnapshot) { - String toolsLib = mSdkManager.getLocation() + File.separator - + SdkConstants.OS_SDK_TOOLS_LIB_EMULATOR_FOLDER; - File snapshotBlank = new File(toolsLib, SNAPSHOTS_IMG); - if (snapshotBlank.exists() == false) { - log.error(null, "Unable to find a '%2$s%1$s' file to copy into the AVD folder.", - SNAPSHOTS_IMG, toolsLib); - needCleanup = true; - return null; - } File snapshotDest = new File(avdFolder, SNAPSHOTS_IMG); - copyImageFile(snapshotBlank, snapshotDest); + if (snapshotDest.isFile() && editExisting) { + log.printf("Snapshot image already present, was not changed."); + + } else { + String toolsLib = mSdkManager.getLocation() + File.separator + + SdkConstants.OS_SDK_TOOLS_LIB_EMULATOR_FOLDER; + File snapshotBlank = new File(toolsLib, SNAPSHOTS_IMG); + if (snapshotBlank.exists() == false) { + log.error(null, + "Unable to find a '%2$s%1$s' file to copy into the AVD folder.", + SNAPSHOTS_IMG, toolsLib); + needCleanup = true; + return null; + } + + copyImageFile(snapshotBlank, snapshotDest); + } values.put(AVD_INI_SNAPSHOT_PRESENT, "true"); } + // Now the abi type + values.put(AVD_INI_ABI_TYPE, abiType); + // Now the skin. if (skinName == null || skinName.length() == 0) { skinName = target.getDefaultSkin(); @@ -616,6 +589,7 @@ public final class AvdManager { // assume skin name is valid String skinPath = getSkinRelativePath(skinName, target, log); if (skinPath == null) { + log.error(null, "Missing skinpath in the AVD folder."); needCleanup = true; return null; } @@ -625,54 +599,48 @@ public final class AvdManager { } if (sdcard != null && sdcard.length() > 0) { - File sdcardFile = new File(sdcard); - if (sdcardFile.isFile()) { - // sdcard value is an external sdcard, so we put its path into the config.ini - values.put(AVD_INI_SDCARD_PATH, sdcard); + // Sdcard is possibly a size. In that case we create a file called 'sdcard.img' + // in the AVD folder, and do not put any value in config.ini. + + long sdcardSize = parseSdcardSize(sdcard, null/*parsedStrings*/); + + if (sdcardSize == SDCARD_SIZE_NOT_IN_RANGE) { + log.error(null, "SD Card size must be in the range 9 MiB..1023 GiB."); + needCleanup = true; + return null; + + } else if (sdcardSize == SDCARD_SIZE_INVALID) { + log.error(null, "Unable to parse SD Card size"); + needCleanup = true; + return null; + + } else if (sdcardSize == SDCARD_NOT_SIZE_PATTERN) { + File sdcardFile = new File(sdcard); + if (sdcardFile.isFile()) { + // sdcard value is an external sdcard, so we put its path into the config.ini + values.put(AVD_INI_SDCARD_PATH, sdcard); + } else { + log.error(null, "'%1$s' is not recognized as a valid sdcard value.\n" + + "Value should be:\n" + "1. path to an sdcard.\n" + + "2. size of the sdcard to create: <size>[K|M]", sdcard); + needCleanup = true; + return null; + } } else { - // Sdcard is possibly a size. In that case we create a file called 'sdcard.img' - // in the AVD folder, and do not put any value in config.ini. - - // First, check that it matches the pattern for sdcard size - Matcher m = SDCARD_SIZE_PATTERN.matcher(sdcard); - if (m.matches()) { - // get the sdcard values for checks - try { - long sdcardSize = Long.parseLong(m.group(1)); - - // prevent overflow: no more than 999GB - // 10 digit for MiB, 13 for KiB - int digitCount = m.group(1).length(); - - String sdcardSizeModifier = m.group(2); - if ("K".equals(sdcardSizeModifier)) { - sdcardSize *= 1024L; - } else { // must be "M" per the pattern - sdcardSize *= 1024L * 1024L; - digitCount += 3; // convert the number of digit into "KiB" - } - - if (digitCount >= 13) { - log.error(null, "SD Card size is too big!"); - needCleanup = true; - return null; - } - - if (sdcardSize < 9 * 1024 * 1024) { - log.error(null, "SD Card size must be at least 9MB"); - needCleanup = true; - return null; - } - } catch (NumberFormatException e) { - // this should never happen since the string is validated - // by the regexp - log.error(null, "Unable to parse SD Card size"); - needCleanup = true; - return null; + // create the sdcard. + File sdcardFile = new File(avdFolder, SDCARD_IMG); + + boolean runMkSdcard = true; + if (sdcardFile.exists()) { + if (sdcardFile.length() == sdcardSize && editExisting) { + // There's already an sdcard file with the right size and we're + // not overriding it... so don't remove it. + runMkSdcard = false; + log.printf("SD Card already present with same size, was not changed."); } + } - // create the sdcard. - sdcardFile = new File(avdFolder, SDCARD_IMG); + if (runMkSdcard) { String path = sdcardFile.getAbsolutePath(); // execute mksdcard with the proper parameters. @@ -688,21 +656,16 @@ public final class AvdManager { } if (createSdCard(mkSdCard.getAbsolutePath(), sdcard, path, log) == false) { + log.error(null, "Failed to create sdcard in the AVD folder."); needCleanup = true; return null; // mksdcard output has already been displayed, no need to // output anything else. } - - // add a property containing the size of the sdcard for display purpose - // only when the dev does 'android list avd' - values.put(AVD_INI_SDCARD_SIZE, sdcard); - } else { - log.error(null, "'%1$s' is not recognized as a valid sdcard value.\n" - + "Value should be:\n" + "1. path to an sdcard.\n" - + "2. size of the sdcard to create: <size>[K|M]", sdcard); - needCleanup = true; - return null; } + + // add a property containing the size of the sdcard for display purpose + // only when the dev does 'android list avd' + values.put(AVD_INI_SDCARD_SIZE, sdcard); } } @@ -757,12 +720,23 @@ public final class AvdManager { StringBuilder report = new StringBuilder(); if (target.isPlatform()) { - report.append(String.format("Created AVD '%1$s' based on %2$s", - name, target.getName())); + if (editExisting) { + report.append(String.format("Updated AVD '%1$s' based on %2$s", + avdName, target.getName())); + } else { + report.append(String.format("Created AVD '%1$s' based on %2$s", + avdName, target.getName())); + } } else { - report.append(String.format("Created AVD '%1$s' based on %2$s (%3$s)", name, - target.getName(), target.getVendor())); + if (editExisting) { + report.append(String.format("Updated AVD '%1$s' based on %2$s (%3$s)", avdName, + target.getName(), target.getVendor())); + } else { + report.append(String.format("Created AVD '%1$s' based on %2$s (%3$s)", avdName, + target.getName(), target.getVendor())); + } } + report.append(String.format(", %s processor", AvdInfo.getPrettyAbiType(abiType))); // display the chosen hardware config if (finalHardwareValues.size() > 0) { @@ -777,28 +751,31 @@ public final class AvdManager { log.printf(report.toString()); // create the AvdInfo object, and add it to the list - AvdInfo newAvdInfo = new AvdInfo(name, + AvdInfo newAvdInfo = new AvdInfo( + avdName, + iniFile, avdFolder.getAbsolutePath(), target.hashString(), - target, values); + target, abiType, values); - AvdInfo oldAvdInfo = getAvd(name, false /*validAvdOnly*/); + AvdInfo oldAvdInfo = getAvd(avdName, false /*validAvdOnly*/); synchronized (mAllAvdList) { - if (oldAvdInfo != null && removePrevious) { + if (oldAvdInfo != null && (removePrevious || editExisting)) { mAllAvdList.remove(oldAvdInfo); } mAllAvdList.add(newAvdInfo); mValidAvdList = mBrokenAvdList = null; } - if (removePrevious && + if ((removePrevious || editExisting) && newAvdInfo != null && oldAvdInfo != null && - !oldAvdInfo.getPath().equals(newAvdInfo.getPath())) { - log.warning("Removing previous AVD directory at %s", oldAvdInfo.getPath()); + !oldAvdInfo.getDataFolderPath().equals(newAvdInfo.getDataFolderPath())) { + log.warning("Removing previous AVD directory at %s", + oldAvdInfo.getDataFolderPath()); // Remove the old data directory - File dir = new File(oldAvdInfo.getPath()); + File dir = new File(oldAvdInfo.getDataFolderPath()); try { deleteContentOf(dir); dir.delete(); @@ -859,9 +836,9 @@ public final class AvdManager { * is not empty. If the image folder is empty or does not exist, <code>null</code> is returned. * @throws InvalidTargetPathException if the target image folder is not in the current SDK. */ - private String getImageRelativePath(IAndroidTarget target) + private String getImageRelativePath(IAndroidTarget target, String abiType) throws InvalidTargetPathException { - String imageFullPath = target.getPath(IAndroidTarget.IMAGES); + String imageFullPath = target.getImagePath(abiType); // make this path relative to the SDK location String sdkLocation = mSdkManager.getLocation(); @@ -957,13 +934,27 @@ public final class AvdManager { * @param name of the AVD. * @param avdFolder path for the data folder of the AVD. * @param target of the AVD. + * @param removePrevious True if an existing ini file should be removed. * @throws AndroidLocationException if there's a problem getting android root directory. * @throws IOException if {@link File#getAbsolutePath()} fails. */ - private File createAvdIniFile(String name, File avdFolder, IAndroidTarget target) + private File createAvdIniFile(String name, + File avdFolder, + IAndroidTarget target, + boolean removePrevious) throws AndroidLocationException, IOException { + File iniFile = AvdInfo.getDefaultIniFile(this, name); + + if (removePrevious) { + if (iniFile.isFile()) { + iniFile.delete(); + } else if (iniFile.isDirectory()) { + deleteContentOf(iniFile); + iniFile.delete(); + } + } + HashMap<String, String> values = new HashMap<String, String>(); - File iniFile = AvdInfo.getIniFile(name); values.put(AVD_INFO_PATH, avdFolder.getAbsolutePath()); values.put(AVD_INFO_TARGET, target.hashString()); writeIniFile(iniFile, values); @@ -979,7 +970,10 @@ public final class AvdManager { * @throws IOException if {@link File#getAbsolutePath()} fails. */ private File createAvdIniFile(AvdInfo info) throws AndroidLocationException, IOException { - return createAvdIniFile(info.getName(), new File(info.getPath()), info.getTarget()); + return createAvdIniFile(info.getName(), + new File(info.getDataFolderPath()), + info.getTarget(), + false /*removePrevious*/); } /** @@ -1010,7 +1004,7 @@ public final class AvdManager { } } - String path = avdInfo.getPath(); + String path = avdInfo.getDataFolderPath(); if (path != null) { f = new File(path); if (f.exists()) { @@ -1032,8 +1026,6 @@ public final class AvdManager { return true; } - } catch (AndroidLocationException e) { - log.error(e, null); } catch (IOException e) { log.error(e, null); } catch (SecurityException e) { @@ -1060,17 +1052,25 @@ public final class AvdManager { try { if (paramFolderPath != null) { - File f = new File(avdInfo.getPath()); - log.warning("Moving '%1$s' to '%2$s'.", avdInfo.getPath(), paramFolderPath); + File f = new File(avdInfo.getDataFolderPath()); + log.warning("Moving '%1$s' to '%2$s'.", + avdInfo.getDataFolderPath(), + paramFolderPath); if (!f.renameTo(new File(paramFolderPath))) { log.error(null, "Failed to move '%1$s' to '%2$s'.", - avdInfo.getPath(), paramFolderPath); + avdInfo.getDataFolderPath(), paramFolderPath); return false; } // update AVD info - AvdInfo info = new AvdInfo(avdInfo.getName(), paramFolderPath, - avdInfo.getTargetHash(), avdInfo.getTarget(), avdInfo.getProperties()); + AvdInfo info = new AvdInfo( + avdInfo.getName(), + avdInfo.getIniFile(), + paramFolderPath, + avdInfo.getTargetHash(), + avdInfo.getTarget(), + avdInfo.getAbiType(), + avdInfo.getProperties()); replaceAvd(avdInfo, info); // update the ini file @@ -1079,7 +1079,7 @@ public final class AvdManager { if (newName != null) { File oldIniFile = avdInfo.getIniFile(); - File newIniFile = AvdInfo.getIniFile(newName); + File newIniFile = AvdInfo.getDefaultIniFile(this, newName); log.warning("Moving '%1$s' to '%2$s'.", oldIniFile.getPath(), newIniFile.getPath()); if (!oldIniFile.renameTo(newIniFile)) { @@ -1089,8 +1089,14 @@ public final class AvdManager { } // update AVD info - AvdInfo info = new AvdInfo(newName, avdInfo.getPath(), - avdInfo.getTargetHash(), avdInfo.getTarget(), avdInfo.getProperties()); + AvdInfo info = new AvdInfo( + newName, + avdInfo.getIniFile(), + avdInfo.getDataFolderPath(), + avdInfo.getTargetHash(), + avdInfo.getTarget(), + avdInfo.getAbiType(), + avdInfo.getProperties()); replaceAvd(avdInfo, info); } @@ -1135,19 +1141,20 @@ public final class AvdManager { * <p/> * This lists the $HOME/.android/avd/<name>.ini files. * Such files are properties file than then indicate where the AVD folder is located. + * <p/> + * Note: the method is to be considered private. It is made protected so that + * unit tests can easily override the AVD root. * * @return A new {@link File} array or null. The array might be empty. * @throws AndroidLocationException if there's a problem getting android root directory. */ private File[] buildAvdFilesList() throws AndroidLocationException { - // get the Android prefs location. - String avdRoot = AvdManager.getBaseAvdFolder(); + File folder = new File(getBaseAvdFolder()); // ensure folder validity. - File folder = new File(avdRoot); if (folder.isFile()) { throw new AndroidLocationException( - String.format("%1$s is not a valid folder.", avdRoot)); + String.format("%1$s is not a valid folder.", folder.getAbsolutePath())); } else if (folder.exists() == false) { // folder is not there, we create it and return folder.mkdirs(); @@ -1192,14 +1199,14 @@ public final class AvdManager { /** * Parses an AVD .ini file to create an {@link AvdInfo}. * - * @param path The path to the AVD .ini file + * @param iniPath The path to the AVD .ini file * @param log the log object to receive action logs. Cannot be null. * @return A new {@link AvdInfo} with an {@link AvdStatus} indicating whether this AVD is * valid or not. */ - private AvdInfo parseAvdInfo(File path, ISdkLog log) { + private AvdInfo parseAvdInfo(File iniPath, ISdkLog log) { Map<String, String> map = ProjectProperties.parsePropertyFile( - new FileWrapper(path), + new FileWrapper(iniPath), log); String avdPath = map.get(AVD_INFO_PATH); @@ -1227,12 +1234,20 @@ public final class AvdManager { } // get name - String name = path.getName(); - Matcher matcher = INI_NAME_PATTERN.matcher(path.getName()); + String name = iniPath.getName(); + Matcher matcher = INI_NAME_PATTERN.matcher(iniPath.getName()); if (matcher.matches()) { name = matcher.group(1); } + // get abi type + String abiType = properties == null ? null : properties.get(AVD_INI_ABI_TYPE); + // for the avds created previously without enhancement, i.e. They are created based + // on previous API Levels. They are supposed to have ARM processor type + if (abiType == null) { + abiType = SdkConstants.ABI_ARMEABI; + } + // check the image.sysdir are valid boolean validImageSysdir = true; if (properties != null) { @@ -1275,9 +1290,11 @@ public final class AvdManager { AvdInfo info = new AvdInfo( name, + iniPath, avdPath, targetHash, target, + abiType, properties, status); @@ -1489,7 +1506,7 @@ public final class AvdManager { AvdStatus status; // create the path to the new system images. - if (setImagePathProperties(avd.getTarget(), properties, log)) { + if (setImagePathProperties(avd.getTarget(), avd.getAbiType(), properties, log)) { if (properties.containsKey(AVD_INI_IMAGES_1)) { log.printf("Updated '%1$s' with value '%2$s'\n", AVD_INI_IMAGES_1, properties.get(AVD_INI_IMAGES_1)); @@ -1509,7 +1526,7 @@ public final class AvdManager { } // now write the config file - File configIniFile = new File(avd.getPath(), CONFIG_INI); + File configIniFile = new File(avd.getDataFolderPath(), CONFIG_INI); writeIniFile(configIniFile, properties); // finally create a new AvdInfo for this unbroken avd and add it to the list. @@ -1518,9 +1535,11 @@ public final class AvdManager { // FIXME: We may want to create this AvdInfo by reparsing the AVD instead. This could detect other errors. AvdInfo newAvd = new AvdInfo( avd.getName(), - avd.getPath(), + avd.getIniFile(), + avd.getDataFolderPath(), avd.getTargetHash(), avd.getTarget(), + avd.getAbiType(), properties, status); @@ -1530,13 +1549,14 @@ public final class AvdManager { /** * Sets the paths to the system images in a properties map. * @param target the target in which to find the system images. + * @param abiType the abi type of the avd to find + * the architecture-dependent system images. * @param properties the properties in which to set the paths. * @param log the log object to receive action logs. Cannot be null. * @return true if success, false if some path are missing. */ private boolean setImagePathProperties(IAndroidTarget target, - Map<String, String> properties, - ISdkLog log) { + String abiType, Map<String, String> properties, ISdkLog log) { properties.remove(AVD_INI_IMAGES_1); properties.remove(AVD_INI_IMAGES_2); @@ -1544,7 +1564,7 @@ public final class AvdManager { String property = AVD_INI_IMAGES_1; // First the image folders of the target itself - String imagePath = getImageRelativePath(target); + String imagePath = getImageRelativePath(target, abiType); if (imagePath != null) { properties.put(property, imagePath); property = AVD_INI_IMAGES_2; @@ -1554,7 +1574,7 @@ public final class AvdManager { // If the target is an add-on we need to add the Platform image as a backup. IAndroidTarget parent = target.getParent(); if (parent != null) { - imagePath = getImageRelativePath(parent); + imagePath = getImageRelativePath(parent, abiType); if (imagePath != null) { properties.put(property, imagePath); } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/DebugKeyProvider.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/DebugKeyProvider.java index bef7ccf..d31414c 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/DebugKeyProvider.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/DebugKeyProvider.java @@ -36,49 +36,49 @@ import java.security.cert.CertificateException; * <p/>This provider uses a custom keystore to create and store a key with a known password. */ public class DebugKeyProvider { - + public interface IKeyGenOutput { public void out(String message); public void err(String message); } - + private static final String PASSWORD_STRING = "android"; private static final char[] PASSWORD_CHAR = PASSWORD_STRING.toCharArray(); private static final String DEBUG_ALIAS = "AndroidDebugKey"; - + // Certificate CN value. This is a hard-coded value for the debug key. // Android Market checks against this value in order to refuse applications signed with // debug keys. private static final String CERTIFICATE_DESC = "CN=Android Debug,O=Android,C=US"; - + private KeyStore.PrivateKeyEntry mEntry; - + public static class KeytoolException extends Exception { /** default serial uid */ private static final long serialVersionUID = 1L; private String mJavaHome = null; private String mCommandLine = null; - + KeytoolException(String message) { super(message); } KeytoolException(String message, String javaHome, String commandLine) { super(message); - + mJavaHome = javaHome; mCommandLine = commandLine; } - + public String getJavaHome() { return mJavaHome; } - + public String getCommandLine() { return mCommandLine; } } - + /** * Creates a provider using a keystore at the given location. * <p/>The keystore, and a new random android debug key are created if they do not yet exist. @@ -91,16 +91,16 @@ public class DebugKeyProvider { * @param output an optional {@link IKeyGenOutput} object to get the stdout and stderr * of the keytool process call. * @throws KeytoolException If the creation of the debug key failed. - * @throws AndroidLocationException + * @throws AndroidLocationException */ public DebugKeyProvider(String osKeyStorePath, String storeType, IKeyGenOutput output) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException, IOException, KeytoolException, AndroidLocationException { - + if (osKeyStorePath == null) { osKeyStorePath = getDefaultKeyStoreOsPath(); } - + if (loadKeyEntry(osKeyStorePath, storeType) == false) { // create the store with the key createNewStore(osKeyStorePath, storeType, output); @@ -109,7 +109,7 @@ public class DebugKeyProvider { /** * Returns the OS path to the default debug keystore. - * + * * @return The OS path to the default debug keystore. * @throws KeytoolException * @throws AndroidLocationException @@ -134,7 +134,7 @@ public class DebugKeyProvider { if (mEntry != null) { return mEntry.getPrivateKey(); } - + return null; } @@ -150,7 +150,7 @@ public class DebugKeyProvider { return null; } - + /** * Loads the debug key from the keystore. * @param osKeyStorePath the OS path to the keystore. @@ -172,7 +172,7 @@ public class DebugKeyProvider { } catch (FileNotFoundException e) { return false; } - + return true; } @@ -193,9 +193,9 @@ public class DebugKeyProvider { private void createNewStore(String osKeyStorePath, String storeType, IKeyGenOutput output) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException, IOException, KeytoolException { - + if (KeystoreHelper.createNewStore(osKeyStorePath, storeType, PASSWORD_STRING, DEBUG_ALIAS, - PASSWORD_STRING, CERTIFICATE_DESC, 1 /* validity*/, output)) { + PASSWORD_STRING, CERTIFICATE_DESC, 30 /* validity*/, output)) { loadKeyEntry(osKeyStorePath, storeType); } } 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 ee2a5a6..f05e9a6 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 @@ -16,10 +16,10 @@ package com.android.sdklib.internal.export; +import com.android.io.FileWrapper; +import com.android.io.IAbstractFile; +import com.android.io.StreamException; import com.android.sdklib.SdkConstants; -import com.android.sdklib.io.FileWrapper; -import com.android.sdklib.io.IAbstractFile; -import com.android.sdklib.io.StreamException; import com.android.sdklib.xml.AndroidManifestParser; import com.android.sdklib.xml.ManifestData; import com.android.sdklib.xml.ManifestData.SupportsScreens; diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java index 16dd8eb..7840b91 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java @@ -16,6 +16,7 @@ package com.android.sdklib.internal.project; +import com.android.AndroidConstants; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.ISdkLog; import com.android.sdklib.SdkConstants; @@ -325,11 +326,11 @@ public class ProjectCreator { if (isTestProject == false) { /* Make res files only for non test projects */ - File valueFolder = createDirs(resourceFolder, SdkConstants.FD_VALUES); + File valueFolder = createDirs(resourceFolder, AndroidConstants.FD_RES_VALUES); installTargetTemplate("strings.template", new File(valueFolder, "strings.xml"), keywords, target); - File layoutFolder = createDirs(resourceFolder, SdkConstants.FD_LAYOUT); + File layoutFolder = createDirs(resourceFolder, AndroidConstants.FD_RES_LAYOUT); installTargetTemplate("layout.template", new File(layoutFolder, "main.xml"), keywords, target); diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java index 19cad00..11cd277 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java @@ -16,12 +16,12 @@ package com.android.sdklib.internal.project; +import com.android.io.FolderWrapper; +import com.android.io.IAbstractFile; +import com.android.io.IAbstractFolder; +import com.android.io.StreamException; import com.android.sdklib.ISdkLog; import com.android.sdklib.SdkConstants; -import com.android.sdklib.io.FolderWrapper; -import com.android.sdklib.io.IAbstractFile; -import com.android.sdklib.io.IAbstractFolder; -import com.android.sdklib.io.StreamException; import java.io.BufferedReader; import java.io.FileNotFoundException; diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectPropertiesWorkingCopy.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectPropertiesWorkingCopy.java index 23cdd09..14cac2a 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectPropertiesWorkingCopy.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectPropertiesWorkingCopy.java @@ -16,11 +16,11 @@ package com.android.sdklib.internal.project; +import com.android.io.IAbstractFile; +import com.android.io.IAbstractFolder; +import com.android.io.StreamException; import com.android.sdklib.SdkConstants; import com.android.sdklib.internal.project.ProjectProperties.PropertyType; -import com.android.sdklib.io.IAbstractFile; -import com.android.sdklib.io.IAbstractFolder; -import com.android.sdklib.io.StreamException; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AdbWrapper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AdbWrapper.java index 3fab9ce..ba2d501 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AdbWrapper.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AdbWrapper.java @@ -54,7 +54,11 @@ public class AdbWrapper { }
private void display(String format, Object...args) {
- mMonitor.setResult(format, args);
+ mMonitor.log(format, args);
+ }
+
+ private void displayError(String format, Object...args) {
+ mMonitor.logError(format, args);
}
/**
@@ -63,7 +67,7 @@ public class AdbWrapper { */
public synchronized boolean startAdb() {
if (mAdbOsLocation == null) {
- display("Error: missing path to ADB."); //$NON-NLS-1$
+ displayError("Error: missing path to ADB."); //$NON-NLS-1$
return false;
}
@@ -82,15 +86,15 @@ public class AdbWrapper { false /* waitForReaders */);
} catch (IOException ioe) {
- display("Unable to run 'adb': %1$s.", ioe.getMessage()); //$NON-NLS-1$
+ displayError("Unable to run 'adb': %1$s.", ioe.getMessage()); //$NON-NLS-1$
// we'll return false;
} catch (InterruptedException ie) {
- display("Unable to run 'adb': %1$s.", ie.getMessage()); //$NON-NLS-1$
+ displayError("Unable to run 'adb': %1$s.", ie.getMessage()); //$NON-NLS-1$
// we'll return false;
}
if (status != 0) {
- display("'adb start-server' failed."); //$NON-NLS-1$
+ displayError("'adb start-server' failed."); //$NON-NLS-1$
return false;
}
@@ -105,7 +109,7 @@ public class AdbWrapper { */
public synchronized boolean stopAdb() {
if (mAdbOsLocation == null) {
- display("Error: missing path to ADB."); //$NON-NLS-1$
+ displayError("Error: missing path to ADB."); //$NON-NLS-1$
return false;
}
@@ -127,7 +131,7 @@ public class AdbWrapper { }
if (status != 0) {
- display("'adb kill-server' failed -- run manually if necessary."); //$NON-NLS-1$
+ displayError("'adb kill-server' failed -- run manually if necessary."); //$NON-NLS-1$
return false;
}
@@ -163,7 +167,7 @@ public class AdbWrapper { while (true) {
String line = errReader.readLine();
if (line != null) {
- display("ADB Error: %1$s", line);
+ displayError("ADB Error: %1$s", line);
errorOutput.add(line);
} else {
break;
@@ -185,7 +189,7 @@ public class AdbWrapper { while (true) {
String line = outReader.readLine();
if (line != null) {
- display("ADB: %1$s", line);
+ displayError("ADB: %1$s", line);
stdOutput.add(line);
} else {
break;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java index bed9174..526bfcb 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java @@ -247,7 +247,22 @@ public class AddonPackage extends Package return mLibs;
}
- /** Returns a short description for an {@link IDescription}. */
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ return String.format("%1$s by %2$s%3$s",
+ getName(),
+ getVendor(),
+ isObsolete() ? " (Obsolete)" : "");
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
@Override
public String getShortDescription() {
return String.format("%1$s by %2$s, Android API %3$s, revision %4$s%5$s",
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonsListFetcher.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonsListFetcher.java index 9b8d808..c0b7041 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonsListFetcher.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonsListFetcher.java @@ -142,11 +142,11 @@ public class AddonsListFetcher { reason = String.format("Unknown (%1$s)", exception[0].getClass().getName());
}
- monitor.setResult("Failed to fetch URL %1$s, reason: %2$s", url, reason);
+ monitor.logError("Failed to fetch URL %1$s, reason: %2$s", url, reason);
}
if (validationError[0] != null) {
- monitor.setResult("%s", validationError[0]); //$NON-NLS-1$
+ monitor.logError("%s", validationError[0]); //$NON-NLS-1$
}
// Stop here if we failed to validate the XML. We don't want to load it.
@@ -409,13 +409,13 @@ public class AddonsListFetcher { return doc;
} catch (ParserConfigurationException e) {
- monitor.setResult("Failed to create XML document builder");
+ monitor.logError("Failed to create XML document builder");
} catch (SAXException e) {
- monitor.setResult("Failed to parse XML document");
+ monitor.logError("Failed to parse XML document");
} catch (IOException e) {
- monitor.setResult("Failed to read XML document");
+ monitor.logError("Failed to read XML document");
}
return null;
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..5807ca3 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 @@ -63,7 +63,7 @@ public class ArchiveInstaller { String name = pkg.getShortDescription();
if (pkg instanceof ExtraPackage && !((ExtraPackage) pkg).isPathValid()) {
- monitor.setResult("Skipping %1$s: %2$s is not a valid install path.",
+ monitor.log("Skipping %1$s: %2$s is not a valid install path.",
name,
((ExtraPackage) pkg).getPath());
return false;
@@ -71,14 +71,14 @@ public class ArchiveInstaller { if (archive.isLocal()) {
// This should never happen.
- monitor.setResult("Skipping already installed archive: %1$s for %2$s",
+ monitor.log("Skipping already installed archive: %1$s for %2$s",
name,
archive.getOsDescription());
return false;
}
if (!archive.isCompatible()) {
- monitor.setResult("Skipping incompatible archive: %1$s for %2$s",
+ monitor.log("Skipping incompatible archive: %1$s for %2$s",
name,
archive.getOsDescription());
return false;
@@ -88,7 +88,7 @@ public class ArchiveInstaller { if (archiveFile != null) {
// Unarchive calls the pre/postInstallHook methods.
if (unarchive(archive, osSdkRoot, archiveFile, sdkManager, monitor)) {
- monitor.setResult("Installed %1$s", name);
+ monitor.log("Installed %1$s", name);
// Delete the temp archive if it exists, only on success
OsHelper.deleteFileOrFolder(archiveFile);
return true;
@@ -110,7 +110,7 @@ public class ArchiveInstaller { String name = archive.getParentPackage().getShortDescription();
String desc = String.format("Downloading %1$s", name);
monitor.setDescription(desc);
- monitor.setResult(desc);
+ monitor.log(desc);
String link = archive.getUrl();
if (!link.startsWith("http://") //$NON-NLS-1$
@@ -120,7 +120,7 @@ public class ArchiveInstaller { Package pkg = archive.getParentPackage();
SdkSource src = pkg.getParentSource();
if (src == null) {
- monitor.setResult("Internal error: no source for archive %1$s", name);
+ monitor.logError("Internal error: no source for archive %1$s", name);
return null;
}
@@ -152,7 +152,7 @@ public class ArchiveInstaller { OsHelper.deleteFileOrFolder(tmpFolder);
}
if (!tmpFolder.mkdirs()) {
- monitor.setResult("Failed to create directory %1$s", tmpFolder.getPath());
+ monitor.logError("Failed to create directory %1$s", tmpFolder.getPath());
return null;
}
}
@@ -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,
@@ -214,10 +214,10 @@ public class ArchiveInstaller { } catch (FileNotFoundException e) {
// The FNF message is just the URL. Make it a bit more useful.
- monitor.setResult("File not found: %1$s", e.getMessage());
+ monitor.logError("File not found: %1$s", e.getMessage());
} catch (Exception e) {
- monitor.setResult(e.getMessage());
+ monitor.logError(e.getMessage());
} finally {
if (is != null) {
@@ -327,14 +327,15 @@ public class ArchiveInstaller { }
if (monitor.isCancelRequested()) {
- monitor.setResult("Download aborted by user at %1$d bytes.", total);
+ monitor.log("Download aborted by user at %1$d bytes.", total);
return false;
}
}
if (total != size) {
- monitor.setResult("Download finished with wrong size. Expected %1$d bytes, got %2$d bytes.",
+ monitor.logError(
+ "Download finished with wrong size. Expected %1$d bytes, got %2$d bytes.",
size, total);
return false;
}
@@ -343,7 +344,7 @@ public class ArchiveInstaller { String actual = getDigestChecksum(digester);
String expected = archive.getChecksum();
if (!actual.equalsIgnoreCase(expected)) {
- monitor.setResult("Download finished with wrong checksum. Expected %1$s, got %2$s.",
+ monitor.logError("Download finished with wrong checksum. Expected %1$s, got %2$s.",
expected, actual);
return false;
}
@@ -352,10 +353,10 @@ public class ArchiveInstaller { } catch (FileNotFoundException e) {
// The FNF message is just the URL. Make it a bit more useful.
- monitor.setResult("File not found: %1$s", e.getMessage());
+ monitor.logError("File not found: %1$s", e.getMessage());
} catch (Exception e) {
- monitor.setResult(e.getMessage());
+ monitor.logError(e.getMessage());
} finally {
if (os != null) {
@@ -391,7 +392,7 @@ public class ArchiveInstaller { String pkgName = pkg.getShortDescription();
String pkgDesc = String.format("Installing %1$s", pkgName);
monitor.setDescription(pkgDesc);
- monitor.setResult(pkgDesc);
+ monitor.log(pkgDesc);
// Ideally we want to always unzip in a temp folder which name depends on the package
// type (e.g. addon, tools, etc.) and then move the folder to the destination folder.
@@ -432,12 +433,12 @@ public class ArchiveInstaller { if (destFolder == null) {
// this should not seriously happen.
- monitor.setResult("Failed to compute installation directory for %1$s.", pkgName);
+ monitor.log("Failed to compute installation directory for %1$s.", pkgName);
return false;
}
if (!pkg.preInstallHook(archive, monitor, osSdkRoot, destFolder)) {
- monitor.setResult("Skipping archive: %1$s", pkgName);
+ monitor.log("Skipping archive: %1$s", pkgName);
return false;
}
@@ -450,14 +451,14 @@ public class ArchiveInstaller { }
if (oldDestFolder == null) {
// this should not seriously happen.
- monitor.setResult("Failed to find a temp directory in %1$s.", osSdkRoot);
+ monitor.logError("Failed to find a temp directory in %1$s.", osSdkRoot);
return false;
}
// Try to move the current dest dir to the temp/old one. Tell the user if it failed.
while(true) {
if (!moveFolder(destFolder, oldDestFolder)) {
- monitor.setResult("Failed to rename directory %1$s to %2$s.",
+ monitor.logError("Failed to rename directory %1$s to %2$s.",
destFolder.getPath(), oldDestFolder.getPath());
if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) {
@@ -489,7 +490,7 @@ public class ArchiveInstaller { // -2- Unzip new content directly in place.
if (!destFolder.mkdirs()) {
- monitor.setResult("Failed to create directory %1$s", destFolder.getPath());
+ monitor.logError("Failed to create directory %1$s", destFolder.getPath());
return false;
}
@@ -498,7 +499,7 @@ public class ArchiveInstaller { }
if (!generateSourceProperties(archive, destFolder)) {
- monitor.setResult("Failed to generate source.properties in directory %1$s",
+ monitor.logError("Failed to generate source.properties in directory %1$s",
destFolder.getPath());
return false;
}
@@ -627,7 +628,7 @@ public class ArchiveInstaller { // Create directory if it doesn't exist yet. This allows us to create
// empty directories.
if (!destFile.isDirectory() && !destFile.mkdirs()) {
- monitor.setResult("Failed to create temp directory %1$s",
+ monitor.logError("Failed to create temp directory %1$s",
destFile.getPath());
return false;
}
@@ -638,7 +639,7 @@ public class ArchiveInstaller { File parentDir = destFile.getParentFile();
if (!parentDir.isDirectory()) {
if (!parentDir.mkdirs()) {
- monitor.setResult("Failed to create temp directory %1$s",
+ monitor.logError("Failed to create temp directory %1$s",
parentDir.getPath());
return false;
}
@@ -689,7 +690,7 @@ public class ArchiveInstaller { return true;
} catch (IOException e) {
- monitor.setResult("Unzip failed: %1$s", e.getMessage());
+ monitor.logError("Unzip failed: %1$s", e.getMessage());
} finally {
if (zipFile != null) {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/BrokenPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/BrokenPackage.java index 1629045..ca6f463 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/BrokenPackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/BrokenPackage.java @@ -99,7 +99,19 @@ public class BrokenPackage extends Package return mExactApiLevel;
}
- /** Returns a short description for an {@link IDescription}. */
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ return mShortDescription;
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
@Override
public String getShortDescription() {
return mShortDescription;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java index 8a4c19d..5171454 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java @@ -117,13 +117,35 @@ public class DocPackage extends Package implements IPackageVersion { mVersion.saveProperties(props);
}
- /** Returns the version, for platform, add-on and doc packages.
- * Can be 0 if this is a local package of unknown api-level. */
+ /**
+ * Returns the version, for platform, add-on and doc packages.
+ * Can be 0 if this is a local package of unknown api-level.
+ */
public AndroidVersion getVersion() {
return mVersion;
}
- /** Returns a short description for an {@link IDescription}. */
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ if (mVersion.isPreview()) {
+ return String.format("Documentation for Android '%1$s' Preview SDK%2$s",
+ mVersion.getCodename(),
+ isObsolete() ? " (Obsolete)" : "");
+ } else {
+ return String.format("Documentation for Android SDK%2$s",
+ mVersion.getApiLevel(),
+ isObsolete() ? " (Obsolete)" : "");
+ }
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
@Override
public String getShortDescription() {
if (mVersion.isPreview()) {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java index 49236dc..bdf2805 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java @@ -253,9 +253,7 @@ public class ExtraPackage extends MinToolsPackage return ""; //$NON-NLS-1$
}
- /** Returns a short description for an {@link IDescription}. */
- @Override
- public String getShortDescription() {
+ private String getPrettyName() {
String name = mPath;
// In the past, we used to save the extras in a folder vendor-path,
@@ -299,8 +297,31 @@ public class ExtraPackage extends MinToolsPackage name = name.replaceAll(" Usb ", " USB "); //$NON-NLS-1$
name = name.replaceAll(" Api ", " API "); //$NON-NLS-1$
+ return name;
+ }
+
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ String s = String.format("%1$s package%2$s",
+ getPrettyName(),
+ isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$
+
+ return s;
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
+ @Override
+ public String getShortDescription() {
+
String s = String.format("%1$s package, revision %2$d%3$s",
- name,
+ getPrettyName(),
getRevision(),
isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$
@@ -394,19 +415,33 @@ public class ExtraPackage extends MinToolsPackage ExtraPackage ep = (ExtraPackage) pkg;
// To be backward compatible, we need to support the old vendor-path form
- if (ep.mPath != null && (ep.mVendor == null || ep.mVendor.length() == 0) &&
- mPath != null && mVendor != null) {
- if (ep.mPath.equals(mVendor + "-" + mPath)) { //$NON-NLS-1$
+ // in either the current or the remote package.
+ //
+ // The vendor test below needs to account for an old installed package
+ // (e.g. with an install path of vendor-name) that has then beeen updated
+ // in-place and thus when reloaded contains the vendor name in both the
+ // path and the vendor attributes.
+ if (ep.mPath != null && mPath != null && mVendor != null) {
+ if (ep.mPath.equals(mVendor + "-" + mPath) && //$NON-NLS-1$
+ (ep.mVendor == null || ep.mVendor.length() == 0
+ || ep.mVendor.equals(mVendor))) {
+ return true;
+ }
+ }
+ if (mPath != null && ep.mPath != null && ep.mVendor != null) {
+ if (mPath.equals(ep.mVendor + "-" + ep.mPath) && //$NON-NLS-1$
+ (mVendor == null || mVendor.length() == 0 || mVendor.equals(ep.mVendor))) {
return true;
}
}
+
if (!mPath.equals(ep.mPath)) {
return false;
}
if ((mVendor == null && ep.mVendor == null) ||
- (mVendor != null && !mVendor.equals(ep.mVendor))) {
- return false;
+ (mVendor != null && mVendor.equals(ep.mVendor))) {
+ return true;
}
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskMonitor.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskMonitor.java index e08e27c..40f1ddb 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskMonitor.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskMonitor.java @@ -27,6 +27,21 @@ package com.android.sdklib.internal.repository; * If the task runs in a non-UI worker thread, the task factory implementation
* will take care of the update the UI in the correct thread. The task itself
* must not have to deal with it.
+ * <p/>
+ * A monitor typically has 3 levels of text displayed: <br/>
+ * - A <b>title</b> <em>may</em> be present on a task dialog, typically when a task
+ * dialog is created. This is not covered by this monitor interface. <br/>
+ * - A <b>description</b> displays prominent information on what the task
+ * is currently doing. This is expected to vary over time, typically changing
+ * with each sub-monitor, and typically only the last description is visible.
+ * For example an updater would typically have descriptions such as "downloading",
+ * "installing" and finally "done". This is set using {@link #setDescription}. <br/>
+ * - A <b>verbose</b> optional log that can provide more information than the summary
+ * description and is typically displayed in some kind of scrollable multi-line
+ * text field so that the user can keep track of what happened. 3 levels are
+ * provided: error, normal and verbose. An UI may hide the log till an error is
+ * logged and/or might hide the verbose text unless a flag is checked by the user.
+ * This is set using {@link #log}, {@link #logError} and {@link #logVerbose}.
*/
public interface ITaskMonitor {
@@ -34,13 +49,26 @@ public interface ITaskMonitor { * Sets the description in the current task dialog.
* This method can be invoked from a non-UI thread.
*/
- public void setDescription(String descriptionFormat, Object...args);
+ public void setDescription(String format, Object...args);
+
+ /**
+ * Logs a "normal" information line.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void log(String format, Object...args);
+
+ /**
+ * Logs an "error" information line.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void logError(String format, Object...args);
/**
- * Sets the result text in the current task dialog.
+ * Logs a "verbose" information line, that is extra details which are typically
+ * not that useful for the end-user and might be hidden until explicitly shown.
* This method can be invoked from a non-UI thread.
*/
- public void setResult(String resultFormat, Object...args);
+ public void logVerbose(String format, Object...args);
/**
* Sets the max value of the progress bar.
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java index eb00562..c4b92b5 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java @@ -303,8 +303,12 @@ public class LocalSdkParser { names.add(file.getName());
}
}
+
+ final String emulatorBinName =
+ SdkConstants.FN_EMULATOR + SdkConstants.FN_EMULATOR_EXTENSION;
+
if (!names.contains(SdkConstants.androidCmdName()) ||
- !names.contains(SdkConstants.FN_EMULATOR)) {
+ !names.contains(emulatorBinName)) {
return null;
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/OsHelper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/OsHelper.java index 02cebe1..c2338f3 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/OsHelper.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/OsHelper.java @@ -22,6 +22,8 @@ import java.io.File; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
/**
@@ -30,6 +32,21 @@ import java.io.IOException; abstract class OsHelper {
/**
+ * Reflection method for File.setExecutable(boolean, boolean). Only present in Java 6.
+ */
+ private static Method sFileSetExecutable = null;
+ /**
+ * Whether File.setExecutable was queried through reflection. This is to only
+ * attempt reflection once.
+ */
+ private static boolean sFileReflectionDone = false;
+ /**
+ * Parameters to call File.setExecutable through reflection.
+ */
+ private final static Object[] sFileSetExecutableParams = new Object[] {
+ Boolean.TRUE, Boolean.FALSE };
+
+ /**
* Helper to delete a file or a directory.
* For a directory, recursively deletes all of its content.
* Files that cannot be deleted right away are marked for deletion on exit.
@@ -76,18 +93,49 @@ abstract class OsHelper { }
/**
- * Sets the executable Unix permission (0777) on a file or folder.
+ * Sets the executable Unix permission (+x) on a file or folder.
+ * <p/>
+ * This attempts to use {@link File#setExecutable(boolean, boolean)} through reflection if
+ * it's available.
+ * If this is not available, this invokes a chmod exec instead,
+ * so there is no guarantee of it being fast.
* <p/>
- * This invokes a chmod exec, so there is no guarantee of it being fast.
* Caller must make sure to not invoke this under Windows.
*
* @param file The file to set permissions on.
* @throws IOException If an I/O error occurs
*/
static void setExecutablePermission(File file) throws IOException {
+ if (sFileReflectionDone == false) {
+ try {
+ sFileSetExecutable = File.class.getMethod("setExecutable", //$NON-NLS-1$
+ boolean.class, boolean.class);
+
+ } catch (SecurityException e) {
+ // do nothing we'll use chdmod instead
+ } catch (NoSuchMethodException e) {
+ // do nothing we'll use chdmod instead
+ }
+
+ sFileReflectionDone = true;
+ }
+
+ if (sFileSetExecutable != null) {
+ try {
+ sFileSetExecutable.invoke(file, sFileSetExecutableParams);
+ return;
+ } catch (IllegalArgumentException e) {
+ // we'll run chmod below
+ } catch (IllegalAccessException e) {
+ // we'll run chmod below
+ } catch (InvocationTargetException e) {
+ // we'll run chmod below
+ }
+ }
+
Runtime.getRuntime().exec(new String[] {
- "chmod", "777", file.getAbsolutePath()
- });
+ "chmod", "+x", file.getAbsolutePath() //$NON-NLS-1$ //$NON-NLS-2$
+ });
}
/**
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java index 58be3c9..c597ad8 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java @@ -372,6 +372,17 @@ public abstract class Package implements IDescription, Comparable<Package> { }
/**
+ * Returns a description of this package that is suitable for a list display.
+ * Should not be empty. Must never be null.
+ * <p/>
+ * Note that this is the "base" name for the package
+ * with no specific revision nor API mentionned.
+ * In contrast, {@link #getShortDescription()} should be used if you want more details
+ * such as the package revision number or the API, if applicable.
+ */
+ public abstract String getListDescription();
+
+ /**
* Returns a short description for an {@link IDescription}.
* Can be empty but not null.
*/
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java index c303e2f..622a922 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java @@ -121,7 +121,31 @@ public class PlatformPackage extends MinToolsPackage implements IPackageVersion return mVersion;
}
- /** Returns a short description for an {@link IDescription}. */
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ String s;
+
+ if (mVersion.isPreview()) {
+ s = String.format("SDK Platform Android %1$s Preview%2$s",
+ getVersionName(),
+ isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$
+ } else {
+ s = String.format("SDK Platform Android %1$s%2$s",
+ getVersionName(),
+ isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$
+ }
+
+ return s;
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
@Override
public String getShortDescription() {
String s;
@@ -130,13 +154,13 @@ public class PlatformPackage extends MinToolsPackage implements IPackageVersion s = String.format("SDK Platform Android %1$s Preview, revision %2$s%3$s",
getVersionName(),
getRevision(),
- isObsolete() ? " (Obsolete)" : "");
+ isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$
} else {
s = String.format("SDK Platform Android %1$s, API %2$d, revision %3$s%4$s",
getVersionName(),
mVersion.getApiLevel(),
getRevision(),
- isObsolete() ? " (Obsolete)" : "");
+ isObsolete() ? " (Obsolete)" : ""); //$NON-NLS-2$
}
return s;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformToolPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformToolPackage.java index 860d703..9a2de62 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformToolPackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformToolPackage.java @@ -143,7 +143,20 @@ public class PlatformToolPackage extends Package { archiveOsPath); } - /** Returns a short description for an {@link IDescription}. */ + /** + * Returns a description of this package that is suitable for a list display. + * <p/> + * {@inheritDoc} + */ + @Override + public String getListDescription() { + return String.format("Android SDK Platform-tools%1$s", + isObsolete() ? " (Obsolete)" : ""); + } + + /** + * Returns a short description for an {@link IDescription}. + */ @Override public String getShortDescription() { return String.format("Android SDK Platform-tools, revision %1$d%2$s", diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java index 035677b..b436b91 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java @@ -175,7 +175,23 @@ public class SamplePackage extends MinToolsPackage return mVersion;
}
- /** Returns a short description for an {@link IDescription}. */
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ String s = String.format("Samples for SDK API %1$s%2$s%3$s",
+ mVersion.getApiString(),
+ mVersion.isPreview() ? " Preview" : "",
+ isObsolete() ? " (Obsolete)" : "");
+ return s;
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
@Override
public String getShortDescription() {
String s = String.format("Samples for SDK API %1$s%2$s, revision %3$d%4$s",
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 58bf314..22b9c42 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 @@ -57,7 +57,7 @@ import javax.xml.validation.Validator; * It may be a full repository or an add-on only repository.
* A repository describes one or {@link Package}s available for download.
*/
-public abstract class SdkSource implements IDescription {
+public abstract class SdkSource implements IDescription, Comparable<SdkSource> {
private String mUrl;
@@ -151,6 +151,14 @@ public abstract class SdkSource implements IDescription { }
/**
+ * Implementation of the {@link Comparable} interface.
+ * Simply compares the URL using the string's default ordering.
+ */
+ public int compareTo(SdkSource rhs) {
+ return this.getUrl().compareTo(rhs.getUrl());
+ }
+
+ /**
* Returns the UI-visible name of the source. Can be null.
*/
public String getUiName() {
@@ -250,7 +258,7 @@ public abstract class SdkSource implements IDescription { 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;
@@ -276,7 +284,7 @@ public abstract class SdkSource implements IDescription { }
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
@@ -295,7 +303,7 @@ public abstract class SdkSource implements IDescription { if (usingAlternateUrl && validatedDoc != null) {
// If the second tentative succeeded, indicate it in the console
// with the URL that worked.
- monitor.setResult("Repository found at %1$s", url);
+ monitor.log("Repository found at %1$s", url);
// Keep the modified URL
mUrl = url;
@@ -380,11 +388,11 @@ public abstract class SdkSource implements IDescription { reason = String.format("Unknown (%1$s)", exception[0].getClass().getName());
}
- monitor.setResult("Failed to fetch URL %1$s, reason: %2$s", url, reason);
+ monitor.logError("Failed to fetch URL %1$s, reason: %2$s", url, reason);
}
if (validationError[0] != null) {
- monitor.setResult("%s", validationError[0]); //$NON-NLS-1$
+ monitor.logError("%s", validationError[0]); //$NON-NLS-1$
}
// Stop here if we failed to validate the XML. We don't want to load it.
@@ -424,7 +432,7 @@ public abstract class SdkSource implements IDescription { 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) {
@@ -740,12 +748,11 @@ public abstract class SdkSource implements IDescription { if (p != null) {
packages.add(p);
- monitor.setDescription("Found %1$s", p.getShortDescription());
+ monitor.logVerbose("Found %1$s", p.getShortDescription());
}
} catch (Exception e) {
// Ignore invalid packages
- monitor.setResult("Ignoring invalid %1$s element: %2$s",
- name, e.toString());
+ monitor.logError("Ignoring invalid %1$s element: %2$s", name, e.toString());
}
}
}
@@ -794,13 +801,13 @@ public abstract class SdkSource implements IDescription { return doc;
} catch (ParserConfigurationException e) {
- monitor.setResult("Failed to create XML document builder");
+ monitor.logError("Failed to create XML document builder");
} catch (SAXException e) {
- monitor.setResult("Failed to parse XML document");
+ monitor.logError("Failed to parse XML document");
} catch (IOException e) {
- monitor.setResult("Failed to read XML document");
+ monitor.logError("Failed to read XML document");
}
return null;
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java index 22678b3..be99f22 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java @@ -111,7 +111,7 @@ public class SdkSources { }
/**
- * Returns an array of sources attached to the given category.
+ * Returns a new array of sources attached to the given category.
* Might return an empty array, but never returns null.
*/
public SdkSource[] getSources(SdkSourceCategory category) {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java index 6e53dd7..69039ea 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java @@ -151,7 +151,20 @@ public class ToolPackage extends Package implements IMinPlatformToolsDependency return mMinPlatformToolsRevision;
}
- /** Returns a short description for an {@link IDescription}. */
+ /**
+ * Returns a description of this package that is suitable for a list display.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public String getListDescription() {
+ return String.format("Android SDK Tools%1$s",
+ isObsolete() ? " (Obsolete)" : "");
+ }
+
+ /**
+ * Returns a short description for an {@link IDescription}.
+ */
@Override
public String getShortDescription() {
return String.format("Android SDK Tools, revision %1$d%2$s",
@@ -250,17 +263,17 @@ public class ToolPackage extends Package implements IMinPlatformToolsDependency status = grabProcessOutput(proc, monitor, scriptName);
} catch (Exception e) {
- monitor.setResult("Exception: %s", e.toString());
+ monitor.logError("Exception: %s", e.toString());
}
if (status != 0) {
- monitor.setResult("Failed to execute %s", scriptName);
+ monitor.logError("Failed to execute %s", scriptName);
return;
}
}
/**
- * Get the stderr/stdout outputs of a process and return when the process is done.
+ * Gets the stderr/stdout outputs of a process and returns when the process is done.
* Both <b>must</b> be read or the process will block on windows.
* @param process The process to get the ouput from.
* @param monitor The monitor where to output errors.
@@ -285,7 +298,7 @@ public class ToolPackage extends Package implements IMinPlatformToolsDependency while (true) {
String line = errReader.readLine();
if (line != null) {
- monitor.setResult("[%1$s] Error: %2$s", scriptName, line);
+ monitor.logError("[%1$s] Error: %2$s", scriptName, line);
} else {
break;
}
@@ -306,7 +319,7 @@ public class ToolPackage extends Package implements IMinPlatformToolsDependency while (true) {
String line = outReader.readLine();
if (line != null) {
- monitor.setResult("[%1$s] %2$s", scriptName, line);
+ monitor.log("[%1$s] %2$s", scriptName, line);
} else {
break;
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FileWrapper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FileWrapper.java deleted file mode 100644 index afc19b2..0000000 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FileWrapper.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.io; - - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; - -/** - * An implementation of {@link IAbstractFile} extending {@link File}. - */ -public class FileWrapper extends File implements IAbstractFile { - private static final long serialVersionUID = 1L; - - /** - * Creates a new File instance matching a given {@link File} object. - * @param file the file to match - */ - public FileWrapper(File file) { - super(file.getAbsolutePath()); - } - - /** - * Creates a new File instance from a parent abstract pathname and a child pathname string. - * @param parent the parent pathname - * @param child the child name - * - * @see File#File(File, String) - */ - public FileWrapper(File parent, String child) { - super(parent, child); - } - - /** - * Creates a new File instance by converting the given pathname string into an abstract - * pathname. - * @param osPathname the OS pathname - * - * @see File#File(String) - */ - public FileWrapper(String osPathname) { - super(osPathname); - } - - /** - * Creates a new File instance from a parent abstract pathname and a child pathname string. - * @param parent the parent pathname - * @param child the child name - * - * @see File#File(String, String) - */ - public FileWrapper(String parent, String child) { - super(parent, child); - } - - /** - * Creates a new File instance by converting the given <code>file:</code> URI into an - * abstract pathname. - * @param uri An absolute, hierarchical URI with a scheme equal to "file", a non-empty path - * component, and undefined authority, query, and fragment components - * - * @see File#File(URI) - */ - public FileWrapper(URI uri) { - super(uri); - } - - public InputStream getContents() throws StreamException { - try { - return new FileInputStream(this); - } catch (FileNotFoundException e) { - throw new StreamException(e); - } - } - - public void setContents(InputStream source) throws StreamException { - FileOutputStream fos = null; - try { - fos = new FileOutputStream(this); - - byte[] buffer = new byte[1024]; - int count = 0; - while ((count = source.read(buffer)) != -1) { - fos.write(buffer, 0, count); - } - } catch (IOException e) { - throw new StreamException(e); - } finally { - if (fos != null) { - try { - fos.close(); - } catch (IOException e) { - throw new StreamException(e); - } - } - } - } - - public OutputStream getOutputStream() throws StreamException { - try { - return new FileOutputStream(this); - } catch (FileNotFoundException e) { - throw new StreamException(e); - } - } - - public PreferredWriteMode getPreferredWriteMode() { - return PreferredWriteMode.OUTPUTSTREAM; - } - - public String getOsLocation() { - return getAbsolutePath(); - } - - @Override - public boolean exists() { - return isFile(); - } - - public IAbstractFolder getParentFolder() { - String p = this.getParent(); - if (p == null) { - return null; - } - return new FolderWrapper(p); - } -} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FolderWrapper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FolderWrapper.java deleted file mode 100644 index 33e31c1..0000000 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/FolderWrapper.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.io; - - -import java.io.File; -import java.net.URI; -import java.util.ArrayList; - -/** - * An implementation of {@link IAbstractFolder} extending {@link File}. - */ -public class FolderWrapper extends File implements IAbstractFolder { - - private static final long serialVersionUID = 1L; - - /** - * Creates a new File instance from a parent abstract pathname and a child pathname string. - * @param parent the parent pathname - * @param child the child name - * - * @see File#File(File, String) - */ - public FolderWrapper(File parent, String child) { - super(parent, child); - } - - /** - * Creates a new File instance by converting the given pathname string into an abstract - * pathname. - * @param pathname the pathname - * - * @see File#File(String) - */ - public FolderWrapper(String pathname) { - super(pathname); - } - - /** - * Creates a new File instance from a parent abstract pathname and a child pathname string. - * @param parent the parent pathname - * @param child the child name - * - * @see File#File(String, String) - */ - public FolderWrapper(String parent, String child) { - super(parent, child); - } - - /** - * Creates a new File instance by converting the given <code>file:</code> URI into an - * abstract pathname. - * @param uri An absolute, hierarchical URI with a scheme equal to "file", a non-empty path - * component, and undefined authority, query, and fragment components - * - * @see File#File(URI) - */ - public FolderWrapper(URI uri) { - super(uri); - } - - /** - * Creates a new File instance matching a give {@link File} object. - * @param file the file to match - */ - public FolderWrapper(File file) { - super(file.getAbsolutePath()); - } - - public IAbstractResource[] listMembers() { - File[] files = listFiles(); - final int count = files == null ? 0 : files.length; - IAbstractResource[] afiles = new IAbstractResource[count]; - - if (files != null) { - for (int i = 0 ; i < count ; i++) { - File f = files[i]; - if (f.isFile()) { - afiles[i] = new FileWrapper(f); - } else if (f.isDirectory()) { - afiles[i] = new FolderWrapper(f); - } - } - } - - return afiles; - } - - public boolean hasFile(final String name) { - String[] match = list(new FilenameFilter() { - public boolean accept(IAbstractFolder dir, String filename) { - return name.equals(filename); - } - }); - - return match.length > 0; - } - - public IAbstractFile getFile(String name) { - return new FileWrapper(this, name); - } - - public IAbstractFolder getFolder(String name) { - return new FolderWrapper(this, name); - } - - public IAbstractFolder getParentFolder() { - String p = this.getParent(); - if (p == null) { - return null; - } - return new FolderWrapper(p); - } - - public String getOsLocation() { - return getAbsolutePath(); - } - - @Override - public boolean exists() { - return isDirectory(); - } - - public String[] list(FilenameFilter filter) { - File[] files = listFiles(); - if (files != null && files.length > 0) { - ArrayList<String> list = new ArrayList<String>(); - - for (File file : files) { - if (filter.accept(this, file.getName())) { - list.add(file.getName()); - } - } - - return list.toArray(new String[list.size()]); - } - - return new String[0]; - } -} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFile.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFile.java deleted file mode 100644 index 2ff1fc8..0000000 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFile.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.io; - -import java.io.InputStream; -import java.io.OutputStream; - -/** - * A file. - */ -public interface IAbstractFile extends IAbstractResource { - public static enum PreferredWriteMode { - INPUTSTREAM, OUTPUTSTREAM; - } - - /** - * Returns an {@link InputStream} object on the file content. - * @throws StreamException - */ - InputStream getContents() throws StreamException; - - /** - * Sets the content of the file. - * @param source the content - * @throws StreamException - */ - void setContents(InputStream source) throws StreamException; - - /** - * Returns an {@link OutputStream} to write into the file. - * @throws StreamException - */ - OutputStream getOutputStream() throws StreamException; - - /** - * Returns the preferred mode to write into the file. - */ - PreferredWriteMode getPreferredWriteMode(); -} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFolder.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFolder.java deleted file mode 100644 index 17c0dbd..0000000 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFolder.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.io; - -import java.io.File; - -/** - * A folder. - */ -public interface IAbstractFolder extends IAbstractResource { - /** - * Instances of classes that implement this interface are used to - * filter filenames. - */ - public interface FilenameFilter { - /** - * Tests if a specified file should be included in a file list. - * - * @param dir the directory in which the file was found. - * @param name the name of the file. - * @return <code>true</code> if and only if the name should be - * included in the file list; <code>false</code> otherwise. - */ - boolean accept(IAbstractFolder dir, String name); - } - - /** - * Returns true if the receiver contains a file with a given name - * @param name the name of the file. This is the name without the path leading to the - * parent folder. - */ - boolean hasFile(String name); - - /** - * Returns an {@link IAbstractFile} representing a child of the current folder with the - * given name. The file may not actually exist. - * @param name the name of the file. - */ - IAbstractFile getFile(String name); - - /** - * Returns an {@link IAbstractFolder} representing a child of the current folder with the - * given name. The folder may not actually exist. - * @param name the name of the folder. - */ - IAbstractFolder getFolder(String name); - - /** - * Returns a list of all existing file and directory members in this folder. - * The returned array can be empty but is never null. - */ - IAbstractResource[] listMembers(); - - /** - * Returns a list of all existing file and directory members in this folder - * that satisfy the specified filter. - * - * @param filter A filename filter instance. Must not be null. - * @return An array of file names (generated using {@link File#getName()}). - * The array can be empty but is never null. - */ - String[] list(FilenameFilter filter); -} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractResource.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractResource.java deleted file mode 100644 index 0ccb107..0000000 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractResource.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.io; - -/** - * Base representation of a file system resource.<p/> - * This somewhat limited interface is designed to let classes use file-system resources, without - * having the manually handle either the standard Java file or the Eclipse file API.. - */ -public interface IAbstractResource { - - /** - * Returns the name of the resource. - */ - String getName(); - - /** - * Returns the OS path of the folder location. - */ - String getOsLocation(); - - /** - * Returns whether the resource actually exists. - */ - boolean exists(); - - /** - * Returns the parent folder or null if there is no parent. - */ - IAbstractFolder getParentFolder(); -} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java index 2cb6ace..26fcd7b 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java @@ -16,10 +16,10 @@ package com.android.sdklib.xml; +import com.android.io.IAbstractFile; +import com.android.io.IAbstractFolder; +import com.android.io.StreamException; import com.android.sdklib.SdkConstants; -import com.android.sdklib.io.IAbstractFile; -import com.android.sdklib.io.IAbstractFolder; -import com.android.sdklib.io.StreamException; import org.w3c.dom.Node; import org.xml.sax.InputSource; @@ -76,6 +76,7 @@ public final class AndroidManifest { public final static String ATTRIBUTE_REQ_HARDKEYBOARD = "reqHardKeyboard"; public final static String ATTRIBUTE_REQ_KEYBOARDTYPE = "reqKeyboardType"; public final static String ATTRIBUTE_REQ_TOUCHSCREEN = "reqTouchScreen"; + public static final String ATTRIBUTE_THEME = "theme"; /** * Returns an {@link IAbstractFile} object representing the manifest for the given project. diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifestParser.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifestParser.java index 09e81f7..ca59a8e 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifestParser.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifestParser.java @@ -16,13 +16,13 @@ package com.android.sdklib.xml; +import com.android.io.IAbstractFile; +import com.android.io.IAbstractFolder; +import com.android.io.StreamException; import com.android.resources.Keyboard; import com.android.resources.Navigation; import com.android.resources.TouchScreen; import com.android.sdklib.SdkConstants; -import com.android.sdklib.io.IAbstractFile; -import com.android.sdklib.io.IAbstractFolder; -import com.android.sdklib.io.StreamException; import com.android.sdklib.xml.ManifestData.Activity; import com.android.sdklib.xml.ManifestData.Instrumentation; import com.android.sdklib.xml.ManifestData.SupportsScreens; diff --git a/sdkmanager/libs/sdklib/tests/Android.mk b/sdkmanager/libs/sdklib/tests/Android.mk new file mode 100644 index 0000000..0179dc1 --- /dev/null +++ b/sdkmanager/libs/sdklib/tests/Android.mk @@ -0,0 +1,28 @@ +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +# Only compile source java files in this lib. +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_JAVA_RESOURCE_DIRS := src + +LOCAL_MODULE := sdklib-tests +LOCAL_MODULE_TAGS := optional + +LOCAL_JAVA_LIBRARIES := sdklib junit + +include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/build/DebugKeyProviderTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/build/DebugKeyProviderTest.java new file mode 100755 index 0000000..6f6896e --- /dev/null +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/build/DebugKeyProviderTest.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdklib.internal.build; + +import com.android.sdklib.internal.build.DebugKeyProvider.IKeyGenOutput; + +import java.io.File; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Calendar; +import java.util.Date; + +import junit.framework.TestCase; + +public class DebugKeyProviderTest extends TestCase { + + private File mTmpFile; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + // We want to allocate a new tmp file but not have it actually exist + mTmpFile = File.createTempFile(this.getClass().getSimpleName(), ".keystore"); + assertTrue(mTmpFile.delete()); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + if (mTmpFile != null) { + if (!mTmpFile.delete()) { + mTmpFile.deleteOnExit(); + } + mTmpFile = null; + } + } + + public void testCreateAndCheckKey() throws Exception { + String osPath = mTmpFile.getAbsolutePath(); + + KeygenOutput keygenOutput = new KeygenOutput(); + + // "now" is just slightly before the key was created + long now = System.currentTimeMillis(); + + DebugKeyProvider provider; + try { + provider = new DebugKeyProvider(osPath, null /*storeType*/, keygenOutput); + } catch (Throwable t) { + // In case we get any kind of exception, rewrap it to make sure we output + // the path used. + String msg = String.format("%1$s in %2$s\n%3$s", + t.getClass().getSimpleName(), osPath, t.toString()); + throw new Exception(msg, t); + } + assertNotNull(provider); + + assertEquals("", keygenOutput.getOut()); + assertEquals("", keygenOutput.getErr()); + + PrivateKey key = provider.getDebugKey(); + assertNotNull(key); + + X509Certificate certificate = (X509Certificate) provider.getCertificate(); + assertNotNull(certificate); + + // The "not-after" (a.k.a. expiration) date should be after "now" + Calendar c = Calendar.getInstance(); + c.setTimeInMillis(now); + assertTrue(certificate.getNotAfter().compareTo(c.getTime()) > 0); + + // It should be valid after 1 year from now (adjust by a second since the 'now' time + // doesn't exactly match the creation time... 1 second should be enough.) + c.add(Calendar.DAY_OF_YEAR, 365); + c.add(Calendar.SECOND, -1); + assertTrue("1 year expiration failed", + certificate.getNotAfter().compareTo(c.getTime()) > 0); + + // and 30 years from now + c.add(Calendar.DAY_OF_YEAR, 29 * 365); + assertTrue("30 year expiration failed", + certificate.getNotAfter().compareTo(c.getTime()) > 0); + + // however expiration date should be passed in 30 years + 1 hour + c.add(Calendar.HOUR, 1); + assertFalse("30 year and 1 hour expiration failed", + certificate.getNotAfter().compareTo(c.getTime()) > 0); + } + + private static class KeygenOutput implements IKeyGenOutput { + private String mOut = ""; //$NON-NLS-1$ + private String mErr = ""; //$NON-NLS-1$ + + public void out(String message) { + mOut += message + "\n"; //$NON-NLS-1$ + } + + public void err(String message) { + mErr += message + "\n"; //$NON-NLS-1$ + } + + public String getOut() { + return mOut; + } + + public String getErr() { + return mErr; + } + } +} diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/AddonsListFetcherTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/AddonsListFetcherTest.java index 3f2bb84..9cf84a2 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/AddonsListFetcherTest.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/AddonsListFetcherTest.java @@ -104,7 +104,9 @@ public class AddonsListFetcherTest extends TestCase { Site[] result = mFetcher._parseAddonsList(doc, uri, monitor);
assertEquals("", monitor.getCapturedDescriptions());
- assertEquals("", monitor.getCapturedResults());
+ assertEquals("", monitor.getCapturedLog());
+ assertEquals("", monitor.getCapturedErrorLog());
+ assertEquals("", monitor.getCapturedVerboseLog());
// check the sites we found...
assertEquals(3, result.length);
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/BrokenPackageTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/BrokenPackageTest.java index 3e9ba8d..3e9ba8d 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/BrokenPackageTest.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/BrokenPackageTest.java diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockAddonPackage.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockAddonPackage.java index e99463b..55ace17 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockAddonPackage.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockAddonPackage.java @@ -18,6 +18,7 @@ package com.android.sdklib.internal.repository; import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
import java.util.Map;
@@ -68,6 +69,14 @@ public class MockAddonPackage extends AddonPackage { return "mock addon target";
}
+ public String[] getAbiList() {
+ return new String[] { SdkConstants.ABI_ARMEABI };
+ }
+
+ public String getImagePath(String abiType) {
+ return SdkConstants.OS_IMAGES_FOLDER;
+ }
+
public String getLocation() {
return "";
}
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockBrokenPackage.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockBrokenPackage.java index 289305b..289305b 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockBrokenPackage.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockBrokenPackage.java diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockEmptySdkManager.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockEmptySdkManager.java index f66734b..f66734b 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockEmptySdkManager.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockEmptySdkManager.java diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockMonitor.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockMonitor.java index 56a7c6c..3b7e608 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockMonitor.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockMonitor.java @@ -24,26 +24,44 @@ package com.android.sdklib.internal.repository; */
public class MockMonitor implements ITaskMonitor {
- String mCapturedResults = "";
- String mCapturedDescriptions = "";
+ String mCapturedLog = ""; //$NON-NLS-1$
+ String mCapturedErrorLog = ""; //$NON-NLS-1$
+ String mCapturedVerboseLog = ""; //$NON-NLS-1$
+ String mCapturedDescriptions = ""; //$NON-NLS-1$
- public String getCapturedResults() {
- return mCapturedResults;
+ public String getCapturedLog() {
+ return mCapturedLog;
+ }
+
+ public String getCapturedErrorLog() {
+ return mCapturedErrorLog;
+ }
+
+ public String getCapturedVerboseLog() {
+ return mCapturedVerboseLog;
}
public String getCapturedDescriptions() {
return mCapturedDescriptions;
}
- public void setResult(String resultFormat, Object... args) {
- mCapturedResults += String.format(resultFormat, args) + "\n";
+ public void log(String format, Object... args) {
+ mCapturedLog += String.format(format, args) + "\n"; //$NON-NLS-1$
+ }
+
+ public void logError(String format, Object... args) {
+ mCapturedErrorLog += String.format(format, args) + "\n"; //$NON-NLS-1$
+ }
+
+ public void logVerbose(String format, Object... args) {
+ mCapturedVerboseLog += String.format(format, args) + "\n"; //$NON-NLS-1$
}
public void setProgressMax(int max) {
}
- public void setDescription(String descriptionFormat, Object... args) {
- mCapturedDescriptions += String.format(descriptionFormat, args) + "\n";
+ public void setDescription(String format, Object... args) {
+ mCapturedDescriptions += String.format(format, args) + "\n"; //$NON-NLS-1$
}
public boolean isCancelRequested() {
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockPlatformPackage.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockPlatformPackage.java index 0a58487..ad1ab16 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockPlatformPackage.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockPlatformPackage.java @@ -18,6 +18,7 @@ package com.android.sdklib.internal.repository; import com.android.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
import java.util.Map;
import java.util.Properties;
@@ -100,6 +101,14 @@ public class MockPlatformPackage extends PlatformPackage { return "mock platform target";
}
+ public String[] getAbiList() {
+ return new String[] { SdkConstants.ABI_ARMEABI };
+ }
+
+ public String getImagePath(String abiType) {
+ return SdkConstants.OS_IMAGES_FOLDER;
+ }
+
public String getLocation() {
return "";
}
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockPlatformToolPackage.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockPlatformToolPackage.java index 0befa80..0befa80 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockPlatformToolPackage.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockPlatformToolPackage.java diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockToolPackage.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockToolPackage.java index 8ce704c..8ce704c 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockToolPackage.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockToolPackage.java diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkAddonSourceTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkAddonSourceTest.java index ea6c4f6..6df440e 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkAddonSourceTest.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkAddonSourceTest.java @@ -176,8 +176,9 @@ public class SdkAddonSourceTest extends TestCase { "Found G USB Driver package, revision 43 (Obsolete)\n" +
"Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +
"Found Unkown Extra package, revision 2 (Obsolete)\n",
- monitor.getCapturedDescriptions());
- assertEquals("", monitor.getCapturedResults());
+ monitor.getCapturedVerboseLog());
+ assertEquals("", monitor.getCapturedLog());
+ assertEquals("", monitor.getCapturedErrorLog());
// check the packages we found... we expected to find 11 packages with each at least
// one archive.
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkRepoSourceTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkRepoSourceTest.java index aee811f..24a6fb6 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkRepoSourceTest.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkRepoSourceTest.java @@ -141,14 +141,15 @@ public class SdkRepoSourceTest extends TestCase { Document result = mSource._findAlternateToolsXml(xmlStream);
assertNotNull(result);
- MockMonitor mon = new MockMonitor();
- assertTrue(mSource._parsePackages(result, SdkRepoConstants.NS_URI, mon));
+ MockMonitor monitor = new MockMonitor();
+ assertTrue(mSource._parsePackages(result, SdkRepoConstants.NS_URI, monitor));
assertEquals("Found Android SDK Tools, revision 1\n" +
"Found Android SDK Tools, revision 42\n" +
"Found Android SDK Platform-tools, revision 3\n",
- mon.getCapturedDescriptions());
- assertEquals("", mon.getCapturedResults());
+ monitor.getCapturedVerboseLog());
+ assertEquals("", monitor.getCapturedLog());
+ assertEquals("", monitor.getCapturedErrorLog());
// check the packages we found... we expected to find 2 tool packages and 1
// platform-tools package, with at least 1 archive each.
@@ -199,8 +200,9 @@ public class SdkRepoSourceTest extends TestCase { "Found Android SDK Tools, revision 42\n" +
"Found This add-on has no libraries by Joe Bar, Android API 4, revision 3\n" +
"Found Usb Driver package, revision 43\n",
- monitor.getCapturedDescriptions());
- assertEquals("", monitor.getCapturedResults());
+ monitor.getCapturedVerboseLog());
+ assertEquals("", monitor.getCapturedLog());
+ assertEquals("", monitor.getCapturedErrorLog());
// check the packages we found... we expected to find 11 packages with each at least
// one archive.
@@ -277,8 +279,9 @@ public class SdkRepoSourceTest extends TestCase { "Found Usb Driver package, revision 43 (Obsolete)\n" +
"Found Extra API Dep package, revision 2 (Obsolete)\n" +
"Found Samples for SDK API 14, revision 24 (Obsolete)\n",
- monitor.getCapturedDescriptions());
- assertEquals("", monitor.getCapturedResults());
+ monitor.getCapturedVerboseLog());
+ assertEquals("", monitor.getCapturedLog());
+ assertEquals("", monitor.getCapturedErrorLog());
// check the packages we found... we expected to find 13 packages with each at least
// one archive.
@@ -353,8 +356,9 @@ public class SdkRepoSourceTest extends TestCase { "Found A USB Driver package, revision 43 (Obsolete)\n" +
"Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +
"Found Samples for SDK API 14, revision 24 (Obsolete)\n",
- monitor.getCapturedDescriptions());
- assertEquals("", monitor.getCapturedResults());
+ monitor.getCapturedVerboseLog());
+ assertEquals("", monitor.getCapturedLog());
+ assertEquals("", monitor.getCapturedErrorLog());
// check the packages we found... we expected to find 13 packages with each at least
// one archive.
diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/mock/MockLog.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/mock/MockLog.java index 3ef0140..3ef0140 100644 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/mock/MockLog.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/mock/MockLog.java diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/SdkRepositoryTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/repository/SdkRepositoryTest.java index 2bbc088..2bbc088 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/SdkRepositoryTest.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/repository/SdkRepositoryTest.java diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-instrumentation.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-instrumentation.xml index f1dce67..f1dce67 100644 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-instrumentation.xml +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-instrumentation.xml diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-testapp.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-testapp.xml index f55b4e0..f55b4e0 100644 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-testapp.xml +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-testapp.xml diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-testapp2.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-testapp2.xml index d5bcac7..d5bcac7 100644 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-testapp2.xml +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-testapp2.xml diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addon_sample_1.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addon_sample_1.xml index d761d73..d761d73 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addon_sample_1.xml +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addon_sample_1.xml diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addons_list_sample_1.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addons_list_sample_1.xml index bda69d4..bda69d4 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addons_list_sample_1.xml +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addons_list_sample_1.xml diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_1.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_1.xml index aa209d3..aa209d3 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_1.xml +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_1.xml diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_2.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_2.xml index 4b8e329..4b8e329 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_2.xml +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_2.xml diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_3.xml b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_3.xml index 05a9c79..05a9c79 100755 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_3.xml +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_3.xml diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/AndroidManifestParserTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/xml/AndroidManifestParserTest.java index 02725f4..02725f4 100644 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/AndroidManifestParserTest.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/xml/AndroidManifestParserTest.java diff --git a/sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/SupportsScreensTest.java b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/xml/SupportsScreensTest.java index baf13b1..baf13b1 100644 --- a/sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/SupportsScreensTest.java +++ b/sdkmanager/libs/sdklib/tests/src/com/android/sdklib/xml/SupportsScreensTest.java diff --git a/sdkmanager/libs/sdkuilib/.classpath b/sdkmanager/libs/sdkuilib/.classpath index b62b292..9080f57 100644 --- a/sdkmanager/libs/sdkuilib/.classpath +++ b/sdkmanager/libs/sdkuilib/.classpath @@ -1,12 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> - <classpathentry kind="src" path="src"/> - <classpathentry kind="src" path="tests"/> + <classpathentry excluding="**/Android.mk" kind="src" path="src"/> + <classpathentry excluding="**/Android.mk" kind="src" path="tests"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry exported="true" kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/> <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/> <classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/> <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> <classpathentry combineaccessrules="false" kind="src" path="/common"/> + <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/swtmenubar.jar" sourcepath="/ANDROID_SRC/sdk/swtmenubar/src"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/sdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.core.prefs b/sdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..17cc168 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,64 @@ +#Wed Mar 16 15:10:22 PDT 2011 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning diff --git a/sdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.ui.prefs b/sdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.ui.prefs new file mode 100755 index 0000000..7471e0b --- /dev/null +++ b/sdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,54 @@ +#Fri Apr 08 10:49:40 PDT 2011 +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=false +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.make_local_variable_final=false +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=false +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/sdkmanager/libs/sdkuilib/Android.mk b/sdkmanager/libs/sdkuilib/Android.mk index 8e0bc23..740615e 100644 --- a/sdkmanager/libs/sdkuilib/Android.mk +++ b/sdkmanager/libs/sdkuilib/Android.mk @@ -1,4 +1,43 @@ -# Copyright 2008 The Android Open Source Project # -SDKUILIB_LOCAL_DIR := $(call my-dir) -include $(SDKUILIB_LOCAL_DIR)/src/Android.mk +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_JAVA_RESOURCE_DIRS := src + +# IMPORTANT: if you add a new dependency here, please make sure +# to also check the following file: +# sdkmanager/app/etc/android.bat +# (Note: there is no manifest.txt for sdkuilib.) +LOCAL_JAVA_LIBRARIES := \ + sdklib \ + common \ + androidprefs \ + swtmenubar \ + swt \ + org.eclipse.jface_3.4.2.M20090107-0800 \ + org.eclipse.equinox.common_3.4.0.v20080421-2006 \ + org.eclipse.core.commands_3.4.0.I20080509-2000 + +LOCAL_MODULE := sdkuilib + +include $(BUILD_HOST_JAVA_LIBRARY) + + + +# Build all sub-directories +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/sdkmanager/libs/sdkuilib/NOTICE b/sdkmanager/libs/sdkuilib/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/sdkmanager/libs/sdkuilib/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/sdkmanager/libs/sdkuilib/README b/sdkmanager/libs/sdkuilib/README index d66b84a..dee4a24 100644 --- a/sdkmanager/libs/sdkuilib/README +++ b/sdkmanager/libs/sdkuilib/README @@ -1,11 +1,45 @@ -Using the Eclipse projects for ddmuilib. +Using the Eclipse project SdkUiLib +---------------------------------- -ddmuilib requires SWT to compile. +1- sdkuilib requires SWT to compile. -SWT is available in the depot under prebuild/<platform>/swt +SWT is available in the tree under prebuild/<platform>/swt Because the build path cannot contain relative path that are not inside the project directory, the .classpath file references a user library called ANDROID_SWT. -In order to compile the project, make a user library called ANDROID_SWT containing the jar -available at prebuild/<platform>/swt.
\ No newline at end of file +In order to compile the project: +- Open Preferences > Java > Build Path > User Libraries +- Create a new user library named ANDROID_SWT +- Add the following 4 JAR files: + + - prebuilt/<platform>/swt/swt.jar + - prebuilt/common/eclipse/org.eclipse.core.commands_3.*.jar + - prebuilt/common/eclipse/org.eclipse.equinox.common_3.*.jar + - prebuilt/common/eclipse/org.eclipse.jface_3.*.jar + + +2- sdkuilib also requires the compiled swtmenubar library. + +Build the swtmenubar library: +$ cd $TOP (top of Android tree) +$ . build/envsetup.sh && lunch sdk-eng +$ sdk/eclipse/scripts/create_sdkman_symlinks.sh + +Define a classpath variable in Eclipse: +- Open Preferences > Java > Build Path > Classpath Variables +- Create a new classpath variable named ANDROID_OUT_FRAMEWORK +- Set its folder value to <Android tree>/out/host/<platform>/framework +- Create a new classpath variable named ANDROID_SRC +- Set its folder value to <Android tree> + +You might need to clean the SdkUiLib project (Project > Clean...) after +you add the new classpath variable, otherwise previous errors might not +go away automatically. + +The ANDROID_SRC part should be optional. It allows you to have access to +the SwtMenuBar generic parts from the Java editor. + + +-- +EOF diff --git a/sdkmanager/libs/sdkuilib/src/Android.mk b/sdkmanager/libs/sdkuilib/src/Android.mk deleted file mode 100644 index 9aceba1..0000000 --- a/sdkmanager/libs/sdkuilib/src/Android.mk +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2008 The Android Open Source Project -# -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_JAVA_RESOURCE_DIRS := . - -LOCAL_JAVA_LIBRARIES := \ - sdklib \ - common \ - androidprefs \ - swt \ - org.eclipse.jface_3.4.2.M20090107-0800 \ - org.eclipse.equinox.common_3.4.0.v20080421-2006 \ - org.eclipse.core.commands_3.4.0.I20080509-2000 - -LOCAL_MODULE := sdkuilib - -include $(BUILD_HOST_JAVA_LIBRARY) - diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AddonSitesDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AddonSitesDialog.java new file mode 100755 index 0000000..9502099 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AddonSitesDialog.java @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdkuilib.internal.repository; + +import com.android.sdklib.SdkConstants; +import com.android.sdklib.internal.repository.SdkAddonSource; +import com.android.sdklib.internal.repository.SdkSource; +import com.android.sdklib.internal.repository.SdkSourceCategory; +import com.android.sdklib.internal.repository.SdkSources; + +import org.eclipse.jface.dialogs.IInputValidator; +import org.eclipse.jface.dialogs.InputDialog; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Dialog; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.MessageBox; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; + +import java.util.Arrays; + +public class AddonSitesDialog extends Dialog { + + /** + * Min Y location for dialog. Need to deal with the menu bar on mac os. + */ + private final static int MIN_Y = SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN ? + 20 : 0; + + + /** Last dialog size for this session. */ + private static Point sLastSize; + + private final UpdaterData mUpdaterData; + private boolean mChanged; + + private Shell mShell; + private Table mTable; + private TableViewer mTableViewer; + private Button mButtonNew; + private Button mButtonDelete; + private Button mButtonClose; + private Label mlabel; + private Button mButtonEdit; + private TableColumn mColumnUrl; + + /** + * Create the dialog. + * + * @param parent The parent's shell + */ + public AddonSitesDialog(Shell parent, UpdaterData updaterData) { + super(parent, SWT.NONE); + mUpdaterData = updaterData; + setText("Add-on Sites"); + } + + /** + * Open the dialog. + * + * @return True if anything was changed. + */ + public boolean open() { + createContents(); + positionShell(); + postCreate(); + mShell.open(); + mShell.layout(); + Display display = getParent().getDisplay(); + while (!mShell.isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + + return mChanged; + } + + /** + * Create contents of the dialog. + */ + private void createContents() { + mShell = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.RESIZE | SWT.APPLICATION_MODAL); + mShell.setMinimumSize(new Point(450, 300)); + mShell.setSize(450, 300); + mShell.setText(getText()); + GridLayout gl_shell = new GridLayout(); + gl_shell.numColumns = 2; + mShell.setLayout(gl_shell); + + mlabel = new Label(mShell, SWT.NONE); + mlabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); + mlabel.setText( + "This dialog lets you manage the URLs of external add-on sites to be used.\n" + + "\n" + + "Add-on sites can provide new add-ons or \"user\" packages.\n" + + "They cannot provide standard Android platforms, docs or samples packages.\n" + + "Adding a URL here will not allow you to clone an official Android repository." + ); + + mTableViewer = new TableViewer(mShell, SWT.BORDER | SWT.FULL_SELECTION); + mTableViewer.addPostSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + on_TableViewer_selectionChanged(event); + } + }); + mTable = mTableViewer.getTable(); + mTable.setLinesVisible(false); + mTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseUp(MouseEvent e) { + on_Table_mouseUp(e); + } + }); + mTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 5)); + + TableViewerColumn tableViewerColumn = new TableViewerColumn(mTableViewer, SWT.NONE); + mColumnUrl = tableViewerColumn.getColumn(); + mColumnUrl.setWidth(100); + mColumnUrl.setText("New Column"); + + mButtonNew = new Button(mShell, SWT.NONE); + mButtonNew.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + newOrEdit(false /*isEdit*/); + } + }); + mButtonNew.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); + mButtonNew.setText("New..."); + + mButtonEdit = new Button(mShell, SWT.NONE); + mButtonEdit.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + newOrEdit(true /*isEdit*/); + } + }); + mButtonEdit.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); + mButtonEdit.setText("Edit..."); + + mButtonDelete = new Button(mShell, SWT.NONE); + mButtonDelete.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + on_ButtonDelete_widgetSelected(e); + } + }); + mButtonDelete.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); + mButtonDelete.setText("Delete..."); + new Label(mShell, SWT.NONE); + + mButtonClose = new Button(mShell, SWT.NONE); + mButtonClose.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + on_ButtonClose_widgetSelected(e); + } + }); + mButtonClose.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, false, false, 1, 1)); + mButtonClose.setText("Close"); + + adjustColumnsWidth(mTable, mColumnUrl); + } + + /** + * Adds a listener to adjust the column width when the parent is resized. + */ + private void adjustColumnsWidth(final Table table, final TableColumn column0) { + // Add a listener to resize the column to the full width of the table + table.addControlListener(new ControlAdapter() { + @Override + public void controlResized(ControlEvent e) { + Rectangle r = table.getClientArea(); + column0.setWidth(r.width * 100 / 100); // 100% + } + }); + } + + /** + * Centers the dialog in its parent shell. + */ + private void positionShell() { + // Centers the dialog in its parent shell + Shell child = mShell; + Shell parent = getParent(); + if (child != null && parent != null) { + + // get the parent client area with a location relative to the display + Rectangle parentArea = parent.getClientArea(); + Point parentLoc = parent.getLocation(); + int px = parentLoc.x; + int py = parentLoc.y; + int pw = parentArea.width; + int ph = parentArea.height; + + // Reuse the last size if there's one, otherwise use the default + Point childSize = sLastSize != null ? sLastSize : child.getSize(); + int cw = childSize.x; + int ch = childSize.y; + + int x = px + (pw - cw) / 2; + if (x < 0) x = 0; + + int y = py + (ph - ch) / 2; + if (y < MIN_Y) y = MIN_Y; + + child.setLocation(x, y); + child.setSize(cw, ch); + } + } + + private void newOrEdit(final boolean isEdit) { + SdkSources sources = mUpdaterData.getSources(); + final SdkSource[] knownSources = sources.getAllSources(); + String title = isEdit ? "Edit Add-on Site URL" : "Add Add-on Site URL"; + String msg = "Please enter the URL of the addon.xml:"; + IStructuredSelection sel = (IStructuredSelection) mTableViewer.getSelection(); + final String initialValue = !isEdit || sel.isEmpty() ? null : + sel.getFirstElement().toString(); + + if (isEdit && initialValue == null) { + // Edit with no actual value is not supposed to happen. Ignore this case. + return; + } + + InputDialog dlg = new InputDialog(mShell, title, msg, initialValue, new IInputValidator() { + public String isValid(String newText) { + + newText = newText == null ? null : newText.trim(); + + if (newText == null || newText.length() == 0) { + return "Error: URL field is empty. Please enter a URL."; + } + + // A URL should have one of the following prefixes + if (!newText.startsWith("file://") && //$NON-NLS-1$ + !newText.startsWith("ftp://") && //$NON-NLS-1$ + !newText.startsWith("http://") && //$NON-NLS-1$ + !newText.startsWith("https://")) { //$NON-NLS-1$ + return "Error: The URL must start by one of file://, ftp://, http:// or https://"; + } + + if (isEdit && newText.equals(initialValue)) { + // Edited value hasn't changed. This isn't an error. + return null; + } + + // Reject URLs that are already in the source list. + // URLs are generally case-insensitive (except for file:// where it all depends + // on the current OS so we'll ignore this case.) + for (SdkSource s : knownSources) { + if (newText.equalsIgnoreCase(s.getUrl())) { + return "Error: This site is already listed."; + } + } + + return null; + } + }); + + if (dlg.open() == Window.OK) { + String url = dlg.getValue().trim(); + + if (!url.equals(initialValue)) { + if (isEdit && initialValue != null) { + // Remove the old value before we add the new one, which is we just + // asserted will be different. + for (SdkSource source : sources.getSources(SdkSourceCategory.USER_ADDONS)) { + if (initialValue.equals(source.getUrl())) { + sources.remove(source); + break; + } + } + + } + + // create the source, store it and update the list + SdkAddonSource newSource = new SdkAddonSource(url, null/*uiName*/); + sources.add( + SdkSourceCategory.USER_ADDONS, + newSource); + mChanged = true; + loadList(); + + // select the new source + IStructuredSelection newSel = new StructuredSelection(newSource); + mTableViewer.setSelection(newSel, true /*reveal*/); + } + } + } + + private void on_ButtonDelete_widgetSelected(SelectionEvent e) { + IStructuredSelection sel = (IStructuredSelection) mTableViewer.getSelection(); + String selectedUrl = sel.isEmpty() ? null : sel.getFirstElement().toString(); + + if (selectedUrl == null) { + return; + } + + MessageBox mb = new MessageBox(mShell, + SWT.YES | SWT.NO | SWT.ICON_QUESTION | SWT.APPLICATION_MODAL); + mb.setText("Delete add-on site"); + mb.setMessage(String.format("Do you want to delete the URL %1$s?", selectedUrl)); + if (mb.open() == SWT.YES) { + SdkSources sources = mUpdaterData.getSources(); + for (SdkSource source : sources.getSources(SdkSourceCategory.USER_ADDONS)) { + if (selectedUrl.equals(source.getUrl())) { + sources.remove(source); + mChanged = true; + loadList(); + } + } + } + } + + private void on_ButtonClose_widgetSelected(SelectionEvent e) { + mShell.close(); + } + + private void on_Table_mouseUp(MouseEvent e) { + Point p = new Point(e.x, e.y); + if (mTable.getItem(p) == null) { + mTable.deselectAll(); + on_TableViewer_selectionChanged(null /*event*/); + } + } + + private void on_TableViewer_selectionChanged(SelectionChangedEvent event) { + ISelection sel = mTableViewer.getSelection(); + mButtonDelete.setEnabled(!sel.isEmpty()); + mButtonEdit.setEnabled(!sel.isEmpty()); + } + + private void postCreate() { + // initialize the list + mTableViewer.setLabelProvider(new LabelProvider()); + mTableViewer.setContentProvider(new SourcesContentProvider()); + loadList(); + } + + private void loadList() { + if (mUpdaterData != null) { + SdkSource[] knownSources = + mUpdaterData.getSources().getSources(SdkSourceCategory.USER_ADDONS); + Arrays.sort(knownSources); + + ISelection oldSelection = mTableViewer.getSelection(); + + mTableViewer.setInput(knownSources); + mTableViewer.refresh(); + // initialize buttons' state that depend on the list + on_TableViewer_selectionChanged(null /*event*/); + + if (oldSelection != null && !oldSelection.isEmpty()) { + mTableViewer.setSelection(oldSelection, true /*reveal*/); + } + } + } + + private static class SourcesContentProvider implements IStructuredContentProvider { + + public void dispose() { + // pass + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // pass + } + + public Object[] getElements(Object inputElement) { + if (inputElement instanceof SdkSource[]) { + return (Object[]) inputElement; + } else { + return new Object[0]; + } + } + } +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java index 7594f16..7c78983 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java @@ -17,7 +17,6 @@ package com.android.sdkuilib.internal.repository;
import com.android.prefs.AndroidLocation.AndroidLocationException;
-import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdkuilib.internal.widgets.AvdSelector;
import com.android.sdkuilib.internal.widgets.AvdSelector.DisplayMode;
import com.android.sdkuilib.repository.ISdkChangeListener;
@@ -58,7 +57,7 @@ public class AvdManagerPage extends Composite implements ISdkChangeListener { try {
label.setText(String.format(
"List of existing Android Virtual Devices located at %s",
- AvdManager.getBaseAvdFolder()));
+ mUpdaterData.getAvdManager().getBaseAvdFolder()));
} catch (AndroidLocationException e) {
label.setText(e.getMessage());
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/StreamException.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/IPageListener.java index 2088864..fc59404 100644..100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/io/StreamException.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/IPageListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,17 @@ * limitations under the License. */ -package com.android.sdklib.io; +package com.android.sdkuilib.internal.repository; + + /** - * Exception thrown when {@link IAbstractFile#getContents()} fails. + * Interface for lifecycle events of pages. */ -public class StreamException extends Exception { - private static final long serialVersionUID = 1L; +interface IPageListener { - public StreamException(Exception e) { - super(e); - } + /** + * The page was just selected and brought to front. + */ + public void onPageSelected(); } diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java index d579094..d2824ea 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java @@ -42,6 +42,9 @@ import org.eclipse.swt.widgets.TableColumn; import java.io.File;
+/**
+ * Page that displays all locally installed packages from the current SDK.
+ */
public class LocalPackagesPage extends Composite implements ISdkChangeListener {
private final UpdaterData mUpdaterData;
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/PackagesPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/PackagesPage.java new file mode 100755 index 0000000..708c2d6 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/PackagesPage.java @@ -0,0 +1,1441 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdkuilib.internal.repository; + +import com.android.sdklib.internal.repository.Archive; +import com.android.sdklib.internal.repository.IDescription; +import com.android.sdklib.internal.repository.IPackageVersion; +import com.android.sdklib.internal.repository.ITask; +import com.android.sdklib.internal.repository.ITaskMonitor; +import com.android.sdklib.internal.repository.Package; +import com.android.sdklib.internal.repository.PlatformPackage; +import com.android.sdklib.internal.repository.PlatformToolPackage; +import com.android.sdklib.internal.repository.SdkSource; +import com.android.sdklib.internal.repository.ToolPackage; +import com.android.sdklib.internal.repository.Package.UpdateInfo; +import com.android.sdkuilib.internal.repository.icons.ImageFactory; +import com.android.sdkuilib.repository.ISdkChangeListener; +import com.android.util.Pair; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTreeViewer; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.ITableColorProvider; +import org.eclipse.jface.viewers.ITableFontProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeColumnViewerLabelProvider; +import org.eclipse.jface.viewers.TreeViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.MenuItem; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeColumn; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +/** + * Page that displays both locally installed packages as well as all known + * remote available packages. This gives an overview of what is installed + * vs what is available and allows the user to update or install packages. + */ +public class PackagesPage extends Composite + implements ISdkChangeListener, IPageListener { + + private static final String ICON_CAT_OTHER = "pkgcat_other_16.png"; //$NON-NLS-1$ + private static final String ICON_CAT_PLATFORM = "pkgcat_16.png"; //$NON-NLS-1$ + private static final String ICON_SORT_BY_SOURCE = "source_icon16.png"; //$NON-NLS-1$ + private static final String ICON_SORT_BY_API = "platform_pkg_16.png"; //$NON-NLS-1$ + private static final String ICON_PKG_NEW = "pkg_new_16.png"; //$NON-NLS-1$ + private static final String ICON_PKG_UPDATE = "pkg_update_16.png"; //$NON-NLS-1$ + private static final String ICON_PKG_INSTALLED = "pkg_installed_16.png"; //$NON-NLS-1$ + + enum MenuAction { + RELOAD (SWT.NONE, "Reload"), + SHOW_ADDON_SITES (SWT.NONE, "Manage Sources..."), + TOGGLE_SHOW_ARCHIVES (SWT.CHECK, "Show Archives"), + TOGGLE_SHOW_INSTALLED_PKG (SWT.CHECK, "Show Installed Packages"), + TOGGLE_SHOW_OBSOLETE_PKG (SWT.CHECK, "Show Obsolete Packages"), + TOGGLE_SHOW_UPDATE_NEW_PKG (SWT.CHECK, "Show Updates/New Packages"), + SORT_API_LEVEL (SWT.RADIO, "Sort by API Level"), + SORT_SOURCE (SWT.RADIO, "Sort by Source") + ; + + private final int mMenuStyle; + private final String mMenuTitle; + + MenuAction(int menuStyle, String menuTitle) { + mMenuStyle = menuStyle; + mMenuTitle = menuTitle; + } + + public int getMenuStyle() { + return mMenuStyle; + } + + public String getMenuTitle() { + return mMenuTitle; + } + }; + + private final Map<MenuAction, MenuItem> mMenuActions = new HashMap<MenuAction, MenuItem>(); + + private final List<PkgItem> mPackages = new ArrayList<PkgItem>(); + private final List<PkgCategory> mCategories = new ArrayList<PkgCategory>(); + private final UpdaterData mUpdaterData; + + private boolean mDisplayArchives = false; + + private Text mTextSdkOsPath; + private Button mCheckSortSource; + private Button mCheckSortApi; + private Button mCheckFilterObsolete; + private Button mCheckFilterInstalled; + private Button mCheckFilterNew; + private Composite mGroupOptions; + private Composite mGroupSdk; + private Group mGroupPackages; + private Button mButtonDelete; + private Button mButtonInstall; + private Tree mTree; + private CheckboxTreeViewer mTreeViewer; + private TreeViewerColumn mColumnName; + private TreeViewerColumn mColumnApi; + private TreeViewerColumn mColumnRevision; + private TreeViewerColumn mColumnStatus; + private Color mColorUpdate; + private Font mTreeFontItalic; + private TreeColumn mTreeColumnName; + + public PackagesPage(Composite parent, UpdaterData updaterData) { + super(parent, SWT.NONE); + + mUpdaterData = updaterData; + createContents(this); + + postCreate(); //$hide$ + } + + public void onPageSelected() { + if (mPackages.isEmpty()) { + // Initialize the package list the first time the page is shown. + loadPackages(); + } + } + + private void createContents(Composite parent) { + GridLayout gridLayout = new GridLayout(2, false); + gridLayout.marginWidth = 0; + gridLayout.marginHeight = 0; + parent.setLayout(gridLayout); + + mGroupSdk = new Composite(parent, SWT.NONE); + mGroupSdk.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + mGroupSdk.setLayout(new GridLayout(2, false)); + + Label label1 = new Label(mGroupSdk, SWT.NONE); + label1.setText("SDK Path:"); + + mTextSdkOsPath = new Text(mGroupSdk, SWT.NONE); + mTextSdkOsPath.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + mTextSdkOsPath.setEnabled(false); + + mGroupPackages = new Group(parent, SWT.NONE); + mGroupPackages.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); + mGroupPackages.setText("Packages"); + mGroupPackages.setLayout(new GridLayout(1, false)); + + mTreeViewer = new CheckboxTreeViewer(mGroupPackages, SWT.BORDER); + + mTreeViewer.addCheckStateListener(new ICheckStateListener() { + public void checkStateChanged(CheckStateChangedEvent event) { + onTreeCheckStateChanged(event); //$hide$ + } + }); + + mTree = mTreeViewer.getTree(); + mTree.setLinesVisible(true); + mTree.setHeaderVisible(true); + mTree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); + + // column name icon is set in sortPackages() depending on the current filter type + // (e.g. API level or source) + mColumnName = new TreeViewerColumn(mTreeViewer, SWT.NONE); + mTreeColumnName = mColumnName.getColumn(); + mTreeColumnName.setWidth(340); + mTreeColumnName.setText("Name"); + + mColumnApi = new TreeViewerColumn(mTreeViewer, SWT.NONE); + TreeColumn treeColumn2 = mColumnApi.getColumn(); + treeColumn2.setAlignment(SWT.CENTER); + treeColumn2.setWidth(50); + treeColumn2.setText("API"); + + mColumnRevision = new TreeViewerColumn(mTreeViewer, SWT.NONE); + TreeColumn treeColumn3 = mColumnRevision.getColumn(); + treeColumn3.setAlignment(SWT.CENTER); + treeColumn3.setWidth(50); + treeColumn3.setText("Rev."); + treeColumn3.setToolTipText("Revision currently installed"); + + + mColumnStatus = new TreeViewerColumn(mTreeViewer, SWT.NONE); + TreeColumn treeColumn4 = mColumnStatus.getColumn(); + treeColumn4.setAlignment(SWT.LEAD); + treeColumn4.setWidth(190); + treeColumn4.setText("Status"); + + mGroupOptions = new Composite(mGroupPackages, SWT.NONE); + mGroupOptions.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + GridLayout gl_GroupOptions = new GridLayout(6, false); + gl_GroupOptions.marginWidth = 0; + gl_GroupOptions.marginHeight = 0; + mGroupOptions.setLayout(gl_GroupOptions); + + Label label3 = new Label(mGroupOptions, SWT.NONE); + label3.setText("Show:"); + + mCheckFilterNew = new Button(mGroupOptions, SWT.CHECK); + mCheckFilterNew.setToolTipText("Show Updates and New"); + mCheckFilterNew.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + sortPackages(true /*updateButtons*/); + } + }); + mCheckFilterNew.setSelection(true); + mCheckFilterNew.setText("Updates/New"); + + mCheckFilterInstalled = new Button(mGroupOptions, SWT.CHECK); + mCheckFilterInstalled.setToolTipText("Show Installed"); + mCheckFilterInstalled.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + sortPackages(true /*updateButtons*/); + } + }); + mCheckFilterInstalled.setSelection(true); + mCheckFilterInstalled.setText("Installed"); + + mCheckFilterObsolete = new Button(mGroupOptions, SWT.CHECK); + mCheckFilterObsolete.setToolTipText("Also show obsolete packages"); + mCheckFilterObsolete.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + sortPackages(true /*updateButtons*/); + } + }); + mCheckFilterObsolete.setSelection(false); + mCheckFilterObsolete.setText("Obsolete"); + + Label placeholder2 = new Label(mGroupOptions, SWT.NONE); + placeholder2.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + + mButtonInstall = new Button(mGroupOptions, SWT.NONE); + mButtonInstall.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + mButtonInstall.setToolTipText("Install all the selected packages"); + mButtonInstall.setText("Install Selected..."); + mButtonInstall.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onButtonInstall(); //$hide$ + } + }); + + Label label2 = new Label(mGroupOptions, SWT.NONE); + label2.setText("Sort by:"); + + mCheckSortApi = new Button(mGroupOptions, SWT.RADIO); + mCheckSortApi.setToolTipText("Sort by API level"); + mCheckSortApi.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + sortPackages(true /*updateButtons*/); + // Reset the expanded state when changing sort algorithm + expandInitial(mCategories); + } + }); + mCheckSortApi.setText("API level"); + mCheckSortApi.setSelection(true); + + mCheckSortSource = new Button(mGroupOptions, SWT.RADIO); + mCheckSortSource.setToolTipText("Sort by Source"); + mCheckSortSource.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + sortPackages(true /*updateButtons*/); + // Reset the expanded state when changing sort algorithm + expandInitial(mCategories); + } + }); + mCheckSortSource.setText("Source"); + + new Label(mGroupOptions, SWT.NONE); + new Label(mGroupOptions, SWT.NONE); + + mButtonDelete = new Button(mGroupOptions, SWT.NONE); + mButtonDelete.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + mButtonDelete.setToolTipText("Delete an installed package"); + mButtonDelete.setText("Delete..."); + mButtonDelete.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onButtonDelete(); //$hide$ + } + }); + } + + private Image getImage(String filename) { + if (mUpdaterData != null) { + ImageFactory imgFactory = mUpdaterData.getImageFactory(); + if (imgFactory != null) { + return imgFactory.getImageByName(filename); + } + } + return null; + } + + + // -- Start of internal part ---------- + // Hide everything down-below from SWT designer + //$hide>>$ + + + // --- menu interactions --- + + public void registerMenuAction(final MenuAction action, MenuItem item) { + item.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + Button button = null; + + switch (action) { + case RELOAD: + loadPackages(); + break; + case SHOW_ADDON_SITES: + AddonSitesDialog d = new AddonSitesDialog(getShell(), mUpdaterData); + if (d.open()) { + loadPackages(); + } + break; + case TOGGLE_SHOW_ARCHIVES: + mDisplayArchives = !mDisplayArchives; + sortPackages(true /*updateButtons*/); + break; + case TOGGLE_SHOW_INSTALLED_PKG: + button = mCheckFilterInstalled; + break; + case TOGGLE_SHOW_OBSOLETE_PKG: + button = mCheckFilterObsolete; + break; + case TOGGLE_SHOW_UPDATE_NEW_PKG: + button = mCheckFilterNew; + break; + case SORT_API_LEVEL: + button = mCheckSortApi; + break; + case SORT_SOURCE: + button = mCheckSortSource; + break; + } + + if (button != null && !button.isDisposed()) { + // Toggle this button (radio or checkbox) + + boolean value = button.getSelection(); + + // SWT doesn't automatically switch radio buttons when using the + // Widget#setSelection method, so we'll do it here manually. + if (!value && (button.getStyle() & SWT.RADIO) != 0) { + // we'll be selecting this radio button, so deselect all ther other ones + // in the parent group. + for (Control child : button.getParent().getChildren()) { + if (child instanceof Button && + child != button && + (child.getStyle() & SWT.RADIO) != 0) { + ((Button) child).setSelection(value); + } + } + } + + button.setSelection(!value); + + // SWT doesn't actually invoke the listeners when using Widget#setSelection + // so let's run the actual action. + button.notifyListeners(SWT.Selection, new Event()); + } + + updateMenuCheckmarks(); + } + }); + + mMenuActions.put(action, item); + } + + // --- internal methods --- + + private void updateMenuCheckmarks() { + + for (Entry<MenuAction, MenuItem> entry : mMenuActions.entrySet()) { + MenuAction action = entry.getKey(); + MenuItem item = entry.getValue(); + + if (action.getMenuStyle() == SWT.NONE) { + continue; + } + + boolean value = false; + Button button = null; + + switch (action) { + case TOGGLE_SHOW_ARCHIVES: + value = mDisplayArchives; + break; + case TOGGLE_SHOW_INSTALLED_PKG: + button = mCheckFilterInstalled; + break; + case TOGGLE_SHOW_OBSOLETE_PKG: + button = mCheckFilterObsolete; + break; + case TOGGLE_SHOW_UPDATE_NEW_PKG: + button = mCheckFilterNew; + break; + case SORT_API_LEVEL: + button = mCheckSortApi; + break; + case SORT_SOURCE: + button = mCheckSortSource; + break; + } + + if (button != null && !button.isDisposed()) { + value = button.getSelection(); + } + + item.setSelection(value); + } + + } + + private void postCreate() { + if (mUpdaterData != null) { + mTextSdkOsPath.setText(mUpdaterData.getOsSdkRoot()); + } + + mTreeViewer.setContentProvider(new PkgContentProvider()); + + mColumnApi.setLabelProvider( + new TreeColumnViewerLabelProvider(new PkgCellLabelProvider(mColumnApi))); + mColumnName.setLabelProvider( + new TreeColumnViewerLabelProvider(new PkgCellLabelProvider(mColumnName))); + mColumnStatus.setLabelProvider( + new TreeColumnViewerLabelProvider(new PkgCellLabelProvider(mColumnStatus))); + mColumnRevision.setLabelProvider( + new TreeColumnViewerLabelProvider(new PkgCellLabelProvider(mColumnRevision))); + + FontData fontData = mTree.getFont().getFontData()[0]; + fontData.setStyle(SWT.ITALIC); + mTreeFontItalic = new Font(mTree.getDisplay(), fontData); + + mColorUpdate = new Color(mTree.getDisplay(), 0xff, 0xff, 0xcc); + + mTree.addDisposeListener(new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + mTreeFontItalic.dispose(); + mColorUpdate.dispose(); + mTreeFontItalic = null; + mColorUpdate = null; + } + }); + } + + private void loadPackages() { + if (mUpdaterData == null) { + return; + } + + try { + enableUi(mGroupPackages, false); + + boolean firstLoad = mPackages.size() == 0; + mPackages.clear(); + + // get local packages + for (Package pkg : mUpdaterData.getInstalledPackages()) { + PkgItem pi = new PkgItem(pkg, PkgState.INSTALLED); + mPackages.add(pi); + } + + // get remote packages + final boolean forceHttp = mUpdaterData.getSettingsController().getForceHttp(); + mUpdaterData.loadRemoteAddonsList(); + mUpdaterData.getTaskFactory().start("Loading Sources", new ITask() { + public void run(ITaskMonitor monitor) { + SdkSource[] sources = mUpdaterData.getSources().getAllSources(); + for (SdkSource source : sources) { + Package[] pkgs = source.getPackages(); + if (pkgs == null) { + source.load(monitor, forceHttp); + pkgs = source.getPackages(); + } + if (pkgs == null) { + continue; + } + + nextPkg: for(Package pkg : pkgs) { + boolean isUpdate = false; + for (PkgItem pi: mPackages) { + if (pi.isSameAs(pkg)) { + continue nextPkg; + } + if (pi.isUpdatedBy(pkg)) { + isUpdate = true; + break; + } + } + + if (!isUpdate) { + PkgItem pi = new PkgItem(pkg, PkgState.NEW); + mPackages.add(pi); + } + } + + // Dynamically update the table while we load after each source. + // Since the official Android source gets loaded first, it makes the + // window look non-empty a lot sooner. + mGroupPackages.getDisplay().syncExec(new Runnable() { + public void run() { + sortPackages(true /*updateButtons*/); + } + }); + } + + monitor.setDescription("Done loading %1$d packages from %2$d sources", + mPackages.size(), + sources.length); + } + }); + + if (firstLoad) { + // set the initial expanded state + expandInitial(mCategories); + } + + } finally { + enableUi(mGroupPackages, true); + updateButtonsState(); + updateMenuCheckmarks(); + } + } + + private void enableUi(Composite root, boolean enabled) { + root.setEnabled(enabled); + for (Control child : root.getChildren()) { + if (child instanceof Composite) { + enableUi((Composite) child, enabled); + } else { + child.setEnabled(enabled); + } + } + } + + private void sortPackages(boolean updateButtons) { + if (mCheckSortApi != null && !mCheckSortApi.isDisposed() && mCheckSortApi.getSelection()) { + sortByApiLevel(); + } else { + sortBySource(); + } + if (updateButtons) { + updateButtonsState(); + updateMenuCheckmarks(); + } + } + + /** + * Recompute the tree by sorting all the packages by API. + * This does an update in-place of the mCategories list so that the table + * can preserve its state (checked / expanded / selected) properly. + */ + private void sortByApiLevel() { + + ImageFactory imgFactory = mUpdaterData.getImageFactory(); + + if (!mTreeColumnName.isDisposed()) { + mTreeColumnName.setImage(getImage(ICON_SORT_BY_API)); + } + + // keep a map of the initial state so that we can detect which items or categories are + // no longer being used, so that we can removed them at the end of the in-place update. + final Map<Integer, Pair<PkgCategory, HashSet<PkgItem>> > unusedItemsMap = + new HashMap<Integer, Pair<PkgCategory, HashSet<PkgItem>> >(); + final Set<PkgCategory> unusedCatSet = new HashSet<PkgCategory>(); + + // get existing categories + for (PkgCategory cat : mCategories) { + unusedCatSet.add(cat); + unusedItemsMap.put(cat.getKey(), Pair.of(cat, new HashSet<PkgItem>(cat.getItems()))); + } + + // always add the tools & extras categories, even if empty (unlikely anyway) + if (!unusedItemsMap.containsKey(PkgCategory.KEY_TOOLS)) { + PkgCategory cat = new PkgCategory( + PkgCategory.KEY_TOOLS, + "Tools", + imgFactory.getImageByName(ICON_CAT_OTHER)); + unusedItemsMap.put(PkgCategory.KEY_TOOLS, Pair.of(cat, new HashSet<PkgItem>())); + mCategories.add(cat); + } + + if (!unusedItemsMap.containsKey(PkgCategory.KEY_EXTRA)) { + PkgCategory cat = new PkgCategory( + PkgCategory.KEY_EXTRA, + "Add-ons & Extras", + imgFactory.getImageByName(ICON_CAT_OTHER)); + unusedItemsMap.put(PkgCategory.KEY_EXTRA, Pair.of(cat, new HashSet<PkgItem>())); + mCategories.add(cat); + } + + for (PkgItem item : mPackages) { + if (!keepItem(item)) { + continue; + } + + int apiKey = item.getApi(); + + if (apiKey < 1) { + Package p = item.getPackage(); + if (p instanceof ToolPackage || p instanceof PlatformToolPackage) { + apiKey = PkgCategory.KEY_TOOLS; + } else { + apiKey = PkgCategory.KEY_EXTRA; + } + } + + Pair<PkgCategory, HashSet<PkgItem>> mapEntry = unusedItemsMap.get(apiKey); + + if (mapEntry == null) { + // This is a new category. Create it and add it to the map. + // We need a label for the category. Use null right now and set it later. + PkgCategory cat = new PkgCategory( + apiKey, + null /*label*/, + imgFactory.getImageByName(ICON_CAT_PLATFORM)); + mapEntry = Pair.of(cat, new HashSet<PkgItem>()); + unusedItemsMap.put(apiKey, mapEntry); + mCategories.add(0, cat); + } + PkgCategory cat = mapEntry.getFirst(); + assert cat != null; + unusedCatSet.remove(cat); + + HashSet<PkgItem> unusedItemsSet = mapEntry.getSecond(); + unusedItemsSet.remove(item); + if (!cat.getItems().contains(item)) { + cat.getItems().add(item); + } + + if (apiKey != -1 && cat.getLabel() == null) { + // Check whether we can get the actual platform version name (e.g. "1.5") + // from the first Platform package we find in this category. + Package p = item.getPackage(); + if (p instanceof PlatformPackage) { + String vn = ((PlatformPackage) p).getVersionName(); + String name = String.format("Android %1$s (API %2$d)", vn, apiKey); + cat.setLabel(name); + } + } + } + + for (Iterator<PkgCategory> iterCat = mCategories.iterator(); iterCat.hasNext(); ) { + PkgCategory cat = iterCat.next(); + + // Remove any unused categories. + if (unusedCatSet.contains(cat)) { + iterCat.remove(); + continue; + } + + // Remove any unused items in the category. + Integer apikey = cat.getKey(); + Pair<PkgCategory, HashSet<PkgItem>> mapEntry = unusedItemsMap.get(apikey); + assert mapEntry != null; + HashSet<PkgItem> unusedItems = mapEntry.getSecond(); + for (Iterator<PkgItem> iterItem = cat.getItems().iterator(); iterItem.hasNext(); ) { + PkgItem item = iterItem.next(); + if (unusedItems.contains(item)) { + iterItem.remove(); + } + } + + // Sort the items + Collections.sort(cat.getItems()); + + // Fix the category name for any API where we might not have found a platform package. + if (cat.getLabel() == null) { + int api = cat.getKey().intValue(); + String name = String.format("API %1$d", api); + cat.setLabel(name); + } + } + + // Sort the categories list in decreasing order + Collections.sort(mCategories, new Comparator<PkgCategory>() { + public int compare(PkgCategory cat1, PkgCategory cat2) { + // compare in descending order (o2-o1) + return cat2.getKey().compareTo(cat1.getKey()); + } + }); + + if (mTreeViewer.getInput() != mCategories) { + // set initial input + mTreeViewer.setInput(mCategories); + } else { + // refresh existing, which preserves the expanded state, the selection + // and the checked state. + mTreeViewer.refresh(); + } + } + + /** + * Recompute the tree by sorting all packages by source. + */ + private void sortBySource() { + + if (!mTreeColumnName.isDisposed()) { + mTreeColumnName.setImage(getImage(ICON_SORT_BY_SOURCE)); + } + + mCategories.clear(); + + Set<SdkSource> sourceSet = new HashSet<SdkSource>(); + for (PkgItem item : mPackages) { + if (keepItem(item)) { + sourceSet.add(item.getSource()); + } + } + + SdkSource[] sources = sourceSet.toArray(new SdkSource[sourceSet.size()]); + Arrays.sort(sources, new Comparator<SdkSource>() { + public int compare(SdkSource o1, SdkSource o2) { + if (o1 == o2) { + return 0; + } else if (o1 == null && o2 != null) { + return -1; + } else if (o1 != null && o2 == null) { + return 1; + } + assert o1 != null; + return o1.toString().compareTo(o2.toString()); + } + }); + + for (SdkSource source : sources) { + Object key = source != null ? source : "Installed Packages"; + Object iconRef = source != null ? source : + mUpdaterData.getImageFactory().getImageByName(ICON_PKG_INSTALLED); + + PkgCategory cat = new PkgCategory( + key.hashCode(), + key.toString(), + iconRef); + + for (PkgItem item : mPackages) { + if (item.getSource() == source) { + cat.getItems().add(item); + } + } + + mCategories.add(cat); + } + + // We don't support in-place incremental updates so the table gets reset + // each time we load when sorted by source. + mTreeViewer.setInput(mCategories); + } + + /** + * Decide whether to keep an item in the current tree based on user-choosen filter options. + */ + private boolean keepItem(PkgItem item) { + if (!mCheckFilterObsolete.getSelection()) { + if (item.isObsolete()) { + return false; + } + } + + if (!mCheckFilterInstalled.getSelection()) { + if (item.getState() == PkgState.INSTALLED) { + return false; + } + } + + if (!mCheckFilterNew.getSelection()) { + if (item.getState() == PkgState.NEW || + item.getState() == PkgState.HAS_UPDATE) { + return false; + } + } + + return true; + } + + /** + * Performs the initial expansion of the tree. The expands categories that contains + * at least one installed item and collapses the ones with nothing installed. + */ + private void expandInitial(Object elem) { + mTreeViewer.setExpandedState(elem, true); + for (Object pkg : + ((ITreeContentProvider) mTreeViewer.getContentProvider()).getChildren(elem)) { + if (pkg instanceof PkgCategory) { + PkgCategory cat = (PkgCategory) pkg; + for (PkgItem item : cat.getItems()) { + if (item.getState() == PkgState.INSTALLED + || item.getState() == PkgState.HAS_UPDATE) { + expandInitial(pkg); + break; + } + } + } + } + } + + /** + * Handle checking and unchecking of the tree items. + * + * When unchecking, all sub-tree items checkboxes are cleared too. + * When checking a source, all of its packages are checked too. + * When checking a package, only its compatible archives are checked. + */ + private void onTreeCheckStateChanged(CheckStateChangedEvent event) { + boolean b = event.getChecked(); + Object elem = event.getElement(); + + assert event.getSource() == mTreeViewer; + + // when deselecting, we just deselect all children too + if (b == false) { + mTreeViewer.setSubtreeChecked(elem, b); + updateButtonsState(); + return; + } + + ITreeContentProvider provider = (ITreeContentProvider) mTreeViewer.getContentProvider(); + + // When selecting, we want to only select compatible archives and expand the super nodes. + checkExpandItem(elem, provider); + + updateButtonsState(); + } + + private void checkExpandItem(Object elem, ITreeContentProvider provider) { + if (elem instanceof PkgCategory || elem instanceof PkgItem) { + mTreeViewer.setExpandedState(elem, true); + for (Object pkg : provider.getChildren(elem)) { + mTreeViewer.setChecked(pkg, true); + checkExpandItem(pkg, provider); + } + } else if (elem instanceof Package) { + selectCompatibleArchives(elem, provider); + } + } + + private void selectCompatibleArchives(Object pkg, ITreeContentProvider provider) { + for (Object archive : provider.getChildren(pkg)) { + if (archive instanceof Archive) { + mTreeViewer.setChecked(archive, ((Archive) archive).isCompatible()); + } + } + } + + private void updateButtonsState() { + boolean canInstall = false; + + if (mDisplayArchives) { + // In detail mode, we display archives so we can install if at + // least one archive is selected. + + Object[] checked = mTreeViewer.getCheckedElements(); + if (checked != null) { + for (Object c : checked) { + if (c instanceof Archive) { + if (((Archive) c).isCompatible()) { + canInstall = true; + break; + } + } + } + } + } else { + // In non-detail mode, we need to check if there are any packages + // or pkgitems selected with at least one compatible archive to be + // installed. + + Object[] checked = mTreeViewer.getCheckedElements(); + if (checked != null) { + for (Object c : checked) { + if (c instanceof Package) { + // This is an update package + if (((Package) c).hasCompatibleArchive()) { + canInstall = true; + break; + } + } else if (c instanceof PkgItem) { + if (((PkgItem) c).getPackage().hasCompatibleArchive()) { + canInstall = true; + break; + } + } + } + } + } + + mButtonInstall.setEnabled(canInstall); + + // We can only delete local archives + boolean canDelete = false; + Object[] checked = mTreeViewer.getCheckedElements(); + if (checked != null) { + for (Object c : checked) { + if (c instanceof PkgItem) { + if (((PkgItem) c).getState() == PkgState.INSTALLED) { + canDelete = true; + break; + } + } + } + } + + mButtonDelete.setEnabled(canDelete); + } + + private void onButtonInstall() { + ArrayList<Archive> archives = new ArrayList<Archive>(); + + if (mDisplayArchives) { + // In detail mode, we display archives so we can install only the + // archives that are actually selected. + + Object[] checked = mTreeViewer.getCheckedElements(); + if (checked != null) { + for (Object c : checked) { + if (c instanceof Archive) { + if (((Archive) c).isCompatible()) { + archives.add((Archive) c); + } + } + } + } + } else { + // In non-detail mode, we install all the compatible archives + // found in the selected pkg items or update packages. + + Object[] checked = mTreeViewer.getCheckedElements(); + if (checked != null) { + for (Object c : checked) { + Package p = null; + if (c instanceof Package) { + // This is an update package + p = (Package) c; + } else if (c instanceof PkgItem) { + p = ((PkgItem) c).getPackage(); + } + if (p != null) { + for (Archive a : p.getArchives()) { + if (a.isCompatible()) { + archives.add(a); + } + } + } + } + } + } + + if (mUpdaterData != null) { + try { + enableUi(mGroupPackages, false); + + mUpdaterData.updateOrInstallAll_WithGUI( + archives, + mCheckFilterObsolete.getSelection() /* includeObsoletes */); + } finally { + // loadPackages will also re-enable the UI + loadPackages(); + } + } + } + + private void onButtonDelete() { + // Find selected local packages to be delete + Object[] checked = mTreeViewer.getCheckedElements(); + if (checked == null) { + // This should not happen since the button should be disabled + return; + } + + String title = "Delete SDK Package"; + String msg = "Are you sure you want to delete:"; + final List<Archive> archives = new ArrayList<Archive>(); + + for (Object c : checked) { + if (c instanceof PkgItem && ((PkgItem) c).getState() == PkgState.INSTALLED) { + Package p = ((PkgItem) c).getPackage(); + + Archive[] as = p.getArchives(); + if (as.length == 1 && as[0] != null && as[0].isLocal()) { + Archive archive = as[0]; + String osPath = archive.getLocalOsPath(); + + File dir = new File(osPath); + if (dir.isDirectory()) { + msg += "\n - " + p.getShortDescription(); + archives.add(archive); + } + } + } + } + + if (!archives.isEmpty()) { + msg += "\n" + "This cannot be undone."; + if (MessageDialog.openQuestion(getShell(), title, msg)) { + try { + enableUi(mGroupPackages, false); + + mUpdaterData.getTaskFactory().start("Loading Sources", new ITask() { + public void run(ITaskMonitor monitor) { + monitor.setProgressMax(archives.size() + 1); + for (Archive a : archives) { + monitor.setDescription("Deleting '%1$s' (%2$s)", + a.getParentPackage().getShortDescription(), + a.getLocalOsPath()); + a.deleteLocal(); + monitor.incProgress(1); + if (monitor.isCancelRequested()) { + break; + } + } + + monitor.incProgress(1); + monitor.setDescription("Done"); + } + }); + } finally { + // loadPackages will also re-enable the UI + loadPackages(); + } + } + } + } + + // ---------------------- + + public class PkgCellLabelProvider extends ColumnLabelProvider + implements ITableFontProvider, ITableColorProvider { + + private final TreeViewerColumn mColumn; + + public PkgCellLabelProvider(TreeViewerColumn column) { + super(); + mColumn = column; + } + + @Override + public String getText(Object element) { + + if (mColumn == mColumnName) { + + if (element instanceof PkgCategory) { + return ((PkgCategory) element).getLabel(); + } else if (element instanceof PkgItem) { + return ((PkgItem) element).getName(); + } else if (element instanceof IDescription) { + return ((IDescription) element).getShortDescription(); + } + + } else if (mColumn == mColumnApi) { + + int api = -1; + if (element instanceof PkgItem) { + api = ((PkgItem) element).getApi(); + } + if (api >= 1) { + return Integer.toString(api); + } + + } else if (mColumn == mColumnRevision) { + + if (element instanceof PkgItem) { + PkgItem pkg = (PkgItem) element; + + if (pkg.getState() == PkgState.INSTALLED || + pkg.getState() == PkgState.HAS_UPDATE) { + return Integer.toString(pkg.getRevision()); + } + } + + } else if (mColumn == mColumnStatus) { + + if (element instanceof PkgItem) { + PkgItem pkg = (PkgItem) element; + + switch(pkg.getState()) { + case INSTALLED: + return "Installed"; + case HAS_UPDATE: + return "Update available"; + case NEW: + return "Not installed. New revision " + Integer.toString(pkg.getRevision()); + } + return pkg.getState().toString(); + + } else if (element instanceof Package) { + // This is an update package. + return "New revision " + Integer.toString(((Package) element).getRevision()); + } + } + + return ""; //$NON-NLS-1$ + } + + @Override + public Image getImage(Object element) { + ImageFactory imgFactory = mUpdaterData.getImageFactory(); + + if (imgFactory != null) { + if (mColumn == mColumnName) { + if (element instanceof PkgCategory) { + return imgFactory.getImageForObject(((PkgCategory) element).getIconRef()); + } else if (element instanceof PkgItem) { + return imgFactory.getImageForObject(((PkgItem) element).getPackage()); + } + return imgFactory.getImageForObject(element); + + } else if (mColumn == mColumnStatus && element instanceof PkgItem) { + switch(((PkgItem) element).getState()) { + case INSTALLED: + return imgFactory.getImageByName(ICON_PKG_INSTALLED); + case HAS_UPDATE: + return imgFactory.getImageByName(ICON_PKG_UPDATE); + case NEW: + return imgFactory.getImageByName(ICON_PKG_NEW); + } + } + } + return super.getImage(element); + } + + // -- ITableFontProvider + + public Font getFont(Object element, int columnIndex) { + if (element instanceof PkgItem) { + if (((PkgItem) element).getState() == PkgState.NEW) { + return mTreeFontItalic; + } + } else if (element instanceof Package) { + // update package + return mTreeFontItalic; + } + return super.getFont(element); + } + + // -- ITableColorProvider + + public Color getBackground(Object element, int columnIndex) { + if (element instanceof PkgItem) { + if (((PkgItem) element).getState() == PkgState.HAS_UPDATE) { + return mColorUpdate; + } + } + return null; + } + + public Color getForeground(Object element, int columnIndex) { + // Not used + return null; + } + } + + private class PkgContentProvider implements ITreeContentProvider { + + public Object[] getChildren(Object parentElement) { + + if (parentElement instanceof ArrayList<?>) { + return ((ArrayList<?>) parentElement).toArray(); + + } else if (parentElement instanceof PkgCategory) { + return ((PkgCategory) parentElement).getItems().toArray(); + + } else if (parentElement instanceof PkgItem) { + List<Package> pkgs = ((PkgItem) parentElement).getUpdatePkgs(); + if (pkgs != null) { + return pkgs.toArray(); + } + + if (mDisplayArchives) { + return ((PkgItem) parentElement).getArchives(); + } + + } else if (parentElement instanceof Package) { + if (mDisplayArchives) { + return ((Package) parentElement).getArchives(); + } + + } + + return new Object[0]; + } + + public Object getParent(Object element) { + // TODO Auto-generated method stub + return null; + } + + public boolean hasChildren(Object parentElement) { + if (parentElement instanceof ArrayList<?>) { + return true; + + } else if (parentElement instanceof PkgCategory) { + return true; + + } else if (parentElement instanceof PkgItem) { + List<Package> pkgs = ((PkgItem) parentElement).getUpdatePkgs(); + if (pkgs != null) { + return !pkgs.isEmpty(); + } + + if (mDisplayArchives) { + Archive[] archives = ((PkgItem) parentElement).getArchives(); + return archives.length > 0; + } + } else if (parentElement instanceof Package) { + if (mDisplayArchives) { + return ((Package) parentElement).getArchives().length > 0; + } + } + + return false; + } + + public Object[] getElements(Object inputElement) { + return getChildren(inputElement); + } + + public void dispose() { + // unused + + } + + public void inputChanged(Viewer arg0, Object arg1, Object arg2) { + // unused + } + } + + private static class PkgCategory { + private final Integer mKey; + private final Object mIconRef; + private final List<PkgItem> mItems = new ArrayList<PkgItem>(); + private String mLabel; + + // When storing by API, key is the API level (>=1), except 0 is tools and 1 is extra/addons. + // When sorting by Source, key is the hash of the source's name. + public final static Integer KEY_TOOLS = Integer.valueOf(0); + public final static Integer KEY_EXTRA = Integer.valueOf(-1); + + public PkgCategory(Integer key, String label, Object iconRef) { + mKey = key; + mLabel = label; + mIconRef = iconRef; + } + + public Integer getKey() { + return mKey; + } + + public String getLabel() { + return mLabel; + } + + public void setLabel(String label) { + mLabel = label; + } + + public Object getIconRef() { + return mIconRef; + } + + public List<PkgItem> getItems() { + return mItems; + } + } + + public enum PkgState { + /** + * Package is locally installed and has no update available on remote sites. + */ + INSTALLED, + + /** + * Package is installed and has an update available. + * In this case, {@link PkgItem#getUpdatePkgs()} provides the list of 1 or more + * packages that can update this {@link PkgItem}. + */ + HAS_UPDATE, + + /** + * There's a new package available on the remote site that isn't + * installed locally. + */ + NEW + } + + public static class PkgItem implements Comparable<PkgItem> { + private final Package mPkg; + private PkgState mState; + private List<Package> mUpdatePkgs; + + public PkgItem(Package pkg, PkgState state) { + mPkg = pkg; + mState = state; + assert mPkg != null; + } + + public boolean isObsolete() { + return mPkg.isObsolete(); + } + + public boolean isSameAs(Package pkg) { + return mPkg.canBeUpdatedBy(pkg) == UpdateInfo.NOT_UPDATE; + } + + /** + * Check whether the 'pkg' argument updates this package. + * If it does, record it as a sub-package. + * Returns true if it was recorded as an update, false otherwise. + */ + public boolean isUpdatedBy(Package pkg) { + if (mPkg.canBeUpdatedBy(pkg) == UpdateInfo.UPDATE) { + if (mUpdatePkgs == null) { + mUpdatePkgs = new ArrayList<Package>(); + } + mUpdatePkgs.add(pkg); + mState = PkgState.HAS_UPDATE; + return true; + } + + return false; + } + + public String getName() { + return mPkg.getListDescription(); + } + + public int getRevision() { + return mPkg.getRevision(); + } + + public String getDescription() { + return mPkg.getDescription(); + } + + public Package getPackage() { + return mPkg; + } + + public PkgState getState() { + return mState; + } + + public SdkSource getSource() { + if (mState == PkgState.NEW) { + return mPkg.getParentSource(); + } else { + return null; + } + } + + public int getApi() { + return mPkg instanceof IPackageVersion ? + ((IPackageVersion) mPkg).getVersion().getApiLevel() : + -1; + } + + public List<Package> getUpdatePkgs() { + return mUpdatePkgs; + } + + public Archive[] getArchives() { + return mPkg.getArchives(); + } + + public int compareTo(PkgItem pkg) { + return getPackage().compareTo(pkg.getPackage()); + } + } + + + + // --- Implementation of ISdkChangeListener --- + + public void onSdkLoaded() { + onSdkReload(); + } + + public void onSdkReload() { + loadPackages(); + } + + public void preInstallHook() { + // nothing to be done for now. + } + + public void postInstallHook() { + // nothing to be done for now. + } + + // --- End of hiding from SWT Designer --- + //$hide<<$ +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java index 6fbf060..0a70162 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java @@ -1,495 +1,497 @@ -/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.sdkuilib.internal.repository;
-
-
-import com.android.sdklib.internal.repository.Archive;
-import com.android.sdklib.internal.repository.IDescription;
-import com.android.sdklib.internal.repository.Package;
-import com.android.sdklib.internal.repository.SdkAddonSource;
-import com.android.sdklib.internal.repository.SdkSource;
-import com.android.sdklib.internal.repository.SdkSourceCategory;
-import com.android.sdkuilib.repository.ISdkChangeListener;
-
-import org.eclipse.jface.dialogs.IInputValidator;
-import org.eclipse.jface.dialogs.InputDialog;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.jface.viewers.CheckStateChangedEvent;
-import org.eclipse.jface.viewers.CheckboxTreeViewer;
-import org.eclipse.jface.viewers.ICheckStateListener;
-import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.ITreeSelection;
-import org.eclipse.jface.window.Window;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ControlAdapter;
-import org.eclipse.swt.events.ControlEvent;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Group;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Tree;
-import org.eclipse.swt.widgets.TreeColumn;
-
-import java.util.ArrayList;
-
-
-public class RemotePackagesPage extends Composite implements ISdkChangeListener {
-
- private final UpdaterData mUpdaterData;
-
- private CheckboxTreeViewer mTreeViewerSources;
- private Tree mTreeSources;
- private TreeColumn mColumnSource;
- private Button mUpdateOnlyCheckBox;
- private Group mDescriptionContainer;
- private Button mAddSiteButton;
- private Button mDeleteSiteButton;
- private Button mRefreshButton;
- private Button mInstallSelectedButton;
- private Label mDescriptionLabel;
- private Label mSdkLocLabel;
-
-
-
- /**
- * Create the composite.
- * @param parent The parent of the composite.
- * @param updaterData An instance of {@link UpdaterData}.
- */
- RemotePackagesPage(Composite parent, UpdaterData updaterData) {
- super(parent, SWT.BORDER);
-
- mUpdaterData = updaterData;
-
- createContents(this);
- postCreate(); //$hide$
- }
-
- private void createContents(Composite parent) {
- parent.setLayout(new GridLayout(5, false));
-
- mSdkLocLabel = new Label(parent, SWT.NONE);
- mSdkLocLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false, 5, 1));
- mSdkLocLabel.setText("SDK Location: " +
- (mUpdaterData.getOsSdkRoot() != null ? mUpdaterData.getOsSdkRoot()
- : "<unknown>"));
-
- mTreeViewerSources = new CheckboxTreeViewer(parent, SWT.BORDER);
- mTreeViewerSources.addCheckStateListener(new ICheckStateListener() {
- public void checkStateChanged(CheckStateChangedEvent event) {
- onTreeCheckStateChanged(event); //$hide$
- }
- });
- mTreeSources = mTreeViewerSources.getTree();
- mTreeSources.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onTreeSelected(); //$hide$
- }
- });
- mTreeSources.setHeaderVisible(true);
- mTreeSources.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 5, 1));
-
- mColumnSource = new TreeColumn(mTreeSources, SWT.NONE);
- mColumnSource.setWidth(289);
- mColumnSource.setText("Packages available for download");
-
- mDescriptionContainer = new Group(parent, SWT.NONE);
- mDescriptionContainer.setLayout(new GridLayout(1, false));
- mDescriptionContainer.setText("Description");
- mDescriptionContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 5, 1));
-
- mDescriptionLabel = new Label(mDescriptionContainer, SWT.NONE);
- mDescriptionLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
- mDescriptionLabel.setText("Line1\nLine2\nLine3"); //$NON-NLS-1$
-
- mAddSiteButton = new Button(parent, SWT.NONE);
- mAddSiteButton.setText("Add Add-on Site...");
- mAddSiteButton.setToolTipText("Allows you to enter a new add-on site. " +
- "Such site can only contribute add-ons and extra packages.");
- mAddSiteButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onAddSiteSelected(); //$hide$
- }
- });
-
- mDeleteSiteButton = new Button(parent, SWT.NONE);
- mDeleteSiteButton.setText("Delete Add-on Site...");
- mDeleteSiteButton.setToolTipText("Allows you to remove an add-on site. " +
- "Built-in default sites cannot be removed.");
- mDeleteSiteButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onRemoveSiteSelected(); //$hide$
- }
- });
-
- mUpdateOnlyCheckBox = new Button(parent, SWT.CHECK);
- mUpdateOnlyCheckBox.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 1, 1));
- mUpdateOnlyCheckBox.setText("Display updates only");
- mUpdateOnlyCheckBox.setToolTipText("When selected, only compatible non-obsolete update packages are shown in the list above.");
- mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly());
- mUpdateOnlyCheckBox.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent arg0) {
- onShowUpdateOnly(); //$hide$
- }
- });
-
- mRefreshButton = new Button(parent, SWT.NONE);
- mRefreshButton.setText("Refresh");
- mRefreshButton.setToolTipText("Refreshes the list of packages from open sites.");
- mRefreshButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onRefreshSelected(); //$hide$
- }
- });
-
- mInstallSelectedButton = new Button(parent, SWT.NONE);
- mInstallSelectedButton.setText("Install Selected");
- mInstallSelectedButton.setToolTipText("Allows you to review all selected packages " +
- "and install them.");
- mInstallSelectedButton.addSelectionListener(new SelectionAdapter() {
- @Override
- public void widgetSelected(SelectionEvent e) {
- onInstallSelectedArchives(); //$hide$
- }
- });
- }
-
- @Override
- public void dispose() {
- mUpdaterData.removeListener(this);
- super.dispose();
- }
-
- @Override
- protected void checkSubclass() {
- // Disable the check that prevents subclassing of SWT components
- }
-
- // -- Start of internal part ----------
- // Hide everything down-below from SWT designer
- //$hide>>$
-
- /**
- * Called by the constructor right after {@link #createContents(Composite)}.
- */
- private void postCreate() {
- mUpdaterData.addListeners(this);
- adjustColumnsWidth();
- updateButtonsState();
- }
-
- /**
- * Adds a listener to adjust the columns width when the parent is resized.
- * <p/>
- * If we need something more fancy, we might want to use this:
- * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co
- */
- private void adjustColumnsWidth() {
- // Add a listener to resize the column to the full width of the table
- ControlAdapter resizer = new ControlAdapter() {
- @Override
- public void controlResized(ControlEvent e) {
- Rectangle r = mTreeSources.getClientArea();
- mColumnSource.setWidth(r.width);
- }
- };
-
- mTreeSources.addControlListener(resizer);
- resizer.controlResized(null);
- }
-
- /**
- * Called when an item in the package table viewer is selected.
- * If the items is an {@link IDescription} (as it should), this will display its long
- * description in the description area. Otherwise when the item is not of the expected
- * type or there is no selection, it empties the description area.
- */
- private void onTreeSelected() {
- updateButtonsState();
-
- ISelection sel = mTreeViewerSources.getSelection();
- if (sel instanceof ITreeSelection) {
- Object elem = ((ITreeSelection) sel).getFirstElement();
- if (elem instanceof IDescription) {
- mDescriptionLabel.setText(((IDescription) elem).getLongDescription());
- mDescriptionContainer.layout(true);
- return;
- }
- }
- mDescriptionLabel.setText(""); //$NON-NLS1-$
- }
-
- /**
- * Handle checking and unchecking of the tree items.
- *
- * When unchecking, all sub-tree items checkboxes are cleared too.
- * When checking a source, all of its packages are checked too.
- * When checking a package, only its compatible archives are checked.
- */
- private void onTreeCheckStateChanged(CheckStateChangedEvent event) {
- updateButtonsState();
-
- boolean b = event.getChecked();
- Object elem = event.getElement(); // Will be Archive or Package or RepoSource
-
- assert event.getSource() == mTreeViewerSources;
-
- // when deselecting, we just deselect all children too
- if (b == false) {
- mTreeViewerSources.setSubtreeChecked(elem, b);
- return;
- }
-
- ITreeContentProvider provider =
- (ITreeContentProvider) mTreeViewerSources.getContentProvider();
-
- // When selecting, we want to only select compatible archives
- // and expand the super nodes.
- expandItem(elem, provider);
- }
-
- private void expandItem(Object elem, ITreeContentProvider provider) {
- if (elem instanceof SdkSource || elem instanceof SdkSourceCategory) {
- mTreeViewerSources.setExpandedState(elem, true);
- for (Object pkg : provider.getChildren(elem)) {
- mTreeViewerSources.setChecked(pkg, true);
- expandItem(pkg, provider);
- }
- } else if (elem instanceof Package) {
- selectCompatibleArchives(elem, provider);
- }
- }
-
- private void selectCompatibleArchives(Object pkg, ITreeContentProvider provider) {
- for (Object archive : provider.getChildren(pkg)) {
- if (archive instanceof Archive) {
- mTreeViewerSources.setChecked(archive, ((Archive) archive).isCompatible());
- }
- }
- }
-
- private void onShowUpdateOnly() {
- SettingsController controller = mUpdaterData.getSettingsController();
- controller.setShowUpdateOnly(mUpdateOnlyCheckBox.getSelection());
- controller.saveSettings();
-
- // Get the list of selected archives
- ArrayList<Archive> archives = new ArrayList<Archive>();
- for (Object element : mTreeViewerSources.getCheckedElements()) {
- if (element instanceof Archive) {
- archives.add((Archive) element);
- }
- // Deselect them all
- mTreeViewerSources.setChecked(element, false);
- }
-
- mTreeViewerSources.refresh();
-
- // Now reselect those that still exist in the tree but only if they
- // are compatible archives
- for (Archive a : archives) {
- if (a.isCompatible() && mTreeViewerSources.setChecked(a, true)) {
- // If we managed to select the archive, also select the parent package.
- // Technically we should only select the parent package if *all* the
- // compatible archives children are selected. In practice we'll rarely
- // have more than one compatible archive per package.
- mTreeViewerSources.setChecked(a.getParentPackage(), true);
- }
- }
-
- updateButtonsState();
- }
-
- private void onInstallSelectedArchives() {
- ArrayList<Archive> archives = new ArrayList<Archive>();
- for (Object element : mTreeViewerSources.getCheckedElements()) {
- if (element instanceof Archive) {
- archives.add((Archive) element);
- }
- }
-
- if (mUpdaterData != null) {
- mUpdaterData.updateOrInstallAll_WithGUI(
- archives,
- mUpdateOnlyCheckBox.getSelection() /* includeObsoletes */);
- }
- }
-
- private void onAddSiteSelected() {
-
- final SdkSource[] knowSources = mUpdaterData.getSources().getAllSources();
- String title = "Add Add-on Site URL";
-
- String msg =
- "This dialog lets you add the URL of a new add-on site.\n" +
- "\n" +
- "An add-on site can only provide new add-ons or \"user\" packages.\n" +
- "Add-on sites cannot provide standard Android platforms, docs or samples packages.\n" +
- "Inserting a URL here will not allow you to clone an official Android repository.\n" +
- "\n" +
- "Please enter the URL of the repository.xml for the new add-on site:";
-
- InputDialog dlg = new InputDialog(getShell(), title, msg, null, new IInputValidator() {
- public String isValid(String newText) {
-
- newText = newText == null ? null : newText.trim();
-
- if (newText == null || newText.length() == 0) {
- return "Error: URL field is empty. Please enter a URL.";
- }
-
- // A URL should have one of the following prefixes
- if (!newText.startsWith("file://") && //$NON-NLS-1$
- !newText.startsWith("ftp://") && //$NON-NLS-1$
- !newText.startsWith("http://") && //$NON-NLS-1$
- !newText.startsWith("https://")) { //$NON-NLS-1$
- return "Error: The URL must start by one of file://, ftp://, http:// or https://";
- }
-
- // Reject URLs that are already in the source list.
- // URLs are generally case-insensitive (except for file:// where it all depends
- // on the current OS so we'll ignore this case.)
- for (SdkSource s : knowSources) {
- if (newText.equalsIgnoreCase(s.getUrl())) {
- return "Error : This site is already listed.";
- }
- }
-
- return null;
- }
- });
-
- if (dlg.open() == Window.OK) {
- String url = dlg.getValue().trim();
- mUpdaterData.getSources().add(
- SdkSourceCategory.USER_ADDONS,
- new SdkAddonSource(url, null/*uiName*/));
- onRefreshSelected();
- }
- }
-
- private void onRemoveSiteSelected() {
- boolean changed = false;
-
- ISelection sel = mTreeViewerSources.getSelection();
- if (mUpdaterData != null && sel instanceof ITreeSelection) {
- for (Object c : ((ITreeSelection) sel).toList()) {
- if (c instanceof SdkSource) {
- SdkSource source = (SdkSource) c;
-
- if (mUpdaterData.getSources().hasSourceUrl(
- SdkSourceCategory.USER_ADDONS, source)) {
- String title = "Delete Add-on Site?";
-
- String msg = String.format("Are you sure you want to delete the add-on site '%1$s'?",
- source.getUrl());
-
- if (MessageDialog.openQuestion(getShell(), title, msg)) {
- mUpdaterData.getSources().remove(source);
- changed = true;
- }
- }
- }
- }
- }
-
- if (changed) {
- onRefreshSelected();
- }
- }
-
- private void onRefreshSelected() {
- if (mUpdaterData != null) {
- mUpdaterData.refreshSources(false /*forceFetching*/);
- }
- mTreeViewerSources.refresh();
- updateButtonsState();
- }
-
- private void updateButtonsState() {
- // We install archives, so there should be at least one checked archive.
- // Having sites or packages checked does not count.
- boolean hasCheckedArchive = false;
- Object[] checked = mTreeViewerSources.getCheckedElements();
- if (checked != null) {
- for (Object c : checked) {
- if (c instanceof Archive) {
- hasCheckedArchive = true;
- break;
- }
- }
- }
-
- // Is there a selected site Source?
- boolean hasSelectedUserSource = false;
- ISelection sel = mTreeViewerSources.getSelection();
- if (sel instanceof ITreeSelection) {
- for (Object c : ((ITreeSelection) sel).toList()) {
- if (c instanceof SdkSource &&
- mUpdaterData.getSources().hasSourceUrl(
- SdkSourceCategory.USER_ADDONS, (SdkSource) c)) {
- hasSelectedUserSource = true;
- break;
- }
- }
- }
-
- mAddSiteButton.setEnabled(true);
- mDeleteSiteButton.setEnabled(hasSelectedUserSource);
- mRefreshButton.setEnabled(true);
- mInstallSelectedButton.setEnabled(hasCheckedArchive);
-
- // set value on the show only update checkbox
- mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly());
- }
-
- // --- Implementation of ISdkChangeListener ---
-
- public void onSdkLoaded() {
- onSdkReload();
- }
-
- public void onSdkReload() {
- RepoSourcesAdapter sources = mUpdaterData.getSourcesAdapter();
- mTreeViewerSources.setContentProvider(sources.getContentProvider());
- mTreeViewerSources.setLabelProvider( sources.getLabelProvider());
- mTreeViewerSources.setInput(sources);
- onTreeSelected();
- }
-
- public void preInstallHook() {
- // nothing to be done for now.
- }
-
- public void postInstallHook() {
- // nothing to be done for now.
- }
-
- // End of hiding from SWT Designer
- //$hide<<$
-}
+/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdkuilib.internal.repository; + + +import com.android.sdklib.internal.repository.Archive; +import com.android.sdklib.internal.repository.IDescription; +import com.android.sdklib.internal.repository.Package; +import com.android.sdklib.internal.repository.SdkAddonSource; +import com.android.sdklib.internal.repository.SdkSource; +import com.android.sdklib.internal.repository.SdkSourceCategory; +import com.android.sdkuilib.repository.ISdkChangeListener; + +import org.eclipse.jface.dialogs.IInputValidator; +import org.eclipse.jface.dialogs.InputDialog; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTreeViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.ITreeSelection; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeColumn; + +import java.util.ArrayList; + +/** + * Page that displays remote repository & add-ons sources and let the user + * select packages for installation. + */ +public class RemotePackagesPage extends Composite implements ISdkChangeListener { + + private final UpdaterData mUpdaterData; + + private CheckboxTreeViewer mTreeViewerSources; + private Tree mTreeSources; + private TreeColumn mColumnSource; + private Button mUpdateOnlyCheckBox; + private Group mDescriptionContainer; + private Button mAddSiteButton; + private Button mDeleteSiteButton; + private Button mRefreshButton; + private Button mInstallSelectedButton; + private Label mDescriptionLabel; + private Label mSdkLocLabel; + + + + /** + * Create the composite. + * @param parent The parent of the composite. + * @param updaterData An instance of {@link UpdaterData}. + */ + RemotePackagesPage(Composite parent, UpdaterData updaterData) { + super(parent, SWT.BORDER); + + mUpdaterData = updaterData; + + createContents(this); + postCreate(); //$hide$ + } + + private void createContents(Composite parent) { + parent.setLayout(new GridLayout(5, false)); + + mSdkLocLabel = new Label(parent, SWT.NONE); + mSdkLocLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false, 5, 1)); + mSdkLocLabel.setText("SDK Location: " + + (mUpdaterData.getOsSdkRoot() != null ? mUpdaterData.getOsSdkRoot() + : "<unknown>")); + + mTreeViewerSources = new CheckboxTreeViewer(parent, SWT.BORDER); + mTreeViewerSources.addCheckStateListener(new ICheckStateListener() { + public void checkStateChanged(CheckStateChangedEvent event) { + onTreeCheckStateChanged(event); //$hide$ + } + }); + mTreeSources = mTreeViewerSources.getTree(); + mTreeSources.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onTreeSelected(); //$hide$ + } + }); + mTreeSources.setHeaderVisible(true); + mTreeSources.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 5, 1)); + + mColumnSource = new TreeColumn(mTreeSources, SWT.NONE); + mColumnSource.setWidth(289); + mColumnSource.setText("Packages available for download"); + + mDescriptionContainer = new Group(parent, SWT.NONE); + mDescriptionContainer.setLayout(new GridLayout(1, false)); + mDescriptionContainer.setText("Description"); + mDescriptionContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 5, 1)); + + mDescriptionLabel = new Label(mDescriptionContainer, SWT.NONE); + mDescriptionLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); + mDescriptionLabel.setText("Line1\nLine2\nLine3"); //$NON-NLS-1$ + + mAddSiteButton = new Button(parent, SWT.NONE); + mAddSiteButton.setText("Add Add-on Site..."); + mAddSiteButton.setToolTipText("Allows you to enter a new add-on site. " + + "Such site can only contribute add-ons and extra packages."); + mAddSiteButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onAddSiteSelected(); //$hide$ + } + }); + + mDeleteSiteButton = new Button(parent, SWT.NONE); + mDeleteSiteButton.setText("Delete Add-on Site..."); + mDeleteSiteButton.setToolTipText("Allows you to remove an add-on site. " + + "Built-in default sites cannot be removed."); + mDeleteSiteButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onRemoveSiteSelected(); //$hide$ + } + }); + + mUpdateOnlyCheckBox = new Button(parent, SWT.CHECK); + mUpdateOnlyCheckBox.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 1, 1)); + mUpdateOnlyCheckBox.setText("Display updates only"); + mUpdateOnlyCheckBox.setToolTipText("When selected, only compatible non-obsolete update packages are shown in the list above."); + mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly()); + mUpdateOnlyCheckBox.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent arg0) { + onShowUpdateOnly(); //$hide$ + } + }); + + mRefreshButton = new Button(parent, SWT.NONE); + mRefreshButton.setText("Refresh"); + mRefreshButton.setToolTipText("Refreshes the list of packages from open sites."); + mRefreshButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onRefreshSelected(); //$hide$ + } + }); + + mInstallSelectedButton = new Button(parent, SWT.NONE); + mInstallSelectedButton.setText("Install Selected"); + mInstallSelectedButton.setToolTipText("Allows you to review all selected packages " + + "and install them."); + mInstallSelectedButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + onInstallSelectedArchives(); //$hide$ + } + }); + } + + @Override + public void dispose() { + mUpdaterData.removeListener(this); + super.dispose(); + } + + @Override + protected void checkSubclass() { + // Disable the check that prevents subclassing of SWT components + } + + // -- Start of internal part ---------- + // Hide everything down-below from SWT designer + //$hide>>$ + + /** + * Called by the constructor right after {@link #createContents(Composite)}. + */ + private void postCreate() { + mUpdaterData.addListeners(this); + adjustColumnsWidth(); + updateButtonsState(); + } + + /** + * Adds a listener to adjust the columns width when the parent is resized. + * <p/> + * If we need something more fancy, we might want to use this: + * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co + */ + private void adjustColumnsWidth() { + // Add a listener to resize the column to the full width of the table + ControlAdapter resizer = new ControlAdapter() { + @Override + public void controlResized(ControlEvent e) { + Rectangle r = mTreeSources.getClientArea(); + mColumnSource.setWidth(r.width); + } + }; + + mTreeSources.addControlListener(resizer); + resizer.controlResized(null); + } + + /** + * Called when an item in the package table viewer is selected. + * If the items is an {@link IDescription} (as it should), this will display its long + * description in the description area. Otherwise when the item is not of the expected + * type or there is no selection, it empties the description area. + */ + private void onTreeSelected() { + updateButtonsState(); + + ISelection sel = mTreeViewerSources.getSelection(); + if (sel instanceof ITreeSelection) { + Object elem = ((ITreeSelection) sel).getFirstElement(); + if (elem instanceof IDescription) { + mDescriptionLabel.setText(((IDescription) elem).getLongDescription()); + mDescriptionContainer.layout(true); + return; + } + } + mDescriptionLabel.setText(""); //$NON-NLS1-$ + } + + /** + * Handle checking and unchecking of the tree items. + * + * When unchecking, all sub-tree items checkboxes are cleared too. + * When checking a source, all of its packages are checked too. + * When checking a package, only its compatible archives are checked. + */ + private void onTreeCheckStateChanged(CheckStateChangedEvent event) { + updateButtonsState(); + + boolean b = event.getChecked(); + Object elem = event.getElement(); // Will be Archive or Package or RepoSource + + assert event.getSource() == mTreeViewerSources; + + // when deselecting, we just deselect all children too + if (b == false) { + mTreeViewerSources.setSubtreeChecked(elem, b); + return; + } + + ITreeContentProvider provider = + (ITreeContentProvider) mTreeViewerSources.getContentProvider(); + + // When selecting, we want to only select compatible archives + // and expand the super nodes. + expandItem(elem, provider); + } + + private void expandItem(Object elem, ITreeContentProvider provider) { + if (elem instanceof SdkSource || elem instanceof SdkSourceCategory) { + mTreeViewerSources.setExpandedState(elem, true); + for (Object pkg : provider.getChildren(elem)) { + mTreeViewerSources.setChecked(pkg, true); + expandItem(pkg, provider); + } + } else if (elem instanceof Package) { + selectCompatibleArchives(elem, provider); + } + } + + private void selectCompatibleArchives(Object pkg, ITreeContentProvider provider) { + for (Object archive : provider.getChildren(pkg)) { + if (archive instanceof Archive) { + mTreeViewerSources.setChecked(archive, ((Archive) archive).isCompatible()); + } + } + } + + private void onShowUpdateOnly() { + SettingsController controller = mUpdaterData.getSettingsController(); + controller.setShowUpdateOnly(mUpdateOnlyCheckBox.getSelection()); + controller.saveSettings(); + + // Get the list of selected archives + ArrayList<Archive> archives = new ArrayList<Archive>(); + for (Object element : mTreeViewerSources.getCheckedElements()) { + if (element instanceof Archive) { + archives.add((Archive) element); + } + // Deselect them all + mTreeViewerSources.setChecked(element, false); + } + + mTreeViewerSources.refresh(); + + // Now reselect those that still exist in the tree but only if they + // are compatible archives + for (Archive a : archives) { + if (a.isCompatible() && mTreeViewerSources.setChecked(a, true)) { + // If we managed to select the archive, also select the parent package. + // Technically we should only select the parent package if *all* the + // compatible archives children are selected. In practice we'll rarely + // have more than one compatible archive per package. + mTreeViewerSources.setChecked(a.getParentPackage(), true); + } + } + + updateButtonsState(); + } + + private void onInstallSelectedArchives() { + ArrayList<Archive> archives = new ArrayList<Archive>(); + for (Object element : mTreeViewerSources.getCheckedElements()) { + if (element instanceof Archive) { + archives.add((Archive) element); + } + } + + if (mUpdaterData != null) { + mUpdaterData.updateOrInstallAll_WithGUI( + archives, + mUpdateOnlyCheckBox.getSelection() /* includeObsoletes */); + } + } + + private void onAddSiteSelected() { + final SdkSource[] knowSources = mUpdaterData.getSources().getAllSources(); + String title = "Add Add-on Site URL"; + + String msg = + "This dialog lets you add the URL of a new add-on site.\n" + + "\n" + + "An add-on site can only provide new add-ons or \"user\" packages.\n" + + "Add-on sites cannot provide standard Android platforms, docs or samples packages.\n" + + "Inserting a URL here will not allow you to clone an official Android repository.\n" + + "\n" + + "Please enter the URL of the repository.xml for the new add-on site:"; + + InputDialog dlg = new InputDialog(getShell(), title, msg, null, new IInputValidator() { + public String isValid(String newText) { + + newText = newText == null ? null : newText.trim(); + + if (newText == null || newText.length() == 0) { + return "Error: URL field is empty. Please enter a URL."; + } + + // A URL should have one of the following prefixes + if (!newText.startsWith("file://") && //$NON-NLS-1$ + !newText.startsWith("ftp://") && //$NON-NLS-1$ + !newText.startsWith("http://") && //$NON-NLS-1$ + !newText.startsWith("https://")) { //$NON-NLS-1$ + return "Error: The URL must start by one of file://, ftp://, http:// or https://"; + } + + // Reject URLs that are already in the source list. + // URLs are generally case-insensitive (except for file:// where it all depends + // on the current OS so we'll ignore this case.) + for (SdkSource s : knowSources) { + if (newText.equalsIgnoreCase(s.getUrl())) { + return "Error : This site is already listed."; + } + } + + return null; + } + }); + + if (dlg.open() == Window.OK) { + String url = dlg.getValue().trim(); + mUpdaterData.getSources().add( + SdkSourceCategory.USER_ADDONS, + new SdkAddonSource(url, null/*uiName*/)); + onRefreshSelected(); + } + } + + private void onRemoveSiteSelected() { + boolean changed = false; + + ISelection sel = mTreeViewerSources.getSelection(); + if (mUpdaterData != null && sel instanceof ITreeSelection) { + for (Object c : ((ITreeSelection) sel).toList()) { + if (c instanceof SdkSource) { + SdkSource source = (SdkSource) c; + + if (mUpdaterData.getSources().hasSourceUrl( + SdkSourceCategory.USER_ADDONS, source)) { + String title = "Delete Add-on Site?"; + + String msg = String.format("Are you sure you want to delete the add-on site '%1$s'?", + source.getUrl()); + + if (MessageDialog.openQuestion(getShell(), title, msg)) { + mUpdaterData.getSources().remove(source); + changed = true; + } + } + } + } + } + + if (changed) { + onRefreshSelected(); + } + } + + private void onRefreshSelected() { + if (mUpdaterData != null) { + mUpdaterData.refreshSources(false /*forceFetching*/); + } + mTreeViewerSources.refresh(); + updateButtonsState(); + } + + private void updateButtonsState() { + // We install archives, so there should be at least one checked archive. + // Having sites or packages checked does not count. + boolean hasCheckedArchive = false; + Object[] checked = mTreeViewerSources.getCheckedElements(); + if (checked != null) { + for (Object c : checked) { + if (c instanceof Archive) { + hasCheckedArchive = true; + break; + } + } + } + + // Is there a selected site Source? + boolean hasSelectedUserSource = false; + ISelection sel = mTreeViewerSources.getSelection(); + if (sel instanceof ITreeSelection) { + for (Object c : ((ITreeSelection) sel).toList()) { + if (c instanceof SdkSource && + mUpdaterData.getSources().hasSourceUrl( + SdkSourceCategory.USER_ADDONS, (SdkSource) c)) { + hasSelectedUserSource = true; + break; + } + } + } + + mAddSiteButton.setEnabled(true); + mDeleteSiteButton.setEnabled(hasSelectedUserSource); + mRefreshButton.setEnabled(true); + mInstallSelectedButton.setEnabled(hasCheckedArchive); + + // set value on the show only update checkbox + mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly()); + } + + // --- Implementation of ISdkChangeListener --- + + public void onSdkLoaded() { + onSdkReload(); + } + + public void onSdkReload() { + RepoSourcesAdapter sources = mUpdaterData.getSourcesAdapter(); + mTreeViewerSources.setContentProvider(sources.getContentProvider()); + mTreeViewerSources.setLabelProvider( sources.getLabelProvider()); + mTreeViewerSources.setInput(sources); + onTreeSelected(); + } + + public void preInstallHook() { + // nothing to be done for now. + } + + public void postInstallHook() { + // nothing to be done for now. + } + + // End of hiding from SWT Designer + //$hide<<$ +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java index 8722e02..54bc068 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java @@ -316,13 +316,13 @@ public class RepoSourcesAdapter { // get the installed packages
Package[] installedPackages = mUpdaterData.getInstalledPackages();
+ // we'll populate this package list with either upgrades or new packages.
ArrayList<Package> filteredList = new ArrayList<Package>();
// for each remote packages, we look for an existing version.
// If no existing version -> add to the list
// if existing version but with older revision -> add it to the list
for (Package remotePkg : remotePackages) {
- boolean newPkg = true;
// Obsolete packages are not offered as updates.
if (remotePkg.isObsolete()) {
@@ -332,20 +332,27 @@ public class RepoSourcesAdapter { // For all potential packages, we also make sure that there's an archive for
// the current platform, or we simply skip them.
if (remotePkg.hasCompatibleArchive()) {
- for (Package installedPkg : installedPackages) {
+ boolean keepPkg = true;
+
+ nextPkg: for (Package installedPkg : installedPackages) {
UpdateInfo info = installedPkg.canBeUpdatedBy(remotePkg);
- if (info == UpdateInfo.UPDATE) {
- filteredList.add(remotePkg);
- newPkg = false;
- break; // there shouldn't be 2 revisions of the same package
- } else if (info != UpdateInfo.INCOMPATIBLE) {
- newPkg = false;
- break; // there shouldn't be 2 revisions of the same package
+ switch(info) {
+ case UPDATE:
+ // The remote package is an update to an existing one.
+ // We're done looking.
+ keepPkg = true;
+ break nextPkg;
+ case NOT_UPDATE:
+ // The remote package is the same as one that is already installed.
+ keepPkg = false;
+ break;
+ case INCOMPATIBLE:
+ // We can't compare and decide on incompatible things.
+ break;
}
}
- // if we have not found the same package, then we add it (it's a new package)
- if (newPkg) {
+ if (keepPkg) {
filteredList.add(remotePkg);
}
}
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..6d5b5d4 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); + } + // ----- /** @@ -170,45 +174,52 @@ public class UpdateNoWindow { /** * Sets the description in the current task dialog. */ - public void setDescription(String descriptionFormat, Object...args) { + public void setDescription(String format, Object...args) { String last = mLastDesc; - String line = String.format(" " + descriptionFormat, args); + String line = String.format(" " + format, 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()); + int pos = line.indexOf('%'); + if (pos > -1) { + String base = line.trim(); + if (mLastProgressBase != null && base.startsWith(mLastProgressBase)) { + line = " " + base.substring(mLastProgressBase.length()); //$NON-NLS-1$ } - line += "\r"; + line += '\r'; } else { - mLastProgressBase = line; - line += "\n"; + mLastProgressBase = line.trim(); + line += '\n'; } // Skip line if it's the same as the last one. - if (last != null && last.equals(line)) { + if (last != null && last.equals(line.trim())) { return; } - mLastDesc = line; + mLastDesc = line.trim(); // If the last line terminated with a \r but the new one doesn't, we need to // insert a \n to avoid erasing the previous line. if (last != null && - last.endsWith("\r") && - !line.endsWith("\r")) { - line = "\n" + line; + last.endsWith("\r") && //$NON-NLS-1$ + !line.endsWith("\r")) { //$NON-NLS-1$ + line = '\n' + line; } - mSdkLog.printf("%s", line); + mSdkLog.printf("%s", line); //$NON-NLS-1$ } - /** - * Sets the description in the current task dialog. - */ - public void setResult(String resultFormat, Object...args) { - setDescription(resultFormat, args); + public void log(String format, Object...args) { + setDescription(" " + format, args); //$NON-NLS-1$ + } + + public void logError(String format, Object...args) { + setDescription(format, args); + } + + public void logVerbose(String format, Object...args) { + // The ConsoleTask does not display verbose log messages. } /** @@ -327,12 +338,20 @@ public class UpdateNoWindow { return mRoot.isCancelRequested(); } - public void setDescription(String descriptionFormat, Object... args) { - mRoot.setDescription(descriptionFormat, args); + public void setDescription(String format, Object... args) { + mRoot.setDescription(format, args); + } + + public void log(String format, Object... args) { + mRoot.log(format, args); + } + + public void logError(String format, Object... args) { + mRoot.logError(format, args); } - public void setResult(String resultFormat, Object... args) { - mRoot.setResult(resultFormat, args); + public void logVerbose(String format, Object... args) { + mRoot.logVerbose(format, args); } public void setProgressMax(int max) { 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 b0160d9..1a3b5cb 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;
@@ -437,14 +438,14 @@ class UpdaterData implements IUpdaterData { // This archive depends on a missing archive.
// We shouldn't get here.
// Skip it.
- monitor.setResult("Skipping '%1$s'; it depends on a missing package.",
+ monitor.log("Skipping '%1$s'; it depends on a missing package.",
archive.getParentPackage().getShortDescription());
continue nextArchive;
} else if (!installedArchives.contains(na)) {
// This archive depends on another one that was not installed.
// We shouldn't get here.
// Skip it.
- monitor.setResult("Skipping '%1$s'; it depends on '%2$s' which was not installed.",
+ monitor.logError("Skipping '%1$s'; it depends on '%2$s' which was not installed.",
archive.getParentPackage().getShortDescription(),
adep.getShortDescription());
continue nextArchive;
@@ -499,7 +500,7 @@ class UpdaterData implements IUpdaterData { baos.toString());
}
- monitor.setResult(msg);
+ monitor.log(msg);
mSdkLog.error(t, msg);
} finally {
@@ -514,10 +515,10 @@ class UpdaterData implements IUpdaterData { // Update the USB vendor ids for adb
try {
mSdkManager.updateAdb();
- monitor.setResult("Updated ADB to support the USB devices declared in the SDK add-ons.");
+ monitor.log("Updated ADB to support the USB devices declared in the SDK add-ons.");
} catch (Exception e) {
mSdkLog.error(e, "Update ADB failed");
- monitor.setResult("failed to update adb to support the USB devices declared in the SDK add-ons.");
+ monitor.logError("failed to update adb to support the USB devices declared in the SDK add-ons.");
}
}
@@ -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("-", "");
- }
+ 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
@@ -877,6 +941,7 @@ class UpdaterData implements IUpdaterData { }
SdkSource[] sources = mSources.getAllSources();
+ monitor.setDescription("Refresh Sources");
monitor.setProgressMax(monitor.getProgress() + sources.length);
for (SdkSource source : sources) {
if (forceFetching ||
@@ -919,19 +984,19 @@ class UpdaterData implements IUpdaterData { //
// Since SDK_TEST_URLS can contain many such URLs, we take the first one that
// matches our criteria.
- String url = System.getenv("SDK_TEST_URLS");
+ String url = System.getenv("SDK_TEST_URLS"); //$NON-NLS-1$
if (url == null) {
// No override, use the canonical URL.
url = SdkAddonsListConstants.URL_ADDON_LIST;
} else {
- String[] urls = url.split(";");
+ String[] urls = url.split(";"); //$NON-NLS-1$
url = null;
for (String u : urls) {
u = u.trim();
// This is an URL that comes from the env var. We expect it to either
// end with a / or the canonical name, otherwise we don't use it.
- if (u.endsWith("/")) {
+ if (u.endsWith("/")) { //$NON-NLS-1$
url = u + SdkAddonsListConstants.URL_DEFAULT_FILENAME;
break;
} else if (u.endsWith(SdkAddonsListConstants.URL_DEFAULT_FILENAME)) {
@@ -943,7 +1008,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();
@@ -959,6 +1024,8 @@ class UpdaterData implements IUpdaterData { mStateFetchRemoteAddonsList = 1;
}
}
+
+ monitor.setDescription("Fetched Add-ons List successfully");
}
/**
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..c2472d8 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 @@ -40,9 +40,12 @@ import com.android.sdklib.internal.repository.ToolPackage; import com.android.sdklib.internal.repository.Package.UpdateInfo;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* The logic to compute which packages to install, based on the choices
@@ -120,6 +123,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 +153,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);
+ }
}
}
}
@@ -460,7 +479,7 @@ class UpdaterLogic { // - platform: *might* depends on tools of rev >= min-tools-rev
// - extra: *might* depends on platform with api >= min-api-level
- ArrayList<ArchiveInfo> list = new ArrayList<ArchiveInfo>();
+ Set<ArchiveInfo> aiFound = new HashSet<ArchiveInfo>();
if (pkg instanceof IPlatformDependency) {
ArchiveInfo ai = findPlatformDependency(
@@ -472,7 +491,7 @@ class UpdaterLogic { localArchives);
if (ai != null) {
- list.add(ai);
+ aiFound.add(ai);
}
}
@@ -487,7 +506,7 @@ class UpdaterLogic { localArchives);
if (ai != null) {
- list.add(ai);
+ aiFound.add(ai);
}
}
@@ -502,7 +521,7 @@ class UpdaterLogic { localArchives);
if (ai != null) {
- list.add(ai);
+ aiFound.add(ai);
}
}
@@ -517,7 +536,7 @@ class UpdaterLogic { localArchives);
if (ai != null) {
- list.add(ai);
+ aiFound.add(ai);
}
}
@@ -532,12 +551,14 @@ class UpdaterLogic { localArchives);
if (ai != null) {
- list.add(ai);
+ aiFound.add(ai);
}
}
- if (list.size() > 0) {
- return list.toArray(new ArchiveInfo[list.size()]);
+ if (aiFound.size() > 0) {
+ ArchiveInfo[] result = aiFound.toArray(new ArchiveInfo[aiFound.size()]);
+ Arrays.sort(result);
+ return result;
}
return null;
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java index 19d3916..7514dea 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java @@ -22,6 +22,7 @@ import com.android.sdklib.SdkConstants; import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import com.android.sdkuilib.internal.tasks.ProgressTaskFactory;
import com.android.sdkuilib.repository.ISdkChangeListener;
+import com.android.sdkuilib.repository.IUpdaterWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
@@ -31,7 +32,8 @@ import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.List;
@@ -41,9 +43,14 @@ import java.lang.reflect.Constructor; import java.util.ArrayList;
/**
- * This is the private implementation of the UpdateWindow.
+ * This is the private implementation of the UpdateWindow for the
+ * first version of the SDK Manager.
+ * <p/>
+ * This window has a sash, with a list of available pages on the left
+ * (AVD list, settings, about, installed packages, available packages)
+ * and the corresponding page on the right.
*/
-public class UpdaterWindowImpl {
+public class UpdaterWindowImpl implements IUpdaterWindow {
private final Shell mParentShell;
/** Internal data shared between the window and its pages. */
@@ -65,14 +72,13 @@ public class UpdaterWindowImpl { // --- UI members ---
- protected Shell mAndroidSdkUpdater;
- private SashForm mSashForm;
+ protected Shell mShell;
private List mPageList;
private Composite mPagesRootComposite;
- private LocalPackagesPage mLocalPackagePage;
- private RemotePackagesPage mRemotePackagesPage;
private AvdManagerPage mAvdManagerPage;
private StackLayout mStackLayout;
+ private LocalPackagesPage mLocalPackagePage;
+ private RemotePackagesPage mRemotePackagesPage;
/**
* Creates a new window. Caller must call open(), which will block.
@@ -87,7 +93,7 @@ public class UpdaterWindowImpl { }
/**
- * Open the window.
+ * Opens the window.
* @wbp.parser.entryPoint
*/
public void open() {
@@ -95,13 +101,15 @@ public class UpdaterWindowImpl { Display.setAppName("Android"); //$hide$ (hide from SWT designer)
}
+ createShell();
+ preCreateContent();
createContents();
- mAndroidSdkUpdater.open();
- mAndroidSdkUpdater.layout();
+ mShell.open();
+ mShell.layout();
- if (postCreate()) { //$hide$ (hide from SWT designer)
+ if (postCreateContent()) { //$hide$ (hide from SWT designer)
Display display = Display.getDefault();
- while (!mAndroidSdkUpdater.isDisposed()) {
+ while (!mShell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
@@ -111,27 +119,34 @@ public class UpdaterWindowImpl { dispose(); //$hide$
}
- /**
- * Create contents of the window.
- */
- protected void createContents() {
- mAndroidSdkUpdater = new Shell(mParentShell, SWT.SHELL_TRIM);
- mAndroidSdkUpdater.addDisposeListener(new DisposeListener() {
+ private void createShell() {
+ mShell = new Shell(mParentShell, SWT.SHELL_TRIM);
+ mShell.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
onAndroidSdkUpdaterDispose(); //$hide$ (hide from SWT designer)
}
});
- FillLayout fl;
- mAndroidSdkUpdater.setLayout(fl = new FillLayout(SWT.HORIZONTAL));
- fl.marginHeight = fl.marginWidth = 5;
- mAndroidSdkUpdater.setMinimumSize(new Point(200, 50));
- mAndroidSdkUpdater.setSize(745, 433);
- mAndroidSdkUpdater.setText("Android SDK and AVD Manager");
+ GridLayout glShell = new GridLayout(2, false);
+ glShell.verticalSpacing = 0;
+ glShell.horizontalSpacing = 0;
+ glShell.marginWidth = 0;
+ glShell.marginHeight = 0;
+ mShell.setLayout(glShell);
+
+ mShell.setMinimumSize(new Point(500, 300));
+ mShell.setSize(700, 500);
+ mShell.setText("Android SDK and AVD Manager");
+ }
- mSashForm = new SashForm(mAndroidSdkUpdater, SWT.NONE);
+ /**
+ * Create contents of the window.
+ */
+ private void createContents() {
+ SashForm sashForm = new SashForm(mShell, SWT.NONE);
+ sashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
- mPageList = new List(mSashForm, SWT.BORDER);
+ mPageList = new List(sashForm, SWT.BORDER);
mPageList.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
@@ -139,13 +154,15 @@ public class UpdaterWindowImpl { }
});
- mPagesRootComposite = new Composite(mSashForm, SWT.NONE);
- mStackLayout = new StackLayout();
- mPagesRootComposite.setLayout(mStackLayout);
+ createPagesRoot(sashForm);
- createPages();
+ sashForm.setWeights(new int[] {150, 576});
+ }
- mSashForm.setWeights(new int[] {150, 576});
+ private void createPagesRoot(Composite parent) {
+ mPagesRootComposite = new Composite(parent, SWT.NONE);
+ mStackLayout = new StackLayout();
+ mPagesRootComposite.setLayout(mStackLayout);
}
// -- Start of internal part ----------
@@ -166,7 +183,7 @@ public class UpdaterWindowImpl { * @param title The title of the page.
* @param pageClass The {@link Composite}-derived class that will implement the page.
*/
- public void registerExtraPage(String title, Class<? extends Composite> pageClass) {
+ public void registerPage(String title, Class<? extends Composite> pageClass) {
if (mExtraPages == null) {
mExtraPages = new ArrayList<Object[]>();
}
@@ -208,26 +225,22 @@ public class UpdaterWindowImpl { // --- Internals & UI Callbacks -----------
-
/**
- * Called by {@link #createContents()} to generate the pages that can be
+ * Called by {@link #postCreateContent()} to generate the pages that can be
* displayed in the window.
- * <p/>
- * Implementation detail: This is extracted from {@link #createContents()}
- * so that we can skip it when using WindowsBuilder, since {@link #mUpdaterData}
- * will then be null.
*/
- private void createPages() {
+ protected void createPages() {
mAvdManagerPage = new AvdManagerPage(mPagesRootComposite, mUpdaterData);
+
mLocalPackagePage = new LocalPackagesPage(mPagesRootComposite, mUpdaterData);
mRemotePackagesPage = new RemotePackagesPage(mPagesRootComposite, mUpdaterData);
- }
- /**
- * Helper to return the SWT shell.
- */
- private Shell getShell() {
- return mAndroidSdkUpdater;
+ addPage(mAvdManagerPage, "Virtual devices");
+
+ addPage(mLocalPackagePage, "Installed packages");
+ addPage(mRemotePackagesPage, "Available packages");
+
+ addExtraPages();
}
/**
@@ -254,44 +267,34 @@ public class UpdaterWindowImpl { if (mUpdaterData != null) {
ImageFactory imgFactory = mUpdaterData.getImageFactory();
if (imgFactory != null) {
- mAndroidSdkUpdater.setImage(imgFactory.getImageByName(imageName));
+ mShell.setImage(imgFactory.getImageByName(imageName));
}
}
}
/**
+ * Called before the UI is created.
+ */
+ private void preCreateContent() {
+ mUpdaterData.setWindowShell(mShell);
+ mTaskFactory = new ProgressTaskFactory(mShell);
+ mUpdaterData.setTaskFactory(mTaskFactory);
+ mUpdaterData.setImageFactory(new ImageFactory(mShell.getDisplay()));
+ }
+
+ /**
* Once the UI has been created, initializes the content.
* This creates the pages, selects the first one, setup sources and scan for local folders.
*
* Returns true if we should show the window.
*/
- private boolean postCreate() {
- mUpdaterData.setWindowShell(getShell());
- mTaskFactory = new ProgressTaskFactory(getShell());
- mUpdaterData.setTaskFactory(mTaskFactory);
- mUpdaterData.setImageFactory(new ImageFactory(getShell().getDisplay()));
-
- setWindowImage(mAndroidSdkUpdater);
-
- addPage(mAvdManagerPage, "Virtual devices");
- addPage(mLocalPackagePage, "Installed packages");
- addPage(mRemotePackagesPage, "Available packages");
- addExtraPages();
-
- int pageIndex = 0;
- int i = 0;
- for (Composite p : mPages) {
- if (p.getClass().equals(mInitialPage)) {
- pageIndex = i;
- break;
- }
- i++;
- }
- displayPage(pageIndex);
- mPageList.setSelection(pageIndex);
+ private boolean postCreateContent() {
+ setWindowImage(mShell);
+ createPages();
setupSources();
initializeSettings();
+ selectInitialPage();
if (mUpdaterData.checkIfInitFailed()) {
return false;
@@ -323,10 +326,12 @@ public class UpdaterWindowImpl { * Each page is a {@link Composite}. The title of the page is stored in the
* {@link Composite#getData()} field.
*/
- private void addPage(Composite page, String title) {
+ protected void addPage(Composite page, String title) {
page.setData(title);
mPages.add(page);
- mPageList.add(title);
+ if (mPageList != null) {
+ mPageList.add(title);
+ }
}
/**
@@ -335,7 +340,7 @@ public class UpdaterWindowImpl { * to the page list.
*/
@SuppressWarnings("unchecked")
- private void addExtraPages() {
+ protected void addExtraPages() {
if (mExtraPages == null) {
return;
}
@@ -371,7 +376,7 @@ public class UpdaterWindowImpl { * If this is not an internal page change, displays the given page.
*/
private void onPageListSelected() {
- if (mInternalPageChange == false) {
+ if (mInternalPageChange == false && mPageList != null) {
int index = mPageList.getSelectionIndex();
if (index >= 0) {
displayPage(index);
@@ -390,11 +395,15 @@ public class UpdaterWindowImpl { mStackLayout.topControl = page;
mPagesRootComposite.layout(true);
- if (!mInternalPageChange) {
+ if (!mInternalPageChange && mPageList != null) {
mInternalPageChange = true;
mPageList.setSelection(index);
mInternalPageChange = false;
}
+
+ if (page instanceof IPageListener) {
+ ((IPageListener) page).onPageSelected();
+ }
}
}
@@ -403,7 +412,6 @@ public class UpdaterWindowImpl { */
private void setupSources() {
mUpdaterData.setupDefaultSources();
- mRemotePackagesPage.onSdkReload();
}
/**
@@ -427,6 +435,28 @@ public class UpdaterWindowImpl { }
}
+ /**
+ * Select and show the initial page.
+ * This will be either the page which class matches {@link #mInitialPage} or the
+ * first one in the list.
+ */
+ private void selectInitialPage() {
+ int pageIndex = 0;
+ int i = 0;
+ for (Composite p : mPages) {
+ if (p.getClass().equals(mInitialPage)) {
+ pageIndex = i;
+ break;
+ }
+ i++;
+ }
+
+ displayPage(pageIndex);
+ if (mPageList != null) {
+ mPageList.setSelection(pageIndex);
+ }
+ }
+
// End of hiding from SWT Designer
//$hide<<$
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl2.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl2.java new file mode 100755 index 0000000..6b3cd66 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl2.java @@ -0,0 +1,586 @@ +/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdkuilib.internal.repository;
+
+
+import com.android.menubar.IMenuBarCallback;
+import com.android.menubar.MenuBarEnhancer;
+import com.android.sdklib.ISdkLog;
+import com.android.sdklib.SdkConstants;
+import com.android.sdkuilib.internal.repository.PackagesPage.MenuAction;
+import com.android.sdkuilib.internal.repository.icons.ImageFactory;
+import com.android.sdkuilib.internal.tasks.ProgressView;
+import com.android.sdkuilib.internal.tasks.ProgressViewFactory;
+import com.android.sdkuilib.repository.ISdkChangeListener;
+import com.android.sdkuilib.repository.IUpdaterWindow;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseTrackListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.ProgressBar;
+import org.eclipse.swt.widgets.Shell;
+
+import java.util.ArrayList;
+
+/**
+ * This is the private implementation of the UpdateWindow
+ * for the second version of the SDK Manager.
+ * <p/>
+ * This window features only one embedded page, the combined installed+available package list.
+ */
+public class UpdaterWindowImpl2 implements IUpdaterWindow {
+
+ private static final String APP_NAME = "Android SDK Manager";
+ private final Shell mParentShell;
+ /** Internal data shared between the window and its pages. */
+ private final UpdaterData mUpdaterData;
+ /** A list of extra pages to instantiate. Each entry is an object array with 2 elements:
+ * the string title and the Composite class to instantiate to create the page. */
+ private ArrayList<Object[]> mExtraPages;
+ /** Sets whether the auto-update wizard will be shown when opening the window. */
+ private boolean mRequestAutoUpdate;
+
+ // --- UI members ---
+
+ protected Shell mShell;
+ private PackagesPage mPkgPage;
+ private ProgressBar mProgressBar;
+ private Label mStatusText;
+ private ImgDisabledButton mButtonStop;
+ private ToggleButton mButtonDetails;
+
+ /**
+ * Creates a new window. Caller must call open(), which will block.
+ *
+ * @param parentShell Parent shell.
+ * @param sdkLog Logger. Cannot be null.
+ * @param osSdkRoot The OS path to the SDK root.
+ */
+ public UpdaterWindowImpl2(Shell parentShell, ISdkLog sdkLog, String osSdkRoot) {
+ mParentShell = parentShell;
+ mUpdaterData = new UpdaterData(osSdkRoot, sdkLog);
+ }
+
+ /**
+ * Opens the window.
+ * @wbp.parser.entryPoint
+ */
+ public void open() {
+ if (mParentShell == null) {
+ Display.setAppName(APP_NAME); //$hide$ (hide from SWT designer)
+ }
+
+ createShell();
+ preCreateContent();
+ createContents();
+ createMenuBar();
+ mShell.open();
+ mShell.layout();
+
+ if (postCreateContent()) { //$hide$ (hide from SWT designer)
+ Display display = Display.getDefault();
+ while (!mShell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ }
+
+ dispose(); //$hide$
+ }
+
+ private void createShell() {
+ mShell = new Shell(mParentShell, SWT.SHELL_TRIM);
+ mShell.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ onAndroidSdkUpdaterDispose(); //$hide$ (hide from SWT designer)
+ }
+ });
+
+ GridLayout glShell = new GridLayout(2, false);
+ glShell.verticalSpacing = 0;
+ glShell.horizontalSpacing = 0;
+ glShell.marginWidth = 0;
+ glShell.marginHeight = 0;
+ mShell.setLayout(glShell);
+
+ mShell.setMinimumSize(new Point(500, 300));
+ mShell.setSize(700, 500);
+ mShell.setText(APP_NAME);
+ }
+
+ private void createContents() {
+
+ mPkgPage = new PackagesPage(mShell, mUpdaterData);
+ mPkgPage.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+
+ Composite composite1 = new Composite(mShell, SWT.NONE);
+ composite1.setLayout(new GridLayout(1, false));
+ composite1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+ mProgressBar = new ProgressBar(composite1, SWT.NONE);
+ mProgressBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+ mStatusText = new Label(composite1, SWT.NONE);
+ mStatusText.setText("Status Placeholder"); //$NON-NLS-1$ placeholder
+ mStatusText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+ Composite composite2 = new Composite(mShell, SWT.NONE);
+ composite2.setLayout(new GridLayout(2, false));
+
+ mButtonStop = new ImgDisabledButton(composite2, SWT.NONE,
+ getImage("stop_enabled_16.png"), //$NON-NLS-1$
+ getImage("stop_disabled_16.png")); //$NON-NLS-1$
+ mButtonStop.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ onStopSelected();
+ }
+ });
+
+ mButtonDetails = new ToggleButton(composite2, SWT.NONE,
+ getImage("collapsed_16.png"), //$NON-NLS-1$
+ getImage("expanded_16.png")); //$NON-NLS-1$
+ mButtonDetails.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ onToggleDetails();
+ }
+ });
+ }
+
+ private void createMenuBar() {
+
+ Menu menuBar = new Menu(mShell, SWT.BAR);
+ mShell.setMenuBar(menuBar);
+
+ MenuItem menuBarPackages = new MenuItem(menuBar, SWT.CASCADE);
+ menuBarPackages.setText("Packages");
+
+ Menu menuPkgs = new Menu(menuBarPackages);
+ menuBarPackages.setMenu(menuPkgs);
+
+ MenuItem showUpdatesNew = new MenuItem(menuPkgs,
+ MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG.getMenuStyle());
+ showUpdatesNew.setText(
+ MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG, showUpdatesNew);
+
+ MenuItem showInstalled = new MenuItem(menuPkgs,
+ MenuAction.TOGGLE_SHOW_INSTALLED_PKG.getMenuStyle());
+ showInstalled.setText(
+ MenuAction.TOGGLE_SHOW_INSTALLED_PKG.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.TOGGLE_SHOW_INSTALLED_PKG, showInstalled);
+
+ MenuItem showObsoletePackages = new MenuItem(menuPkgs,
+ MenuAction.TOGGLE_SHOW_OBSOLETE_PKG.getMenuStyle());
+ showObsoletePackages.setText(
+ MenuAction.TOGGLE_SHOW_OBSOLETE_PKG.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.TOGGLE_SHOW_OBSOLETE_PKG, showObsoletePackages);
+
+ MenuItem showArchives = new MenuItem(menuPkgs,
+ MenuAction.TOGGLE_SHOW_ARCHIVES.getMenuStyle());
+ showArchives.setText(
+ MenuAction.TOGGLE_SHOW_ARCHIVES.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.TOGGLE_SHOW_ARCHIVES, showArchives);
+
+ new MenuItem(menuPkgs, SWT.SEPARATOR);
+
+ MenuItem sortByApi = new MenuItem(menuPkgs,
+ MenuAction.SORT_API_LEVEL.getMenuStyle());
+ sortByApi.setText(
+ MenuAction.SORT_API_LEVEL.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.SORT_API_LEVEL, sortByApi);
+
+ MenuItem sortBySource = new MenuItem(menuPkgs,
+ MenuAction.SORT_SOURCE.getMenuStyle());
+ sortBySource.setText(
+ MenuAction.SORT_SOURCE.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.SORT_SOURCE, sortBySource);
+
+ new MenuItem(menuPkgs, SWT.SEPARATOR);
+
+ MenuItem reload = new MenuItem(menuPkgs,
+ MenuAction.RELOAD.getMenuStyle());
+ reload.setText(
+ MenuAction.RELOAD.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.RELOAD, reload);
+
+ MenuItem menuBarTools = new MenuItem(menuBar, SWT.CASCADE);
+ menuBarTools.setText("Tools");
+
+ Menu menuTools = new Menu(menuBarTools);
+ menuBarTools.setMenu(menuTools);
+
+ MenuItem manageAvds = new MenuItem(menuTools, SWT.NONE);
+ manageAvds.setText("Manage AVDs...");
+
+ MenuItem manageSources = new MenuItem(menuTools,
+ MenuAction.SHOW_ADDON_SITES.getMenuStyle());
+ manageSources.setText(
+ MenuAction.SHOW_ADDON_SITES.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.SHOW_ADDON_SITES, manageSources);
+
+ MenuBarEnhancer.setupMenu(APP_NAME, menuTools, new IMenuBarCallback() {
+ public void onPreferencesMenuSelected() {
+ // TODO: plug settings page here
+ MessageDialog.openInformation(mShell, "test", "on prefs");
+ }
+
+ public void onAboutMenuSelected() {
+ // TODO: plug about page here
+ MessageDialog.openInformation(mShell, "test", "on about");
+ }
+
+ public void printError(String format, Object... args) {
+ if (mUpdaterData != null) {
+ // TODO: right now dump to stderr. Use sdklog later.
+ //mUpdaterData.getSdkLog().error(null, format, args);
+ System.err.printf(format, args);
+ }
+ }
+ });
+ }
+
+ private Image getImage(String filename) {
+ if (mUpdaterData != null) {
+ ImageFactory imgFactory = mUpdaterData.getImageFactory();
+ if (imgFactory != null) {
+ return imgFactory.getImageByName(filename);
+ }
+ }
+ return null;
+ }
+
+
+ // -- Start of internal part ----------
+ // Hide everything down-below from SWT designer
+ //$hide>>$
+
+ // --- Public API -----------
+
+
+ /**
+ * Registers an extra page for the updater window.
+ * <p/>
+ * Pages must derive from {@link Composite} and implement a constructor that takes
+ * a single parent {@link Composite} argument.
+ * <p/>
+ * All pages must be registered before the call to {@link #open()}.
+ *
+ * @param title The title of the page.
+ * @param pageClass The {@link Composite}-derived class that will implement the page.
+ */
+ public void registerPage(String title, Class<? extends Composite> pageClass) {
+ if (mExtraPages == null) {
+ mExtraPages = new ArrayList<Object[]>();
+ }
+ mExtraPages.add(new Object[]{ title, pageClass });
+ }
+
+ /**
+ * Indicate the initial page that should be selected when the window opens.
+ * This must be called before the call to {@link #open()}.
+ * If null or if the page class is not found, the first page will be selected.
+ */
+ public void setInitialPage(Class<? extends Composite> pageClass) {
+ // Unused in this case. This window display only one page.
+ }
+
+ /**
+ * Sets whether the auto-update wizard will be shown when opening the window.
+ * <p/>
+ * This must be called before the call to {@link #open()}.
+ */
+ public void setRequestAutoUpdate(boolean requestAutoUpdate) {
+ mRequestAutoUpdate = requestAutoUpdate;
+ }
+
+ /**
+ * Adds a new listener to be notified when a change is made to the content of the SDK.
+ */
+ public void addListener(ISdkChangeListener listener) {
+ mUpdaterData.addListeners(listener);
+ }
+
+ /**
+ * Removes a new listener to be notified anymore when a change is made to the content of
+ * the SDK.
+ */
+ public void removeListener(ISdkChangeListener listener) {
+ mUpdaterData.removeListener(listener);
+ }
+
+ // --- Internals & UI Callbacks -----------
+
+ /**
+ * Called before the UI is created.
+ */
+ private void preCreateContent() {
+ mUpdaterData.setWindowShell(mShell);
+ // We need the UI factory to create the UI
+ mUpdaterData.setImageFactory(new ImageFactory(mShell.getDisplay()));
+ // Note: we can't create the TaskFactory yet because we need the UI
+ // to be created first, so this is done in postCreateContent().
+ }
+
+ /**
+ * Once the UI has been created, initializes the content.
+ * This creates the pages, selects the first one, setup sources and scan for local folders.
+ *
+ * Returns true if we should show the window.
+ */
+ private boolean postCreateContent() {
+ ProgressViewFactory factory = new ProgressViewFactory();
+ factory.setProgressView(new ProgressView(
+ mStatusText, mProgressBar, mButtonStop));
+ mUpdaterData.setTaskFactory(factory);
+
+ setWindowImage(mShell);
+
+ setupSources();
+ initializeSettings();
+
+ if (mUpdaterData.checkIfInitFailed()) {
+ return false;
+ }
+
+ mUpdaterData.broadcastOnSdkLoaded();
+
+ if (mRequestAutoUpdate) {
+ mUpdaterData.updateOrInstallAll_WithGUI(
+ null /*selectedArchives*/,
+ false /* includeObsoletes */);
+ }
+
+ // Tell the one page its the selected one
+ mPkgPage.onPageSelected();
+
+ return true;
+ }
+
+ /**
+ * Creates the icon of the window shell.
+ */
+ private void setWindowImage(Shell androidSdkUpdater) {
+ String imageName = "android_icon_16.png"; //$NON-NLS-1$
+ if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_DARWIN) {
+ imageName = "android_icon_128.png"; //$NON-NLS-1$
+ }
+
+ if (mUpdaterData != null) {
+ ImageFactory imgFactory = mUpdaterData.getImageFactory();
+ if (imgFactory != null) {
+ mShell.setImage(imgFactory.getImageByName(imageName));
+ }
+ }
+ }
+
+ /**
+ * Called by the main loop when the window has been disposed.
+ */
+ private void dispose() {
+ mUpdaterData.getSources().saveUserAddons(mUpdaterData.getSdkLog());
+ }
+
+ /**
+ * Callback called when the window shell is disposed.
+ */
+ private void onAndroidSdkUpdaterDispose() {
+ if (mUpdaterData != null) {
+ ImageFactory imgFactory = mUpdaterData.getImageFactory();
+ if (imgFactory != null) {
+ imgFactory.dispose();
+ }
+ }
+ }
+
+ /**
+ * Used to initialize the sources.
+ */
+ private void setupSources() {
+ mUpdaterData.setupDefaultSources();
+ }
+
+ /**
+ * Initializes settings.
+ * This must be called after addExtraPages(), which created a settings page.
+ * Iterate through all the pages to find the first (and supposedly unique) setting page,
+ * and use it to load and apply these settings.
+ */
+ private void initializeSettings() {
+ SettingsController c = mUpdaterData.getSettingsController();
+ c.loadSettings();
+ c.applySettings();
+
+ // TODO give access to a settings dialog somehow (+about dialog)
+ // TODO c.setSettingsPage(settingsPage);
+ }
+
+ private void onToggleDetails() {
+ mButtonDetails.setState(1 - mButtonDetails.getState());
+ }
+
+ private void onStopSelected() {
+ // TODO
+ }
+
+ // End of hiding from SWT Designer
+ //$hide<<$
+
+ // -----
+
+ /**
+ * A label that can display 2 images depending on its internal state.
+ * This acts as a button by firing the {@link SWT#Selection} listener.
+ */
+ private static class ToggleButton extends CLabel {
+ private Image[] mImage = new Image[2];
+ private boolean mMouseIn;
+ private int mState = 0;
+
+
+ public ToggleButton(Composite parent, int style, Image image1, Image image2) {
+ super(parent, style);
+ mImage[0] = image1;
+ mImage[1] = image2;
+ updateImage();
+
+ addMouseListener(new MouseListener() {
+ public void mouseDown(MouseEvent e) {
+ // pass
+ }
+
+ public void mouseUp(MouseEvent e) {
+ // We select on mouse-up, as it should be properly done since this is the
+ // only way a user can cancel a button click by moving out of the button.
+ if (mMouseIn && e.button == 1) {
+ notifyListeners(SWT.Selection, new Event());
+ }
+ }
+
+ public void mouseDoubleClick(MouseEvent e) {
+ if (mMouseIn && e.button == 1) {
+ notifyListeners(SWT.DefaultSelection, new Event());
+ }
+ }
+ });
+
+ addMouseTrackListener(new MouseTrackListener() {
+ public void mouseExit(MouseEvent e) {
+ if (mMouseIn) {
+ mMouseIn = false;
+ redraw();
+ }
+ }
+
+ public void mouseEnter(MouseEvent e) {
+ if (!mMouseIn) {
+ mMouseIn = true;
+ redraw();
+ }
+ }
+
+ public void mouseHover(MouseEvent e) {
+ // pass
+ }
+ });
+ }
+
+ @Override
+ public int getStyle() {
+ int style = super.getStyle();
+ if (mMouseIn) {
+ style |= SWT.SHADOW_IN;
+ }
+ return style;
+ }
+
+ /**
+ * Sets current state.
+ * @param state A value 0 or 1.
+ */
+ public void setState(int state) {
+ assert state == 0 || state == 1;
+ mState = state;
+ updateImage();
+ redraw();
+ }
+
+ /**
+ * Returns the current state
+ * @return Returns the current state, either 0 or 1.
+ */
+ public int getState() {
+ return mState;
+ }
+
+ protected void updateImage() {
+ setImage(mImage[getState()]);
+ }
+ }
+
+ /**
+ * A label that can display 2 images depending on its enabled/disabled state.
+ * This acts as a button by firing the {@link SWT#Selection} listener.
+ */
+ private static class ImgDisabledButton extends ToggleButton {
+ public ImgDisabledButton(Composite parent, int style,
+ Image imageEnabled, Image imageDisabled) {
+ super(parent, style, imageEnabled, imageDisabled);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ updateImage();
+ redraw();
+ }
+
+ @Override
+ public void setState(int state) {
+ throw new UnsupportedOperationException(); // not available for this type of button
+ }
+
+ @Override
+ public int getState() {
+ return (isDisposed() || !isEnabled()) ? 1 : 0;
+ }
+ }
+}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java index 4a38f75..877ba37 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java @@ -94,6 +94,10 @@ public class ImageFactory { return null;
}
+ if (object instanceof Image) {
+ return (Image) object;
+ }
+
String clz = object.getClass().getSimpleName();
if (clz.endsWith(Package.class.getSimpleName())) {
String name = clz.replaceFirst(Package.class.getSimpleName(), "").toLowerCase() + //$NON-NLS-1$
@@ -122,6 +126,10 @@ public class ImageFactory { }
}
+ if (object instanceof String) {
+ return getImageByName((String) object);
+ }
+
return null;
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/android_icon_128.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/android_icon_128.png Binary files differindex acc124d..830c04b 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/android_icon_128.png +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/android_icon_128.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/collapsed_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/collapsed_16.png Binary files differnew file mode 100755 index 0000000..5f20b86 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/collapsed_16.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/expanded_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/expanded_16.png Binary files differnew file mode 100755 index 0000000..6e13998 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/expanded_16.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon_16.png Binary files differindex 147837f..147837f 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon16.png +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon_16.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_installed_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_installed_16.png Binary files differnew file mode 100755 index 0000000..78b7e5a --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_installed_16.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_new_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_new_16.png Binary files differnew file mode 100755 index 0000000..0976ad4 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_new_16.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_update_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_update_16.png Binary files differnew file mode 100755 index 0000000..e766251 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_update_16.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_16.png Binary files differnew file mode 100755 index 0000000..cd9b807 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_16.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_other_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_other_16.png Binary files differnew file mode 100755 index 0000000..395a240 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_other_16.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/status_ok_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/status_ok_16.png Binary files differnew file mode 100755 index 0000000..eeb0a6f --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/status_ok_16.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_disabled_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_disabled_16.png Binary files differnew file mode 100755 index 0000000..ae6da31 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_disabled_16.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_enabled_16.png b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_enabled_16.png Binary files differnew file mode 100755 index 0000000..7ce1864 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_enabled_16.png diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/IProgressUiProvider.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/IProgressUiProvider.java new file mode 100755 index 0000000..4a7922d --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/IProgressUiProvider.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdkuilib.internal.tasks; + +import com.android.sdklib.internal.repository.ITaskMonitor; + +import org.eclipse.swt.widgets.ProgressBar; + +/** + * Interface for a user interface that displays both a task status + * (e.g. via an {@link ITaskMonitor}) and the progress state of the + * task (e.g. via a progress bar.) + * <p/> + * See {@link ITaskMonitor} for details on how a monitor expects to + * be displayed. + */ +interface IProgressUiProvider { + + public abstract boolean isCancelRequested(); + + /** + * Sets the description in the current task dialog. + * This method can be invoked from a non-UI thread. + */ + public abstract void setDescription(String description); + + /** + * Logs a "normal" information line. + * This method can be invoked from a non-UI thread. + */ + public abstract void log(String log); + + /** + * Logs an "error" information line. + * This method can be invoked from a non-UI thread. + */ + public abstract void logError(String log); + + /** + * Logs a "verbose" information line, that is extra details which are typically + * not that useful for the end-user and might be hidden until explicitly shown. + * This method can be invoked from a non-UI thread. + */ + public abstract void logVerbose(String log); + + /** + * Sets the max value of the progress bar. + * This method can be invoked from a non-UI thread. + * + * @see ProgressBar#setMaximum(int) + */ + public abstract void setProgressMax(int max); + + /** + * Sets the current value of the progress bar. + * This method can be invoked from a non-UI thread. + */ + public abstract void setProgress(int value); + + /** + * Returns the current value of the progress bar, + * between 0 and up to {@link #setProgressMax(int)} - 1. + * This method can be invoked from a non-UI thread. + */ + public abstract int getProgress(); + + /** + * Display a yes/no question dialog box. + * + * This implementation allow this to be called from any thread, it + * makes sure the dialog is opened synchronously in the ui thread. + * + * @param title The title of the dialog box + * @param message The error message + * @return true if YES was clicked. + */ + public abstract boolean displayPrompt(String title, String message); + +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java index db2b781..42d5558 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java @@ -19,23 +19,16 @@ package com.android.sdkuilib.internal.tasks; import com.android.sdklib.internal.repository.ITask;
import com.android.sdklib.internal.repository.ITaskMonitor;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
/**
- * An {@link ITaskMonitor} that displays a {@link ProgressDialog}.
+ * An {@link ITaskMonitor} that displays a {@link ProgressTaskDialog}.
*/
-public final class ProgressTask implements ITaskMonitor {
+public final class ProgressTask extends TaskMonitorImpl {
- private static final double MAX_COUNT = 10000.0;
-
- private final ProgressDialog mDialog;
+ private final ProgressTaskDialog mDialog;
private boolean mAutomaticallyCloseOnTaskCompletion = true;
- private double mIncCoef = 0;
- private double mValue = 0;
/**
@@ -45,86 +38,16 @@ public final class ProgressTask implements ITaskMonitor { * This blocks till the thread ends.
*/
public ProgressTask(Shell parent, String title, ITask task) {
- mDialog = new ProgressDialog(parent, createTaskThread(title, task));
+ super(new ProgressTaskDialog(parent));
+ mDialog = (ProgressTaskDialog) getUiProvider();
mDialog.setText(title);
- mDialog.open();
- }
-
- /**
- * Sets the description in the current task dialog.
- * This method can be invoked from a non-UI thread.
- */
- public void setDescription(String descriptionFormat, Object...args) {
- mDialog.setDescription(descriptionFormat, args);
- }
-
- /**
- * Sets the description in the current task dialog.
- * This method can be invoked from a non-UI thread.
- */
- public void setResult(String resultFormat, Object...args) {
- mAutomaticallyCloseOnTaskCompletion = false;
- mDialog.setResult(resultFormat, args);
- }
-
- /**
- * Sets the max value of the progress bar.
- * This method can be invoked from a non-UI thread.
- *
- * Weird things will happen if setProgressMax is called multiple times
- * *after* {@link #incProgress(int)}: we don't try to adjust it on the
- * fly.
- *
- * @see ProgressBar#setMaximum(int)
- */
- public void setProgressMax(int max) {
- assert max > 0;
- // Always set the dialog's progress max to 10k since it only handles
- // integers and we want to have a better inner granularity. Instead
- // we use the max to compute a coefficient for inc deltas.
- mDialog.setProgressMax((int) MAX_COUNT);
- mIncCoef = max > 0 ? MAX_COUNT / max : 0;
- assert mIncCoef > 0;
- }
-
- /**
- * Increments the current value of the progress bar.
- *
- * This method can be invoked from a non-UI thread.
- */
- public void incProgress(int delta) {
- if (delta > 0 && mIncCoef > 0) {
- internalIncProgress(delta * mIncCoef);
- }
- }
-
- private void internalIncProgress(double realDelta) {
- mValue += realDelta;
- mDialog.setProgress((int)mValue);
- }
-
- /**
- * Returns the current value of the progress bar,
- * between 0 and up to {@link #setProgressMax(int)} - 1.
- *
- * This method can be invoked from a non-UI thread.
- */
- public int getProgress() {
- assert mIncCoef > 0;
- return mIncCoef > 0 ? (int)(mDialog.getProgress() / mIncCoef) : 0;
- }
-
- /**
- * Returns true if the "Cancel" button was selected.
- * It is up to the task thread to pool this and exit.
- */
- public boolean isCancelRequested() {
- return mDialog.isCancelRequested();
+ mDialog.open(createTaskThread(title, task));
}
/**
* Creates a thread to run the task. The thread has not been started yet.
* When the task completes, requests to close the dialog.
+ *
* @return A new thread that will run the task. The thread has not been started yet.
*/
private Thread createTaskThread(String title, final ITask task) {
@@ -145,122 +68,12 @@ public final class ProgressTask implements ITaskMonitor { }
/**
- * Display a yes/no question dialog box.
- *
- * This implementation allow this to be called from any thread, it
- * makes sure the dialog is opened synchronously in the ui thread.
- *
- * @param title The title of the dialog box
- * @param message The error message
- * @return true if YES was clicked.
+ * Sets the dialog to not auto-close since we want the user to see the error.
+ * {@inheritDoc}
*/
- public boolean displayPrompt(final String title, final String message) {
- final Shell shell = mDialog.getParent();
- Display display = shell.getDisplay();
-
- // we need to ask the user what he wants to do.
- final boolean[] result = new boolean[] { false };
- display.syncExec(new Runnable() {
- public void run() {
- result[0] = MessageDialog.openQuestion(shell, title, message);
- }
- });
- return result[0];
- }
-
- /**
- * Creates a sub-monitor that will use up to tickCount on the progress bar.
- * tickCount must be 1 or more.
- */
- public ITaskMonitor createSubMonitor(int tickCount) {
- assert mIncCoef > 0;
- assert tickCount > 0;
- return new SubTaskMonitor(this, null, mValue, tickCount * mIncCoef);
- }
-
- private interface ISubTaskMonitor extends ITaskMonitor {
- public void subIncProgress(double realDelta);
- }
-
- private static class SubTaskMonitor implements ISubTaskMonitor {
-
- private final ProgressTask mRoot;
- private final ISubTaskMonitor mParent;
- private final double mStart;
- private final double mSpan;
- private double mSubValue;
- private double mSubCoef;
-
- /**
- * Creates a new sub task monitor which will work for the given range [start, start+span]
- * in its parent.
- *
- * @param root The ProgressTask root
- * @param parent The immediate parent. Can be the null or another sub task monitor.
- * @param start The start value in the root's coordinates
- * @param span The span value in the root's coordinates
- */
- public SubTaskMonitor(ProgressTask root,
- ISubTaskMonitor parent,
- double start,
- double span) {
- mRoot = root;
- mParent = parent;
- mStart = start;
- mSpan = span;
- mSubValue = start;
- }
-
- public boolean isCancelRequested() {
- return mRoot.isCancelRequested();
- }
-
- public void setDescription(String descriptionFormat, Object... args) {
- mRoot.setDescription(descriptionFormat, args);
- }
-
- public void setResult(String resultFormat, Object... args) {
- mRoot.setResult(resultFormat, args);
- }
-
- public void setProgressMax(int max) {
- assert max > 0;
- mSubCoef = max > 0 ? mSpan / max : 0;
- assert mSubCoef > 0;
- }
-
- public int getProgress() {
- assert mSubCoef > 0;
- return mSubCoef > 0 ? (int)((mSubValue - mStart) / mSubCoef) : 0;
- }
-
- public void incProgress(int delta) {
- if (delta > 0 && mSubCoef > 0) {
- subIncProgress(delta * mSubCoef);
- }
- }
-
- public void subIncProgress(double realDelta) {
- mSubValue += realDelta;
- if (mParent != null) {
- mParent.subIncProgress(realDelta);
- } else {
- mRoot.internalIncProgress(realDelta);
- }
- }
-
- public boolean displayPrompt(String title, String message) {
- return mRoot.displayPrompt(title, message);
- }
-
- public ITaskMonitor createSubMonitor(int tickCount) {
- assert mSubCoef > 0;
- assert tickCount > 0;
- return new SubTaskMonitor(mRoot,
- this,
- mSubValue,
- tickCount * mSubCoef);
- }
+ @Override
+ public void logError(String format, Object...args) {
+ mAutomaticallyCloseOnTaskCompletion = false;
+ super.logError(format, args);
}
-
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskDialog.java index ff79c68..a4236fa 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskDialog.java @@ -19,9 +19,12 @@ package com.android.sdkuilib.internal.tasks; import com.android.sdklib.SdkConstants;
import com.android.sdklib.internal.repository.ITaskMonitor;
+import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
@@ -34,18 +37,16 @@ import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
-import org.eclipse.swt.events.ShellAdapter;
-import org.eclipse.swt.events.ShellEvent;
/**
- * Implements a {@link ProgressDialog}, used by the {@link ProgressTask} class.
+ * Implements a {@link ProgressTaskDialog}, used by the {@link ProgressTask} class.
* This separates the dialog UI from the task logic.
*
* Note: this does not implement the {@link ITaskMonitor} interface to avoid confusing
* SWT Designer.
*/
-final class ProgressDialog extends Dialog {
+final class ProgressTaskDialog extends Dialog implements IProgressUiProvider {
/**
* Min Y location for dialog. Need to deal with the menu bar on mac os.
@@ -79,29 +80,27 @@ final class ProgressDialog extends Dialog { private ProgressBar mProgressBar;
private Button mCancelButton;
private Text mResultText;
- private final Thread mTaskThread;
/**
* Create the dialog.
* @param parent Parent container
- * @param taskThread The thread to run the task.
*/
- public ProgressDialog(Shell parent, Thread taskThread) {
+ public ProgressTaskDialog(Shell parent) {
super(parent, SWT.APPLICATION_MODAL);
- mTaskThread = taskThread;
}
/**
* Open the dialog and blocks till it gets closed
+ * @param taskThread The thread to run the task. Cannot be null.
*/
- public void open() {
+ public void open(Thread taskThread) {
createContents();
- positionShell(); //$hide$ (hide from SWT designer)
+ positionShell(); //$hide$ (hide from SWT designer)
mDialogShell.open();
mDialogShell.layout();
- startThread(); //$hide$ (hide from SWT designer)
+ startThread(taskThread); //$hide$ (hide from SWT designer)
Display display = getParent().getDisplay();
while (!mDialogShell.isDisposed() && mCancelMode != CancelMode.CLOSE_AUTO) {
@@ -273,41 +272,48 @@ final class ProgressDialog extends Dialog { * Sets the description in the current task dialog.
* This method can be invoked from a non-UI thread.
*/
- public void setDescription(final String descriptionFormat, final Object...args) {
+ public void setDescription(final String description) {
mDialogShell.getDisplay().syncExec(new Runnable() {
public void run() {
if (!mLabel.isDisposed()) {
- mLabel.setText(String.format(descriptionFormat, args));
+ mLabel.setText(description);
}
}
});
}
/**
- * Sets the description in the current task dialog.
+ * Adds to the log in the current task dialog.
* This method can be invoked from a non-UI thread.
*/
- public void setResult(final String resultFormat, final Object...args) {
+ public void log(final String info) {
if (!mDialogShell.isDisposed()) {
mDialogShell.getDisplay().syncExec(new Runnable() {
public void run() {
if (!mResultText.isDisposed()) {
mResultText.setVisible(true);
- String newText = String.format(resultFormat, args);
String lastText = mResultText.getText();
if (lastText != null &&
lastText.length() > 0 &&
- !lastText.endsWith("\n") &&
- !newText.startsWith("\n")) {
- mResultText.append("\n");
+ !lastText.endsWith("\n") && //$NON-NLS-1$
+ !info.startsWith("\n")) { //$NON-NLS-1$
+ mResultText.append("\n"); //$NON-NLS-1$
}
- mResultText.append(newText);
+ mResultText.append(info);
}
}
});
}
}
+ public void logError(String info) {
+ log(info);
+ }
+
+ public void logVerbose(String info) {
+ log(info);
+ }
+
/**
* Sets the max value of the progress bar.
* This method can be invoked from a non-UI thread.
@@ -364,12 +370,35 @@ final class ProgressDialog extends Dialog { }
/**
+ * Display a yes/no question dialog box.
+ *
+ * This implementation allow this to be called from any thread, it
+ * makes sure the dialog is opened synchronously in the ui thread.
+ *
+ * @param title The title of the dialog box
+ * @param message The error message
+ * @return true if YES was clicked.
+ */
+ public boolean displayPrompt(final String title, final String message) {
+ Display display = mDialogShell.getDisplay();
+
+ // we need to ask the user what he wants to do.
+ final boolean[] result = new boolean[] { false };
+ display.syncExec(new Runnable() {
+ public void run() {
+ result[0] = MessageDialog.openQuestion(mDialogShell, title, message);
+ }
+ });
+ return result[0];
+ }
+
+ /**
* Starts the thread that runs the task.
* This is deferred till the UI is created.
*/
- private void startThread() {
- if (mTaskThread != null) {
- mTaskThread.start();
+ private void startThread(Thread taskThread) {
+ if (taskThread != null) {
+ taskThread.start();
}
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java new file mode 100755 index 0000000..9878c85 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java @@ -0,0 +1,258 @@ +/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdkuilib.internal.tasks;
+
+import com.android.sdklib.internal.repository.ITask;
+import com.android.sdklib.internal.repository.ITaskMonitor;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.ProgressBar;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+
+
+/**
+ * Implements a "view" that uses an existing progress bar, status button and
+ * status text to display a {@link ITaskMonitor}.
+ */
+public final class ProgressView implements IProgressUiProvider {
+
+ private static enum State {
+ /** View created but there's no task running. Next state can only be ACTIVE. */
+ IDLE,
+ /** A task is currently running. Next state is either STOP_PENDING or IDLE. */
+ ACTIVE,
+ /** Stop button has been clicked. Waiting for thread to finish. Next state is IDLE. */
+ STOP_PENDING,
+ }
+
+ /** The current mode of operation of the dialog. */
+ private State mState = State.IDLE;
+
+ // UI fields
+ private final Label mLabel;
+ private final Control mStopButton;
+ private final ProgressBar mProgressBar;
+
+ /**
+ * Accumulated log text. This is intended to be displayed in a scrollable
+ * text area. The various methods that append to the log might not be called
+ * from the UI thread, so accesses should be synchronized on the builder.
+ */
+ private final StringBuilder mLogText = new StringBuilder();
+
+
+ /**
+ * Creates a new {@link ProgressView} object, a simple "holder" for the various
+ * widgets used to display and update a progress + status bar.
+ */
+ public ProgressView(Label label, ProgressBar progressBar, Control stopButton) {
+ mLabel = label;
+ mProgressBar = progressBar;
+ mProgressBar.setEnabled(false);
+
+ mStopButton = stopButton;
+ mStopButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ if (mState == State.ACTIVE) {
+ changeState(State.STOP_PENDING);
+ }
+ }
+ });
+ }
+
+ /**
+ * Starts the task and block till it's either finished or cancelled.
+ */
+ public void startTask(final String title, final ITask task) {
+ if (task != null) {
+ try {
+ mLabel.setText(title);
+ mProgressBar.setSelection(0);
+ mProgressBar.setEnabled(true);
+ changeState(ProgressView.State.ACTIVE);
+
+ Runnable r = new Runnable() {
+ public void run() {
+ task.run(new TaskMonitorImpl(ProgressView.this));
+ }
+ };
+
+ Thread t = new Thread(r, title);
+ t.start();
+
+ // Process the app's event loop whilst we wait for the thread to finish
+ Display display = mProgressBar.getDisplay();
+ while (!mProgressBar.isDisposed() && t.isAlive()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+
+
+ } catch (Exception e) {
+ // TODO log
+
+ } finally {
+ changeState(ProgressView.State.IDLE);
+ mProgressBar.setSelection(0);
+ mProgressBar.setEnabled(false);
+ }
+ }
+ }
+
+ private void syncExec(final Widget widget, final Runnable runnable) {
+ if (widget != null && !widget.isDisposed()) {
+ widget.getDisplay().syncExec(runnable);
+ }
+ }
+
+ private void changeState(State state) {
+ if (mState != null ) {
+ mState = state;
+ }
+
+ syncExec(mStopButton, new Runnable() {
+ public void run() {
+ mStopButton.setEnabled(mState == State.ACTIVE);
+ }
+ });
+
+ }
+
+ // --- Implementation of ITaskUiProvider ---
+
+ public boolean isCancelRequested() {
+ return mState != State.ACTIVE;
+ }
+
+ /**
+ * Sets the description in the current task dialog.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void setDescription(final String description) {
+ syncExec(mLabel, new Runnable() {
+ public void run() {
+ mLabel.setText(description);
+ }
+ });
+ synchronized (mLogText) {
+ mLogText.append("** ").append(description);
+ }
+ }
+
+ /**
+ * Logs a "normal" information line.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void log(String log) {
+ synchronized (mLogText) {
+ mLogText.append("=> ").append(log);
+ }
+ }
+
+ /**
+ * Logs an "error" information line.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void logError(String log) {
+ synchronized (mLogText) {
+ mLogText.append("=> ").append(log);
+ }
+ }
+
+ /**
+ * Logs a "verbose" information line, that is extra details which are typically
+ * not that useful for the end-user and might be hidden until explicitly shown.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void logVerbose(String log) {
+ synchronized (mLogText) {
+ mLogText.append("=> ").append(log);
+ }
+ }
+
+ /**
+ * Sets the max value of the progress bar.
+ * This method can be invoked from a non-UI thread.
+ *
+ * @see ProgressBar#setMaximum(int)
+ */
+ public void setProgressMax(final int max) {
+ syncExec(mProgressBar, new Runnable() {
+ public void run() {
+ mProgressBar.setMaximum(max);
+ }
+ });
+ }
+
+ /**
+ * Sets the current value of the progress bar.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void setProgress(final int value) {
+ syncExec(mProgressBar, new Runnable() {
+ public void run() {
+ mProgressBar.setSelection(value);
+ }
+ });
+ }
+
+ /**
+ * Returns the current value of the progress bar,
+ * between 0 and up to {@link #setProgressMax(int)} - 1.
+ * This method can be invoked from a non-UI thread.
+ */
+ public int getProgress() {
+ final int[] result = new int[] { 0 };
+
+ if (!mProgressBar.isDisposed()) {
+ mProgressBar.getDisplay().syncExec(new Runnable() {
+ public void run() {
+ if (!mProgressBar.isDisposed()) {
+ result[0] = mProgressBar.getSelection();
+ }
+ }
+ });
+ }
+
+ return result[0];
+ }
+
+ public boolean displayPrompt(final String title, final String message) {
+ final boolean[] result = new boolean[] { false };
+
+ if (!mProgressBar.isDisposed()) {
+ final Shell shell = mProgressBar.getShell();
+ Display display = shell.getDisplay();
+
+ display.syncExec(new Runnable() {
+ public void run() {
+ result[0] = MessageDialog.openQuestion(shell, title, message);
+ }
+ });
+ }
+
+ return result[0];
+ }
+}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressViewFactory.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressViewFactory.java new file mode 100755 index 0000000..448b478 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressViewFactory.java @@ -0,0 +1,41 @@ +/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdkuilib.internal.tasks;
+
+import com.android.sdklib.internal.repository.ITask;
+import com.android.sdklib.internal.repository.ITaskFactory;
+
+/**
+ * An {@link ITaskFactory} that creates a new {@link ProgressTask} dialog
+ * for each new task.
+ */
+public final class ProgressViewFactory implements ITaskFactory {
+
+ private ProgressView mProgressView;
+
+ public ProgressViewFactory() {
+ }
+
+ public void setProgressView(ProgressView progressView) {
+ mProgressView = progressView;
+ }
+
+ public void start(String title, ITask task) {
+ assert mProgressView != null;
+ mProgressView.startTask(title, task);
+ }
+}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/TaskMonitorImpl.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/TaskMonitorImpl.java new file mode 100755 index 0000000..c95db47 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/TaskMonitorImpl.java @@ -0,0 +1,259 @@ +/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdkuilib.internal.tasks;
+
+import com.android.sdklib.internal.repository.ITaskMonitor;
+
+import org.eclipse.swt.widgets.ProgressBar;
+
+/**
+ * Internal class that implements the logic of an {@link ITaskMonitor}.
+ * It doesn't deal with any UI directly. Instead it delegates the UI to
+ * the provided {@link IProgressUiProvider}.
+ */
+class TaskMonitorImpl implements ITaskMonitor {
+
+ private static final double MAX_COUNT = 10000.0;
+
+ private interface ISubTaskMonitor extends ITaskMonitor {
+ public void subIncProgress(double realDelta);
+ }
+
+ private double mIncCoef = 0;
+ private double mValue = 0;
+ private final IProgressUiProvider mUi;
+
+ /**
+ * Constructs a new {@link TaskMonitorImpl} that relies on the given
+ * {@link IProgressUiProvider} to change the user interface.
+ * @param ui The {@link IProgressUiProvider}. Cannot be null.
+ */
+ public TaskMonitorImpl(IProgressUiProvider ui) {
+ mUi = ui;
+ }
+
+ /** Returns the {@link IProgressUiProvider} passed to the constructor. */
+ public IProgressUiProvider getUiProvider() {
+ return mUi;
+ }
+
+ /**
+ * Sets the description in the current task dialog.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void setDescription(String format, Object... args) {
+ final String text = String.format(format, args);
+ mUi.setDescription(text);
+ }
+
+ /**
+ * Logs a "normal" information line.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void log(String format, Object... args) {
+ String text = String.format(format, args);
+ mUi.log(text);
+ }
+
+ /**
+ * Logs an "error" information line.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void logError(String format, Object... args) {
+ String text = String.format(format, args);
+ mUi.logError(text);
+ }
+
+ /**
+ * Logs a "verbose" information line, that is extra details which are typically
+ * not that useful for the end-user and might be hidden until explicitly shown.
+ * This method can be invoked from a non-UI thread.
+ */
+ public void logVerbose(String format, Object... args) {
+ String text = String.format(format, args);
+ mUi.logVerbose(text);
+ }
+
+ /**
+ * Sets the max value of the progress bar.
+ * This method can be invoked from a non-UI thread.
+ *
+ * Weird things will happen if setProgressMax is called multiple times
+ * *after* {@link #incProgress(int)}: we don't try to adjust it on the
+ * fly.
+ *
+ * @see ProgressBar#setMaximum(int)
+ */
+ public void setProgressMax(int max) {
+ assert max > 0;
+ // Always set the dialog's progress max to 10k since it only handles
+ // integers and we want to have a better inner granularity. Instead
+ // we use the max to compute a coefficient for inc deltas.
+ mUi.setProgressMax((int) MAX_COUNT);
+ mIncCoef = max > 0 ? MAX_COUNT / max : 0;
+ assert mIncCoef > 0;
+ }
+
+ /**
+ * Increments the current value of the progress bar.
+ *
+ * This method can be invoked from a non-UI thread.
+ */
+ public void incProgress(int delta) {
+ if (delta > 0 && mIncCoef > 0) {
+ internalIncProgress(delta * mIncCoef);
+ }
+ }
+
+ private void internalIncProgress(double realDelta) {
+ mValue += realDelta;
+ mUi.setProgress((int)mValue);
+ }
+
+ /**
+ * Returns the current value of the progress bar,
+ * between 0 and up to {@link #setProgressMax(int)} - 1.
+ *
+ * This method can be invoked from a non-UI thread.
+ */
+ public int getProgress() {
+ // mIncCoef is 0 if setProgressMax hasn't been used yet.
+ return mIncCoef > 0 ? (int)(mUi.getProgress() / mIncCoef) : 0;
+ }
+
+ /**
+ * Returns true if the "Cancel" button was selected.
+ * It is up to the task thread to pool this and exit.
+ */
+ public boolean isCancelRequested() {
+ return mUi.isCancelRequested();
+ }
+
+ /**
+ * Display a yes/no question dialog box.
+ *
+ * This implementation allow this to be called from any thread, it
+ * makes sure the dialog is opened synchronously in the ui thread.
+ *
+ * @param title The title of the dialog box
+ * @param message The error message
+ * @return true if YES was clicked.
+ */
+ public boolean displayPrompt(final String title, final String message) {
+ return mUi.displayPrompt(title, message);
+ }
+
+ /**
+ * Creates a sub-monitor that will use up to tickCount on the progress bar.
+ * tickCount must be 1 or more.
+ */
+ public ITaskMonitor createSubMonitor(int tickCount) {
+ assert mIncCoef > 0;
+ assert tickCount > 0;
+ return new SubTaskMonitor(this, null, mValue, tickCount * mIncCoef);
+ }
+
+ private static class SubTaskMonitor implements ISubTaskMonitor {
+
+ private final TaskMonitorImpl mRoot;
+ private final ISubTaskMonitor mParent;
+ private final double mStart;
+ private final double mSpan;
+ private double mSubValue;
+ private double mSubCoef;
+
+ /**
+ * Creates a new sub task monitor which will work for the given range [start, start+span]
+ * in its parent.
+ *
+ * @param taskMonitor The ProgressTask root
+ * @param parent The immediate parent. Can be the null or another sub task monitor.
+ * @param start The start value in the root's coordinates
+ * @param span The span value in the root's coordinates
+ */
+ public SubTaskMonitor(TaskMonitorImpl taskMonitor,
+ ISubTaskMonitor parent,
+ double start,
+ double span) {
+ mRoot = taskMonitor;
+ mParent = parent;
+ mStart = start;
+ mSpan = span;
+ mSubValue = start;
+ }
+
+ public boolean isCancelRequested() {
+ return mRoot.isCancelRequested();
+ }
+
+ public void setDescription(String format, Object... args) {
+ mRoot.setDescription(format, args);
+ }
+
+ public void log(String format, Object... args) {
+ mRoot.log(format, args);
+ }
+
+ public void logError(String format, Object... args) {
+ mRoot.logError(format, args);
+ }
+
+ public void logVerbose(String format, Object... args) {
+ mRoot.logVerbose(format, args);
+ }
+
+ public void setProgressMax(int max) {
+ assert max > 0;
+ mSubCoef = max > 0 ? mSpan / max : 0;
+ assert mSubCoef > 0;
+ }
+
+ public int getProgress() {
+ assert mSubCoef > 0;
+ return mSubCoef > 0 ? (int)((mSubValue - mStart) / mSubCoef) : 0;
+ }
+
+ public void incProgress(int delta) {
+ if (delta > 0 && mSubCoef > 0) {
+ subIncProgress(delta * mSubCoef);
+ }
+ }
+
+ public void subIncProgress(double realDelta) {
+ mSubValue += realDelta;
+ if (mParent != null) {
+ mParent.subIncProgress(realDelta);
+ } else {
+ mRoot.internalIncProgress(realDelta);
+ }
+ }
+
+ public boolean displayPrompt(String title, String message) {
+ return mRoot.displayPrompt(title, message);
+ }
+
+ public ITaskMonitor createSubMonitor(int tickCount) {
+ assert mSubCoef > 0;
+ assert tickCount > 0;
+ return new SubTaskMonitor(mRoot,
+ this,
+ mSubValue,
+ tickCount * mSubCoef);
+ }
+ }
+
+}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java index 4411034..0932378 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java @@ -16,20 +16,21 @@ package com.android.sdkuilib.internal.widgets; -import com.android.prefs.AndroidLocation; +import com.android.io.FileWrapper; import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.ISdkLog; import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkManager; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.internal.avd.AvdManager; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.HardwareProperties; +import com.android.sdklib.internal.avd.AvdManager.AvdConflict; import com.android.sdklib.internal.avd.HardwareProperties.HardwareProperty; import com.android.sdklib.internal.project.ProjectProperties; -import com.android.sdklib.io.FileWrapper; import com.android.sdkuilib.internal.repository.icons.ImageFactory; import com.android.sdkuilib.ui.GridDialog; +import com.android.util.Pair; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.viewers.CellEditor; @@ -73,8 +74,8 @@ import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; import java.util.TreeMap; +import java.util.Map.Entry; import java.util.regex.Matcher; /** @@ -97,11 +98,18 @@ final class AvdCreationDialog extends GridDialog { private final ArrayList<String> mEditedProperties = new ArrayList<String>(); private final ImageFactory mImageFactory; private final ISdkLog mSdkLog; + /** + * The original AvdInfo if we're editing an existing AVD. + * Null when we're creating a new AVD. + */ private final AvdInfo mEditAvdInfo; private Text mAvdName; private Combo mTargetCombo; + private Combo mAbiTypeCombo; + private String mAbiType; + private Button mSdCardSizeRadio; private Text mSdCardSize; private Combo mSdCardSizeCombo; @@ -153,8 +161,11 @@ final class AvdCreationDialog extends GridDialog { public void modifyText(ModifyEvent e) { String name = mAvdName.getText().trim(); if (mEditAvdInfo == null || !name.equals(mEditAvdInfo.getName())) { - AvdInfo avdMatch = mAvdManager.getAvd(name, false /*validAvdOnly*/); - if (avdMatch != null) { + // Case where we're creating a new AVD or editing an existing one + // and the AVD name has been changed... check for name uniqueness. + + Pair<AvdConflict, String> conflict = mAvdManager.isAvdNameConflicting(name); + if (conflict.getFirst() != AvdManager.AvdConflict.NO_CONFLICT) { // If we're changing the state from disabled to enabled, make sure // to uncheck the button, to force the user to voluntarily re-enforce it. // This happens when editing an existing AVD and changing the name from @@ -168,8 +179,10 @@ final class AvdCreationDialog extends GridDialog { mForceCreation.setSelection(false); } } else { + // Case where we're editing an existing AVD with the name unchanged. + mForceCreation.setEnabled(false); - mForceCreation.setSelection(true); + mForceCreation.setSelection(false); } validatePage(); } @@ -285,10 +298,29 @@ final class AvdCreationDialog extends GridDialog { public void widgetSelected(SelectionEvent e) { super.widgetSelected(e); reloadSkinCombo(); + reloadAbiTypeCombo(); validatePage(); } }); + //ABI group + label = new Label(parent, SWT.NONE); + label.setText("ABI:"); + tooltip = "The ABI to use in the virtual device"; + label.setToolTipText(tooltip); + + mAbiTypeCombo = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN); + mAbiTypeCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + mAbiTypeCombo.setToolTipText(tooltip); + mAbiTypeCombo.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + super.widgetSelected(e); + validatePage(); + } + }); + mAbiTypeCombo.setEnabled(false); + // --- sd card group label = new Label(parent, SWT.NONE); label.setText("SD Card:"); @@ -322,6 +354,7 @@ final class AvdCreationDialog extends GridDialog { mSdCardSizeCombo = new Combo(sdCardGroup, SWT.DROP_DOWN | SWT.READ_ONLY); mSdCardSizeCombo.add("KiB"); mSdCardSizeCombo.add("MiB"); + mSdCardSizeCombo.add("GiB"); mSdCardSizeCombo.select(1); mSdCardSizeCombo.addSelectionListener(validateListener); @@ -668,80 +701,102 @@ final class AvdCreationDialog extends GridDialog { for (int i = 0;i < n; i++) { if (target.equals(mCurrentTargets.get(mTargetCombo.getItem(i)))) { mTargetCombo.select(i); + reloadAbiTypeCombo(); reloadSkinCombo(); break; } } } - Map<String, String> props = mEditAvdInfo.getProperties(); - - // First try the skin name and if it doesn't work fallback on the skin path - nextSkin: for (int s = 0; s < 2; s++) { - String skin = props.get(s == 0 ? AvdManager.AVD_INI_SKIN_NAME - : AvdManager.AVD_INI_SKIN_PATH); - if (skin != null && skin.length() > 0) { - Matcher m = AvdManager.NUMERIC_SKIN_SIZE.matcher(skin); - if (m.matches() && m.groupCount() == 2) { - enableSkinWidgets(false); - mSkinListRadio.setSelection(false); - mSkinSizeRadio.setSelection(true); - mSkinSizeWidth.setText(m.group(1)); - mSkinSizeHeight.setText(m.group(2)); - break nextSkin; - } else { - enableSkinWidgets(true); - mSkinSizeRadio.setSelection(false); - mSkinListRadio.setSelection(true); + // select the abi type + if (target != null && target.getAbiList().length > 0) { + mAbiTypeCombo.setEnabled(target.getAbiList().length > 1); + String abiType = AvdInfo.getPrettyAbiType(mEditAvdInfo.getAbiType()); + int n = mAbiTypeCombo.getItemCount(); + for (int i = 0; i < n; i++) { + if (abiType.equals(mAbiTypeCombo.getItem(i))) { + mAbiTypeCombo.select(i); + reloadSkinCombo(); + break; + } + } + } - int n = mSkinCombo.getItemCount(); - for (int i = 0; i < n; i++) { - if (skin.equals(mSkinCombo.getItem(i))) { - mSkinCombo.select(i); - break nextSkin; + Map<String, String> props = mEditAvdInfo.getProperties(); + if (props != null) { + // First try the skin name and if it doesn't work fallback on the skin path + nextSkin: for (int s = 0; s < 2; s++) { + String skin = props.get(s == 0 ? AvdManager.AVD_INI_SKIN_NAME + : AvdManager.AVD_INI_SKIN_PATH); + if (skin != null && skin.length() > 0) { + Matcher m = AvdManager.NUMERIC_SKIN_SIZE.matcher(skin); + if (m.matches() && m.groupCount() == 2) { + enableSkinWidgets(false); + mSkinListRadio.setSelection(false); + mSkinSizeRadio.setSelection(true); + mSkinSizeWidth.setText(m.group(1)); + mSkinSizeHeight.setText(m.group(2)); + break nextSkin; + } else { + enableSkinWidgets(true); + mSkinSizeRadio.setSelection(false); + mSkinListRadio.setSelection(true); + + int n = mSkinCombo.getItemCount(); + for (int i = 0; i < n; i++) { + if (skin.equals(mSkinCombo.getItem(i))) { + mSkinCombo.select(i); + break nextSkin; + } } } } } - } - String sdcard = props.get(AvdManager.AVD_INI_SDCARD_PATH); - if (sdcard != null && sdcard.length() > 0) { - enableSdCardWidgets(false); - mSdCardSizeRadio.setSelection(false); - mSdCardFileRadio.setSelection(true); - mSdCardFile.setText(sdcard); - } + String sdcard = props.get(AvdManager.AVD_INI_SDCARD_PATH); + if (sdcard != null && sdcard.length() > 0) { + enableSdCardWidgets(false); + mSdCardSizeRadio.setSelection(false); + mSdCardFileRadio.setSelection(true); + mSdCardFile.setText(sdcard); + } + + sdcard = props.get(AvdManager.AVD_INI_SDCARD_SIZE); + if (sdcard != null && sdcard.length() > 0) { + String[] values = new String[2]; + long sdcardSize = AvdManager.parseSdcardSize(sdcard, values); + + if (sdcardSize != AvdManager.SDCARD_NOT_SIZE_PATTERN) { + enableSdCardWidgets(true); + mSdCardFileRadio.setSelection(false); + mSdCardSizeRadio.setSelection(true); + + mSdCardSize.setText(values[0]); - sdcard = props.get(AvdManager.AVD_INI_SDCARD_SIZE); - if (sdcard != null && sdcard.length() > 0) { - Matcher m = AvdManager.SDCARD_SIZE_PATTERN.matcher(sdcard); - if (m.matches() && m.groupCount() == 2) { - enableSdCardWidgets(true); - mSdCardFileRadio.setSelection(false); - mSdCardSizeRadio.setSelection(true); - - mSdCardSize.setText(m.group(1)); - - String suffix = m.group(2); - int n = mSdCardSizeCombo.getItemCount(); - for (int i = 0; i < n; i++) { - if (mSdCardSizeCombo.getItem(i).startsWith(suffix)) { - mSdCardSizeCombo.select(i); + String suffix = values[1]; + int n = mSdCardSizeCombo.getItemCount(); + for (int i = 0; i < n; i++) { + if (mSdCardSizeCombo.getItem(i).startsWith(suffix)) { + mSdCardSizeCombo.select(i); + } } } } - } - String snapshots = props.get(AvdManager.AVD_INI_SNAPSHOT_PRESENT); - if (snapshots != null && snapshots.length() > 0) { - mSnapshotCheck.setSelection(snapshots.equals("true")); + String snapshots = props.get(AvdManager.AVD_INI_SNAPSHOT_PRESENT); + if (snapshots != null && snapshots.length() > 0) { + mSnapshotCheck.setSelection(snapshots.equals("true")); + } } mProperties.clear(); - mProperties.putAll(props); + + if (props != null) { + mProperties.putAll(props); + } // Cleanup known non-hardware properties + mProperties.remove(AvdManager.AVD_INI_ABI_TYPE); mProperties.remove(AvdManager.AVD_INI_SKIN_PATH); mProperties.remove(AvdManager.AVD_INI_SKIN_NAME); mProperties.remove(AvdManager.AVD_INI_SDCARD_SIZE); @@ -882,6 +937,49 @@ final class AvdCreationDialog extends GridDialog { } /** + * Reload all the abi types in the selection list + */ + private void reloadAbiTypeCombo() { + String selected = null; + boolean found = false; + + int index = mTargetCombo.getSelectionIndex(); + if (index >= 0) { + String targetName = mTargetCombo.getItem(index); + IAndroidTarget target = mCurrentTargets.get(targetName); + String[] arches = target.getAbiList(); + + mAbiTypeCombo.setEnabled(arches.length > 1); + + // If user explicitly selected an ABI before, preserve that option + // If user did not explicitly select before (only one option before) + // force them to select + index = mAbiTypeCombo.getSelectionIndex(); + if (index >= 0 && mAbiTypeCombo.getItemCount() > 1) { + selected = mAbiTypeCombo.getItem(index); + } + + mAbiTypeCombo.removeAll(); + + int i; + for ( i = 0; i < arches.length ; i++ ) { + String prettyAbiType = AvdInfo.getPrettyAbiType(arches[i]); + mAbiTypeCombo.add(prettyAbiType); + if (!found) { + found = prettyAbiType.equals(selected); + if (found) { + mAbiTypeCombo.select(i); + } + } + } + + if (arches.length == 1) { + mAbiTypeCombo.select(0); + } + } + } + + /** * Validates the fields, displays errors and warnings. * Enables the finish button if there are no errors. */ @@ -905,6 +1003,16 @@ final class AvdCreationDialog extends GridDialog { error = "A target must be selected in order to create an AVD."; } + // validate abi type if the selected target supports multi archs. + if (hasAvdName && error == null && mTargetCombo.getSelectionIndex() > 0) { + int index = mTargetCombo.getSelectionIndex(); + String targetName = mTargetCombo.getItem(index); + IAndroidTarget target = mCurrentTargets.get(targetName); + if (target.getAbiList().length > 1 && mAbiTypeCombo.getSelectionIndex() < 0) { + error = "An abi type must be selected in order to create an AVD."; + } + } + // Validate SDCard path or value if (error == null) { // get the mode. We only need to check the file since the @@ -918,29 +1026,40 @@ final class AvdCreationDialog extends GridDialog { } else { String valueString = mSdCardSize.getText(); if (valueString.length() > 0) { - // prevent overflow: no more than 999GB - // 10 digit for MiB, 13 for KiB - if (valueString.length() >= 10 + - (mSdCardSizeCombo.getSelectionIndex() == 0 ? 3 : 0)) { - error = "SD Card size is too big!"; - } else { - try { - long value = Long.parseLong(valueString); - - switch (mSdCardSizeCombo.getSelectionIndex()) { - case 0: - value *= 1024L; - break; - case 1: - value *= 1024L * 1024L; - break; - } + long value = 0; + try { + value = Long.parseLong(valueString); + + int sizeIndex = mSdCardSizeCombo.getSelectionIndex(); + if (sizeIndex >= 0) { + // index 0 shifts by 10 (1024=K), index 1 by 20, etc. + value <<= 10*(1 + sizeIndex); + } - if (value < 9 * 1024 * 1024) { - error = "SD Card size must be at least 9 MiB"; + if (value < AvdManager.SDCARD_MIN_BYTE_SIZE || + value > AvdManager.SDCARD_MAX_BYTE_SIZE) { + value = 0; + } + } catch (Exception e) { + // ignore, we'll test value below. + } + if (value <= 0) { + error = "SD Card size is invalid. Range is 9 MiB..1023 GiB."; + } else if (mEditAvdInfo != null) { + // When editing an existing AVD, compare with the existing + // sdcard size, if any. It only matters if there was an sdcard setting + // before. + Map<String, String> props = mEditAvdInfo.getProperties(); + if (props != null) { + String original = + mEditAvdInfo.getProperties().get(AvdManager.AVD_INI_SDCARD_SIZE); + if (original != null && original.length() > 0) { + long originalSize = + AvdManager.parseSdcardSize(original, null/*parsedStrings*/); + if (originalSize > 0 && value != originalSize) { + warning = "A new SD Card file will be created.\nThe current SD Card file will be lost."; + } } - } catch (NumberFormatException e) { - // will never happen thanks to the VerifyListener. } } } @@ -957,24 +1076,47 @@ final class AvdCreationDialog extends GridDialog { String height = mSkinSizeHeight.getText(); // rejects non digit. if (width.length() == 0 || height.length() == 0) { - error = "Skin size is incorrect.\nBoth dimensions must be > 0"; + error = "Skin size is incorrect.\nBoth dimensions must be > 0."; } } } // Check for duplicate AVD name - if (isCreate && hasAvdName && error == null) { - AvdInfo avdMatch = mAvdManager.getAvd(avdName, false /*validAvdOnly*/); - if (avdMatch != null && !mForceCreation.getSelection()) { + if (isCreate && hasAvdName && error == null && !mForceCreation.getSelection()) { + Pair<AvdConflict, String> conflict = mAvdManager.isAvdNameConflicting(avdName); + assert conflict != null; + switch(conflict.getFirst()) { + case NO_CONFLICT: + break; + case CONFLICT_EXISTING_AVD: + case CONFLICT_INVALID_AVD: error = String.format( "The AVD name '%s' is already used.\n" + "Check \"Override the existing AVD\" to delete the existing one.", avdName); + break; + case CONFLICT_EXISTING_PATH: + error = String.format( + "Conflict with %s\n" + + "Check \"Override the existing AVD\" to delete the existing one.", + conflict.getSecond()); + break; + default: + // Hmm not supposed to happen... probably someone expanded the + // enum without adding something here. In this case just do an + // assert and use a generic error message. + error = String.format( + "Conflict %s with %s.\n" + + "Check \"Override the existing AVD\" to delete the existing one.", + conflict.getFirst().toString(), + conflict.getSecond()); + assert false; + break; } } if (error == null && mEditAvdInfo != null && isCreate) { - warning = String.format("The AVD '%1$s' will be duplicated into '%2$s'", + warning = String.format("The AVD '%1$s' will be duplicated into '%2$s'.", mEditAvdInfo.getName(), avdName); } @@ -1102,6 +1244,19 @@ final class AvdCreationDialog extends GridDialog { return false; } + // get the abi type + mAbiType = SdkConstants.ABI_ARMEABI; + if (target.getAbiList().length > 0) { + int abiIndex = mAbiTypeCombo.getSelectionIndex(); + if (abiIndex >= 0) { + String prettyname = mAbiTypeCombo.getItem(abiIndex); + //Extract the abi type + int firstIndex = prettyname.indexOf("("); + int lastIndex = prettyname.indexOf(")"); + mAbiType = prettyname.substring(firstIndex+1, lastIndex); + } + } + // get the SD card data from the UI. String sdName = null; if (mSdCardSizeRadio.getSelection()) { @@ -1112,10 +1267,13 @@ final class AvdCreationDialog extends GridDialog { // add the unit switch (mSdCardSizeCombo.getSelectionIndex()) { case 0: - sdName += "K"; + sdName += "K"; //$NON-NLS-1$ break; case 1: - sdName += "M"; + sdName += "M"; //$NON-NLS-1$ + break; + case 2: + sdName += "G"; //$NON-NLS-1$ break; default: // shouldn't be here @@ -1154,9 +1312,7 @@ final class AvdCreationDialog extends GridDialog { File avdFolder = null; try { - avdFolder = new File( - AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD, - avdName + AvdManager.AVD_FOLDER_EXTENSION); + avdFolder = AvdInfo.getDefaultAvdFolder(mAvdManager, avdName); } catch (AndroidLocationException e) { return false; } @@ -1169,11 +1325,13 @@ final class AvdCreationDialog extends GridDialog { avdFolder, avdName, target, + mAbiType, skinName, sdName, mProperties, - force, snapshot, + force, + mEditAvdInfo != null, //edit existing log); success = avdInfo != null; diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java index 409c25d..6a85c14 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java @@ -18,9 +18,9 @@ package com.android.sdkuilib.internal.widgets; import com.android.sdklib.AndroidVersion; import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.internal.avd.AvdManager; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus; +import com.android.sdklib.internal.avd.AvdInfo.AvdStatus; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; @@ -101,7 +101,9 @@ final class AvdDetailsDialog extends Dialog { if (mAvdInfo != null) { displayValue(c, "Name:", mAvdInfo.getName()); - displayValue(c, "Path:", mAvdInfo.getPath()); + displayValue(c, "ABI:", AvdInfo.getPrettyAbiType(mAvdInfo.getAbiType())); + + displayValue(c, "Path:", mAvdInfo.getDataFolderPath()); if (mAvdInfo.getStatus() != AvdStatus.OK) { displayValue(c, "Error:", mAvdInfo.getErrorMessage()); @@ -135,6 +137,7 @@ final class AvdDetailsDialog extends Dialog { // display other hardware HashMap<String, String> copy = new HashMap<String, String>(properties); // remove stuff we already displayed (or that we don't want to display) + copy.remove(AvdManager.AVD_INI_ABI_TYPE); copy.remove(AvdManager.AVD_INI_SKIN_NAME); copy.remove(AvdManager.AVD_INI_SKIN_PATH); copy.remove(AvdManager.AVD_INI_SDCARD_SIZE); diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java index 5e26a41..56f2c7e 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java @@ -20,15 +20,15 @@ import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.ISdkLog; import com.android.sdklib.NullSdkLog; -import com.android.sdklib.SdkConstants; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.internal.avd.AvdManager; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus; +import com.android.sdklib.internal.avd.AvdInfo.AvdStatus; import com.android.sdklib.internal.repository.ITask; import com.android.sdklib.internal.repository.ITaskMonitor; import com.android.sdkuilib.internal.repository.SettingsController; import com.android.sdkuilib.internal.repository.icons.ImageFactory; import com.android.sdkuilib.internal.tasks.ProgressTask; +import com.android.sdkuilib.repository.IUpdaterWindow; import com.android.sdkuilib.repository.UpdaterWindow; import org.eclipse.jface.dialogs.MessageDialog; @@ -372,8 +372,10 @@ public final class AvdSelector { column2.setText("Platform"); final TableColumn column3 = new TableColumn(mTable, SWT.NONE); column3.setText("API Level"); + final TableColumn column4 = new TableColumn(mTable, SWT.NONE); + column4.setText("ABI"); - adjustColumnsWidth(mTable, column0, column1, column2, column3); + adjustColumnsWidth(mTable, column0, column1, column2, column3, column4); setupSelectionListener(mTable); fillTable(mTable); setEnabled(true); @@ -633,16 +635,18 @@ public final class AvdSelector { final TableColumn column0, final TableColumn column1, final TableColumn column2, - final TableColumn column3) { + final TableColumn column3, + final TableColumn column4) { // Add a listener to resize the column to the full width of the table table.addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { Rectangle r = table.getClientArea(); - column0.setWidth(r.width * 25 / 100); // 25% - column1.setWidth(r.width * 45 / 100); // 45% + column0.setWidth(r.width * 20 / 100); // 20% + column1.setWidth(r.width * 30 / 100); // 30% column2.setWidth(r.width * 15 / 100); // 15% column3.setWidth(r.width * 15 / 100); // 15% + column4.setWidth(r.width * 20 / 100); // 22% } }); } @@ -777,10 +781,12 @@ public final class AvdSelector { item.setText(1, target.getFullName()); item.setText(2, target.getVersionName()); item.setText(3, target.getVersion().getApiString()); + item.setText(4, AvdInfo.getPrettyAbiType(avd.getAbiType())); } else { item.setText(1, "?"); item.setText(2, "?"); item.setText(3, "?"); + item.setText(4, "?"); } } } @@ -1003,7 +1009,7 @@ public final class AvdSelector { log = new MessageBoxLog("Result of SDK Manager", display, true /*logErrorsOnly*/); } - UpdaterWindow window = new UpdaterWindow( + IUpdaterWindow window = new UpdaterWindow( mTable.getShell(), log, mAvdManager.getSdkManager().getLocation()); @@ -1025,10 +1031,7 @@ public final class AvdSelector { AvdStartDialog dialog = new AvdStartDialog(mTable.getShell(), avdInfo, mOsSdkPath, mController); if (dialog.open() == Window.OK) { - String path = mOsSdkPath + - File.separator + - SdkConstants.OS_SDK_TOOLS_FOLDER + - SdkConstants.FN_EMULATOR; + String path = avdInfo.getEmulatorPath(mOsSdkPath + File.separator); final String avdName = avdInfo.getName(); @@ -1070,7 +1073,8 @@ public final class AvdSelector { new ITask() { public void run(ITaskMonitor monitor) { try { - monitor.setDescription("Starting emulator for AVD '%1$s'", + monitor.setDescription( + "Starting emulator for AVD '%1$s'", avdName); int n = 10; monitor.setProgressMax(n); @@ -1092,7 +1096,7 @@ public final class AvdSelector { } } } catch (IOException e) { - monitor.setResult("Failed to start emulator: %1$s", + monitor.logError("Failed to start emulator: %1$s", e.getMessage()); } } @@ -1119,7 +1123,7 @@ public final class AvdSelector { while (true) { String line = errReader.readLine(); if (line != null) { - monitor.setResult("%1$s", line); //$NON-NLS-1$ + monitor.logError("%1$s", line); //$NON-NLS-1$ } else { break; } @@ -1140,7 +1144,7 @@ public final class AvdSelector { while (true) { String line = outReader.readLine(); if (line != null) { - monitor.setResult("%1$s", line); //$NON-NLS-1$ + monitor.log("%1$s", line); //$NON-NLS-1$ } else { break; } diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java index 77f47d1..7731dc1 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java @@ -16,8 +16,8 @@ package com.android.sdkuilib.internal.widgets; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.internal.avd.AvdManager; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdkuilib.internal.repository.SettingsController; import com.android.sdkuilib.ui.GridDialog; diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/MessageBoxLog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/MessageBoxLog.java index d5c1818..89edb2f 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/MessageBoxLog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/MessageBoxLog.java @@ -95,7 +95,15 @@ public final class MessageBoxLog implements ISdkLog { if (logMessages.size() > 0) { final StringBuilder sb = new StringBuilder(mMessage + "\n\n"); for (String msg : logMessages) { - sb.append(msg); + if (msg.length() > 0) { + if (msg.charAt(0) != '\n') { + int n = sb.length(); + if (n > 0 && sb.charAt(n-1) != '\n') { + sb.append('\n'); + } + } + sb.append(msg); + } } // display the message diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/IUpdaterWindow.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/IUpdaterWindow.java new file mode 100755 index 0000000..a771e53 --- /dev/null +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/IUpdaterWindow.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdkuilib.repository; + +import org.eclipse.swt.widgets.Composite; + +public interface IUpdaterWindow { + + /** + * Registers an extra page for the updater window. + * <p/> + * Pages must derive from {@link Composite} and implement a constructor that takes + * a single parent {@link Composite} argument. + * <p/> + * All pages must be registered before the call to {@link #open()}. + * + * @param title The title of the page. + * @param pageClass The {@link Composite}-derived class that will implement the page. + */ + public abstract void registerPage(String title, + Class<? extends Composite> pageClass); + + /** + * Indicate the initial page that should be selected when the window opens. + * <p/> + * This must be called before the call to {@link #open()}. + * If null or if the page class is not found, the first page will be selected. + */ + public abstract void setInitialPage(Class<? extends Composite> pageClass); + + /** + * Sets whether the auto-update wizard will be shown when opening the window. + * <p/> + * This must be called before the call to {@link #open()}. + */ + public abstract void setRequestAutoUpdate(boolean requestAutoUpdate); + + /** + * Adds a new listener to be notified when a change is made to the content of the SDK. + */ + public abstract void addListener(ISdkChangeListener listener); + + /** + * Removes a new listener to be notified anymore when a change is made to the content of + * the SDK. + */ + public abstract void removeListener(ISdkChangeListener listener); + + /** + * Opens the window. + */ + public abstract void open(); + +} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java index c78b08c..d49b072 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java @@ -18,6 +18,7 @@ package com.android.sdkuilib.repository; import com.android.sdklib.ISdkLog;
import com.android.sdkuilib.internal.repository.UpdaterWindowImpl;
+import com.android.sdkuilib.internal.repository.UpdaterWindowImpl2;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
@@ -27,9 +28,9 @@ import org.eclipse.swt.widgets.Shell; *
* This is the public interface for using the window.
*/
-public class UpdaterWindow {
+public class UpdaterWindow implements IUpdaterWindow {
- private UpdaterWindowImpl mWindow;
+ private IUpdaterWindow mWindow;
/**
* Creates a new window. Caller must call open(), which will block.
@@ -39,7 +40,13 @@ public class UpdaterWindow { * @param osSdkRoot The OS path to the SDK root.
*/
public UpdaterWindow(Shell parentShell, ISdkLog sdkLog, String osSdkRoot) {
- mWindow = new UpdaterWindowImpl(parentShell, sdkLog, osSdkRoot);
+
+ // TODO right now the new PackagesPage is experimental and not enabled by default
+ if (System.getenv("ANDROID_SDKMAN_EXP") != null) { //$NON-NLS-1$
+ mWindow = new UpdaterWindowImpl2(parentShell, sdkLog, osSdkRoot);
+ } else {
+ mWindow = new UpdaterWindowImpl(parentShell, sdkLog, osSdkRoot);
+ }
}
/**
@@ -54,7 +61,7 @@ public class UpdaterWindow { * @param pageClass The {@link Composite}-derived class that will implement the page.
*/
public void registerPage(String title, Class<? extends Composite> pageClass) {
- mWindow.registerExtraPage(title, pageClass);
+ mWindow.registerPage(title, pageClass);
}
/**
diff --git a/sdkmanager/libs/sdklib/src/Android.mk b/sdkmanager/libs/sdkuilib/tests/Android.mk index 37eda21..a7ba9a7 100644 --- a/sdkmanager/libs/sdklib/src/Android.mk +++ b/sdkmanager/libs/sdkuilib/tests/Android.mk @@ -1,5 +1,4 @@ -# -# Copyright (C) 2008 The Android Open Source Project +# Copyright (C) 2011 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,19 +11,22 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# + LOCAL_PATH := $(call my-dir) + include $(CLEAR_VARS) +# Only compile source java files in this lib. LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_JAVA_RESOURCE_DIRS := . -LOCAL_JAR_MANIFEST := ../manifest.txt -LOCAL_JAVA_LIBRARIES := \ - androidprefs \ - common \ - commons-compress-1.0 -LOCAL_MODULE := sdklib +LOCAL_MODULE := sdkuilib-tests +LOCAL_MODULE_TAGS := optional -include $(BUILD_HOST_JAVA_LIBRARY) +LOCAL_JAVA_LIBRARIES := \ + sdklib \ + sdklib-tests \ + sdkuilib \ + junit \ + swt \ +include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterDataTest.java b/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterDataTest.java index 185589f..9470f91 100755 --- a/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterDataTest.java +++ b/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterDataTest.java @@ -31,7 +31,6 @@ import com.android.sdklib.mock.MockLog; import java.io.File; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.List; import java.util.Properties; @@ -187,7 +186,7 @@ public class UpdaterDataTest extends TestCase { return false; } - public void setDescription(String descriptionFormat, Object... args) { + public void setDescription(String format, Object... args) { // ignore } @@ -195,7 +194,15 @@ public class UpdaterDataTest extends TestCase { // ignore } - public void setResult(String resultFormat, Object... args) { + public void log(String format, Object... args) { + // ignore + } + + public void logError(String format, Object... args) { + // ignore + } + + public void logVerbose(String format, Object... args) { // ignore } } @@ -242,6 +249,11 @@ public class UpdaterDataTest extends TestCase { } @Override + public String getListDescription() { + return this.getClass().getSimpleName(); + } + + @Override public String getShortDescription() { return this.getClass().getSimpleName(); } diff --git a/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java b/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java index 72229ff..84241d4 100755 --- a/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java +++ b/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java @@ -21,16 +21,13 @@ import com.android.sdklib.SdkManager; import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdklib.internal.repository.Archive;
import com.android.sdklib.internal.repository.ITaskFactory;
-import com.android.sdklib.internal.repository.ITaskMonitor;
import com.android.sdklib.internal.repository.MockAddonPackage;
import com.android.sdklib.internal.repository.MockBrokenPackage;
import com.android.sdklib.internal.repository.MockPlatformPackage;
import com.android.sdklib.internal.repository.MockPlatformToolPackage;
import com.android.sdklib.internal.repository.MockToolPackage;
import com.android.sdklib.internal.repository.Package;
-import com.android.sdklib.internal.repository.SdkRepoSource;
import com.android.sdklib.internal.repository.SdkSource;
-import com.android.sdklib.internal.repository.SdkSourceCategory;
import com.android.sdklib.internal.repository.SdkSources;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
|