aboutsummaryrefslogtreecommitdiffstats
path: root/sdkmanager
diff options
context:
space:
mode:
Diffstat (limited to 'sdkmanager')
-rw-r--r--sdkmanager/app/.classpath26
-rw-r--r--sdkmanager/app/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--sdkmanager/app/Android.mk34
-rw-r--r--sdkmanager/app/NOTICE190
-rwxr-xr-xsdkmanager/app/etc/android13
-rwxr-xr-xsdkmanager/app/etc/android.bat4
-rw-r--r--sdkmanager/app/etc/manifest.txt2
-rw-r--r--sdkmanager/app/src/Android.mk26
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java38
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/Main.java183
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java263
-rwxr-xr-xsdkmanager/app/src/com/android/sdkmanager/internal/repository/AboutPage.java2
-rw-r--r--sdkmanager/app/tests/Android.mk28
-rw-r--r--sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java77
-rw-r--r--sdkmanager/app/tests/com/android/sdkmanager/MainTest.java174
-rwxr-xr-xsdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestCase.java182
-rw-r--r--sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestUtil.java88
-rw-r--r--sdkmanager/libs/sdklib/.classpath4
-rw-r--r--sdkmanager/libs/sdklib/.settings/org.eclipse.jdt.core.prefs64
-rw-r--r--sdkmanager/libs/sdklib/Android.mk26
-rw-r--r--sdkmanager/libs/sdklib/NOTICE190
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java35
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java12
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java42
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java44
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java77
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdInfo.java334
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java734
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/DebugKeyProvider.java38
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/export/MultiApkExportHelper.java6
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectCreator.java5
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java8
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectPropertiesWorkingCopy.java6
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AdbWrapper.java22
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java17
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonsListFetcher.java10
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java51
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/BrokenPackage.java14
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java28
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java53
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ITaskMonitor.java34
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/LocalSdkParser.java6
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/OsHelper.java56
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/Package.java11
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java30
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformToolPackage.java15
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SamplePackage.java18
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSource.java33
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/SdkSources.java2
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java25
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/io/FileWrapper.java147
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/io/FolderWrapper.java154
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFile.java53
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractFolder.java77
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/io/IAbstractResource.java45
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java7
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifestParser.java6
-rw-r--r--sdkmanager/libs/sdklib/tests/Android.mk28
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/build/DebugKeyProviderTest.java125
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/AddonsListFetcherTest.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/AddonsListFetcherTest.java)4
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/BrokenPackageTest.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/BrokenPackageTest.java)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockAddonPackage.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockAddonPackage.java)9
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockBrokenPackage.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockBrokenPackage.java)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockEmptySdkManager.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockEmptySdkManager.java)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockMonitor.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockMonitor.java)34
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockPlatformPackage.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockPlatformPackage.java)9
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockPlatformToolPackage.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockPlatformToolPackage.java)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/MockToolPackage.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/MockToolPackage.java)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkAddonSourceTest.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkAddonSourceTest.java)5
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/internal/repository/SdkRepoSourceTest.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/internal/repository/SdkRepoSourceTest.java)24
-rw-r--r--sdkmanager/libs/sdklib/tests/src/com/android/sdklib/mock/MockLog.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/mock/MockLog.java)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/repository/SdkRepositoryTest.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/repository/SdkRepositoryTest.java)0
-rw-r--r--sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-instrumentation.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-instrumentation.xml)0
-rw-r--r--sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-testapp.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-testapp.xml)0
-rw-r--r--sdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/AndroidManifest-testapp2.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-testapp2.xml)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addon_sample_1.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addon_sample_1.xml)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/addons_list_sample_1.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/addons_list_sample_1.xml)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_1.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_1.xml)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_2.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_2.xml)0
-rwxr-xr-xsdkmanager/libs/sdklib/tests/src/com/android/sdklib/testdata/repository_sample_3.xml (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/repository_sample_3.xml)0
-rw-r--r--sdkmanager/libs/sdklib/tests/src/com/android/sdklib/xml/AndroidManifestParserTest.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/AndroidManifestParserTest.java)0
-rw-r--r--sdkmanager/libs/sdklib/tests/src/com/android/sdklib/xml/SupportsScreensTest.java (renamed from sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/SupportsScreensTest.java)0
-rw-r--r--sdkmanager/libs/sdkuilib/.classpath5
-rw-r--r--sdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.core.prefs64
-rwxr-xr-xsdkmanager/libs/sdkuilib/.settings/org.eclipse.jdt.ui.prefs54
-rw-r--r--sdkmanager/libs/sdkuilib/Android.mk45
-rw-r--r--sdkmanager/libs/sdkuilib/NOTICE190
-rw-r--r--sdkmanager/libs/sdkuilib/README44
-rw-r--r--sdkmanager/libs/sdkuilib/src/Android.mk21
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AddonSitesDialog.java417
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java3
-rwxr-xr-x[-rw-r--r--]sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/IPageListener.java (renamed from sdkmanager/libs/sdklib/src/com/android/sdklib/io/StreamException.java)18
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java3
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/PackagesPage.java1441
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java992
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java29
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateNoWindow.java75
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java221
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java87
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java182
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl2.java586
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/ImageFactory.java8
-rw-r--r--sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/android_icon_128.pngbin8713 -> 17715 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/collapsed_16.pngbin0 -> 329 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/expanded_16.pngbin0 -> 366 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon_16.png (renamed from sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/nopkg_icon16.png)bin397 -> 397 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_installed_16.pngbin0 -> 356 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_new_16.pngbin0 -> 299 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_update_16.pngbin0 -> 296 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_16.pngbin0 -> 388 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_other_16.pngbin0 -> 335 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/status_ok_16.pngbin0 -> 264 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_disabled_16.pngbin0 -> 321 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_enabled_16.pngbin0 -> 327 bytes
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/IProgressUiProvider.java93
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTask.java213
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressTaskDialog.java (renamed from sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressDialog.java)75
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressView.java258
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/ProgressViewFactory.java41
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/tasks/TaskMonitorImpl.java259
-rw-r--r--sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java340
-rw-r--r--sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java9
-rw-r--r--sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java36
-rw-r--r--sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java2
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/MessageBoxLog.java10
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/IUpdaterWindow.java68
-rwxr-xr-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java15
-rw-r--r--sdkmanager/libs/sdkuilib/tests/Android.mk (renamed from sdkmanager/libs/sdklib/src/Android.mk)24
-rwxr-xr-xsdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterDataTest.java18
-rwxr-xr-xsdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java3
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
index 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
Binary files differ
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
new file mode 100755
index 0000000..5f20b86
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/collapsed_16.png
Binary files differ
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
new file mode 100755
index 0000000..6e13998
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/expanded_16.png
Binary files differ
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
index 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
Binary files differ
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
new file mode 100755
index 0000000..78b7e5a
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_installed_16.png
Binary files differ
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
new file mode 100755
index 0000000..0976ad4
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_new_16.png
Binary files differ
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
new file mode 100755
index 0000000..e766251
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkg_update_16.png
Binary files differ
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
new file mode 100755
index 0000000..cd9b807
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_16.png
Binary files differ
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
new file mode 100755
index 0000000..395a240
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/pkgcat_other_16.png
Binary files differ
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
new file mode 100755
index 0000000..eeb0a6f
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/status_ok_16.png
Binary files differ
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
new file mode 100755
index 0000000..ae6da31
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_disabled_16.png
Binary files differ
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
new file mode 100755
index 0000000..7ce1864
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/icons/stop_enabled_16.png
Binary files differ
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;