aboutsummaryrefslogtreecommitdiffstats
path: root/sdkmanager/libs/sdkuilib
diff options
context:
space:
mode:
Diffstat (limited to 'sdkmanager/libs/sdkuilib')
-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-xsdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/IPageListener.java30
-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.mk32
-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
48 files changed, 4871 insertions, 1098 deletions
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/sdkuilib/src/com/android/sdkuilib/internal/repository/IPageListener.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/IPageListener.java
new file mode 100755
index 0000000..fc59404
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/IPageListener.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+
+
+/**
+ * Interface for lifecycle events of pages.
+ */
+interface IPageListener {
+
+ /**
+ * 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/sdkuilib/tests/Android.mk b/sdkmanager/libs/sdkuilib/tests/Android.mk
new file mode 100644
index 0000000..a7ba9a7
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/tests/Android.mk
@@ -0,0 +1,32 @@
+# 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 := sdkuilib-tests
+LOCAL_MODULE_TAGS := optional
+
+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;