diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:04:04 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:04:04 -0800 |
commit | 9ca1c0b33cc3fc28c21a915730797ec01e71a152 (patch) | |
tree | 86a5d0cea812248b02df654e925f55915e47bdf7 /eclipse | |
parent | 1506a206c0a5e3b593c4c61a62b8805b64e98daf (diff) | |
download | sdk-9ca1c0b33cc3fc28c21a915730797ec01e71a152.zip sdk-9ca1c0b33cc3fc28c21a915730797ec01e71a152.tar.gz sdk-9ca1c0b33cc3fc28c21a915730797ec01e71a152.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'eclipse')
326 files changed, 9571 insertions, 7102 deletions
diff --git a/eclipse/buildConfig/allElements.xml b/eclipse/buildConfig/allElements.xml index 79f06df..99ab3aa 100644 --- a/eclipse/buildConfig/allElements.xml +++ b/eclipse/buildConfig/allElements.xml @@ -19,11 +19,6 @@ <property name="id" value="com.android.ide.eclipse.adt" /> </ant> - <ant antfile="${genericTargets}" target="${target}"> - <property name="type" value="feature" /> - <property name="id" value="com.android.ide.eclipse.editors" /> - </ant> - <antcall target="buildInternalFeatures"/> </target> @@ -35,15 +30,10 @@ <target name="buildInternalFeatures" if="internalSite"> <ant antfile="${genericTargets}" target="${target}"> <property name="type" value="feature" /> - <property name="id" value="com.android.ide.eclipse.platform" /> - </ant> - <ant antfile="${genericTargets}" target="${target}"> - <property name="type" value="feature" /> <property name="id" value="com.android.ide.eclipse.tests" /> </ant> </target> - <!-- ===================================================================== --> <!-- Targets to assemble the built elements for particular configurations --> <!-- These generally call the generated assemble scripts (named in --> @@ -55,14 +45,6 @@ <ant antfile="${assembleScriptName}" dir="${buildDirectory}"/> </target> - <target name="assemble.com.android.ide.eclipse.editors"> - <ant antfile="${assembleScriptName}" dir="${buildDirectory}"/> - </target> - - <target name="assemble.com.android.ide.eclipse.platform"> - <ant antfile="${assembleScriptName}" dir="${buildDirectory}"/> - </target> - <target name="assemble.com.android.ide.eclipse.tests"> <ant antfile="${assembleScriptName}" dir="${buildDirectory}"/> </target> diff --git a/eclipse/changes.txt b/eclipse/changes.txt index bbe26de..5cb8245 100644 --- a/eclipse/changes.txt +++ b/eclipse/changes.txt @@ -1,3 +1,6 @@ +0.9.0 (work in progress) +- Support for SDK with multiple versions of the Android platform and vendor supplied add-ons. + 0.8.1: - Alternate Layout wizard. In the layout editor, the "create" button is now enabled, and allows to easily create alternate versions. @@ -5,6 +8,8 @@ - Export Wizard: To export an application for release, sign with a non debug key. Accessible from the export menu, from the Android Tools contextual menu, or from the overview page of the manifest editor. - New XML File Wizard: To easily create new XML resources file in the /res directory. - New checks on launch when attempting to debug on a device. +- Basic support for drag'n'drop in Graphical layout editor. You can add new items by drag'n'drop from the palette. There's is no support for moving/resizing yet. +- Undo/redo support in all XML form editors and Graphical layout editor. 0.8.0: diff --git a/eclipse/features/com.android.ide.eclipse.adt/feature.xml b/eclipse/features/com.android.ide.eclipse.adt/feature.xml index f8356b0..191b76d 100644 --- a/eclipse/features/com.android.ide.eclipse.adt/feature.xml +++ b/eclipse/features/com.android.ide.eclipse.adt/feature.xml @@ -2,7 +2,7 @@ <feature id="com.android.ide.eclipse.adt" label="Android Development Tools" - version="0.8.1.qualifier" + version="0.9.0.qualifier" provider-name="The Android Open Source Project" plugin="com.android.ide.eclipse.adt"> @@ -14,37 +14,35 @@ Copyright (C) 2007 The Android Open Source Project </copyright> + <license> + License TBD. + </license> + <url> <update label="Android Update Site" url="https://dl-ssl.google.com/android/eclipse/"/> </url> <requires> - <import plugin="org.eclipse.core.resources"/> <import plugin="org.eclipse.core.runtime"/> - <import plugin="org.eclipse.jdt.core"/> - <import plugin="org.eclipse.ui.console"/> - <import plugin="org.eclipse.ui"/> - <import plugin="org.eclipse.jdt.ui"/> - <import plugin="org.eclipse.jface.text"/> - <import plugin="org.eclipse.ui.editors"/> + <import plugin="org.eclipse.core.resources"/> <import plugin="org.eclipse.debug.core"/> <import plugin="org.eclipse.debug.ui"/> <import plugin="org.eclipse.jdt"/> <import plugin="org.eclipse.ant.core"/> + <import plugin="org.eclipse.jdt.core"/> + <import plugin="org.eclipse.jdt.ui"/> <import plugin="org.eclipse.jdt.launching"/> + <import plugin="org.eclipse.jface.text"/> + <import plugin="org.eclipse.ui.editors"/> <import plugin="org.eclipse.ui.workbench.texteditor"/> + <import plugin="org.eclipse.ui.console"/> <import plugin="org.eclipse.core.filesystem"/> + <import plugin="org.eclipse.ui"/> <import plugin="org.eclipse.ui.ide"/> + <import plugin="org.eclipse.ui.forms"/> </requires> <plugin - id="com.android.ide.eclipse.common" - download-size="0" - install-size="0" - version="0.0.0" - unpack="false"/> - - <plugin id="com.android.ide.eclipse.adt" download-size="0" install-size="0" diff --git a/eclipse/features/com.android.ide.eclipse.editors/.project b/eclipse/features/com.android.ide.eclipse.editors/.project deleted file mode 100644 index a960976..0000000 --- a/eclipse/features/com.android.ide.eclipse.editors/.project +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<projectDescription> - <name>editors-feature</name> - <comment></comment> - <projects> - </projects> - <buildSpec> - <buildCommand> - <name>org.eclipse.pde.FeatureBuilder</name> - <arguments> - </arguments> - </buildCommand> - </buildSpec> - <natures> - <nature>org.eclipse.pde.FeatureNature</nature> - </natures> -</projectDescription> diff --git a/eclipse/features/com.android.ide.eclipse.editors/build.properties b/eclipse/features/com.android.ide.eclipse.editors/build.properties deleted file mode 100644 index 64f93a9..0000000 --- a/eclipse/features/com.android.ide.eclipse.editors/build.properties +++ /dev/null @@ -1 +0,0 @@ -bin.includes = feature.xml diff --git a/eclipse/features/com.android.ide.eclipse.editors/feature.xml b/eclipse/features/com.android.ide.eclipse.editors/feature.xml deleted file mode 100644 index f5f92e8..0000000 --- a/eclipse/features/com.android.ide.eclipse.editors/feature.xml +++ /dev/null @@ -1,49 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<feature - id="com.android.ide.eclipse.editors" - label="Android Editors" - version="0.8.1.qualifier" - provider-name="The Android Open Source Project" - plugin="com.android.ide.eclipse.editors"> - - <description> - This feature provides Editors for Android files. - </description> - - <copyright> - Copyright (C) 2007 The Android Open Source Project - </copyright> - - <url> - <update label="Android Update Site" url="https://dl-ssl.google.com/android/eclipse/"/> - </url> - - <requires> - <import plugin="com.android.ide.eclipse.common"/> - <import plugin="org.eclipse.ui"/> - <import plugin="org.eclipse.core.runtime"/> - <import plugin="org.eclipse.core.resources"/> - <import plugin="org.eclipse.ui.editors"/> - <import plugin="org.eclipse.jface.text"/> - <import plugin="org.eclipse.ui.ide"/> - <import plugin="org.eclipse.wst.sse.ui"/> - <import plugin="org.eclipse.wst.xml.ui"/> - <import plugin="org.eclipse.wst.xml.core"/> - <import plugin="org.eclipse.wst.sse.core"/> - <import plugin="org.eclipse.ui.forms"/> - <import plugin="org.eclipse.jdt.core"/> - <import plugin="org.eclipse.ui.browser"/> - <import plugin="org.eclipse.jdt.ui"/> - <import plugin="org.eclipse.gef"/> - <import plugin="org.eclipse.ui.views"/> - <import plugin="org.eclipse.ui.console"/> - </requires> - - <plugin - id="com.android.ide.eclipse.editors" - download-size="0" - install-size="0" - version="0.0.0" - unpack="false"/> - -</feature> diff --git a/eclipse/features/com.android.ide.eclipse.platform/.project b/eclipse/features/com.android.ide.eclipse.platform/.project deleted file mode 100644 index 0bb8c34..0000000 --- a/eclipse/features/com.android.ide.eclipse.platform/.project +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<projectDescription> - <name>platform-feature</name> - <comment></comment> - <projects> - </projects> - <buildSpec> - <buildCommand> - <name>org.eclipse.pde.FeatureBuilder</name> - <arguments> - </arguments> - </buildCommand> - </buildSpec> - <natures> - <nature>org.eclipse.pde.FeatureNature</nature> - </natures> -</projectDescription> diff --git a/eclipse/features/com.android.ide.eclipse.platform/build.properties b/eclipse/features/com.android.ide.eclipse.platform/build.properties deleted file mode 100644 index 64f93a9..0000000 --- a/eclipse/features/com.android.ide.eclipse.platform/build.properties +++ /dev/null @@ -1 +0,0 @@ -bin.includes = feature.xml diff --git a/eclipse/features/com.android.ide.eclipse.platform/feature.xml b/eclipse/features/com.android.ide.eclipse.platform/feature.xml deleted file mode 100644 index aa4b166..0000000 --- a/eclipse/features/com.android.ide.eclipse.platform/feature.xml +++ /dev/null @@ -1,52 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<feature - id="com.android.ide.eclipse.platform" - label="Android Platform Tools" - version="0.8.1.qualifier" - provider-name="The Android Open Source Project"> - - <description> - This feature provides a ddms perspective and editor functionnality for Android. - </description> - - <copyright> - Copyright (C) 2007 The Android Open Source Project - </copyright> - - <url> - <update label="Android Update Site" url="https://android.corp.google.com/adt/"/> - </url> - - <requires> - <import plugin="org.eclipse.core.resources"/> - <import plugin="org.eclipse.core.runtime"/> - <import plugin="org.eclipse.jdt.core"/> - <import plugin="org.eclipse.ui.console"/> - <import plugin="org.eclipse.ui"/> - <import plugin="org.eclipse.jdt.ui"/> - <import plugin="org.eclipse.jface.text"/> - <import plugin="org.eclipse.ui.editors"/> - </requires> - - <plugin - id="com.android.ide.eclipse.common" - download-size="0" - install-size="0" - version="0.0.0" - unpack="false"/> - - <plugin - id="com.android.ide.eclipse.platform" - download-size="0" - install-size="0" - version="0.0.0" - unpack="false"/> - - <plugin - id="com.android.ide.eclipse.ddms" - download-size="0" - install-size="0" - version="0.0.0" - unpack="false"/> - -</feature> diff --git a/eclipse/features/com.android.ide.eclipse.tests/feature.xml b/eclipse/features/com.android.ide.eclipse.tests/feature.xml index b5a848c..2a3a74f 100644 --- a/eclipse/features/com.android.ide.eclipse.tests/feature.xml +++ b/eclipse/features/com.android.ide.eclipse.tests/feature.xml @@ -2,11 +2,11 @@ <feature id="com.android.ide.eclipse.tests" label="ADT Tests" - version="0.8.1.qualifier" + version="0.9.0.qualifier" provider-name="The Android Open Source Project"> - <copyright> - Copyright (C) 2007 The Android Open Source Project + <copyright> + Copyright (C) 2007 The Android Open Source Project </copyright> <requires> @@ -15,8 +15,6 @@ <import plugin="org.eclipse.core.resources"/> <import plugin="com.android.ide.eclipse.adt"/> <import plugin="org.junit"/> - <import plugin="com.android.ide.eclipse.common"/> - <import plugin="com.android.ide.eclipse.editors"/> <import plugin="org.eclipse.jdt.core"/> <import plugin="org.eclipse.jdt.launching"/> <import plugin="org.eclipse.ui.views"/> diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/.classpath b/eclipse/plugins/com.android.ide.eclipse.adt/.classpath index 4dd4cac..bbcdff9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/.classpath +++ b/eclipse/plugins/com.android.ide.eclipse.adt/.classpath @@ -5,5 +5,12 @@ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="lib" path="jarutils.jar"/> <classpathentry kind="lib" path="androidprefs.jar"/> + <classpathentry kind="lib" path="sdkstats.jar"/> + <classpathentry kind="lib" path="kxml2-2.3.0.jar"/> + <classpathentry kind="lib" path="layoutlib_api.jar"/> + <classpathentry kind="lib" path="layoutlib_utils.jar"/> + <classpathentry kind="lib" path="ninepatch.jar"/> + <classpathentry kind="lib" path="sdklib.jar"/> + <classpathentry kind="lib" path="sdkuilib.jar"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF index 9926074..a464d5c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF +++ b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF @@ -2,14 +2,20 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Android Development Toolkit Bundle-SymbolicName: com.android.ide.eclipse.adt;singleton:=true -Bundle-Version: 0.8.1.qualifier +Bundle-Version: 0.9.0.qualifier Bundle-ClassPath: ., jarutils.jar, - androidprefs.jar + androidprefs.jar, + sdkstats.jar, + kxml2-2.3.0.jar, + layoutlib_api.jar, + ninepatch.jar, + layoutlib_utils.jar, + sdklib.jar, + sdkuilib.jar Bundle-Activator: com.android.ide.eclipse.adt.AdtPlugin Bundle-Vendor: The Android Open Source Project -Require-Bundle: com.android.ide.eclipse.common, - com.android.ide.eclipse.ddms, +Require-Bundle: com.android.ide.eclipse.ddms, org.eclipse.core.runtime, org.eclipse.core.resources, org.eclipse.debug.core, @@ -26,11 +32,48 @@ Require-Bundle: com.android.ide.eclipse.common, org.eclipse.core.filesystem, org.eclipse.ui, org.eclipse.ui.ide, - org.eclipse.ui.forms + org.eclipse.ui.forms, + org.eclipse.gef, + org.eclipse.ui.browser, + org.eclipse.ui.views, + org.eclipse.wst.sse.core, + org.eclipse.wst.sse.ui, + org.eclipse.wst.xml.core, + org.eclipse.wst.xml.ui Eclipse-LazyStart: true -Export-Package: com.android.ide.eclipse.adt.build;x-friends:="com.android.ide.eclipse.tests", +Export-Package: com.android.ide.eclipse.adt, + com.android.ide.eclipse.adt.build;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.project;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.project.internal;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.adt.resources;x-friends:="com.android.ide.eclipse.tests" + com.android.ide.eclipse.adt.sdk;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.adt.wizards.newproject;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.common, + com.android.ide.eclipse.common.project, + com.android.ide.eclipse.common.resources, + com.android.ide.eclipse.editors;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.descriptors;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.layout;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.layout.descriptors;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.layout.parts;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.layout.uimodel;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.manifest;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.manifest.descriptors;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.manifest.model;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.manifest.pages;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.menu;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.menu.descriptors;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.resources;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.resources.configurations;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.resources.descriptors;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.resources.explorer;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.resources.manager;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.resources.manager.files;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.resources.uimodel;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.ui;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.ui.tree;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.uimodel;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.wizards;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.xml;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.editors.xml.descriptors;x-friends:="com.android.ide.eclipse.tests" diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/build.properties b/eclipse/plugins/com.android.ide.eclipse.adt/build.properties index 6d6c2e8..c7eb749 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/build.properties +++ b/eclipse/plugins/com.android.ide.eclipse.adt/build.properties @@ -5,6 +5,13 @@ bin.includes = plugin.xml,\ templates/,\ about.ini,\ jarutils.jar,\ - androidprefs.jar + androidprefs.jar,\ + sdkstats.jar,\ + kxml2-2.3.0.jar,\ + layoutlib_api.jar,\ + layoutlib_utils.jar,\ + ninepatch.jar,\ + sdklib.jar,\ + sdkuilib.jar source.. = src/ output.. = bin/ diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/add.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/add.png Binary files differindex eefc2ca..eefc2ca 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/add.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/add.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/az_sort.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/az_sort.png Binary files differindex 5d92f76..5d92f76 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/az_sort.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/az_sort.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/delete.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/delete.png Binary files differindex db5fab8..db5fab8 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/delete.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/delete.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/dimension.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/dimension.png Binary files differindex 10057c8..10057c8 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/dimension.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/dimension.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/down.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/down.png Binary files differindex 36cd223..36cd223 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/down.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/down.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/dpi.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/dpi.png Binary files differindex fae5e96..fae5e96 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/dpi.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/dpi.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/error.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/error.png Binary files differindex 1eecf2c..1eecf2c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/error.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/error.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/keyboard.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/keyboard.png Binary files differindex 7911a85..7911a85 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/keyboard.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/keyboard.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/language.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/language.png Binary files differindex a727dd5..a727dd5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/language.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/language.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/match.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/match.png Binary files differindex 7e939c2..7e939c2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/match.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/match.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/mcc.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/mcc.png Binary files differnew file mode 100644 index 0000000..4dc95d7 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/mcc.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/mnc.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/mnc.png Binary files differindex aefffe4..aefffe4 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/mnc.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/mnc.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/navpad.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/navpad.png Binary files differindex c2bb79a..c2bb79a 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/navpad.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/navpad.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/orientation.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/orientation.png Binary files differindex 423c3cd..423c3cd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/orientation.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/orientation.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/region.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/region.png Binary files differnew file mode 100644 index 0000000..9608cd6 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/region.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/text_input.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/text_input.png Binary files differindex b4ddc87..b4ddc87 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/text_input.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/text_input.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/touch.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/touch.png Binary files differindex 6536576..6536576 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/touch.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/touch.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/up.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/up.png Binary files differindex 35b9a46..35b9a46 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/up.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/up.png diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/warning.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/warning.png Binary files differindex ca3b6ed..ca3b6ed 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/warning.png +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/warning.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml index 51b1291..ade4646 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml +++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml @@ -2,6 +2,38 @@ <?eclipse version="3.2"?> <plugin> <extension + id="com.android.ide.eclipse.common.xmlProblem" + name="Android XML Format Problem" + point="org.eclipse.core.resources.markers"> + <super type="org.eclipse.core.resources.problemmarker"/> + <super type="org.eclipse.core.resources.textmarker"/> + <persistent value="true"/> + </extension> + <extension + id="com.android.ide.eclipse.common.aaptProblem" + name="Android AAPT Problem" + point="org.eclipse.core.resources.markers"> + <super type="org.eclipse.core.resources.problemmarker"/> + <super type="org.eclipse.core.resources.textmarker"/> + <persistent value="true"/> + </extension> + <extension + id="com.android.ide.eclipse.common.aidlProblem" + name="Android AIDL Problem" + point="org.eclipse.core.resources.markers"> + <super type="org.eclipse.core.resources.problemmarker"/> + <super type="org.eclipse.core.resources.textmarker"/> + <persistent value="true"/> + </extension> + <extension + id="com.android.ide.eclipse.common.androidProblem" + name="Android XML Content Problem" + point="org.eclipse.core.resources.markers"> + <super type="org.eclipse.core.resources.problemmarker"/> + <super type="org.eclipse.core.resources.textmarker"/> + <persistent value="true"/> + </extension> + <extension id="ResourceManagerBuilder" name="Android Resource Manager" point="org.eclipse.core.resources.builders"> @@ -47,7 +79,7 @@ <wizard canFinishEarly="false" category="com.android.ide.eclipse.wizards.category" - class="com.android.ide.eclipse.adt.project.internal.NewProjectWizard" + class="com.android.ide.eclipse.adt.wizards.newproject.NewProjectWizard" finalPerspective="org.eclipse.jdt.ui.JavaPerspective" hasPages="true" icon="icons/android.png" @@ -55,6 +87,18 @@ name="Android Project" preferredPerspectives="org.eclipse.jdt.ui.JavaPerspective" project="true"/> + <wizard + canFinishEarly="false" + category="com.android.ide.eclipse.wizards.category" + class="com.android.ide.eclipse.editors.wizards.NewXmlFileWizard" + finalPerspective="org.eclipse.jdt.ui.JavaPerspective" + hasPages="true" + icon="icons/android.png" + id="com.android.ide.eclipse.editors.wizards.NewXmlFileWizard" + name="Android XML File" + preferredPerspectives="org.eclipse.jdt.ui.JavaPerspective" + project="false"> + </wizard> </extension> <extension point="org.eclipse.debug.core.launchConfigurationTypes"> @@ -174,6 +218,13 @@ label="Create Aidl preprocess file for Parcelable classes" menubarPath="com.android.ide.eclipse.adt.AndroidTools/group1"/> <action + class="com.android.ide.eclipse.adt.project.NewXmlFileWizardAction" + enablesFor="1" + id="com.android.ide.eclipse.adt.project.NewXmlFileWizardAction" + label="New Resource File..." + menubarPath="com.android.ide.eclipse.adt.AndroidTools/group1"> + </action> + <action class="com.android.ide.eclipse.adt.project.ExportAction" enablesFor="1" id="com.android.ide.eclipse.adt.project.ExportAction" @@ -183,7 +234,7 @@ class="com.android.ide.eclipse.adt.project.ExportWizardAction" enablesFor="1" id="com.android.ide.eclipse.adt.project.ExportWizardAction" - label="Export Application..." + label="Export Signed Application Package..." menubarPath="com.android.ide.eclipse.adt.AndroidTools/group2"/> <action class="com.android.ide.eclipse.adt.project.FixProjectAction" @@ -209,30 +260,32 @@ class="com.android.ide.eclipse.adt.preferences.LaunchPreferencePage" id="com.android.ide.eclipse.adt.preferences.LaunchPreferencePage" name="Launch"/> + <page + category="com.android.ide.eclipse.preferences.main" + class="com.android.ide.eclipse.common.preferences.UsagePreferencePage" + id="com.android.ide.eclipse.common.preferences.UsagePreferencePage" + name="Usage Stats"> + </page> </extension> <extension point="org.eclipse.core.runtime.preferences"> <initializer class="com.android.ide.eclipse.adt.preferences.PreferenceInitializer"/> </extension> <extension - point="org.eclipse.ui.editors"> - <editor - class="com.android.ide.eclipse.adt.editors.java.ReadOnlyJavaEditor" - contributorClass="org.eclipse.ui.texteditor.BasicTextEditorActionContributor" - default="true" - filenames="R.java, Manifest.java" - icon="icons/android.png" - id="com.android.ide.eclipse.adt.editors.java.ReadOnlyJavaEditor" - name="Android Java Editor"/> - </extension> - <extension id="com.android.ide.eclipse.adt.adtProblem" - name="Generic ADT Problem" + name="Android ADT Problem" point="org.eclipse.core.resources.markers"> <super type="org.eclipse.core.resources.problemmarker"/> <persistent value="true"/> </extension> <extension + id="com.android.ide.eclipse.adt.targetProblem" + name="Android Target Problem" + point="org.eclipse.core.resources.markers"> + <super type="org.eclipse.core.resources.problemmarker"/> + <persistent value="false"/> + </extension> + <extension point="org.eclipse.ui.perspectiveExtensions"> <perspectiveExtension targetID="org.eclipse.jdt.ui.JavaPerspective"> <newWizardShortcut id="com.android.ide.eclipse.adt.project.NewProjectWizard" /> @@ -305,4 +358,110 @@ keyConfigurationId="org.eclipse.ui.defaultAcceleratorConfiguration"> </keyBinding> </extension> + <extension + point="org.eclipse.ui.decorators"> + <decorator + adaptable="true" + class="com.android.ide.eclipse.adt.project.FolderDecorator" + id="com.android.ide.eclipse.adt.project.FolderDecorator" + label="Android Decorator" + lightweight="true" + location="TOP_RIGHT" + objectClass="org.eclipse.core.resources.IFolder" + state="true"> + </decorator> + </extension> + <extension + point="org.eclipse.ui.editors"> + <editor + class="com.android.ide.eclipse.editors.manifest.ManifestEditor" + default="true" + filenames="AndroidManifest.xml" + icon="icons/android.png" + id="com.android.ide.eclipse.editors.manifest.ManifestEditor" + name="Android Manifest Editor"> + </editor> + <editor + class="com.android.ide.eclipse.editors.resources.ResourcesEditor" + default="false" + extensions="xml" + icon="icons/android.png" + id="com.android.ide.eclipse.editors.resources.ResourcesEditor" + name="Android Resource Editor"> + </editor> + <editor + class="com.android.ide.eclipse.editors.layout.LayoutEditor" + default="false" + extensions="xml" + icon="icons/android.png" + id="com.android.ide.eclipse.editors.layout.LayoutEditor" + matchingStrategy="com.android.ide.eclipse.editors.layout.MatchingStrategy" + name="Android Layout Editor"> + </editor> + <editor + class="com.android.ide.eclipse.editors.menu.MenuEditor" + default="false" + extensions="xml" + icon="icons/android.png" + id="com.android.ide.eclipse.editors.menu.MenuEditor" + name="Android Menu Editor"> + </editor> + <editor + class="com.android.ide.eclipse.editors.xml.XmlEditor" + default="false" + extensions="xml" + icon="icons/android.png" + id="com.android.ide.eclipse.editors.xml.XmlEditor" + name="Android Xml Resources Editor"> + </editor> + </extension> + <extension + point="org.eclipse.ui.views"> + <view + allowMultiple="false" + category="com.android.ide.eclipse.ddms.views.category" + class="com.android.ide.eclipse.editors.resources.explorer.ResourceExplorerView" + icon="icons/android.png" + id="com.android.ide.eclipse.editors.resources.explorer.ResourceExplorerView" + name="Resource Explorer"> + </view> + </extension> + <extension + point="org.eclipse.wst.sse.ui.editorConfiguration"> + <sourceViewerConfiguration + class="com.android.ide.eclipse.editors.manifest.ManifestSourceViewerConfig" + target="com.android.ide.eclipse.editors.manifest.ManifestEditor"> + </sourceViewerConfiguration> + <sourceViewerConfiguration + class="com.android.ide.eclipse.editors.resources.ResourcesSourceViewerConfig" + target="com.android.ide.eclipse.editors.resources.ResourcesEditor"> + </sourceViewerConfiguration> + <sourceViewerConfiguration + class="com.android.ide.eclipse.editors.layout.LayoutSourceViewerConfig" + target="com.android.ide.eclipse.editors.layout.LayoutEditor"> + </sourceViewerConfiguration> + <sourceViewerConfiguration + class="com.android.ide.eclipse.editors.menu.MenuSourceViewerConfig" + target="com.android.ide.eclipse.editors.menu.MenuEditor"> + </sourceViewerConfiguration> + <sourceViewerConfiguration + class="com.android.ide.eclipse.editors.xml.XmlSourceViewerConfig" + target="com.android.ide.eclipse.editors.xml.XmlEditor"> + </sourceViewerConfiguration> + </extension> + <extension + point="org.eclipse.ui.propertyPages"> + <page + adaptable="true" + class="com.android.ide.eclipse.adt.project.properties.AndroidPropertyPage" + id="com.android.ide.eclipse.adt.project.properties.AndroidPropertyPage" + name="Android" + nameFilter="*" + objectClass="org.eclipse.core.resources.IProject"> + <enabledWhen> + <test property="org.eclipse.jdt.launching.hasProjectNature" + args="com.android.ide.eclipse.adt.AndroidNature"/> + </enabledWhen> + </page> + </extension> </plugin> diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java index 6d52aa5..61b3f4d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt; + /** * Constant definition class.<br> * <br> @@ -39,6 +40,12 @@ public class AdtConstants { /** Generic marker for ADT errors. */ public final static String MARKER_ADT = AdtPlugin.PLUGIN_ID + ".adtProblem"; //$NON-NLS-1$ + /** Marker for Android Target errors. + * This is not cleared on each like other markers. Instead, it's cleared + * when a ContainerClasspathInitialized has succeeded in creating an + * {@link AndroidClasspathContainer}*/ + public final static String MARKER_TARGET = AdtPlugin.PLUGIN_ID + ".targetProblem"; //$NON-NLS-1$ + /** Build verbosity "Always". Those messages are always displayed. */ public final static int BUILD_ALWAYS = 0; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java index 9b24b07..d9c18cf 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java @@ -22,34 +22,55 @@ import com.android.ddmuilib.console.DdmConsole; import com.android.ddmuilib.console.IDdmConsole; import com.android.ide.eclipse.adt.build.DexWrapper; import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController; -import com.android.ide.eclipse.adt.debug.ui.SkinRepository; import com.android.ide.eclipse.adt.preferences.BuildPreferencePage; import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.adt.project.export.ExportWizard; import com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerInitializer; -import com.android.ide.eclipse.adt.resources.FrameworkResourceParser; +import com.android.ide.eclipse.adt.sdk.AndroidTargetParser; +import com.android.ide.eclipse.adt.sdk.LoadStatus; +import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.CommonPlugin; import com.android.ide.eclipse.common.EclipseUiHelper; import com.android.ide.eclipse.common.SdkStatsHelper; import com.android.ide.eclipse.common.StreamHelper; import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.ide.eclipse.common.project.ExportHelper; import com.android.ide.eclipse.common.project.ExportHelper.IExportCallback; -import com.android.ide.eclipse.common.resources.FrameworkResourceManager; import com.android.ide.eclipse.ddms.DdmsPlugin; import com.android.ide.eclipse.ddms.ImageLoader; - +import com.android.ide.eclipse.editors.IconFactory; +import com.android.ide.eclipse.editors.layout.LayoutEditor; +import com.android.ide.eclipse.editors.menu.MenuEditor; +import com.android.ide.eclipse.editors.resources.ResourcesEditor; +import com.android.ide.eclipse.editors.resources.manager.ProjectResources; +import com.android.ide.eclipse.editors.resources.manager.ResourceFolder; +import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType; +import com.android.ide.eclipse.editors.resources.manager.ResourceManager; +import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor; +import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IFileListener; +import com.android.ide.eclipse.editors.xml.XmlEditor; +import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.SdkConstants; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IMarkerDelta; import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Preferences; +import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.Preferences.IPropertyChangeListener; import org.eclipse.core.runtime.Preferences.PropertyChangeEvent; +import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; @@ -57,14 +78,21 @@ import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorDescriptor; +import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.console.ConsolePlugin; +import org.eclipse.ui.console.IConsole; import org.eclipse.ui.console.IConsoleConstants; import org.eclipse.ui.console.MessageConsole; import org.eclipse.ui.console.MessageConsoleStream; +import org.eclipse.ui.ide.IDE; +import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; @@ -81,6 +109,8 @@ import java.io.PrintStream; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; /** * The activator class controls the plug-in life cycle @@ -106,14 +136,17 @@ public class AdtPlugin extends AbstractUIPlugin { /** singleton instance */ private static AdtPlugin sPlugin; + private static Image sAndroidLogo; + private static ImageDescriptor sAndroidLogoDesc; + /** default store, provided by eclipse */ private IPreferenceStore mStore; /** cached location for the sdk folder */ private String mOsSdkLocation; - /** SDK Api Version */ - String mSdkApiVersion; + /** The global android console */ + private MessageConsole mAndroidConsole; /** Stream to write in the android console */ private MessageConsoleStream mAndroidConsoleStream; @@ -130,14 +163,14 @@ public class AdtPlugin extends AbstractUIPlugin { /** Color used in the error console */ private Color mRed; - private final ArrayList<IJavaProject> mPostDexProjects = new ArrayList<IJavaProject>(); + /** Load status of the SDK. Any access MUST be in a synchronized(mPostLoadProjects) block */ + private LoadStatus mSdkIsLoaded = LoadStatus.LOADING; + /** Project to update once the SDK is loaded. + * Any access MUST be in a synchronized(mPostLoadProjects) block */ + private final ArrayList<IJavaProject> mPostLoadProjects = new ArrayList<IJavaProject>(); - /** Boolean wrapper to run dialog in the UI thread, and still get the - * return code. - */ - private static final class BooleanWrapper { - public boolean b; - } + private ResourceMonitor mResourceMonitor; + private ArrayList<Runnable> mResourceRefreshListener = new ArrayList<Runnable>(); /** * Custom PrintStream for Dx output. This class overrides the method @@ -210,10 +243,14 @@ public class AdtPlugin extends AbstractUIPlugin { Display display = getDisplay(); + // set the default android console. + mAndroidConsole = new MessageConsole("Android", null); //$NON-NLS-1$ + ConsolePlugin.getDefault().getConsoleManager().addConsoles( + new IConsole[] { mAndroidConsole }); + // get the stream to write in the android console. - MessageConsole androidConsole = CommonPlugin.getDefault().getAndroidConsole(); - mAndroidConsoleStream = androidConsole.newMessageStream(); - mAndroidConsoleErrorStream = androidConsole.newMessageStream(); + mAndroidConsoleStream = mAndroidConsole.newMessageStream(); + mAndroidConsoleErrorStream = mAndroidConsole.newMessageStream(); mRed = new Color(display, 0xFF, 0x00, 0x00); // because this can be run, in some cases, by a non ui thread, and beccause @@ -267,22 +304,19 @@ public class AdtPlugin extends AbstractUIPlugin { // get the SDK location and build id. if (checkSdkLocationAndId()) { - // if sdk if valid, reparse the skin folder - SkinRepository.getInstance().parseFolder(getOsSkinFolder()); + // if sdk if valid, reparse it + + // add the current Android project to the list of projects to be updated + // after the SDK is reloaded + synchronized (mPostLoadProjects) { + // get the project to refresh. + IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects(); + mPostLoadProjects.addAll(Arrays.asList(androidProjects)); + } + + // parse the SDK resources at the new location + parseSdkContent(); } - - // parse the SDK resources at the new location - parseSdkContent(); - - // get the project to refresh. - IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects(); - - // Setup the new container for each project. By providing new instances of - // AndroidClasspathContainer, this will force JDT to call - // IClasspathContainer#getClasspathEntries() again and receive the new - // path to the framework jar. - AndroidClasspathContainerInitializer.updateProjects(androidProjects); - } else if (PREFS_BUILD_VERBOSITY.equals(property)) { mBuildVerbosity = BuildPreferencePage.getBuildLevel( mStore.getString(PREFS_BUILD_VERBOSITY)); @@ -299,20 +333,11 @@ public class AdtPlugin extends AbstractUIPlugin { } // check the location of SDK - if (checkSdkLocationAndId()) { - // if sdk if valid, parse the skin folder - SkinRepository.getInstance().parseFolder(getOsSkinFolder()); - - // parse the SDK resources. - parseSdkContent(); - } + final boolean isSdkLocationValid = checkSdkLocationAndId(); mBuildVerbosity = BuildPreferencePage.getBuildLevel( mStore.getString(PREFS_BUILD_VERBOSITY)); - // Ping the usage start server. - pingUsageServer(); - // create the loader that's able to load the images mLoader = new ImageLoader(this); @@ -356,18 +381,31 @@ public class AdtPlugin extends AbstractUIPlugin { dialog.open(); } }); - - /* The Editors plugin must be started as soon as Android projects are opened or created, - * in order to properly set default editors on the layout/values XML files. - * - * This ensures that the default editors is really only set when a new XML file - * is added to the workspace (IResourceDelta.ADDED event), through project creation or - * manual add. - * Other methods would force to go through existing projects when the Editors plugin is - * started, and set the default editors for their XML files, possibly erasing user set - * default editors. - */ - startEditorsPlugin(); + + // initialize editors + startEditors(); + + // Ping the usage server and parse the SDK content. + // This is deferred in separate jobs to avoid blocking the bundle start. + // We also serialize them to avoid too many parallel jobs when Eclipse starts. + Job pingJob = createPingUsageServerJob(); + pingJob.addJobChangeListener(new JobChangeAdapter() { + @Override + public void done(IJobChangeEvent event) { + super.done(event); + + // Once the ping job is finished, start the SDK parser + if (isSdkLocationValid) { + // parse the SDK resources. + parseSdkContent(); + } + } + }); + // build jobs are run after other interactive jobs + pingJob.setPriority(Job.BUILD); + // Wait 2 seconds before starting the ping job. This leaves some time to the + // other bundles to initialize. + pingJob.schedule(2000 /*milliseconds*/); } /* @@ -379,6 +417,8 @@ public class AdtPlugin extends AbstractUIPlugin { public void stop(BundleContext context) throws Exception { super.stop(context); + stopEditors(); + DexWrapper.unloadDex(); mRed.dispose(); @@ -418,37 +458,22 @@ public class AdtPlugin extends AbstractUIPlugin { /** Returns the adb path relative to the sdk folder */ public static String getOsRelativeAdb() { - return AndroidConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_ADB; + return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_ADB; } /** Returns the aapt path relative to the sdk folder */ public static String getOsRelativeAapt() { - return AndroidConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_AAPT; + return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_AAPT; } /** Returns the emulator path relative to the sdk folder */ public static String getOsRelativeEmulator() { - return AndroidConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_EMULATOR; + return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_EMULATOR; } /** Returns the aidl path relative to the sdk folder */ public static String getOsRelativeAidl() { - return AndroidConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_AIDL; - } - - /** Returns the framework jar path relative to the sdk folder */ - public static String getOsRelativeFramework() { - return AndroidConstants.FN_FRAMEWORK_LIBRARY; - } - - /** Returns the android sources path relative to the sdk folder */ - public static String getOsRelativeAndroidSources() { - return AndroidConstants.FD_ANDROID_SOURCES; - } - - /** Returns the framework jar path relative to the sdk folder */ - public static String getOsRelativeAttrsXml() { - return AndroidConstants.OS_SDK_LIBS_FOLDER + AndroidConstants.FN_ATTRS_XML; + return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_AIDL; } /** Returns the absolute adb path */ @@ -458,7 +483,7 @@ public class AdtPlugin extends AbstractUIPlugin { /** Returns the absolute traceview path */ public static String getOsAbsoluteTraceview() { - return getOsSdkFolder() + AndroidConstants.OS_SDK_TOOLS_FOLDER + + return getOsSdkFolder() + SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_TRACEVIEW; } @@ -467,21 +492,6 @@ public class AdtPlugin extends AbstractUIPlugin { return getOsSdkFolder() + getOsRelativeAapt(); } - /** Returns the absolute sdk framework path */ - public static String getOsAbsoluteFramework() { - return getOsSdkFolder() + getOsRelativeFramework(); - } - - /** Returns the absolute android sources path in the sdk */ - public static String getOsAbsoluteAndroidSources() { - return getOsSdkFolder() + getOsRelativeAndroidSources(); - } - - /** Returns the absolute attrs.xml path */ - public static String getOsAbsoluteAttrsXml() { - return getOsSdkFolder() + getOsRelativeAttrsXml(); - } - /** Returns the absolute emulator path */ public static String getOsAbsoluteEmulator() { return getOsSdkFolder() + getOsRelativeEmulator(); @@ -492,16 +502,6 @@ public class AdtPlugin extends AbstractUIPlugin { return getOsSdkFolder() + getOsRelativeAidl(); } - /** Returns the absolute path to the aidl framework import file. */ - public static String getOsAbsoluteFrameworkAidl() { - return getOsSdkFolder() + AndroidConstants.OS_SDK_LIBS_FOLDER + - AndroidConstants.FN_FRAMEWORK_AIDL; - } - - public static String getOsSdkSamplesFolder() { - return getOsSdkFolder() + AndroidConstants.OS_SDK_SAMPLES_FOLDER; - } - /** * Returns a Url file path to the javaDoc folder. */ @@ -526,11 +526,7 @@ public class AdtPlugin extends AbstractUIPlugin { } public static String getOsSdkToolsFolder() { - return getOsSdkFolder() + AndroidConstants.OS_SDK_TOOLS_FOLDER; - } - - public static String getOsSkinFolder() { - return getOsSdkFolder() + AndroidConstants.OS_SDK_SKINS_FOLDER; + return getOsSdkFolder() + SdkConstants.OS_SDK_TOOLS_FOLDER; } public static synchronized boolean getAutoResRefresh() { @@ -540,18 +536,6 @@ public class AdtPlugin extends AbstractUIPlugin { return sPlugin.mStore.getBoolean(PREFS_RES_AUTO_REFRESH); } - /** - * Returns the SDK build id. - * @return a string containing the SDK build id, or null it it is unknownn. - */ - public static synchronized String getSdkApiVersion() { - if (sPlugin != null) { - return sPlugin.mSdkApiVersion; - } - - return null; - } - public static synchronized int getBuildVerbosity() { if (sPlugin != null) { return sPlugin.mBuildVerbosity; @@ -705,23 +689,25 @@ public class AdtPlugin extends AbstractUIPlugin { final Display display = getDisplay(); // we need to ask the user what he wants to do. - final BooleanWrapper wrapper = new BooleanWrapper(); + final boolean[] result = new boolean[1]; display.syncExec(new Runnable() { public void run() { Shell shell = display.getActiveShell(); - wrapper.b = MessageDialog.openQuestion(shell, title, message); + result[0] = MessageDialog.openQuestion(shell, title, message); } }); - return wrapper.b; + return result[0]; } /** * Logs a message to the default Eclipse log. * - * @param severity The severity code. Valid values are: {@link IStatus#OK}, {@link IStatus#ERROR}, - * {@link IStatus#INFO}, {@link IStatus#WARNING} or {@link IStatus#CANCEL}. + * @param severity The severity code. Valid values are: {@link IStatus#OK}, + * {@link IStatus#ERROR}, {@link IStatus#INFO}, {@link IStatus#WARNING} or + * {@link IStatus#CANCEL}. * @param format The format string, like for {@link String#format(String, Object...)}. - * @param args The arguments for the format string, like for {@link String#format(String, Object...)}. + * @param args The arguments for the format string, like for + * {@link String#format(String, Object...)}. */ public static void log(int severity, String format, Object ... args) { String message = String.format(format, args); @@ -845,7 +831,7 @@ public class AdtPlugin extends AbstractUIPlugin { // now make sure it's not docked. ConsolePlugin.getDefault().getConsoleManager().showConsoleView( - CommonPlugin.getDefault().getAndroidConsole()); + AdtPlugin.getDefault().getAndroidConsole()); } /** @@ -883,21 +869,17 @@ public class AdtPlugin extends AbstractUIPlugin { } /** - * Adds a {@link IJavaProject} to a list of projects to be recompiled once dx.jar is loaded. - * @param javaProject + * Returns whether the Sdk has been loaded. If the SDK has not been loaded, the given + * <var>project</var> is added to a list of projects to recompile after the SDK is loaded. */ - public void addPostDexProject(IJavaProject javaProject) { - synchronized (mPostDexProjects) { - if (DexWrapper.getStatus() == DexWrapper.LoadStatus.LOADED) { - // Setup the new container for each project. By providing new instances of - // AndroidClasspathContainer, this will force JDT to call - // IClasspathContainer#getClasspathEntries() again and receive the new - // path to the framework jar, and the project will be recompiled. - AndroidClasspathContainerInitializer.updateProjects(new IJavaProject [] { - javaProject }); - } else { - mPostDexProjects.add(javaProject); + public LoadStatus getSdkLoadStatus(IJavaProject project) { + synchronized (mPostLoadProjects) { + // only add the project to the list, if we are still loading. + if (mSdkIsLoaded == LoadStatus.LOADING && project != null) { + mPostLoadProjects.add(project); } + + return mSdkIsLoaded; } } @@ -907,9 +889,6 @@ public class AdtPlugin extends AbstractUIPlugin { * @return false if the location is not correct. */ private boolean checkSdkLocationAndId() { - // Reset the sdk build first in case the SDK is invalid and we abort. - mSdkApiVersion = null; - if (mOsSdkLocation == null || mOsSdkLocation.length() == 0) { displayError(Messages.Dialog_Title_SDK_Location, Messages.SDK_Not_Setup); return false; @@ -949,17 +928,16 @@ public class AdtPlugin extends AbstractUIPlugin { String.format(Messages.Could_Not_Find_Folder, osSdkLocation)); } - String osTools = osSdkLocation + AndroidConstants.OS_SDK_TOOLS_FOLDER; + String osTools = osSdkLocation + SdkConstants.OS_SDK_TOOLS_FOLDER; File toolsFolder = new File(osTools); if (toolsFolder.isDirectory() == false) { return errorHandler.handleError( String.format(Messages.Could_Not_Find_Folder_In_SDK, - AndroidConstants.FD_TOOLS, osSdkLocation)); + SdkConstants.FD_TOOLS, osSdkLocation)); } // check the path to various tools we use String[] filesToCheck = new String[] { - osSdkLocation + getOsRelativeFramework(), osSdkLocation + getOsRelativeAdb(), osSdkLocation + getOsRelativeAapt(), osSdkLocation + getOsRelativeAidl(), @@ -990,118 +968,106 @@ public class AdtPlugin extends AbstractUIPlugin { } /** - * Pings the usage start server. + * Creates a job than can ping the usage server. */ - private void pingUsageServer() { + private Job createPingUsageServerJob() { // In order to not block the plugin loading, so we spawn another thread. - new Thread("Ping!") { //$NON-NLS-1$ + Job job = new Job("Android SDK Ping") { // Job name, visible in progress view @Override - public void run() { - // get the version of the plugin - String versionString = (String) getBundle().getHeaders().get( - Constants.BUNDLE_VERSION); - Version version = new Version(versionString); - - SdkStatsHelper.pingUsageServer("adt", version); //$NON-NLS-1$ - } - }.start(); - } - - /** - * Starts the Editors plugin. - * <p/> - * Since we do not want any dependencies between the plugins (Editors is an optional - * plugin not needed for Android development), we attempt to start the plugin through - * OSGi directly. - * <p/> - * This is done in another thread to not delay the start of this plugin. - */ - private void startEditorsPlugin() { - new Thread() { - @Override - public void run() { + protected IStatus run(IProgressMonitor monitor) { try { - // look for the bundle of the Editors plugin - Bundle editorsBundle = Platform.getBundle(AndroidConstants.EDITORS_PLUGIN_ID); - if (editorsBundle != null) { - // we only start if the bundle is installed and not started. - // STARTING means that its start is pending a triggering. - int bundleState = editorsBundle.getState(); - if ((bundleState & (Bundle.RESOLVED | Bundle.INSTALLED - | Bundle.STARTING)) != 0) { - // Attempt to start it. - // START_TRANSIENT is used because we don't want - // to change the auto start value. - editorsBundle.start(Bundle.START_TRANSIENT); - } - } - } catch (Exception e) { - log(e, Messages.AdtPlugin_Failed_To_Start_s, AndroidConstants.EDITORS_PLUGIN_ID); + + // get the version of the plugin + String versionString = (String) getBundle().getHeaders().get( + Constants.BUNDLE_VERSION); + Version version = new Version(versionString); + + SdkStatsHelper.pingUsageServer("editors", version); //$NON-NLS-1$ + + return Status.OK_STATUS; + } catch (Throwable t) { + log(t, "pingUsageServer failed"); //$NON-NLS-1$ + return new Status(IStatus.ERROR, PLUGIN_ID, + "pingUsageServer failed", t); } } - }.start(); + }; + return job; } /** - * Parses the SDK resources and set them in the {@link FrameworkResourceManager}. + * Parses the SDK resources. */ private void parseSdkContent() { // Perform the update in a thread (here an Eclipse runtime job) // since this should never block the caller (especially the start method) - new Job(Messages.AdtPlugin_Android_SDK_Content_Loader) { + Job job = new Job(Messages.AdtPlugin_Android_SDK_Content_Loader) { + @SuppressWarnings("unchecked") @Override protected IStatus run(IProgressMonitor monitor) { - try { - SubMonitor progress = null; - try { - progress = SubMonitor.convert(monitor, Messages.AdtPlugin_Parsing_Resources, 100); + try { + SubMonitor progress = SubMonitor.convert(monitor, + "Initialize SDK Manager", 100); + + Sdk sdk = Sdk.loadSdk(mOsSdkLocation); + + if (sdk != null) { - // load the values. - FrameworkResourceParser parser = new FrameworkResourceParser(); - parser.parse(mOsSdkLocation, FrameworkResourceManager.getInstance(), - progress); - - // set the location of the layout lib jar file. - FrameworkResourceManager.getInstance().setLayoutLibLocation( - mOsSdkLocation + AndroidConstants.OS_SDK_LIBS_LAYOUTLIB_JAR); - FrameworkResourceManager.getInstance().setFrameworkResourcesLocation( - mOsSdkLocation + AndroidConstants.OS_SDK_RESOURCES_FOLDER); - FrameworkResourceManager.getInstance().setFrameworkFontLocation( - mOsSdkLocation + AndroidConstants.OS_SDK_FONTS_FOLDER); - } catch (Throwable e) { - AdtPlugin.log(e, "Android SDK Resource Parser failed"); //$NON-NLS-1$ - AdtPlugin.printErrorToConsole(Messages.AdtPlugin_Android_SDK_Resource_Parser, - Messages.AdtPlugin_Failed_To_Parse_s + e.getMessage()); + progress.setTaskName(Messages.AdtPlugin_Parsing_Resources); - return new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage(), e); - } finally { - if (progress != null) { - progress.worked(100); + for (IAndroidTarget target : sdk.getTargets()) { + IStatus status = new AndroidTargetParser(target).run(progress); + if (status.getCode() != IStatus.OK) { + synchronized (mPostLoadProjects) { + mSdkIsLoaded = LoadStatus.FAILED; + mPostLoadProjects.clear(); + } + return status; + } } - } - - try { - progress = SubMonitor.convert(monitor, Messages.AdtPlugin_Parsing_Resources, 20); + + // FIXME: move this per platform, or somewhere else. + progress = SubMonitor.convert(monitor, + Messages.AdtPlugin_Parsing_Resources, 20); DexWrapper.unloadDex(); IStatus res = DexWrapper.loadDex( mOsSdkLocation + AndroidConstants.OS_SDK_LIBS_DX_JAR); if (res != Status.OK_STATUS) { + synchronized (mPostLoadProjects) { + mSdkIsLoaded = LoadStatus.FAILED; + mPostLoadProjects.clear(); + } return res; - } else { + } + + synchronized (mPostLoadProjects) { + mSdkIsLoaded = LoadStatus.LOADED; + // update the project that needs recompiling. - synchronized (mPostDexProjects) { - if (mPostDexProjects.size() > 0) { - IJavaProject[] array = mPostDexProjects.toArray( - new IJavaProject[mPostDexProjects.size()]); - AndroidClasspathContainerInitializer.updateProjects(array); - mPostDexProjects.clear(); - } + if (mPostLoadProjects.size() > 0) { + IJavaProject[] array = mPostLoadProjects.toArray( + new IJavaProject[mPostLoadProjects.size()]); + AndroidClasspathContainerInitializer.updateProjects(array); + mPostLoadProjects.clear(); } } - } finally { - if (progress != null) { - progress.worked(20); + } + + // Notify resource changed listeners + progress.subTask("Refresh UI"); + progress.setWorkRemaining(mResourceRefreshListener.size()); + + // Clone the list before iterating, to avoid Concurrent Modification + // exceptions + List<Runnable> listeners = (List<Runnable>)mResourceRefreshListener.clone(); + for (Runnable listener : listeners) { + try { + AdtPlugin.getDisplay().syncExec(listener); + } catch (Exception e) { + AdtPlugin.log(e, "ResourceRefreshListener Failed"); //$NON-NLS-1$ + } finally { + progress.worked(1); } } } finally { @@ -1112,6 +1078,252 @@ public class AdtPlugin extends AbstractUIPlugin { return Status.OK_STATUS; } - }.schedule(); + }; + job.setPriority(Job.BUILD); // build jobs are run after other interactive jobs + job.schedule(); + } + + /** Returns the global android console */ + public MessageConsole getAndroidConsole() { + return mAndroidConsole; + } + + // ----- Methods for Editors ------- + + public void startEditors() { + sAndroidLogoDesc = imageDescriptorFromPlugin(AdtPlugin.PLUGIN_ID, + "/icons/android.png"); //$NON-NLS-1$ + sAndroidLogo = sAndroidLogoDesc.createImage(); + + // get the stream to write in the android console. + MessageConsole androidConsole = AdtPlugin.getDefault().getAndroidConsole(); + mAndroidConsoleStream = androidConsole.newMessageStream(); + + mAndroidConsoleErrorStream = androidConsole.newMessageStream(); + mRed = new Color(getDisplay(), 0xFF, 0x00, 0x00); + + // because this can be run, in some cases, by a non ui thread, and beccause + // changing the console properties update the ui, we need to make this change + // in the ui thread. + getDisplay().asyncExec(new Runnable() { + public void run() { + mAndroidConsoleErrorStream.setColor(mRed); + } + }); + + // Add a resource listener to handle compiled resources. + IWorkspace ws = ResourcesPlugin.getWorkspace(); + mResourceMonitor = ResourceMonitor.startMonitoring(ws); + + if (mResourceMonitor != null) { + try { + setupDefaultEditor(mResourceMonitor); + ResourceManager.setup(mResourceMonitor); + } catch (Throwable t) { + log(t, "ResourceManager.setup failed"); //$NON-NLS-1$ + } + } + } + + /** + * The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code> + * method saves this plug-in's preference and dialog stores and shuts down + * its image registry (if they are in use). Subclasses may extend this + * method, but must send super <b>last</b>. A try-finally statement should + * be used where necessary to ensure that <code>super.shutdown()</code> is + * always done. + * + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stopEditors() { + sAndroidLogo.dispose(); + + IconFactory.getInstance().Dispose(); + + // Remove the resource listener that handles compiled resources. + IWorkspace ws = ResourcesPlugin.getWorkspace(); + ResourceMonitor.stopMonitoring(ws); + + mRed.dispose(); + } + + /** + * Returns an Image for the small Android logo. + * + * Callers should not dispose it. + */ + public static Image getAndroidLogo() { + return sAndroidLogo; + } + + /** + * Returns an {@link ImageDescriptor} for the small Android logo. + * + * Callers should not dispose it. + */ + public static ImageDescriptor getAndroidLogoDesc() { + return sAndroidLogoDesc; + } + + /** + * Returns the ResourceMonitor object. + */ + public ResourceMonitor getResourceMonitor() { + return mResourceMonitor; + } + + /** + * Sets up the editor to register default editors for resource files when needed. + * + * This is called by the {@link AdtPlugin} during initialization. + * + * @param monitor The main Resource Monitor object. + */ + public void setupDefaultEditor(ResourceMonitor monitor) { + monitor.addFileListener(new IFileListener() { + + private static final String UNKNOWN_EDITOR = "unknown-editor"; //$NON-NLS-1$ + + /* (non-Javadoc) + * Sent when a file changed. + * @param file The file that changed. + * @param markerDeltas The marker deltas for the file. + * @param kind The change kind. This is equivalent to + * {@link IResourceDelta#accept(IResourceDeltaVisitor)} + * + * @see IFileListener#fileChanged + */ + public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) { + if (AndroidConstants.EXT_XML.equals(file.getFileExtension())) { + // The resources files must have a file path similar to + // project/res/.../*.xml + // There is no support for sub folders, so the segment count must be 4 + if (file.getFullPath().segmentCount() == 4) { + // check if we are inside the res folder. + String segment = file.getFullPath().segment(1); + if (segment.equalsIgnoreCase(AndroidConstants.FD_RESOURCES)) { + // we are inside a res/ folder, get the actual ResourceFolder + ProjectResources resources = ResourceManager.getInstance(). + getProjectResources(file.getProject()); + + // This happens when importing old Android projects in Eclipse + // that lack the container (probably because resources fail to build + // properly.) + if (resources == null) { + log(IStatus.INFO, + "getProjectResources failed for path %1$s in project %2$s", //$NON-NLS-1$ + file.getFullPath().toOSString(), + file.getProject().getName()); + return; + } + + ResourceFolder resFolder = resources.getResourceFolder( + (IFolder)file.getParent()); + + if (resFolder != null) { + if (kind == IResourceDelta.ADDED) { + resourceAdded(file, resFolder.getType()); + } else if (kind == IResourceDelta.CHANGED) { + resourceChanged(file, resFolder.getType()); + } + } else { + // if the res folder is null, this means the name is invalid, + // in this case we remove whatever android editors that was set + // as the default editor. + IEditorDescriptor desc = IDE.getDefaultEditor(file); + String editorId = desc.getId(); + if (editorId.startsWith(AndroidConstants.EDITORS_NAMESPACE)) { + // reset the default editor. + IDE.setDefaultEditor(file, null); + } + } + } + } + } + } + + private void resourceAdded(IFile file, ResourceFolderType type) { + // set the default editor based on the type. + if (type == ResourceFolderType.LAYOUT) { + IDE.setDefaultEditor(file, LayoutEditor.ID); + } else if (type == ResourceFolderType.DRAWABLE + || type == ResourceFolderType.VALUES) { + IDE.setDefaultEditor(file, ResourcesEditor.ID); + } else if (type == ResourceFolderType.MENU) { + IDE.setDefaultEditor(file, MenuEditor.ID); + } else if (type == ResourceFolderType.XML) { + if (XmlEditor.canHandleFile(file)) { + IDE.setDefaultEditor(file, XmlEditor.ID); + } else { + // set a property to determine later if the XML can be handled + QualifiedName qname = new QualifiedName( + AdtPlugin.PLUGIN_ID, + UNKNOWN_EDITOR); + try { + file.setPersistentProperty(qname, "1"); + } catch (CoreException e) { + // pass + } + } + } + } + + private void resourceChanged(IFile file, ResourceFolderType type) { + if (type == ResourceFolderType.XML) { + IEditorDescriptor ed = IDE.getDefaultEditor(file); + if (ed == null || ed.getId() != XmlEditor.ID) { + QualifiedName qname = new QualifiedName( + AdtPlugin.PLUGIN_ID, + UNKNOWN_EDITOR); + String prop = null; + try { + prop = file.getPersistentProperty(qname); + } catch (CoreException e) { + // pass + } + if (prop != null && XmlEditor.canHandleFile(file)) { + try { + // remove the property & set editor + file.setPersistentProperty(qname, null); + IWorkbenchPage page = PlatformUI.getWorkbench(). + getActiveWorkbenchWindow().getActivePage(); + + IEditorPart oldEditor = page.findEditor(new FileEditorInput(file)); + if (oldEditor != null && + AdtPlugin.displayPrompt("Android XML Editor", + String.format("The file you just saved as been recognized as a file that could be better handled using the Android XML Editor. Do you want to edit '%1$s' using the Android XML editor instead?", + file.getFullPath()))) { + IDE.setDefaultEditor(file, XmlEditor.ID); + IEditorPart newEditor = page.openEditor( + new FileEditorInput(file), + XmlEditor.ID, + true, /* activate */ + IWorkbenchPage.MATCH_NONE); + + if (newEditor != null) { + page.closeEditor(oldEditor, true /* save */); + } + } + } catch (CoreException e) { + // setPersistentProperty or page.openEditor may have failed + } + } + } + } + } + + }, IResourceDelta.ADDED | IResourceDelta.CHANGED); + } + + public void addResourceChangedListener(Runnable resourceRefreshListener) { + mResourceRefreshListener.add(resourceRefreshListener); + } + + public void removeResourceChangedListener(Runnable resourceRefreshListener) { + mResourceRefreshListener.remove(resourceRefreshListener); + } + + public static synchronized OutputStream getErrorStream() { + return sPlugin.mAndroidConsoleErrorStream; } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/VersionCheck.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/VersionCheck.java index bac940a..6d85af3 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/VersionCheck.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/VersionCheck.java @@ -16,9 +16,8 @@ package com.android.ide.eclipse.adt; -import com.android.ddmlib.Device; import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler; -import com.android.ide.eclipse.common.AndroidConstants; +import com.android.sdklib.SdkConstants; import org.osgi.framework.Constants; import org.osgi.framework.Version; @@ -42,19 +41,6 @@ import java.util.regex.Pattern; * */ final class VersionCheck { - - /** Pattern to get the SDK build incremental version from the - * <code>$SDK/tools/lib/build.prop file</code>. */ - private final static Pattern sBuildVersionPattern = Pattern.compile( - "^" + Device.PROP_BUILD_VERSION + "=(.+)$"); //$NON-NLS-1$ - - /** - * Pattern to parse release type SDK version number. This parses the content read with - * <code>sBuildIdPattern</code>. - */ - private final static Pattern sSdkVersionPattern = Pattern.compile( - "^(\\d+)\\.(\\d+)_r(\\d+)$", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ - /** * Pattern to get the minimum plugin version supported by the SDK. This is read from * the file <code>$SDK/tools/lib/plugin.prop</code>. @@ -69,58 +55,7 @@ final class VersionCheck { */ public static boolean checkVersion(String osSdkPath, CheckSdkErrorHandler errorHandler) { AdtPlugin plugin = AdtPlugin.getDefault(); - String osLibs = osSdkPath + AndroidConstants.OS_SDK_LIBS_FOLDER; - - /* - * All plugins should work with all SDKs. Newer SDKs may require a newer plugin - * but this is handled below. - * Still, we need to grab the SDK version from this file. This is used - * to compare to running emulator/device when launching run/debug sessions. - */ - try { - FileReader reader = new FileReader(osLibs + AndroidConstants.FN_BUILD_PROP); - BufferedReader bReader = new BufferedReader(reader); - String line; - while ((line = bReader.readLine()) != null) { - Matcher m = sBuildVersionPattern.matcher(line); - if (m.matches()) { - plugin.mSdkApiVersion = m.group(1).trim(); - - /* - * No checks on the version at the moment. - */ - /* - if (plugin.mSdkBuildVersion != null) { - // attempt to get version number from the build id - m = sSdkVersionPattern.matcher(plugin.mSdkBuildVersion); - if (m.matches()) { - // get the platform version number - int platformMajor = Integer.parseInt(m.group(1)); - int platformMinor = Integer.parseInt(m.group(2)); - @SuppressWarnings("unused") //$NON-NLS-1$ - int sdkRelease = Integer.parseInt(m.group(3)); - - if (platformMajor != 0 || platformMinor != 9) { - return errorHandler.handleError(String.format( - "This version of ADT requires the Android SDK version 0.9\n\nCurrent version is %1$s.\n\nPlease update your SDK to the latest version.", - plugin.mSdkBuildVersion)); - } - } else { - // unknown version format. - AdtPlugin.printErrorToConsole( - (Object)String.format(Messages.VersionCheck_Unable_To_Parse_Version_s, - plugin.mSdkBuildVersion)); - } - } - */ - break; - } - } - } catch (FileNotFoundException e) { - // the build id will be null, and this is handled by the builders. - } catch (IOException e) { - // the build id will be null, and this is handled by the builders. - } + String osLibs = osSdkPath + SdkConstants.OS_SDK_TOOLS_LIB_FOLDER; // get the plugin property file, and grab the minimum plugin version required // to work with the sdk @@ -128,7 +63,7 @@ final class VersionCheck { int minMinorVersion = -1; int minMicroVersion = -1; try { - FileReader reader = new FileReader(osLibs + AndroidConstants.FN_PLUGIN_PROP); + FileReader reader = new FileReader(osLibs + SdkConstants.FN_PLUGIN_PROP); BufferedReader bReader = new BufferedReader(reader); String line; while ((line = bReader.readLine()) != null) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java index 0b1b647..4d16120 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java @@ -19,6 +19,8 @@ package com.android.ide.eclipse.adt.build; import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.project.ProjectHelper; +import com.android.ide.eclipse.adt.sdk.LoadStatus; +import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.jarutils.DebugKeyProvider; @@ -28,6 +30,7 @@ import com.android.jarutils.DebugKeyProvider.IKeyGenOutput; import com.android.jarutils.DebugKeyProvider.KeytoolException; import com.android.jarutils.SignedJarBuilder.IZipEntryFilter; import com.android.prefs.AndroidLocation.AndroidLocationException; +import com.android.sdklib.IAndroidTarget; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; @@ -44,6 +47,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; @@ -296,6 +300,15 @@ public class ApkBuilder extends BaseBuilder { saveProjectBooleanProperty(PROPERTY_PACKAGE_RESOURCES, mPackageResources); saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage); + // At this point, we can abort the build if we have to, as we have computed + // our resource delta and stored the result. + + // check if we have finished loading the SDK. + if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) { + // we exit silently + return referencedProjects; + } + // Now check the compiler compliance level, not displaying the error // message since this is not the first builder. if (ProjectHelper.checkCompilerCompliance(getProject()) @@ -502,7 +515,8 @@ public class ApkBuilder extends BaseBuilder { commandArray.add(osAssetsPath); } commandArray.add("-I"); //$NON-NLS-1$ - commandArray.add(AdtPlugin.getOsAbsoluteFramework()); + commandArray.add( + Sdk.getCurrent().getTarget(project).getPath(IAndroidTarget.ANDROID_JAR)); commandArray.add("-F"); //$NON-NLS-1$ commandArray.add(osOutFilePath); @@ -584,16 +598,9 @@ public class ApkBuilder extends BaseBuilder { DexWrapper wrapper = DexWrapper.getWrapper(); if (wrapper == null) { - if (DexWrapper.getStatus() == DexWrapper.LoadStatus.FAILED) { + if (DexWrapper.getStatus() == LoadStatus.FAILED) { throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, Messages.ApkBuilder_UnableBuild_Dex_Not_loaded)); - } else { - // means we haven't loaded the dex jar yet. - // We set the project to be recompiled after dex is loaded. - AdtPlugin.getDefault().addPostDexProject(javaProject); - - // and we exit silently - return false; } } @@ -677,7 +684,7 @@ public class ApkBuilder extends BaseBuilder { } // TODO: get the store type from somewhere else. - DebugKeyProvider provider = new DebugKeyProvider(null /* storeType */, + DebugKeyProvider provider = new DebugKeyProvider(osKeyPath, null /* storeType */, new IKeyGenOutput() { public void err(String message) { AdtPlugin.printErrorToConsole(javaProject.getProject(), @@ -747,9 +754,18 @@ public class ApkBuilder extends BaseBuilder { } } + // now write the native libraries. + // First look if the lib folder is there. + IResource libFolder = javaProject.getProject().findMember( + AndroidConstants.FD_NATIVE_LIBS); + if (libFolder != null && libFolder.exists() && + libFolder.getType() == IResource.FOLDER) { + // look inside and put .so in lib/* by keeping the relative folder path. + writeNativeLibraries(libFolder.getFullPath().segmentCount(), builder, libFolder); + } + // close the jar file and write the manifest and sign it. builder.close(); - } catch (GeneralSecurityException e1) { // mark project and return String msg = String.format(Messages.Final_Archive_Error_s, e1.getMessage()); @@ -784,6 +800,12 @@ public class ApkBuilder extends BaseBuilder { // and also output it in the console AdtPlugin.printErrorToConsole(javaProject.getProject(), msg); + } catch (CoreException e) { + // mark project and return + String msg = String.format(Messages.Final_Archive_Error_s, e.getMessage()); + AdtPlugin.printErrorToConsole(javaProject.getProject(), msg); + markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); + return false; } finally { if (fos != null) { try { @@ -798,6 +820,48 @@ public class ApkBuilder extends BaseBuilder { } /** + * Writes native libraries into a {@link SignedJarBuilder}. + * <p/>This recursively go through folder and writes .so files. + * The path in the archive is based on the root folder containing the libraries in the project. + * Its segment count is passed to the method to compute the resources path relative to the root + * folder. + * Native libraries in the archive must be in a "lib" folder. Everything in the project native + * lib folder directly goes in this "lib" folder in the archive. + * + * + * @param rooSegmentCount The number of segment of the path of the folder containing the + * libraries. This is used to compute the path in the archive. + * @param jarBuilder the {@link SignedJarBuilder} used to create the archive. + * @param resource the IResource to write. + * @throws CoreException + * @throws IOException + */ + private void writeNativeLibraries(int rootSegmentCount, SignedJarBuilder jarBuilder, + IResource resource) throws CoreException, IOException { + if (resource.getType() == IResource.FILE) { + IPath path = resource.getFullPath(); + + // check the extension. + if (path.getFileExtension().equalsIgnoreCase(AndroidConstants.EXT_NATIVE_LIB)) { + // remove the first segment to build the path inside the archive. + path = path.removeFirstSegments(rootSegmentCount); + + // add it to the archive. + IPath apkPath = new Path(AndroidConstants.FD_APK_NATIVE_LIBS); + apkPath = apkPath.append(path); + + // writes the file in the apk. + jarBuilder.writeFile(resource.getLocation().toFile(), apkPath.toString()); + } + } else if (resource.getType() == IResource.FOLDER) { + IResource[] members = ((IFolder)resource).members(); + for (IResource member : members) { + writeNativeLibraries(rootSegmentCount, jarBuilder, member); + } + } + } + + /** * Writes the standard resources of a project and its referenced projects * into a {@link SignedJarBuilder}. * Standard resources are non java/aidl files placed in the java package folders. diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java index fb60fdb..47ef626 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java @@ -42,6 +42,7 @@ import java.util.ArrayList; * <li>Any change to the classes.dex inside the output folder</li> * <li>Any change to the packaged resources file inside the output folder</li> * <li>Any change to a non java/aidl file inside the source folders</li> + * <li>Any change to .so file inside the lib (native library) folder</li> * </ul> */ public class ApkDeltaVisitor extends BaseDeltaVisitor @@ -79,6 +80,8 @@ public class ApkDeltaVisitor extends BaseDeltaVisitor private IPath mResPath; + private IPath mLibFolder; + /** * Builds the object with a specified output folder. * @param builder the xml builder using this object to visit the @@ -104,6 +107,11 @@ public class ApkDeltaVisitor extends BaseDeltaVisitor if (resFolder != null) { mResPath = resFolder.getFullPath(); } + + IResource libFolder = builder.getProject().findMember(AndroidConstants.FD_NATIVE_LIBS); + if (libFolder != null) { + mLibFolder = libFolder.getFullPath(); + } } public boolean getConvertToDex() { @@ -161,6 +169,7 @@ public class ApkDeltaVisitor extends BaseDeltaVisitor // check the other folders. if (mOutputPath != null && mOutputPath.isPrefixOf(path)) { + // a resource changed inside the output folder. if (type == IResource.FILE) { // just check this is a .class file. Any modification will // trigger a change in the classes.dex file @@ -217,6 +226,17 @@ public class ApkDeltaVisitor extends BaseDeltaVisitor mPackageResources = true; mMakeFinalPackage = true; return false; + } else if (mLibFolder != null && mLibFolder.isPrefixOf(path)) { + // inside the native library folder. Test if the changed resource is a .so file. + if (type == IResource.FILE && + path.getFileExtension().equalsIgnoreCase(AndroidConstants.EXT_NATIVE_LIB)) { + mMakeFinalPackage = true; + return false; // return false for file. + } + + // for folders, return true only if we don't already know we have to make the + // final package. + return mMakeFinalPackage == false; } else { // we are in a folder that is neither the resource folders, nor the output. // check against all the source folders, unless we already know we need to do diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java index f79be3d..f94bdc7 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java @@ -806,12 +806,6 @@ abstract class BaseBuilder extends IncrementalProjectBuilder { // get the IPath IPath path = e.getPath(); - // get the file name. if it's the framework jar, we ignore that file. - // since we now use classpath container, this is here for legacy purpose only. - if (AndroidConstants.FN_FRAMEWORK_LIBRARY.equals(path.lastSegment())) { - continue; - } - // check the name ends with .jar if (AndroidConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) { boolean local = false; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java index 9ba4026..cba8ad7 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.build; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.sdk.LoadStatus; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; @@ -47,9 +48,6 @@ public final class DexWrapper { private static DexWrapper sWrapper; - /** Status for the Loading of the dex jar file */ - public enum LoadStatus { LOADING, LOADED, FAILED } - private static LoadStatus sLoadStatus = LoadStatus.LOADING; private Method mRunMethod; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java index 850a79a..1a4aa8c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java @@ -18,14 +18,16 @@ package com.android.ide.eclipse.adt.build; import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.editors.java.ReadOnlyJavaEditor; import com.android.ide.eclipse.adt.project.FixLaunchConfig; import com.android.ide.eclipse.adt.project.ProjectHelper; +import com.android.ide.eclipse.adt.sdk.LoadStatus; +import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestHelper; import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.ide.eclipse.common.project.XmlErrorHandler.BasicXmlErrorListener; +import com.android.sdklib.IAndroidTarget; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; @@ -44,7 +46,6 @@ import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; -import org.eclipse.ui.ide.IDE; import java.io.IOException; import java.util.ArrayList; @@ -130,7 +131,6 @@ public class PreCompilerBuilder extends BaseBuilder { mSource); try { mNewFile.setDerived(true); - IDE.setDefaultEditor(mNewFile, ReadOnlyJavaEditor.ID); } catch (CoreException e) { // This really shouldn't happen since we check that the resource exist. // Worst case scenario, the resource isn't marked as derived. @@ -266,8 +266,14 @@ public class PreCompilerBuilder extends BaseBuilder { // record the state mCompileResources |= dv.getCompileResources(); - mergeAidlFileModifications(dv.getAidlToCompile(), - dv.getAidlToRemove()); + + // handle aidl modification + if (dv.getFullAidlRecompilation()) { + buildAidlCompilationList(project, sourceList); + } else { + mergeAidlFileModifications(dv.getAidlToCompile(), + dv.getAidlToRemove()); + } // if there was some XML errors, we just return w/o doing // anything since we've put some markers in the files anyway. @@ -290,6 +296,12 @@ public class PreCompilerBuilder extends BaseBuilder { // At this point we have stored what needs to be build, so we can // do some high level test and abort if needed. + // check if we have finished loading the SDK. + if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) { + // we exit silently + return null; + } + // check the compiler compliance level, not displaying the error message // since this is not the first builder. if (ProjectHelper.checkCompilerCompliance(getProject()) @@ -302,13 +314,19 @@ public class PreCompilerBuilder extends BaseBuilder { // Check that the SDK directory has been setup. String osSdkFolder = AdtPlugin.getOsSdkFolder(); - if (osSdkFolder.length() == 0) { + if (osSdkFolder == null || osSdkFolder.length() == 0) { AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, Messages.No_SDK_Setup_Error); markProject(AdtConstants.MARKER_ADT, Messages.No_SDK_Setup_Error, IMarker.SEVERITY_ERROR); return null; } + + IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project); + if (projectTarget == null) { + // no target. error has been output by the container initializer: exit silently. + return null; + } // get the manifest file IFile manifest = AndroidManifestHelper.getManifest(project); @@ -448,7 +466,7 @@ public class PreCompilerBuilder extends BaseBuilder { array.add("-S"); //$NON-NLS-1$ array.add(osResPath); array.add("-I"); //$NON-NLS-1$ - array.add(AdtPlugin.getOsAbsoluteFramework()); + array.add(projectTarget.getPath(IAndroidTarget.ANDROID_JAR)); if (AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE) { StringBuilder sb = new StringBuilder(); @@ -563,6 +581,8 @@ public class PreCompilerBuilder extends BaseBuilder { deleteObsoleteGeneratedClass(AndroidConstants.FN_MANIFEST_CLASS, mManifestPackageSourceFolder, mManifestPackage); } + + // FIXME: delete all java generated from aidl. } @Override @@ -736,12 +756,14 @@ public class PreCompilerBuilder extends BaseBuilder { if (mAidlToCompile.size() == 0 && mAidlToRemove.size() == 0) { return false; } + // create the command line String[] command = new String[4 + sourceFolders.size() + (folderAidlPath != null ? 1 : 0)]; int index = 0; + int aidlIndex; command[index++] = AdtPlugin.getOsAbsoluteAidl(); - command[index++] = "-p" + AdtPlugin.getOsAbsoluteFrameworkAidl(); //$NON-NLS-1$ + command[aidlIndex = index++] = "-p"; //$NON-NLS-1$ if (folderAidlPath != null) { command[index++] = "-p" + folderAidlPath; //$NON-NLS-1$ } @@ -813,6 +835,8 @@ public class PreCompilerBuilder extends BaseBuilder { prepareFileForExternalModification(javaFile); // finish to set the command line. + command[aidlIndex] = "-p" + Sdk.getCurrent().getTarget(aidlFile.getProject()).getPath( + IAndroidTarget.ANDROID_AIDL); //$NON-NLS-1$ command[index] = osPath; command[index + 1] = osJavaPath; @@ -922,7 +946,8 @@ public class PreCompilerBuilder extends BaseBuilder { } /** - * Goes through the buildpath and fills the list of aidl files to compile. + * Goes through the build paths and fills the list of aidl files to compile + * ({@link #mAidlToCompile}). * @param project The project. * @param buildPaths The list of build paths. */ @@ -977,7 +1002,7 @@ public class PreCompilerBuilder extends BaseBuilder { scanContainerForAidl((IFolder)r); break; default: - // this would mean it's a project or the workspaceroot + // this would mean it's a project or the workspace root // which is unlikely to happen. we do nothing break; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerDeltaVisitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerDeltaVisitor.java index 9c3bc5c..33d5fa6 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerDeltaVisitor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerDeltaVisitor.java @@ -42,6 +42,7 @@ import java.util.ArrayList; class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDeltaVisitor { + // Result fields. /** * Compile flag. This is set to true if one of the changed/added/removed * file is a resource file. Upon visiting all the delta resources, if @@ -50,6 +51,22 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements */ private boolean mCompileResources = false; + /** List of .aidl files found that are modified or new. */ + private final ArrayList<IFile> mAidlToCompile = new ArrayList<IFile>(); + + /** List of .aidl files that have been removed. */ + private final ArrayList<IFile> mAidlToRemove = new ArrayList<IFile>(); + + /** Aidl forced recompilation flag. This is set to true if project.aidl is modified. */ + private boolean mFullAidlCompilation = false; + + /** Manifest check/parsing flag. */ + private boolean mCheckedManifestXml = false; + + /** Application Pacakge, gathered from the parsing of the manifest */ + private String mJavaPackage = null; + + // Internal usage fields. /** * In Resource folder flag. This allows us to know if we're in the * resource folder. @@ -65,14 +82,6 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements /** List of source folders. */ private ArrayList<IPath> mSourceFolders; - /** List of .aidl files found that are modified or new. */ - private final ArrayList<IFile> mAidlToCompile = new ArrayList<IFile>(); - - /** List of .aidl files that have been removed. */ - private final ArrayList<IFile> mAidlToRemove = new ArrayList<IFile>(); - - private boolean mCheckedManifestXml = false; - private String mJavaPackage = null; public PreCompilerDeltaVisitor(BaseBuilder builder, ArrayList<IPath> sourceFolders) { super(builder); @@ -90,6 +99,10 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements public ArrayList<IFile> getAidlToRemove() { return mAidlToRemove; } + + public boolean getFullAidlRecompilation() { + return mFullAidlCompilation; + } /** * Returns whether the manifest file was parsed/checked for error during the resource delta @@ -100,7 +113,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements } /** - * Returns the manifest package if the manifest was checked. + * Returns the manifest package if the manifest was checked/parsed. * <p/> * This can return null in two cases: * <ul> @@ -169,11 +182,14 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements // we don't want to go to the children, not like they are // any for this resource anyway. return false; + } else if (AndroidConstants.FN_PROJECT_AIDL.equalsIgnoreCase(segments[1])) { + // need to force recompilation of all the aidl files + mFullAidlCompilation = true; } } // at this point we can either be in the source folder or in the - // resource foler or in a different folder that contains a source + // resource folder or in a different folder that contains a source // folder. // This is due to not all source folder being src/. Some could be // something/somethingelse/src/ @@ -227,7 +243,8 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements PreCompilerBuilder.PROPERTY_ANDROID_CONFLICT, true); } - // we add it anyway so that we can try to compile it every time. + // we add it anyway so that we can try to compile it at every compilation + // until the conflict is fixed. mAidlToCompile.add(file); } else { @@ -245,6 +262,8 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements boolean outputMessage = false; + // Special case of R.java/Manifest.java. + // FIXME: This does not check the package. Any modification of R.java/Manifest.java in another project will trigger a new recompilation of the resources. if (AndroidConstants.FN_RESOURCE_CLASS.equals(fileName) || AndroidConstants.FN_MANIFEST_CLASS.equals(fileName)) { // if it was removed, there's a possibility that it was removed due to a diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java index 1de1438..1219aac 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java @@ -62,13 +62,10 @@ public class ResourceManagerBuilder extends IncrementalProjectBuilder { switch (res) { case ProjectHelper.COMPILER_COMPLIANCE_LEVEL: errorMessage = Messages.Requires_Compiler_Compliance_5; - return null; case ProjectHelper.COMPILER_COMPLIANCE_SOURCE: errorMessage = Messages.Requires_Source_Compatibility_5; - return null; case ProjectHelper.COMPILER_COMPLIANCE_CODEGEN_TARGET: errorMessage = Messages.Requires_Class_Compatibility_5; - return null; } if (errorMessage != null) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java index 10be077..48ec7c3 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java @@ -30,9 +30,9 @@ import com.android.ddmlib.SyncService.SyncResult; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.debug.launching.DeviceChooserDialog.DeviceChooserResponse; import com.android.ide.eclipse.adt.debug.ui.EmulatorConfigTab; -import com.android.ide.eclipse.adt.debug.ui.SkinRepository; import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.common.project.AndroidManifestHelper; +import com.android.sdklib.SdkConstants; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; @@ -102,6 +102,9 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener /** Debuggable attribute of the manifest file. */ Boolean mDebuggable = null; + /** Required ApiVersionNumber by the app. 0 means no requirements */ + int mRequiredApiVersionNumber = 0; + InstallRetryMode mRetryMode = InstallRetryMode.NEVER; /** @@ -126,8 +129,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener /** Basic constructor with activity and package info. */ public DelayedLaunchInfo(IProject project, String packageName, String activity, - IFile pack, Boolean debuggable, int launchAction, AndroidLaunch launch, - IProgressMonitor monitor) { + IFile pack, Boolean debuggable, int requiredApiVersionNumber, int launchAction, + AndroidLaunch launch, IProgressMonitor monitor) { mProject = project; mPackageName = packageName; mActivity = activity; @@ -136,6 +139,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener mLaunch = launch; mMonitor = monitor; mDebuggable = debuggable; + mRequiredApiVersionNumber = requiredApiVersionNumber; } } @@ -256,13 +260,10 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener try { mSkin = config.getAttribute(LaunchConfigDelegate.ATTR_SKIN, mSkin); if (mSkin == null) { - mSkin = SkinRepository.getInstance().checkSkin( - LaunchConfigDelegate.DEFAULT_SKIN); - } else { - mSkin = SkinRepository.getInstance().checkSkin(mSkin); + mSkin = SdkConstants.SKIN_DEFAULT; } } catch (CoreException e) { - mSkin = SkinRepository.getInstance().checkSkin(LaunchConfigDelegate.DEFAULT_SKIN); + mSkin = SdkConstants.SKIN_DEFAULT; } int index = LaunchConfigDelegate.DEFAULT_SPEED; @@ -539,7 +540,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener LaunchConfigDelegate.DEFAULT_DELAY); // default skin - wc.setAttribute(LaunchConfigDelegate.ATTR_SKIN, LaunchConfigDelegate.DEFAULT_SKIN); + wc.setAttribute(LaunchConfigDelegate.ATTR_SKIN, SdkConstants.SKIN_DEFAULT); // default wipe data mode wc.setAttribute(LaunchConfigDelegate.ATTR_WIPE_DATA, @@ -599,13 +600,14 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * defined by <code>ILaunchManager</code> - <code>RUN_MODE</code> or * <code>DEBUG_MODE</code>. * @param apk the resource to the apk to launch. - * @param debuggable + * @param debuggable the debuggable value of the app, or null if not set. + * @param requiredApiVersionNumber the api version required by the app, or -1 if none. * @param activity the class to provide to am to launch * @param config the launch configuration * @param launch the launch object */ public void launch(final IProject project, String mode, IFile apk, - String packageName, Boolean debuggable, String activity, + String packageName, Boolean debuggable, int requiredApiVersionNumber, String activity, final AndroidLaunchConfiguration config, final AndroidLaunch launch, IProgressMonitor monitor) { @@ -619,7 +621,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener // create the launch info final DelayedLaunchInfo launchInfo = new DelayedLaunchInfo(project, packageName, - activity, apk, debuggable, config.mLaunchAction, launch, monitor); + activity, apk, debuggable, requiredApiVersionNumber, config.mLaunchAction, + launch, monitor); // set the debug mode launchInfo.mDebugMode = mode.equals(ILaunchManager.DEBUG_MODE); @@ -711,6 +714,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener // stop the launch and return mWaitingForEmulatorLaunches.remove(launchInfo); + AdtPlugin.printErrorToConsole(project, "Launch canceled!"); launch.stopLaunch(); return; } @@ -761,31 +765,63 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener } } - private void checkBuildInfo(DelayedLaunchInfo launchInfo, Device device) { + /** + * Checks the build information, and returns whether the launch should continue. + * <p/>The value tested are: + * <ul> + * <li>Minimum API version requested by the application. If the target device does not match, + * the launch is canceled.</li> + * <li>Debuggable attribute of the application and whether or not the device requires it. If + * the device requires it and it is not set in the manifest, the launch will be forced to + * "release" mode instead of "debug"</li> + * <ul> + * @param launchInfo + * @param device + * @return + */ + private boolean checkBuildInfo(DelayedLaunchInfo launchInfo, Device device) { if (device != null) { - // get the SDK build - String sdkBuild = AdtPlugin.getSdkApiVersion(); - - // can only complain if the sdkBuild is known - if (sdkBuild != null) { - - String deviceVersion = device.getProperty(Device.PROP_BUILD_VERSION); + // check the app required API level versus the target device API level + + String deviceApiVersionName = device.getProperty(Device.PROP_BUILD_VERSION); + String value = device.getProperty(Device.PROP_BUILD_VERSION_NUMBER); + int deviceApiVersionNumber = 0; + try { + deviceApiVersionNumber = Integer.parseInt(value); + } catch (NumberFormatException e) { + // pass, we'll keep the deviceVersionNumber value at 0. + } + + if (launchInfo.mRequiredApiVersionNumber == 0) { + // warn the API level requirement is not set. + AdtPlugin.printErrorToConsole(launchInfo.mProject, + "WARNING: Application does not specify an API level requirement!"); - if (deviceVersion == null) { - AdtPlugin.printToConsole(launchInfo.mProject, "WARNING: Unknown device API version!"); + // and display the target device API level (if known) + if (deviceApiVersionName == null || deviceApiVersionNumber == 0) { + AdtPlugin.printErrorToConsole(launchInfo.mProject, + "WARNING: Unknown device API version!"); } else { - if (sdkBuild.equals(deviceVersion) == false) { - // TODO do a proper check, including testing the content of the uses-sdk string in the manifest to detect real incompatibility. - String msg = String.format( - "WARNING: Device API version (%1$s) does not match SDK API version (%2$s)", - deviceVersion, sdkBuild); - AdtPlugin.printErrorToConsole(launchInfo.mProject, msg); - } + AdtPlugin.printErrorToConsole(launchInfo.mProject, String.format( + "Device API version is %1$d (Android %2$s)", deviceApiVersionNumber, + deviceApiVersionName)); + } + } else { // app requires a specific API level + if (deviceApiVersionName == null || deviceApiVersionNumber == 0) { + AdtPlugin.printToConsole(launchInfo.mProject, + "WARNING: Unknown device API version!"); + } else if (deviceApiVersionNumber < launchInfo.mRequiredApiVersionNumber) { + String msg = String.format( + "ERROR: Application requires API version %1$d. Device API version is %2$d (Android %3$s).", + launchInfo.mRequiredApiVersionNumber, deviceApiVersionNumber, + deviceApiVersionName); + AdtPlugin.printErrorToConsole(launchInfo.mProject, msg); + + // abort the launch + return false; } - } else { - AdtPlugin.printToConsole(launchInfo.mProject, "WARNING: Unknown SDK API version!"); } - + // now checks that the device/app can be debugged (if needed) if (device.isEmulator() == false && launchInfo.mDebugMode) { String debuggableDevice = device.getProperty(Device.PROP_DEBUGGABLE); @@ -818,6 +854,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener } } } + + return true; } /** @@ -829,10 +867,16 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * @return true if succeed */ private boolean simpleLaunch(DelayedLaunchInfo launchInfo, Device device) { - checkBuildInfo(launchInfo, device); + // API level check + if (checkBuildInfo(launchInfo, device) == false) { + AdtPlugin.printErrorToConsole(launchInfo.mProject, "Launch canceled!"); + launchInfo.mLaunch.stopLaunch(); + return false; + } // sync the app if (syncApp(launchInfo, device) == false) { + AdtPlugin.printErrorToConsole(launchInfo.mProject, "Launch canceled!"); launchInfo.mLaunch.stopLaunch(); return false; } @@ -1127,6 +1171,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener } catch (IOException e) { // something went wrong trying to launch the app. // lets stop the Launch + AdtPlugin.printErrorToConsole(info.mProject, + String.format("Launch error: %s", e.getMessage())); info.mLaunch.stopLaunch(); // and remove it from the list of app waiting for debuggers @@ -1139,25 +1185,20 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener private boolean launchEmulator(AndroidLaunchConfiguration config) { // split the custom command line in segments - String[] segs; + ArrayList<String> customArgs = new ArrayList<String>(); boolean has_wipe_data = false; if (config.mEmulatorCommandLine != null && config.mEmulatorCommandLine.length() > 0) { - segs = config.mEmulatorCommandLine.split("\\s+"); //$NON-NLS-1$ + String[] segments = config.mEmulatorCommandLine.split("\\s+"); //$NON-NLS-1$ // we need to remove the empty strings - ArrayList<String> array = new ArrayList<String>(); - for (String s : segs) { + for (String s : segments) { if (s.length() > 0) { - array.add(s); + customArgs.add(s); if (!has_wipe_data && s.equals(FLAG_WIPE_DATA)) { has_wipe_data = true; } } } - - segs = array.toArray(new String[array.size()]); - } else { - segs = new String[0]; } boolean needs_wipe_data = config.mWipeData && !has_wipe_data; @@ -1167,30 +1208,38 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener } } - boolean needs_no_boot_anim = config.mNoBootAnim; + // build the command line based on the available parameters. + ArrayList<String> list = new ArrayList<String>(); + + list.add(AdtPlugin.getOsAbsoluteEmulator()); + if (config.mSkin != null) { + list.add(FLAG_SKIN); + list.add(config.mSkin); + } - // get the command line - String[] command = new String[7 + segs.length + - (needs_wipe_data ? 1 : 0) + - (needs_no_boot_anim ? 1 : 0)]; - int index = 0; - command[index++] = AdtPlugin.getOsAbsoluteEmulator(); - command[index++] = FLAG_SKIN; //$NON-NLS-1$ - command[index++] = config.mSkin; - command[index++] = FLAG_NETSPEED; //$NON-NLS-1$ - command[index++] = config.mNetworkSpeed; - command[index++] = FLAG_NETDELAY; //$NON-NLS-1$ - command[index++] = config.mNetworkDelay; - if (needs_wipe_data) { - command[index++] = FLAG_WIPE_DATA; + if (config.mNetworkSpeed != null) { + list.add(FLAG_NETSPEED); + list.add(config.mNetworkSpeed); } - if (needs_no_boot_anim) { - command[index++] = FLAG_NO_BOOT_ANIM; + + if (config.mNetworkDelay != null) { + list.add(FLAG_NETDELAY); + list.add(config.mNetworkDelay); + } + + if (needs_wipe_data) { + list.add(FLAG_WIPE_DATA); } - for (String s : segs) { - command[index++] = s; + + if (config.mNoBootAnim) { + list.add(FLAG_NO_BOOT_ANIM); } + list.addAll(customArgs); + + // convert the list into an array for the call to exec. + String[] command = list.toArray(new String[list.size()]); + // launch the emulator try { Process process = Runtime.getRuntime().exec(command); @@ -1278,12 +1327,11 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener /** * Launch a new thread that connects a remote debugger on the specified port. * @param debugPort The port to connect the debugger to - * @param launch The associated AndroidLaunch object. + * @param androidLaunch The associated AndroidLaunch object. * @param monitor A Progress monitor * @see connectRemoveDebugger() */ - public static void launchRemoteDebugger(final ILaunchConfiguration config, - final int debugPort, final AndroidLaunch androidLaunch, + public static void launchRemoteDebugger( final int debugPort, final AndroidLaunch androidLaunch, final IProgressMonitor monitor) { new Thread("Debugger connection") { //$NON-NLS-1$ @Override @@ -1342,13 +1390,14 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener synchronized (sListLock) { // look if there's an app waiting for a device if (mWaitingForEmulatorLaunches.size() > 0) { - // remove first item from the list + // get/remove first launch item from the list + // FIXME: what if we have multiple launches waiting? DelayedLaunchInfo launchInfo = mWaitingForEmulatorLaunches.get(0); mWaitingForEmulatorLaunches.remove(0); - - // give it its device + + // give the launch item its device for later use. launchInfo.mDevice = device; - + // and move it to the other list mWaitingForReadyEmulatorList.add(launchInfo); @@ -1433,12 +1482,23 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener // look for application waiting for home synchronized (sListLock) { - boolean foundMatch = false; for (int i = 0 ; i < mWaitingForReadyEmulatorList.size() ;) { DelayedLaunchInfo launchInfo = mWaitingForReadyEmulatorList.get(i); if (launchInfo.mDevice == device) { // it's match, remove from the list mWaitingForReadyEmulatorList.remove(i); + + // We couldn't check earlier the API level of the device + // (it's asynchronous when the device boot, and usually + // deviceConnected is called before it's queried for its build info) + // so we check now + if (checkBuildInfo(launchInfo, device) == false) { + // device is not the proper API! + AdtPlugin.printErrorToConsole(launchInfo.mProject, + "Launch canceled!"); + launchInfo.mLaunch.stopLaunch(); + return; + } AdtPlugin.printToConsole(launchInfo.mProject, String.format("HOME is up on device '%1$s'", @@ -1448,16 +1508,14 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener if (syncApp(launchInfo, device)) { // application package is sync'ed, lets attempt to launch it. launchApp(launchInfo, device); - - // if we haven't checked the device build info, lets do it here - if (foundMatch == false) { - foundMatch = true; - checkBuildInfo(launchInfo, device); - } } else { // failure! Cancel and return + AdtPlugin.printErrorToConsole(launchInfo.mProject, + "Launch canceled!"); launchInfo.mLaunch.stopLaunch(); } + + break; } else { i++; } @@ -1530,6 +1588,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener } } catch (CoreException e) { // well something went wrong. + AdtPlugin.printErrorToConsole(launchInfo.mProject, + String.format("Launch error: %s", e.getMessage())); // stop the launch launchInfo.mLaunch.stopLaunch(); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java index e5ccb2b..5d3e349 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java @@ -83,11 +83,6 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate { /** Skin to be used to launch the emulator */ public static final String ATTR_SKIN = AdtPlugin.PLUGIN_ID + ".skin"; //$NON-NLS-1$ - /** - * Name of the default Skin. - */ - public static final String DEFAULT_SKIN = "HVGA"; //$NON-NLS-1$ - public static final String ATTR_SPEED = AdtPlugin.PLUGIN_ID + ".speed"; //$NON-NLS-1$ /** @@ -138,8 +133,7 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate { // if we have a valid debug port, this means we're debugging an app // that's already launched. if (debugPort != INVALID_DEBUG_PORT) { - AndroidLaunchController.launchRemoteDebugger(configuration, - debugPort, androidLaunch, monitor); + AndroidLaunchController.launchRemoteDebugger(debugPort, androidLaunch, monitor); return; } @@ -302,7 +296,8 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate { // everything seems fine, we ask the launch controller to handle // the rest controller.launch(project, mode, applicationPackage, manifestParser.getPackage(), - manifestParser.getDebuggable(), activityName, config, androidLaunch, monitor); + manifestParser.getDebuggable(), manifestParser.getApiLevelRequirement(), + activityName, config, androidLaunch, monitor); } @Override diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java index 1d36add..c7b340c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java @@ -18,13 +18,19 @@ package com.android.ide.eclipse.adt.debug.ui; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.debug.launching.LaunchConfigDelegate; -import com.android.ide.eclipse.adt.debug.ui.SkinRepository.Skin; +import com.android.ide.eclipse.adt.sdk.Sdk; +import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.ide.eclipse.ddms.DdmsPlugin; +import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.SdkConstants; +import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; @@ -82,6 +88,8 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab { private Button mNoBootAnimButton; + private IAndroidTarget mTarget; + /** * Returns the emulator ready speed option value. * @param value The index of the combo selection. @@ -169,12 +177,6 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab { new Label(mEmulatorOptionsGroup, SWT.NONE).setText("Screen Size:"); mSkinCombo = new Combo(mEmulatorOptionsGroup, SWT.READ_ONLY); - Skin[] skins = SkinRepository.getInstance().getSkins(); - if (skins != null) { - for (Skin skin : skins) { - mSkinCombo.add(skin.getDescription()); - } - } mSkinCombo.addSelectionListener(new SelectionAdapter() { // called when selection changes @Override @@ -182,7 +184,6 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab { updateLaunchConfigurationDialog(); } }); - mSkinCombo.pack(); // network options new Label(mEmulatorOptionsGroup, SWT.NONE).setText("Network Speed:"); @@ -287,6 +288,42 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab { } mAutoTargetButton.setSelection(value); mManualTargetButton.setSelection(!value); + + // look for the project name to get its target. + String projectName = ""; + try { + projectName = configuration.getAttribute( + IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, projectName); + } catch (CoreException ce) { + } + + IProject project = null; + + // get the list of existing Android projects from the workspace. + IJavaProject[] projects = BaseProjectHelper.getAndroidProjects(); + if (projects != null) { + // look for the project whose name we read from the configuration. + for (IJavaProject p : projects) { + if (p.getElementName().equals(projectName)) { + project = p.getProject(); + break; + } + } + } + + mSkinCombo.removeAll(); + if (project != null) { + mTarget = Sdk.getCurrent().getTarget(project); + if (mTarget != null) { + String[] skins = mTarget.getSkins(); + if (skins != null) { + for (String skin : skins) { + mSkinCombo.add(skin); + } + mSkinCombo.pack(); + } + } + } value = LaunchConfigDelegate.DEFAULT_WIPE_DATA; try { @@ -307,16 +344,18 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab { int index = -1; try { String skin = configuration.getAttribute(LaunchConfigDelegate.ATTR_SKIN, (String)null); - if (skin != null) { - index = getSkinIndex(skin); + if (skin == null) { + skin = SdkConstants.SKIN_DEFAULT; } + + index = getSkinIndex(skin); } catch (CoreException e) { - index = getSkinIndex(SkinRepository.getInstance().checkSkin( - LaunchConfigDelegate.DEFAULT_SKIN)); + index = getSkinIndex(SdkConstants.SKIN_DEFAULT); } if (index == -1) { - mSkinCombo.clearSelection(); + mSkinCombo.select(0); + updateLaunchConfigurationDialog(); } else { mSkinCombo.select(index); } @@ -387,7 +426,7 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab { configuration.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE, LaunchConfigDelegate.DEFAULT_TARGET_MODE); configuration.setAttribute(LaunchConfigDelegate.ATTR_SKIN, - LaunchConfigDelegate.DEFAULT_SKIN); + SdkConstants.SKIN_DEFAULT); configuration.setAttribute(LaunchConfigDelegate.ATTR_SPEED, LaunchConfigDelegate.DEFAULT_SPEED); configuration.setAttribute(LaunchConfigDelegate.ATTR_DELAY, @@ -403,11 +442,31 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab { } private String getSkinNameByIndex(int index) { - return SkinRepository.getInstance().getSkinNameByIndex(index); + if (mTarget != null && index > 0) { + String[] skins = mTarget.getSkins(); + if (skins != null && index < skins.length) { + return skins[index]; + } + } + + return null; } private int getSkinIndex(String name) { - return SkinRepository.getInstance().getSkinIndex(name); + if (mTarget != null) { + String[] skins = mTarget.getSkins(); + if (skins != null) { + int index = 0; + for (String skin : skins) { + if (skin.equalsIgnoreCase(name)) { + return index; + } + index++; + } + } + } + + return -1; } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/SkinRepository.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/SkinRepository.java deleted file mode 100644 index a813026..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/SkinRepository.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.adt.debug.ui; - -import com.android.ide.eclipse.common.AndroidConstants; - -import java.io.File; -import java.util.ArrayList; - -/** - * Repository for the emulator skins. This class is responsible for parsing the skin folder. - */ -public class SkinRepository { - - private final static SkinRepository sInstance = new SkinRepository(); - - private Skin[] mSkins; - - public static class Skin { - - String mName; - - public Skin(String name) { - mName = name; - } - - public String getName() { - return mName; - } - - /** - * Returns the human readable description of the skin. - */ - public String getDescription() { - // TODO: parse the skin and output the description. - return mName; - } - } - - /** - * Returns the singleton instance. - */ - public static SkinRepository getInstance() { - return sInstance; - } - - /** - * Parse the skin folder and build the skin list. - * @param osPath The path of the skin folder. - */ - public void parseFolder(String osPath) { - File skinFolder = new File(osPath); - - if (skinFolder.isDirectory()) { - ArrayList<Skin> skinList = new ArrayList<Skin>(); - - File[] files = skinFolder.listFiles(); - - for (File skin : files) { - if (skin.isDirectory()) { - // check for layout file - File layout = new File(skin.getPath() + File.separator - + AndroidConstants.FN_LAYOUT); - - if (layout.isFile()) { - // for now we don't parse the content of the layout and - // simply add the directory to the list. - skinList.add(new Skin(skin.getName())); - } - } - } - - mSkins = skinList.toArray(new Skin[skinList.size()]); - } else { - mSkins = new Skin[0]; - } - } - - public Skin[] getSkins() { - return mSkins; - } - - /** - * Returns a valid skin folder name. If <code>skin</code> is valid, then it is returned, - * otherwise the first valid skin name is returned. - * @param skin the Skin name to check - * @return a valid skin name or null if there aren't any. - */ - public String checkSkin(String skin) { - if (mSkins != null) { - for (Skin s : mSkins) { - if (s.getName().equals(skin)) { - return skin; - } - } - - if (mSkins.length > 0) { - return mSkins[0].getName(); - } - } - - return null; - } - - - /** - * Returns the name of a skin by index. - * @param index The index of the skin to return - * @return the skin name of null if the index is invalid. - */ - public String getSkinNameByIndex(int index) { - if (mSkins != null) { - if (index >= 0 && index < mSkins.length) { - return mSkins[index].getName(); - } - } - return null; - } - - /** - * Returns the index (0 based) of the skin matching the name. - * @param name The name of the skin to look for. - * @return the index of the skin or -1 if the skin was not found. - */ - public int getSkinIndex(String name) { - if (mSkins != null) { - int count = mSkins.length; - for (int i = 0 ; i < count ; i++) { - Skin s = mSkins[i]; - if (s.mName.equals(name)) { - return i; - } - } - } - - return -1; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/java/ReadOnlyJavaEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/java/ReadOnlyJavaEditor.java deleted file mode 100644 index 1c9a569..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/java/ReadOnlyJavaEditor.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.adt.editors.java; - -import org.eclipse.jdt.ui.PreferenceConstants; -import org.eclipse.jdt.ui.text.IJavaPartitions; -import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration; -import org.eclipse.jdt.ui.text.JavaTextTools; -import org.eclipse.jface.preference.IPreferenceStore; -import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor; - -/** - * Read only java editors. This looks like the regular Java editor, except that it - * prevents editing the files. This is used for automatically generated java classes. - */ -public class ReadOnlyJavaEditor extends AbstractDecoratedTextEditor { - - public final static String ID = - "com.android.ide.eclipse.adt.editors.java.ReadOnlyJavaEditor"; //$NON-NLS-1$ - - public ReadOnlyJavaEditor() { - IPreferenceStore javaUiStore = PreferenceConstants.getPreferenceStore(); - JavaTextTools jtt = new JavaTextTools(javaUiStore); - - JavaSourceViewerConfiguration jsvc = new JavaSourceViewerConfiguration( - jtt.getColorManager(), javaUiStore, this, IJavaPartitions.JAVA_PARTITIONING); - - setSourceViewerConfiguration(jsvc); - } - - @Override - public boolean isEditable() { - return false; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/preferences/BuildPreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/preferences/BuildPreferencePage.java index 136c0f3..434269c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/preferences/BuildPreferencePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/preferences/BuildPreferencePage.java @@ -18,6 +18,9 @@ package com.android.ide.eclipse.adt.preferences; import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.jarutils.DebugKeyProvider; +import com.android.jarutils.DebugKeyProvider.KeytoolException; +import com.android.prefs.AndroidLocation.AndroidLocationException; import org.eclipse.jface.preference.BooleanFieldEditor; import org.eclipse.jface.preference.FieldEditorPreferencePage; @@ -29,6 +32,13 @@ import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; +import java.io.File; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Date; + /** * Preference page for build options. * @@ -75,7 +85,7 @@ public class BuildPreferencePage extends FieldEditorPreferencePage implements addField(new ReadOnlyFieldEditor(AdtPlugin.PREFS_DEFAULT_DEBUG_KEYSTORE, Messages.BuildPreferencePage_Default_KeyStore, getFieldEditorParent())); - addField(new FileFieldEditor(AdtPlugin.PREFS_CUSTOM_DEBUG_KEYSTORE, + addField(new KeystoreFieldEditor(AdtPlugin.PREFS_CUSTOM_DEBUG_KEYSTORE, Messages.BuildPreferencePage_Custom_Keystore, getFieldEditorParent())); } @@ -105,4 +115,103 @@ public class BuildPreferencePage extends FieldEditorPreferencePage implements control.setEditable(false); } } + + /** + * Custom {@link FileFieldEditor} that checks that the keystore is valid. + */ + private static class KeystoreFieldEditor extends FileFieldEditor { + public KeystoreFieldEditor(String name, String label, Composite parent) { + super(name, label, parent); + setValidateStrategy(VALIDATE_ON_KEY_STROKE); + } + + @Override + protected boolean checkState() { + String fileName = getTextControl().getText(); + fileName = fileName.trim(); + + // empty values are considered ok. + if (fileName.length() > 0) { + File file = new File(fileName); + if (file.isFile()) { + // attempt to load the debug key. + try { + DebugKeyProvider provider = new DebugKeyProvider(fileName, + null /* storeType */, null /* key gen output */); + PrivateKey key = provider.getDebugKey(); + X509Certificate certificate = (X509Certificate)provider.getCertificate(); + + if (key == null || certificate == null) { + showErrorMessage("Unable to find debug key in keystore!"); + return false; + } + + Date today = new Date(); + if (certificate.getNotAfter().compareTo(today) < 0) { + showErrorMessage("Certificate is expired!"); + return false; + } + + if (certificate.getNotBefore().compareTo(today) > 0) { + showErrorMessage("Certificate validity is in the future!"); + return false; + } + + // we're good! + clearErrorMessage(); + return true; + } catch (GeneralSecurityException e) { + handleException(e); + return false; + } catch (IOException e) { + handleException(e); + return false; + } catch (KeytoolException e) { + handleException(e); + return false; + } catch (AndroidLocationException e) { + handleException(e); + return false; + } + + + } else { + // file does not exist. + showErrorMessage("Not a valid keystore path."); + return false; // Apply/OK must be disabled + } + } + + clearErrorMessage(); + return true; + } + + @Override + public Text getTextControl(Composite parent) { + setValidateStrategy(VALIDATE_ON_KEY_STROKE); + return super.getTextControl(parent); + } + + /** + * Set the error message from a {@link Throwable}. If the exception has no message, try + * to get the message from the cause. + * @param t the Throwable. + */ + private void handleException(Throwable t) { + String msg = t.getMessage(); + if (t == null) { + Throwable cause = t.getCause(); + if (cause != null) { + handleException(cause); + } else { + setErrorMessage("Uknown error when getting the debug key!"); + } + + return; + } + + // valid text, display it. + showErrorMessage(msg); + } + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/CreateAidlImportAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/CreateAidlImportAction.java index efbbf94..a1b3c38 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/CreateAidlImportAction.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/CreateAidlImportAction.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.project; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.common.AndroidConstants; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -117,7 +118,7 @@ public class CreateAidlImportAction implements IObjectActionDelegate { // create the file with the parcelables if (parcelables.size() > 0) { IPath path = project.getLocation(); - path = path.append("/project.aidl"); //$NON-NLS-1$ + path = path.append(AndroidConstants.FN_PROJECT_AIDL); File f = new File(path.toOSString()); if (f.exists() == false) { @@ -194,7 +195,7 @@ public class CreateAidlImportAction implements IObjectActionDelegate { IType[] superInterfaces = typeHierarchy.getAllSuperInterfaces(type); for (IType superInterface : superInterfaces) { - if ("android.os.Parcelable".equals(superInterface.getFullyQualifiedName())) { //$NON-NLS-1$ + if (AndroidConstants.CLASS_PARCELABLE.equals(superInterface.getFullyQualifiedName())) { parcelables.add(type.getFullyQualifiedName()); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FolderDecorator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FolderDecorator.java new file mode 100644 index 0000000..1ca89cd --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FolderDecorator.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.project; + +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.common.AndroidConstants; + +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.IDecoration; +import org.eclipse.jface.viewers.ILabelDecorator; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ILightweightLabelDecorator; + +/** + * A {@link ILabelDecorator} associated with an org.eclipse.ui.decorators extension. + * This is used to add android icons in some special folders in the package explorer. + */ +public class FolderDecorator implements ILightweightLabelDecorator { + + private ImageDescriptor mDescriptor; + + public FolderDecorator() { + mDescriptor = AdtPlugin.getImageDescriptor("/icons/android_project.png"); + } + + public void decorate(Object element, IDecoration decoration) { + if (element instanceof IFolder) { + IFolder folder = (IFolder)element; + + // get the project and make sure this is an android project + IProject project = folder.getProject(); + + try { + if (project.hasNature(AndroidConstants.NATURE)) { + // check the folder is directly under the project. + if (folder.getParent().getType() == IResource.PROJECT) { + String name = folder.getName(); + if (name.equals(AndroidConstants.FD_ASSETS)) { + decorate(decoration, " [Android assets]"); + decoration.addOverlay(mDescriptor, IDecoration.TOP_RIGHT); + } else if (name.equals(AndroidConstants.FD_RESOURCES)) { + decorate(decoration, " [Android resources]"); + decoration.addOverlay(mDescriptor, IDecoration.TOP_RIGHT); + } else if (name.equals(AndroidConstants.FD_NATIVE_LIBS)) { + decorate(decoration, " [Native Libraries]"); + } + } + } + } catch (CoreException e) { + // log the error + AdtPlugin.log(e, "Unable to get nature of project '%s'.", project.getName()); + } + } + } + + public void decorate(IDecoration decoration, String suffix) { + decoration.addOverlay(mDescriptor, IDecoration.TOP_RIGHT); + + // this is broken as it changes the color of the whole text, not only of the decoration. + // TODO: figure out how to change the color of the decoration only. +// decoration.addSuffix(suffix); +// ITheme theme = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme(); +// ColorRegistry registry = theme.getColorRegistry(); +// decoration.setForegroundColor(registry.get("org.eclipse.jdt.ui.ColoredLabels.decorations")); + + } + + public boolean isLabelProperty(Object element, String property) { + // at this time return false. + return false; + } + + public void addListener(ILabelProviderListener listener) { + // No state change will affect the rendering. + } + + + + public void removeListener(ILabelProviderListener listener) { + // No state change will affect the rendering. + } + + public void dispose() { + // nothind to dispose + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/NewXmlFileWizardAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/NewXmlFileWizardAction.java new file mode 100644 index 0000000..c117b4e --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/NewXmlFileWizardAction.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.project; + +import com.android.ide.eclipse.editors.wizards.NewXmlFileWizard; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPart; + +public class NewXmlFileWizardAction implements IObjectActionDelegate { + + private ISelection mSelection; + private IWorkbench mWorkbench; + + /** + * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart) + */ + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + mWorkbench = targetPart.getSite().getWorkbenchWindow().getWorkbench(); + } + + public void run(IAction action) { + if (mSelection instanceof IStructuredSelection) { + IStructuredSelection selection = (IStructuredSelection)mSelection; + + // call the new xml file wizard on the current selection. + NewXmlFileWizard wizard = new NewXmlFileWizard(); + wizard.init(mWorkbench, selection); + WizardDialog dialog = new WizardDialog(mWorkbench.getDisplay().getActiveShell(), + wizard); + dialog.open(); + } + } + + public void selectionChanged(IAction action, ISelection selection) { + this.mSelection = selection; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java index 24132b6..8678923 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java @@ -33,7 +33,6 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.QualifiedName; -import org.eclipse.jdt.core.IClasspathAttribute; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; @@ -178,31 +177,6 @@ public final class ProjectHelper { } /** - * Check the validity of the javadoc attributes in a classpath entry. - * @param frameworkEntry the classpath entry to check. - * @return true if the javadoc attributes is valid, false otherwise. - */ - public static boolean checkJavaDocPath(IClasspathEntry frameworkEntry) { - // get the list of extra attributes - IClasspathAttribute[] attributes = frameworkEntry.getExtraAttributes(); - - // and search for the one about the javadoc - for (IClasspathAttribute att : attributes) { - if (IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME. - equals(att.getName())) { - // we found a javadoc attribute. Now we test its value. - // get the expect value - String validJavaDoc = getJavaDocPath(AdtPlugin.getOsAbsoluteFramework()); - - // now compare and return - return validJavaDoc.equals(att.getValue()); - } - } - - return false; - } - - /** * Fix the project. This checks the SDK location. * @param project The project to fix. * @throws JavaModelException @@ -264,14 +238,7 @@ public final class ProjectHelper { continue; } } else if (kind == IClasspathEntry.CPE_CONTAINER) { - IPath containerPath = entry.getPath(); - - if (AndroidClasspathContainerInitializer.checkOldPath(containerPath)) { - entries = ProjectHelper.removeEntryFromClasspath(entries, i); - - // continue, to skip the i++; - continue; - } else if (AndroidClasspathContainerInitializer.checkPath(containerPath)) { + if (AndroidClasspathContainerInitializer.checkPath(entry.getPath())) { foundContainer = true; } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ExportWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ExportWizard.java index 1f51d09..de4b339 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ExportWizard.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ExportWizard.java @@ -18,7 +18,10 @@ package com.android.ide.eclipse.adt.project.export; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.project.ProjectHelper; +import com.android.jarutils.KeystoreHelper; import com.android.jarutils.SignedJarBuilder; +import com.android.jarutils.DebugKeyProvider.IKeyGenOutput; +import com.android.jarutils.DebugKeyProvider.KeytoolException; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -27,28 +30,38 @@ import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.wizard.Wizard; import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.events.VerifyEvent; +import org.eclipse.swt.events.VerifyListener; +import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IExportWizard; import org.eclipse.ui.IWorkbench; +import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.security.GeneralSecurityException; +import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; +import java.security.KeyStore.PrivateKeyEntry; import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; /** * Export wizard to export an apk signed with a release key/certificate. */ -public class ExportWizard extends Wizard implements IExportWizard { +public final class ExportWizard extends Wizard implements IExportWizard { private static final String PROJECT_LOGO_LARGE = "icons/android_large.png"; //$NON-NLS-1$ - private static final String PAGE_PRE = "preExportPage"; //$NON-NLS-1$ - private static final String PAGE_SIGNING = "signingExportPage"; //$NON-NLS-1$ - private static final String PAGE_FINAL = "finalExportPage"; //$NON-NLS-1$ + private static final String PAGE_PROJECT_CHECK = "Page_ProjectCheck"; //$NON-NLS-1$ + private static final String PAGE_KEYSTORE_SELECTION = "Page_KeystoreSelection"; //$NON-NLS-1$ + private static final String PAGE_KEY_CREATION = "Page_KeyCreation"; //$NON-NLS-1$ + private static final String PAGE_KEY_SELECTION = "Page_KeySelection"; //$NON-NLS-1$ + private static final String PAGE_KEY_CHECK = "Page_KeyCheck"; //$NON-NLS-1$ static final String PROPERTY_KEYSTORE = "keystore"; //$NON-NLS-1$ static final String PROPERTY_ALIAS = "alias"; //$NON-NLS-1$ @@ -59,7 +72,41 @@ public class ExportWizard extends Wizard implements IExportWizard { */ static abstract class ExportWizardPage extends WizardPage { - protected boolean mNewProjectReference = true; + /** bit mask constant for project data change event */ + protected static final int DATA_PROJECT = 0x001; + /** bit mask constant for keystore data change event */ + protected static final int DATA_KEYSTORE = 0x002; + /** bit mask constant for key data change event */ + protected static final int DATA_KEY = 0x004; + + protected static final VerifyListener sPasswordVerifier = new VerifyListener() { + public void verifyText(VerifyEvent e) { + // verify the characters are valid for password. + int len = e.text.length(); + + // first limit to 127 characters max + if (len + ((Text)e.getSource()).getText().length() > 127) { + e.doit = false; + return; + } + + // now only take non control characters + for (int i = 0 ; i < len ; i++) { + if (e.text.charAt(i) < 32) { + e.doit = false; + return; + } + } + } + }; + + /** + * Bit mask indicating what changed while the page was hidden. + * @see #DATA_PROJECT + * @see #DATA_KEYSTORE + * @see #DATA_KEY + */ + protected int mProjectDataChanged = 0; ExportWizardPage(String name) { super(name); @@ -72,23 +119,38 @@ public class ExportWizard extends Wizard implements IExportWizard { super.setVisible(visible); if (visible) { onShow(); - mNewProjectReference = false; + mProjectDataChanged = 0; } } - void newProjectReference() { - mNewProjectReference = true; + final void projectDataChanged(int changeMask) { + mProjectDataChanged |= changeMask; + } + + /** + * Calls {@link #setErrorMessage(String)} and {@link #setPageComplete(boolean)} based on a + * {@link Throwable} object. + */ + protected final void onException(Throwable t) { + String message = getExceptionMessage(t); + + setErrorMessage(message); + setPageComplete(false); } } - private ExportWizardPage mPages[] = new ExportWizardPage[3]; + private ExportWizardPage mPages[] = new ExportWizardPage[5]; private IProject mProject; private String mKeystore; + private String mKeystorePassword; + private boolean mKeystoreCreationMode; + private String mKeyAlias; - private char[] mKeystorePassword; - private char[] mKeyPassword; + private String mKeyPassword; + private int mValidity; + private String mDName; private PrivateKey mPrivateKey; private X509Certificate mCertificate; @@ -97,6 +159,15 @@ public class ExportWizard extends Wizard implements IExportWizard { private String mApkFilePath; private String mApkFileName; + private ExportWizardPage mKeystoreSelectionPage; + private ExportWizardPage mKeyCreationPage; + private ExportWizardPage mKeySelectionPage; + private ExportWizardPage mKeyCheckPage; + + private boolean mKeyCreationMode; + + private List<String> mExistingAliases; + public ExportWizard() { setHelpAvailable(false); // TODO have help setWindowTitle("Export Android Application"); @@ -105,46 +176,101 @@ public class ExportWizard extends Wizard implements IExportWizard { @Override public void addPages() { - addPage(mPages[0] = new PreExportPage(this, PAGE_PRE)); - addPage(mPages[1] = new SigningExportPage(this, PAGE_SIGNING)); - addPage(mPages[2] = new FinalExportPage(this, PAGE_FINAL)); + addPage(mPages[0] = new ProjectCheckPage(this, PAGE_PROJECT_CHECK)); + addPage(mKeystoreSelectionPage = mPages[1] = new KeystoreSelectionPage(this, + PAGE_KEYSTORE_SELECTION)); + addPage(mKeyCreationPage = mPages[2] = new KeyCreationPage(this, PAGE_KEY_CREATION)); + addPage(mKeySelectionPage = mPages[3] = new KeySelectionPage(this, PAGE_KEY_SELECTION)); + addPage(mKeyCheckPage = mPages[4] = new KeyCheckPage(this, PAGE_KEY_CHECK)); } @Override public boolean performFinish() { + // first we make sure export is fine if the destination file already exists + File f = new File(mDestinationPath); + if (f.isFile()) { + if (AdtPlugin.displayPrompt("Export Wizard", + "File already exists. Do you want to overwrite it?") == false) { + return false; + } + } + // save the properties ProjectHelper.saveStringProperty(mProject, PROPERTY_KEYSTORE, mKeystore); ProjectHelper.saveStringProperty(mProject, PROPERTY_ALIAS, mKeyAlias); ProjectHelper.saveStringProperty(mProject, PROPERTY_DESTINATION, mDestinationPath); try { - FileOutputStream fos = new FileOutputStream(mDestinationPath); - SignedJarBuilder builder = new SignedJarBuilder(fos, mPrivateKey, mCertificate); - - // get the input file. - FileInputStream fis = new FileInputStream(mApkFilePath); - try { - builder.writeZip(fis, null /* filter */); - } finally { + if (mKeystoreCreationMode || mKeyCreationMode) { + final ArrayList<String> output = new ArrayList<String>(); + if (KeystoreHelper.createNewStore( + mKeystore, + null /*storeType*/, + mKeystorePassword, + mKeyAlias, + mKeyPassword, + mDName, + mValidity, + new IKeyGenOutput() { + public void err(String message) { + output.add(message); + } + public void out(String message) { + output.add(message); + } + }) == false) { + // keystore creation error! + displayError(output.toArray(new String[output.size()])); + return false; + } + + // keystore is created, now load the private key and certificate. + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + FileInputStream fis = new FileInputStream(mKeystore); + keyStore.load(fis, mKeystorePassword.toCharArray()); fis.close(); + PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry( + mKeyAlias, new KeyStore.PasswordProtection(mKeyPassword.toCharArray())); + + if (entry != null) { + mPrivateKey = entry.getPrivateKey(); + mCertificate = (X509Certificate)entry.getCertificate(); + } else { + // this really shouldn't happen since we now let the user choose the key + // from a list read from the store. + displayError("Could not find key"); + return false; + } } - - builder.close(); - fos.close(); - return true; + // check the private key/certificate again since it may have been created just above. + if (mPrivateKey != null && mCertificate != null) { + FileOutputStream fos = new FileOutputStream(mDestinationPath); + SignedJarBuilder builder = new SignedJarBuilder(fos, mPrivateKey, mCertificate); + + // get the input file. + FileInputStream fis = new FileInputStream(mApkFilePath); + try { + builder.writeZip(fis, null /* filter */); + } finally { + fis.close(); + } + + builder.close(); + fos.close(); + + return true; + } } catch (FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + displayError(e); } catch (NoSuchAlgorithmException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + displayError(e); } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + displayError(e); } catch (GeneralSecurityException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + displayError(e); + } catch (KeytoolException e) { + displayError(e); } return false; @@ -152,8 +278,13 @@ public class ExportWizard extends Wizard implements IExportWizard { @Override public boolean canFinish() { + // check if we have the apk to resign, the destination location, and either + // a private key/certificate or the creation mode. In creation mode, unless + // all the key/keystore info is valid, the user cannot reach the last page, so there's + // no need to check them again here. return mApkFilePath != null && - mPrivateKey != null && mCertificate != null && + ((mPrivateKey != null && mCertificate != null) + || mKeystoreCreationMode || mKeyCreationMode) && mDestinationPath != null; } @@ -175,6 +306,22 @@ public class ExportWizard extends Wizard implements IExportWizard { } } + ExportWizardPage getKeystoreSelectionPage() { + return mKeystoreSelectionPage; + } + + ExportWizardPage getKeyCreationPage() { + return mKeyCreationPage; + } + + ExportWizardPage getKeySelectionPage() { + return mKeySelectionPage; + } + + ExportWizardPage getKeyCheckPage() { + return mKeyCheckPage; + } + /** * Returns an image descriptor for the wizard logo. */ @@ -192,10 +339,7 @@ public class ExportWizard extends Wizard implements IExportWizard { mApkFilePath = apkFilePath; mApkFileName = filename; - // indicate to the page that the project was changed. - for (ExportWizardPage page : mPages) { - page.newProjectReference(); - } + updatePageOnChange(ExportWizardPage.DATA_PROJECT); } String getApkFilename() { @@ -206,42 +350,95 @@ public class ExportWizard extends Wizard implements IExportWizard { mKeystore = path; mPrivateKey = null; mCertificate = null; + + updatePageOnChange(ExportWizardPage.DATA_KEYSTORE); } String getKeystore() { return mKeystore; } - void setKeyAlias(String name) { - mKeyAlias = name; - mPrivateKey = null; - mCertificate = null; + void setKeystoreCreationMode(boolean createStore) { + mKeystoreCreationMode = createStore; + updatePageOnChange(ExportWizardPage.DATA_KEYSTORE); } - String getKeyAlias() { - return mKeyAlias; + boolean getKeystoreCreationMode() { + return mKeystoreCreationMode; } - void setKeystorePassword(char[] password) { + + void setKeystorePassword(String password) { mKeystorePassword = password; mPrivateKey = null; mCertificate = null; + + updatePageOnChange(ExportWizardPage.DATA_KEYSTORE); } - char[] getKeystorePassword() { + String getKeystorePassword() { return mKeystorePassword; } + + void setKeyCreationMode(boolean createKey) { + mKeyCreationMode = createKey; + updatePageOnChange(ExportWizardPage.DATA_KEY); + } + + boolean getKeyCreationMode() { + return mKeyCreationMode; + } + + void setExistingAliases(List<String> aliases) { + mExistingAliases = aliases; + } - void setKeyPassword(char[] password) { + List<String> getExistingAliases() { + return mExistingAliases; + } + + void setKeyAlias(String name) { + mKeyAlias = name; + mPrivateKey = null; + mCertificate = null; + + updatePageOnChange(ExportWizardPage.DATA_KEY); + } + + String getKeyAlias() { + return mKeyAlias; + } + + void setKeyPassword(String password) { mKeyPassword = password; mPrivateKey = null; mCertificate = null; + + updatePageOnChange(ExportWizardPage.DATA_KEY); } - char[] getKeyPassword() { + String getKeyPassword() { return mKeyPassword; } + void setValidity(int validity) { + mValidity = validity; + updatePageOnChange(ExportWizardPage.DATA_KEY); + } + + int getValidity() { + return mValidity; + } + + void setDName(String dName) { + mDName = dName; + updatePageOnChange(ExportWizardPage.DATA_KEY); + } + + String getDName() { + return mDName; + } + void setSigningInfo(PrivateKey privateKey, X509Certificate certificate) { mPrivateKey = privateKey; mCertificate = certificate; @@ -251,4 +448,54 @@ public class ExportWizard extends Wizard implements IExportWizard { mDestinationPath = path; } + void updatePageOnChange(int changeMask) { + for (ExportWizardPage page : mPages) { + page.projectDataChanged(changeMask); + } + } + + private void displayError(String... messages) { + String message = null; + if (messages.length == 1) { + message = messages[0]; + } else { + StringBuilder sb = new StringBuilder(messages[0]); + for (int i = 1; i < messages.length; i++) { + sb.append('\n'); + sb.append(messages[i]); + } + + message = sb.toString(); + } + + AdtPlugin.displayError("Export Wizard", message); + } + + private void displayError(Exception e) { + String message = getExceptionMessage(e); + displayError(message); + + AdtPlugin.log(e, "Export Wizard Error"); + } + + /** + * Returns the {@link Throwable#getMessage()}. If the {@link Throwable#getMessage()} returns + * <code>null</code>, the method is called again on the cause of the Throwable object. + * <p/>If no Throwable in the chain has a valid message, the canonical name of the first + * exception is returned. + */ + private static String getExceptionMessage(Throwable t) { + String message = t.getMessage(); + if (message == null) { + Throwable cause = t.getCause(); + if (cause != null) { + return getExceptionMessage(cause); + } + + // no more cause and still no message. display the first exception. + return cause.getClass().getCanonicalName(); + } + + return message; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/FinalExportPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/FinalExportPage.java deleted file mode 100644 index 206e3aa..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/FinalExportPage.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.adt.project.export; - -import com.android.ide.eclipse.adt.project.ProjectHelper; -import com.android.ide.eclipse.adt.project.export.ExportWizard.ExportWizardPage; - -import org.eclipse.core.resources.IProject; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -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.FileDialog; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; -import org.eclipse.ui.forms.widgets.FormText; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.UnrecoverableEntryException; -import java.security.KeyStore.PrivateKeyEntry; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.Calendar; - -public class FinalExportPage extends ExportWizardPage { - - private final ExportWizard mWizard; - private PrivateKey mPrivateKey; - private X509Certificate mCertificate; - private Text mDestination; - private Button mBrowseButton; - private boolean mFatalSigningError; - private FormText mDetailText; - - protected FinalExportPage(ExportWizard wizard, String pageName) { - super(pageName); - mWizard = wizard; - - setTitle("Application Export"); - setDescription("Export the signed Application package."); - } - - public void createControl(Composite parent) { - setErrorMessage(null); - setMessage(null); - - // build the ui. - Composite composite = new Composite(parent, SWT.NULL); - composite.setLayoutData(new GridData(GridData.FILL_BOTH)); - GridLayout gl = new GridLayout(3, false); - gl.verticalSpacing *= 3; - composite.setLayout(gl); - - GridData gd; - - new Label(composite, SWT.NONE).setText("Destination APK file:"); - mDestination = new Text(composite, SWT.BORDER); - mDestination.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); - mDestination.addModifyListener(new ModifyListener() { - public void modifyText(ModifyEvent e) { - onDestinationChange(); - } - }); - mBrowseButton = new Button(composite, SWT.PUSH); - mBrowseButton.setText("Browse..."); - mBrowseButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - FileDialog fileDialog = new FileDialog(mBrowseButton.getShell(), SWT.SAVE); - - fileDialog.setText("Destination file name"); - fileDialog.setFileName(mWizard.getApkFilename()); - - String saveLocation = fileDialog.open(); - if (saveLocation != null) { - mDestination.setText(saveLocation); - } - } - }); - - mDetailText = new FormText(composite, SWT.NONE); - mDetailText.setLayoutData(gd = new GridData(GridData.FILL_BOTH)); - gd.horizontalSpan = 3; - - setControl(composite); - } - - @Override - void onShow() { - // fill the texts with information loaded from the project. - if (mNewProjectReference) { - // reset the destination from the content of the project - IProject project = mWizard.getProject(); - - String destination = ProjectHelper.loadStringProperty(project, - ExportWizard.PROPERTY_DESTINATION); - if (destination != null) { - mDestination.setText(destination); - } - } - - // reset the wizard with no key/cert to make it not finishable, unless a valid - // key/cert is found. - mWizard.setSigningInfo(null, null); - - try { - KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - FileInputStream fis = new FileInputStream(mWizard.getKeystore()); - keyStore.load(fis, mWizard.getKeystorePassword()); - fis.close(); - PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry( - mWizard.getKeyAlias(), - new KeyStore.PasswordProtection(mWizard.getKeyPassword())); - - if (entry != null) { - mPrivateKey = entry.getPrivateKey(); - mCertificate = (X509Certificate)entry.getCertificate(); - } else { - setErrorMessage("Unable to find key"); - - setPageComplete(false); - } - } catch (FileNotFoundException e) { - // this was checked at the first previous step and will not happen here, unless - // the file was removed during the export wizard execution. - onException(e); - } catch (KeyStoreException e) { - onException(e); - } catch (NoSuchAlgorithmException e) { - onException(e); - } catch (UnrecoverableEntryException e) { - onException(e); - } catch (CertificateException e) { - onException(e); - } catch (IOException e) { - onException(e); - } - - if (mPrivateKey != null && mCertificate != null) { - mFatalSigningError = false; - - Calendar expirationCalendar = Calendar.getInstance(); - expirationCalendar.setTime(mCertificate.getNotAfter()); - Calendar today = Calendar.getInstance(); - - if (expirationCalendar.before(today)) { - mDetailText.setText(String.format("<form><p>Certificate expired on %s</p></form>", - mCertificate.getNotAfter().toString()), - true /* parseTags */, true /* expandURLs */); - - // fatal error = nothing can make the page complete. - mFatalSigningError = true; - - setErrorMessage("Certificate is expired!"); - setPageComplete(false); - } else { - // valid, key/cert: put it in the wizard so that it can be finished - mWizard.setSigningInfo(mPrivateKey, mCertificate); - - StringBuilder sb = new StringBuilder(String.format("<form><p>Certificate expires on %s.</p>", - mCertificate.getNotAfter().toString())); - - int expirationYear = expirationCalendar.get(Calendar.YEAR); - int thisYear = today.get(Calendar.YEAR); - - if (thisYear + 25 < expirationYear) { - // do nothing - } else { - if (expirationYear == thisYear) { - sb.append("<p>The certificate expires this year!</p>"); - } else { - int count = expirationYear-thisYear; - sb.append(String.format("<p>The Certificate expires in %1$s %2$s!</p>", - count, count == 1 ? "year" : "years")); - } - - sb.append("<p>Make sure the certificate is valid for the planned lifetime of the product.</p>"); - sb.append("<p>If the certificate expires, you will be forced to sign your application with a different one.</p>"); - sb.append("<p>Applications cannot be upgraded if their certificate changes from one version to another, "); - sb.append("forcing a full uninstall/install, which will make the user lose his/her data.</p>"); - sb.append("<p>Android Market currently requires certificates to be valid until 2033.</p>"); - - sb.append("</form>"); - } - - mDetailText.setText(sb.toString(), true /* parseTags */, true /* expandURLs */); - } - mDetailText.getParent().layout(); - } else { - // fatal error = nothing can make the page complete. - mFatalSigningError = true; - } - - onDestinationChange(); - } - - private void onDestinationChange() { - if (mFatalSigningError == false) { - String path = mDestination.getText().trim(); - if (path.length() == 0) { - setErrorMessage("Enter destination for the APK file."); - mWizard.setDestination(null); // this is to reset canFinish in the wizard - setPageComplete(false); - } else { - File file = new File(path); - if (file.isDirectory()) { - setErrorMessage("Destination is a directory!"); - mWizard.setDestination(null); // this is to reset canFinish in the wizard - setPageComplete(false); - } else { - File parentFile = file.getParentFile(); - if (parentFile == null || parentFile.isDirectory() == false) { - setErrorMessage("Not a valid directory."); - mWizard.setDestination(null); // this is to reset canFinish in the wizard - setPageComplete(false); - } else { - mWizard.setDestination(path); - setErrorMessage(null); - setPageComplete(true); - } - } - } - } - } - - /** - * Calls {@link #setErrorMessage(String)} and {@link #setPageComplete(boolean)} based on a - * {@link Throwable} object. If the {@link Throwable#getMessage()} returns <code>null</code>, - * the method is called again on the cause of the Throwable. - */ - private void onException(Throwable t) { - String message = t.getMessage(); - if (message == null) { - Throwable cause = t.getCause(); - if (cause != null) { - onException(cause); - } else { - // no more cause and still no message. display the first exception. - setErrorMessage(cause.getClass().getCanonicalName()); - setPageComplete(false); - } - return; - } - - setErrorMessage(message); - setPageComplete(false); - } - -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/KeyCheckPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/KeyCheckPage.java new file mode 100644 index 0000000..c64bf10 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/KeyCheckPage.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.project.export; + +import com.android.ide.eclipse.adt.project.ProjectHelper; +import com.android.ide.eclipse.adt.project.export.ExportWizard.ExportWizardPage; + +import org.eclipse.core.resources.IProject; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +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.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.forms.widgets.FormText; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.UnrecoverableEntryException; +import java.security.KeyStore.PrivateKeyEntry; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Calendar; + +/** + * Final page of the wizard that checks the key and ask for the ouput location. + */ +final class KeyCheckPage extends ExportWizardPage { + + private final ExportWizard mWizard; + private PrivateKey mPrivateKey; + private X509Certificate mCertificate; + private Text mDestination; + private boolean mFatalSigningError; + private FormText mDetailText; + + protected KeyCheckPage(ExportWizard wizard, String pageName) { + super(pageName); + mWizard = wizard; + + setTitle("Destination and key/certificate checks"); + setDescription(""); // TODO + } + + public void createControl(Composite parent) { + setErrorMessage(null); + setMessage(null); + + // build the ui. + Composite composite = new Composite(parent, SWT.NULL); + composite.setLayoutData(new GridData(GridData.FILL_BOTH)); + GridLayout gl = new GridLayout(3, false); + gl.verticalSpacing *= 3; + composite.setLayout(gl); + + GridData gd; + + new Label(composite, SWT.NONE).setText("Destination APK file:"); + mDestination = new Text(composite, SWT.BORDER); + mDestination.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + mDestination.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + onDestinationChange(); + } + }); + final Button browseButton = new Button(composite, SWT.PUSH); + browseButton.setText("Browse..."); + browseButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + FileDialog fileDialog = new FileDialog(browseButton.getShell(), SWT.SAVE); + + fileDialog.setText("Destination file name"); + fileDialog.setFileName(mWizard.getApkFilename()); + + String saveLocation = fileDialog.open(); + if (saveLocation != null) { + mDestination.setText(saveLocation); + } + } + }); + + mDetailText = new FormText(composite, SWT.NONE); + mDetailText.setLayoutData(gd = new GridData(GridData.FILL_BOTH)); + gd.horizontalSpan = 3; + + setControl(composite); + } + + @Override + void onShow() { + // fill the texts with information loaded from the project. + if ((mProjectDataChanged & DATA_PROJECT) != 0) { + // reset the destination from the content of the project + IProject project = mWizard.getProject(); + + String destination = ProjectHelper.loadStringProperty(project, + ExportWizard.PROPERTY_DESTINATION); + if (destination != null) { + mDestination.setText(destination); + } + } + + // if anything change we basically reload the data. + if (mProjectDataChanged != 0) { + mFatalSigningError = false; + + // reset the wizard with no key/cert to make it not finishable, unless a valid + // key/cert is found. + mWizard.setSigningInfo(null, null); + + if (mWizard.getKeystoreCreationMode() || mWizard.getKeyCreationMode()) { + int validity = mWizard.getValidity(); + StringBuilder sb = new StringBuilder( + String.format("<form><p>Certificate expires in %d years.</p>", + validity)); + + if (validity < 25) { + sb.append("<p>Make sure the certificate is valid for the planned lifetime of the product.</p>"); + sb.append("<p>If the certificate expires, you will be forced to sign your application with a different one.</p>"); + sb.append("<p>Applications cannot be upgraded if their certificate changes from one version to another, "); + sb.append("forcing a full uninstall/install, which will make the user lose his/her data.</p>"); + sb.append("<p>Android Market currently requires certificates to be valid until 2033.</p>"); + } + + sb.append("</form>"); + mDetailText.setText(sb.toString(), true /* parseTags */, true /* expandURLs */); + } else { + try { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + FileInputStream fis = new FileInputStream(mWizard.getKeystore()); + keyStore.load(fis, mWizard.getKeystorePassword().toCharArray()); + fis.close(); + PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry( + mWizard.getKeyAlias(), + new KeyStore.PasswordProtection( + mWizard.getKeyPassword().toCharArray())); + + if (entry != null) { + mPrivateKey = entry.getPrivateKey(); + mCertificate = (X509Certificate)entry.getCertificate(); + } else { + setErrorMessage("Unable to find key."); + + setPageComplete(false); + } + } catch (FileNotFoundException e) { + // this was checked at the first previous step and will not happen here, unless + // the file was removed during the export wizard execution. + onException(e); + } catch (KeyStoreException e) { + onException(e); + } catch (NoSuchAlgorithmException e) { + onException(e); + } catch (UnrecoverableEntryException e) { + onException(e); + } catch (CertificateException e) { + onException(e); + } catch (IOException e) { + onException(e); + } + + if (mPrivateKey != null && mCertificate != null) { + Calendar expirationCalendar = Calendar.getInstance(); + expirationCalendar.setTime(mCertificate.getNotAfter()); + Calendar today = Calendar.getInstance(); + + if (expirationCalendar.before(today)) { + mDetailText.setText(String.format( + "<form><p>Certificate expired on %s</p></form>", + mCertificate.getNotAfter().toString()), + true /* parseTags */, true /* expandURLs */); + + // fatal error = nothing can make the page complete. + mFatalSigningError = true; + + setErrorMessage("Certificate is expired."); + setPageComplete(false); + } else { + // valid, key/cert: put it in the wizard so that it can be finished + mWizard.setSigningInfo(mPrivateKey, mCertificate); + + StringBuilder sb = new StringBuilder(String.format( + "<form><p>Certificate expires on %s.</p>", + mCertificate.getNotAfter().toString())); + + int expirationYear = expirationCalendar.get(Calendar.YEAR); + int thisYear = today.get(Calendar.YEAR); + + if (thisYear + 25 < expirationYear) { + // do nothing + } else { + if (expirationYear == thisYear) { + sb.append("<p>The certificate expires this year.</p>"); + } else { + int count = expirationYear-thisYear; + sb.append(String.format( + "<p>The Certificate expires in %1$s %2$s.</p>", + count, count == 1 ? "year" : "years")); + } + + sb.append("<p>Make sure the certificate is valid for the planned lifetime of the product.</p>"); + sb.append("<p>If the certificate expires, you will be forced to sign your application with a different one.</p>"); + sb.append("<p>Applications cannot be upgraded if their certificate changes from one version to another, "); + sb.append("forcing a full uninstall/install, which will make the user lose his/her data.</p>"); + sb.append("<p>Android Market currently requires certificates to be valid until 2033.</p>"); + } + + sb.append("</form>"); + + mDetailText.setText(sb.toString(), true /* parseTags */, true /* expandURLs */); + } + mDetailText.getParent().layout(); + } else { + // fatal error = nothing can make the page complete. + mFatalSigningError = true; + } + } + } + + onDestinationChange(); + } + + private void onDestinationChange() { + if (mFatalSigningError == false) { + // reset messages for now. + setErrorMessage(null); + setMessage(null); + + String path = mDestination.getText().trim(); + + if (path.length() == 0) { + setErrorMessage("Enter destination for the APK file."); + mWizard.setDestination(null); // this is to reset canFinish in the wizard + setPageComplete(false); + return; + } + + File file = new File(path); + if (file.isDirectory()) { + setErrorMessage("Destination is a directory."); + mWizard.setDestination(null); // this is to reset canFinish in the wizard + setPageComplete(false); + return; + } + + File parentFile = file.getParentFile(); + if (parentFile == null || parentFile.isDirectory() == false) { + setErrorMessage("Not a valid directory."); + mWizard.setDestination(null); // this is to reset canFinish in the wizard + setPageComplete(false); + return; + } + + // no error, set the destination in the wizard. + mWizard.setDestination(path); + setPageComplete(true); + + // However, we should also test if the file already exists. + if (file.isFile()) { + setMessage("Destination file already exists.", WARNING); + } + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/KeyCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/KeyCreationPage.java new file mode 100644 index 0000000..d7365f7 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/KeyCreationPage.java @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.project.export; + +import com.android.ide.eclipse.adt.project.ProjectHelper; +import com.android.ide.eclipse.adt.project.export.ExportWizard.ExportWizardPage; + +import org.eclipse.core.resources.IProject; +import org.eclipse.jface.wizard.IWizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.VerifyEvent; +import org.eclipse.swt.events.VerifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +import java.util.List; + +/** + * Key creation page. + */ +final class KeyCreationPage extends ExportWizardPage { + + private final ExportWizard mWizard; + private Text mAlias; + private Text mKeyPassword; + private Text mKeyPassword2; + private Text mCnField; + private boolean mDisableOnChange = false; + private Text mOuField; + private Text mOField; + private Text mLField; + private Text mStField; + private Text mCField; + private String mDName; + private int mValidity = 0; + private List<String> mExistingAliases; + + + protected KeyCreationPage(ExportWizard wizard, String pageName) { + super(pageName); + mWizard = wizard; + + setTitle("Key Creation"); + setDescription(""); // TODO? + } + + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NULL); + composite.setLayoutData(new GridData(GridData.FILL_BOTH)); + GridLayout gl = new GridLayout(2, false); + composite.setLayout(gl); + + GridData gd; + + new Label(composite, SWT.NONE).setText("Alias:"); + mAlias = new Text(composite, SWT.BORDER); + mAlias.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + + new Label(composite, SWT.NONE).setText("Password:"); + mKeyPassword = new Text(composite, SWT.BORDER | SWT.PASSWORD); + mKeyPassword.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + mKeyPassword.addVerifyListener(sPasswordVerifier); + + new Label(composite, SWT.NONE).setText("Confirm:"); + mKeyPassword2 = new Text(composite, SWT.BORDER | SWT.PASSWORD); + mKeyPassword2.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + mKeyPassword2.addVerifyListener(sPasswordVerifier); + + new Label(composite, SWT.NONE).setText("Validity (years):"); + final Text validityText = new Text(composite, SWT.BORDER); + validityText.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + validityText.addVerifyListener(new VerifyListener() { + public void verifyText(VerifyEvent e) { + // check for digit only. + for (int i = 0 ; i < e.text.length(); i++) { + char letter = e.text.charAt(i); + if (letter < '0' || letter > '9') { + e.doit = false; + return; + } + } + } + }); + + new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL).setLayoutData( + gd = new GridData(GridData.FILL_HORIZONTAL)); + gd.horizontalSpan = 2; + + new Label(composite, SWT.NONE).setText("First and Last Name:"); + mCnField = new Text(composite, SWT.BORDER); + mCnField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + + new Label(composite, SWT.NONE).setText("Organizational Unit:"); + mOuField = new Text(composite, SWT.BORDER); + mOuField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + + new Label(composite, SWT.NONE).setText("Organization:"); + mOField = new Text(composite, SWT.BORDER); + mOField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + + new Label(composite, SWT.NONE).setText("City or Locality:"); + mLField = new Text(composite, SWT.BORDER); + mLField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + + new Label(composite, SWT.NONE).setText("State or Province:"); + mStField = new Text(composite, SWT.BORDER); + mStField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + + new Label(composite, SWT.NONE).setText("Country Code (XX):"); + mCField = new Text(composite, SWT.BORDER); + mCField.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + + // Show description the first time + setErrorMessage(null); + setMessage(null); + setControl(composite); + + mAlias.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + mWizard.setKeyAlias(mAlias.getText().trim()); + onChange(); + } + }); + mKeyPassword.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + mWizard.setKeyPassword(mKeyPassword.getText()); + onChange(); + } + }); + mKeyPassword2.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + onChange(); + } + }); + + validityText.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + try { + mValidity = Integer.parseInt(validityText.getText()); + } catch (NumberFormatException e2) { + // this should only happen if the text field is empty due to the verifyListener. + mValidity = 0; + } + mWizard.setValidity(mValidity); + onChange(); + } + }); + + ModifyListener dNameListener = new ModifyListener() { + public void modifyText(ModifyEvent e) { + onDNameChange(); + } + }; + + mCnField.addModifyListener(dNameListener); + mOuField.addModifyListener(dNameListener); + mOField.addModifyListener(dNameListener); + mLField.addModifyListener(dNameListener); + mStField.addModifyListener(dNameListener); + mCField.addModifyListener(dNameListener); + } + + @Override + void onShow() { + // fill the texts with information loaded from the project. + if ((mProjectDataChanged & (DATA_PROJECT | DATA_KEYSTORE)) != 0) { + // reset the keystore/alias from the content of the project + IProject project = mWizard.getProject(); + + // disable onChange for now. we'll call it once at the end. + mDisableOnChange = true; + + String alias = ProjectHelper.loadStringProperty(project, ExportWizard.PROPERTY_ALIAS); + if (alias != null) { + mAlias.setText(alias); + } + + // get the existing list of keys if applicable + if (mWizard.getKeyCreationMode()) { + mExistingAliases = mWizard.getExistingAliases(); + } else { + mExistingAliases = null; + } + + // reset the passwords + mKeyPassword.setText(""); //$NON-NLS-1$ + mKeyPassword2.setText(""); //$NON-NLS-1$ + + // enable onChange, and call it to display errors and enable/disable pageCompleted. + mDisableOnChange = false; + onChange(); + } + } + + @Override + public IWizardPage getPreviousPage() { + if (mWizard.getKeyCreationMode()) { // this means we create a key from an existing store + return mWizard.getKeySelectionPage(); + } + + return mWizard.getKeystoreSelectionPage(); + } + + @Override + public IWizardPage getNextPage() { + return mWizard.getKeyCheckPage(); + } + + /** + * Handles changes and update the error message and calls {@link #setPageComplete(boolean)}. + */ + private void onChange() { + if (mDisableOnChange) { + return; + } + + setErrorMessage(null); + setMessage(null); + + if (mAlias.getText().trim().length() == 0) { + setErrorMessage("Enter key alias."); + setPageComplete(false); + return; + } else if (mExistingAliases != null) { + // we cannot use indexOf, because we need to do a case-insensitive check + String keyAlias = mAlias.getText().trim(); + for (String alias : mExistingAliases) { + if (alias.equalsIgnoreCase(keyAlias)) { + setErrorMessage("Key alias already exists in keystore."); + setPageComplete(false); + return; + } + } + } + + String value = mKeyPassword.getText(); + if (value.length() == 0) { + setErrorMessage("Enter key password."); + setPageComplete(false); + return; + } else if (value.length() < 6) { + setErrorMessage("Key password is too short - must be at least 6 characters."); + setPageComplete(false); + return; + } + + if (value.equals(mKeyPassword2.getText()) == false) { + setErrorMessage("Key passwords don't match."); + setPageComplete(false); + return; + } + + if (mValidity == 0) { + setErrorMessage("Key certificate validity is required."); + setPageComplete(false); + return; + } else if (mValidity < 25) { + setMessage("A 25 year certificate validity is recommended.", WARNING); + } else if (mValidity > 1000) { + setErrorMessage("Key certificate validity must be between 1 and 1000 years."); + setPageComplete(false); + return; + } + + if (mDName == null || mDName.length() == 0) { + setErrorMessage("At least one Certificate issuer field is required to be non-empty."); + setPageComplete(false); + return; + } + + setPageComplete(true); + } + + /** + * Handles changes in the DName fields. + */ + private void onDNameChange() { + StringBuilder sb = new StringBuilder(); + + buildDName("CN", mCnField, sb); + buildDName("OU", mOuField, sb); + buildDName("O", mOField, sb); + buildDName("L", mLField, sb); + buildDName("ST", mStField, sb); + buildDName("C", mCField, sb); + + mDName = sb.toString(); + mWizard.setDName(mDName); + + onChange(); + } + + /** + * Builds the distinguished name string with the provided {@link StringBuilder}. + * @param prefix the prefix of the entry. + * @param textField The {@link Text} field containing the entry value. + * @param sb the string builder containing the dname. + */ + private void buildDName(String prefix, Text textField, StringBuilder sb) { + if (textField != null) { + String value = textField.getText().trim(); + if (value.length() > 0) { + if (sb.length() > 0) { + sb.append(","); + } + + sb.append(prefix); + sb.append('='); + sb.append(value); + } + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/KeySelectionPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/KeySelectionPage.java new file mode 100644 index 0000000..2fcd757 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/KeySelectionPage.java @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.project.export; + +import com.android.ide.eclipse.adt.project.ProjectHelper; +import com.android.ide.eclipse.adt.project.export.ExportWizard.ExportWizardPage; + +import org.eclipse.core.resources.IProject; +import org.eclipse.jface.wizard.IWizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Enumeration; + +/** + * Key Selection Page. This is used when an existing keystore is used. + */ +final class KeySelectionPage extends ExportWizardPage { + + private final ExportWizard mWizard; + private Label mKeyAliasesLabel; + private Combo mKeyAliases; + private Label mKeyPasswordLabel; + private Text mKeyPassword; + private boolean mDisableOnChange = false; + private Button mUseExistingKey; + private Button mCreateKey; + + protected KeySelectionPage(ExportWizard wizard, String pageName) { + super(pageName); + mWizard = wizard; + + setTitle("Key alias selection"); + setDescription(""); // TODO + } + + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NULL); + composite.setLayoutData(new GridData(GridData.FILL_BOTH)); + GridLayout gl = new GridLayout(3, false); + composite.setLayout(gl); + + GridData gd; + + mUseExistingKey = new Button(composite, SWT.RADIO); + mUseExistingKey.setText("Use existing key"); + mUseExistingKey.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + gd.horizontalSpan = 3; + mUseExistingKey.setSelection(true); + + new Composite(composite, SWT.NONE).setLayoutData(gd = new GridData()); + gd.heightHint = 0; + gd.widthHint = 50; + mKeyAliasesLabel = new Label(composite, SWT.NONE); + mKeyAliasesLabel.setText("Alias:"); + mKeyAliases = new Combo(composite, SWT.READ_ONLY); + mKeyAliases.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + new Composite(composite, SWT.NONE).setLayoutData(gd = new GridData()); + gd.heightHint = 0; + gd.widthHint = 50; + mKeyPasswordLabel = new Label(composite, SWT.NONE); + mKeyPasswordLabel.setText("Password:"); + mKeyPassword = new Text(composite, SWT.BORDER | SWT.PASSWORD); + mKeyPassword.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + mCreateKey = new Button(composite, SWT.RADIO); + mCreateKey.setText("Create new key"); + mCreateKey.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + gd.horizontalSpan = 3; + + // Show description the first time + setErrorMessage(null); + setMessage(null); + setControl(composite); + + mUseExistingKey.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + mWizard.setKeyCreationMode(!mUseExistingKey.getSelection()); + enableWidgets(); + onChange(); + } + }); + + mKeyAliases.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + mWizard.setKeyAlias(mKeyAliases.getItem(mKeyAliases.getSelectionIndex())); + onChange(); + } + }); + + mKeyPassword.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + mWizard.setKeyPassword(mKeyPassword.getText()); + onChange(); + } + }); + } + + @Override + void onShow() { + // fill the texts with information loaded from the project. + if ((mProjectDataChanged & (DATA_PROJECT | DATA_KEYSTORE)) != 0) { + // disable onChange for now. we'll call it once at the end. + mDisableOnChange = true; + + // reset the alias from the content of the project + try { + // reset to using a key + mWizard.setKeyCreationMode(false); + mUseExistingKey.setSelection(true); + mCreateKey.setSelection(false); + enableWidgets(); + + // remove the content of the alias combo always and first, in case the + // keystore password is wrong + mKeyAliases.removeAll(); + + // get the alias list (also used as a keystore password test) + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + FileInputStream fis = new FileInputStream(mWizard.getKeystore()); + keyStore.load(fis, mWizard.getKeystorePassword().toCharArray()); + fis.close(); + + Enumeration<String> aliases = keyStore.aliases(); + + // get the alias from the project previous export, and look for a match as + // we add the aliases to the combo. + IProject project = mWizard.getProject(); + + String keyAlias = ProjectHelper.loadStringProperty(project, + ExportWizard.PROPERTY_ALIAS); + + ArrayList<String> aliasList = new ArrayList<String>(); + + int selection = -1; + int count = 0; + while (aliases.hasMoreElements()) { + String alias = aliases.nextElement(); + mKeyAliases.add(alias); + aliasList.add(alias); + if (selection == -1 && alias.equalsIgnoreCase(keyAlias)) { + selection = count; + } + count++; + } + + mWizard.setExistingAliases(aliasList); + + if (selection != -1) { + mKeyAliases.select(selection); + + // since a match was found and is selected, we need to give it to + // the wizard as well + mWizard.setKeyAlias(keyAlias); + } else { + mKeyAliases.clearSelection(); + } + + // reset the password + mKeyPassword.setText(""); //$NON-NLS-1$ + + // enable onChange, and call it to display errors and enable/disable pageCompleted. + mDisableOnChange = false; + onChange(); + } catch (KeyStoreException e) { + onException(e); + } catch (FileNotFoundException e) { + onException(e); + } catch (NoSuchAlgorithmException e) { + onException(e); + } catch (CertificateException e) { + onException(e); + } catch (IOException e) { + onException(e); + } finally { + // in case we exit with an exception, we need to reset this + mDisableOnChange = false; + } + } + } + + @Override + public IWizardPage getPreviousPage() { + return mWizard.getKeystoreSelectionPage(); + } + + @Override + public IWizardPage getNextPage() { + if (mWizard.getKeyCreationMode()) { + return mWizard.getKeyCreationPage(); + } + + return mWizard.getKeyCheckPage(); + } + + /** + * Handles changes and update the error message and calls {@link #setPageComplete(boolean)}. + */ + private void onChange() { + if (mDisableOnChange) { + return; + } + + setErrorMessage(null); + setMessage(null); + + if (mWizard.getKeyCreationMode() == false) { + if (mKeyAliases.getSelectionIndex() == -1) { + setErrorMessage("Select a key alias."); + setPageComplete(false); + return; + } + + if (mKeyPassword.getText().trim().length() == 0) { + setErrorMessage("Enter key password."); + setPageComplete(false); + return; + } + } + + setPageComplete(true); + } + + private void enableWidgets() { + boolean useKey = !mWizard.getKeyCreationMode(); + mKeyAliasesLabel.setEnabled(useKey); + mKeyAliases.setEnabled(useKey); + mKeyPassword.setEnabled(useKey); + mKeyPasswordLabel.setEnabled(useKey); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/SigningExportPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/KeystoreSelectionPage.java index 5e7ed0f..c5a4d47 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/SigningExportPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/KeystoreSelectionPage.java @@ -20,6 +20,7 @@ import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.adt.project.export.ExportWizard.ExportWizardPage; import org.eclipse.core.resources.IProject; +import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; @@ -36,23 +37,26 @@ import org.eclipse.swt.widgets.Text; import java.io.File; /** - * Second export wizard page. + * Keystore selection page. This page allows to choose to create a new keystore or use an + * existing one. */ -public class SigningExportPage extends ExportWizardPage { +final class KeystoreSelectionPage extends ExportWizardPage { private final ExportWizard mWizard; + private Button mUseExistingKeystore; + private Button mCreateKeystore; private Text mKeystore; - private Text mAlias; private Text mKeystorePassword; - private Text mKeyPassword; + private Label mConfirmLabel; + private Text mKeystorePassword2; private boolean mDisableOnChange = false; - protected SigningExportPage(ExportWizard wizard, String pageName) { + protected KeystoreSelectionPage(ExportWizard wizard, String pageName) { super(pageName); mWizard = wizard; - setTitle("Application Signing"); - setDescription("Defines which store, key and certificate to use to sign the Android Application."); + setTitle("Keystore selection"); + setDescription(""); //TODO } public void createControl(Composite parent) { @@ -62,8 +66,19 @@ public class SigningExportPage extends ExportWizardPage { composite.setLayout(gl); GridData gd; + + mUseExistingKeystore = new Button(composite, SWT.RADIO); + mUseExistingKeystore.setText("Use existing keystore"); + mUseExistingKeystore.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + gd.horizontalSpan = 3; + mUseExistingKeystore.setSelection(true); + + mCreateKeystore = new Button(composite, SWT.RADIO); + mCreateKeystore.setText("Create new keystore"); + mCreateKeystore.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + gd.horizontalSpan = 3; - new Label(composite, SWT.NONE).setText("Keystore:"); + new Label(composite, SWT.NONE).setText("Location:"); mKeystore = new Text(composite, SWT.BORDER); mKeystore.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); final Button browseButton = new Button(composite, SWT.PUSH); @@ -71,8 +86,14 @@ public class SigningExportPage extends ExportWizardPage { browseButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { - FileDialog fileDialog = new FileDialog(browseButton.getShell(), SWT.OPEN); - fileDialog.setText("Load Keystore"); + FileDialog fileDialog; + if (mUseExistingKeystore.getSelection()) { + fileDialog = new FileDialog(browseButton.getShell(),SWT.OPEN); + fileDialog.setText("Load Keystore"); + } else { + fileDialog = new FileDialog(browseButton.getShell(),SWT.SAVE); + fileDialog.setText("Select Keystore Name"); + } String fileName = fileDialog.open(); if (fileName != null) { @@ -80,64 +101,73 @@ public class SigningExportPage extends ExportWizardPage { } } }); - - new Composite(composite, SWT.NONE).setLayoutData(gd = new GridData()); - gd.horizontalSpan = 2; - gd.heightHint = 0; - new Button(composite, SWT.PUSH).setText("New..."); - - new Label(composite, SWT.NONE).setText("Key Alias:"); - mAlias = new Text(composite, SWT.BORDER); - mAlias.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); - - new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL).setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); - gd.horizontalSpan = 3; - new Label(composite, SWT.NONE).setText("Store password:"); + new Label(composite, SWT.NONE).setText("Password:"); mKeystorePassword = new Text(composite, SWT.BORDER | SWT.PASSWORD); mKeystorePassword.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + mKeystorePassword.addVerifyListener(sPasswordVerifier); new Composite(composite, SWT.NONE).setLayoutData(gd = new GridData()); gd.heightHint = gd.widthHint = 0; - new Label(composite, SWT.NONE).setText("Key password:"); - mKeyPassword = new Text(composite, SWT.BORDER | SWT.PASSWORD); - mKeyPassword.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + mConfirmLabel = new Label(composite, SWT.NONE); + mConfirmLabel.setText("Confirm:"); + mKeystorePassword2 = new Text(composite, SWT.BORDER | SWT.PASSWORD); + mKeystorePassword2.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + mKeystorePassword2.addVerifyListener(sPasswordVerifier); + new Composite(composite, SWT.NONE).setLayoutData(gd = new GridData()); + gd.heightHint = gd.widthHint = 0; + mKeystorePassword2.setEnabled(false); // Show description the first time setErrorMessage(null); setMessage(null); setControl(composite); - mKeystore.addModifyListener(new ModifyListener() { - public void modifyText(ModifyEvent e) { - mWizard.setKeystore(mKeystore.getText().trim()); - onChange(); + mUseExistingKeystore.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + boolean createStore = !mUseExistingKeystore.getSelection(); + mKeystorePassword2.setEnabled(createStore); + mConfirmLabel.setEnabled(createStore); + mWizard.setKeystoreCreationMode(createStore); + onChange(); } }); - mAlias.addModifyListener(new ModifyListener() { + + mKeystore.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { - mWizard.setKeyAlias(mAlias.getText().trim()); + mWizard.setKeystore(mKeystore.getText().trim()); onChange(); } }); + mKeystorePassword.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { - mWizard.setKeystorePassword(mKeystorePassword.getText().trim().toCharArray()); + mWizard.setKeystorePassword(mKeystorePassword.getText()); onChange(); } }); - mKeyPassword.addModifyListener(new ModifyListener() { + + mKeystorePassword2.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { - mWizard.setKeyPassword(mKeyPassword.getText().trim().toCharArray()); onChange(); } }); } @Override + public IWizardPage getNextPage() { + if (mUseExistingKeystore.getSelection()) { + return mWizard.getKeySelectionPage(); + } + + return mWizard.getKeyCreationPage(); + } + + @Override void onShow() { // fill the texts with information loaded from the project. - if (mNewProjectReference) { + if ((mProjectDataChanged & DATA_PROJECT) != 0) { // reset the keystore/alias from the content of the project IProject project = mWizard.getProject(); @@ -150,14 +180,9 @@ public class SigningExportPage extends ExportWizardPage { mKeystore.setText(keystore); } - String alias = ProjectHelper.loadStringProperty(project, ExportWizard.PROPERTY_ALIAS); - if (alias != null) { - mAlias.setText(alias); - } - // reset the passwords mKeystorePassword.setText(""); //$NON-NLS-1$ - mKeyPassword.setText(""); //$NON-NLS-1$ + mKeystorePassword2.setText(""); //$NON-NLS-1$ // enable onChange, and call it to display errors and enable/disable pageCompleted. mDisableOnChange = false; @@ -176,6 +201,8 @@ public class SigningExportPage extends ExportWizardPage { setErrorMessage(null); setMessage(null); + boolean createStore = !mUseExistingKeystore.getSelection(); + // checks the keystore path is non null. String keystore = mKeystore.getText().trim(); if (keystore.length() == 0) { @@ -185,32 +212,47 @@ public class SigningExportPage extends ExportWizardPage { } else { File f = new File(keystore); if (f.exists() == false) { - setErrorMessage("Keystore does not exists!"); - setPageComplete(false); - return; + if (createStore == false) { + setErrorMessage("Keystore does not exist."); + setPageComplete(false); + return; + } } else if (f.isDirectory()) { - setErrorMessage("Keystore is a directory!"); + setErrorMessage("Keystore path is a directory."); setPageComplete(false); return; + } else if (f.isFile()) { + if (createStore) { + setErrorMessage("File already exists."); + setPageComplete(false); + return; + } } } - if (mAlias.getText().trim().length() == 0) { - setErrorMessage("Enter key alias."); + String value = mKeystorePassword.getText(); + if (value.length() == 0) { + setErrorMessage("Enter keystore password."); setPageComplete(false); return; - } - - if (mKeystorePassword.getText().trim().length() == 0) { - setErrorMessage("Enter keystore password."); + } else if (createStore && value.length() < 6) { + setErrorMessage("Keystore password is too short - must be at least 6 characters."); setPageComplete(false); return; } - if (mKeyPassword.getText().trim().length() == 0) { - setErrorMessage("Enter key password."); - setPageComplete(false); - return; + if (createStore) { + if (mKeystorePassword2.getText().length() == 0) { + setErrorMessage("Confirm keystore password."); + setPageComplete(false); + return; + } + + if (mKeystorePassword.getText().equals(mKeystorePassword2.getText()) == false) { + setErrorMessage("Keystore passwords do not match."); + setPageComplete(false); + return; + } } setPageComplete(true); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/PreExportPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ProjectCheckPage.java index 5fc204d..3614be3 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/PreExportPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ProjectCheckPage.java @@ -47,7 +47,7 @@ import java.io.File; /** * First Export Wizard Page. Display warning/errors. */ -public class PreExportPage extends ExportWizardPage { +final class ProjectCheckPage extends ExportWizardPage { private final static String IMG_ERROR = "error.png"; //$NON-NLS-1$ private final static String IMG_WARNING = "warning.png"; //$NON-NLS-1$ @@ -60,12 +60,13 @@ public class PreExportPage extends ExportWizardPage { private Composite mErrorComposite; private Text mProjectText; private ProjectChooserHelper mProjectChooserHelper; + private boolean mFirstOnShow = true; - protected PreExportPage(ExportWizard wizard, String pageName) { + protected ProjectCheckPage(ExportWizard wizard, String pageName) { super(pageName); mWizard = wizard; - setTitle("Pre Export Checks"); + setTitle("Project Checks"); setDescription("Performs a set of checks to make sure the application can be exported."); } @@ -123,10 +124,14 @@ public class PreExportPage extends ExportWizardPage { @Override void onShow() { - // get the project and init the ui - IProject project = mWizard.getProject(); - if (project != null) { - mProjectText.setText(project.getName()); + if (mFirstOnShow) { + // get the project and init the ui + IProject project = mWizard.getProject(); + if (project != null) { + mProjectText.setText(project.getName()); + } + + mFirstOnShow = false; } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainer.java index d7b290b..945fe52 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainer.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainer.java @@ -27,16 +27,19 @@ class AndroidClasspathContainer implements IClasspathContainer { private IClasspathEntry[] mClasspathEntry; private IPath mContainerPath; + private String mName; /** * Constructs the container with the {@link IClasspathEntry} representing the android * framework jar file and the container id * @param entry the entry representing the android framework. * @param path the path containing the classpath container id. + * @param name the name of the container to display. */ - AndroidClasspathContainer(IClasspathEntry entry, IPath path) { + AndroidClasspathContainer(IClasspathEntry entry, IPath path, String name) { mClasspathEntry = new IClasspathEntry[] { entry }; mContainerPath = path; + mName = name; } public IClasspathEntry[] getClasspathEntries() { @@ -44,7 +47,7 @@ class AndroidClasspathContainer implements IClasspathContainer { } public String getDescription() { - return "Android Library"; + return mName; } public int getKind() { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java index 5dca350..2cafa01 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java @@ -16,10 +16,16 @@ package com.android.ide.eclipse.adt.project.internal; +import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.project.ProjectHelper; +import com.android.ide.eclipse.adt.sdk.LoadStatus; +import com.android.ide.eclipse.adt.sdk.Sdk; +import com.android.ide.eclipse.common.project.BaseProjectHelper; +import com.android.sdklib.IAndroidTarget; +import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.NullProgressMonitor; @@ -38,10 +44,6 @@ import org.eclipse.jdt.core.JavaModelException; * {@link IProject}s. This removes the hard-coded path to the android.jar. */ public class AndroidClasspathContainerInitializer extends ClasspathContainerInitializer { - /** The old container id */ - private final static String OLD_CONTAINER_ID = - "com.android.ide.eclipse.adt.project.AndroidClasspathContainerInitializer"; //$NON-NLS-1$ - /** The container id for the android framework jar file */ private final static String CONTAINER_ID = "com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"; //$NON-NLS-1$ @@ -58,17 +60,10 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit */ @Override public void initialize(IPath containerPath, IJavaProject project) throws CoreException { - String id = null; - if (OLD_CONTAINER_ID.equals(containerPath.toString())) { - id = OLD_CONTAINER_ID; - } else if (CONTAINER_ID.equals(containerPath.toString())) { - id = CONTAINER_ID; - } - - if (id != null) { - JavaCore.setClasspathContainer(new Path(id), + if (CONTAINER_ID.equals(containerPath.toString())) { + JavaCore.setClasspathContainer(new Path(CONTAINER_ID), new IJavaProject[] { project }, - new IClasspathContainer[] { allocateAndroidContainer(id) }, + new IClasspathContainer[] { allocateAndroidContainer(CONTAINER_ID, project) }, new NullProgressMonitor()); } } @@ -82,15 +77,6 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit } /** - * Checks the {@link IPath} objects against the old android framework container id and - * returns <code>true</code> if they are identical. - * @param path the <code>IPath</code> to check. - */ - public static boolean checkOldPath(IPath path) { - return OLD_CONTAINER_ID.equals(path.toString()); - } - - /** * Checks the {@link IPath} objects against the android framework container id and * returns <code>true</code> if they are identical. * @param path the <code>IPath</code> to check. @@ -106,41 +92,18 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit * @return <code>true</code> if success, <code>false</code> otherwise. */ public static boolean updateProjects(IJavaProject[] androidProjects) { - try { - // because those projects could have the old id, we are going to fix - // them dynamically here. - for (IJavaProject javaProject: androidProjects) { - IClasspathEntry[] entries = javaProject.getRawClasspath(); - - int containerIndex = ProjectHelper.findClasspathEntryByPath(entries, - OLD_CONTAINER_ID, - IClasspathEntry.CPE_CONTAINER); - if (containerIndex != -1) { - // the project has the old container, we remove it - entries = ProjectHelper.removeEntryFromClasspath(entries, containerIndex); - - // we add the new one instead - entries = ProjectHelper.addEntryToClasspath(entries, getContainerEntry()); - - // and give the new entries to the project - javaProject.setRawClasspath(entries, new NullProgressMonitor()); - } - } - // Allocate a new AndroidClasspathContainer, and associate it to the android framework // container id for each projects. // By providing a new association between a container id and a IClasspathContainer, // this forces the JDT to query the IClasspathContainer for new IClasspathEntry (with // IClasspathContainer#getClasspathEntries()), and therefore force recompilation of // the projects. - // TODO: We could only do that for the projects haven't fixed above - // (this isn't something that will happen a lot though) int projectCount = androidProjects.length; IClasspathContainer[] containers = new IClasspathContainer[projectCount]; for (int i = 0 ; i < projectCount; i++) { - containers[i] = allocateAndroidContainer(CONTAINER_ID); + containers[i] = allocateAndroidContainer(CONTAINER_ID, androidProjects[i]); } // give each project their new container in one call. @@ -158,23 +121,123 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit * Allocates and returns an {@link AndroidClasspathContainer} object with the proper * path to the framework jar file. * @param containerId the container id to be used. + * @param javaProject The java project that will receive the container. */ - private static IClasspathContainer allocateAndroidContainer(String containerId) { - return new AndroidClasspathContainer(createFrameworkClasspath(), new Path(containerId)); + private static IClasspathContainer allocateAndroidContainer(String containerId, + IJavaProject javaProject) { + IProject iProject = javaProject.getProject(); + + // remove potential MARKER_TARGETs. + try { + if (iProject.exists()) { + iProject.deleteMarkers(AdtConstants.MARKER_TARGET, true, + IResource.DEPTH_INFINITE); + } + } catch (CoreException ce) { + // just log the error + AdtPlugin.log(ce, "Error removing target marker."); + } + + + // first we check if the SDK has been loaded + boolean sdkIsLoaded = AdtPlugin.getDefault().getSdkLoadStatus(javaProject) == + LoadStatus.LOADED; + + // then we check if the project has a valid target. + IAndroidTarget target = null; + if (sdkIsLoaded) { + target = Sdk.getCurrent().getTarget(iProject); + } + + // if we are loaded and the target is non null, we create a valid ClassPathContainer + if (sdkIsLoaded && target != null) { + String targetName = null; + if (target.isPlatform()) { + targetName = target.getName(); + } else { + targetName = String.format("%1$s (%2$s)", target.getName(), + target.getApiVersionName()); + } + + return new AndroidClasspathContainer(createFrameworkClasspath(target), + new Path(containerId), targetName); + } + + // else we put a marker on the project, and return a dummy container (to replace the + // previous one if there was one.) + + // Get the project's target's hash string (if it exists) + String hashString = Sdk.getProjectTargetHashString(iProject); + + String message = null; + boolean outputToConsole = true; + if (hashString == null || hashString.length() == 0) { + message = String.format( + "Project has no target set. Edit the project properties to set one."); + } else if (sdkIsLoaded) { + message = String.format( + "Unable to resolve target '%s'", hashString); + } else { + // this is the case where there is a hashString but the SDK is not yet + // loaded and therefore we can't get the target yet. + message = String.format( + "Unable to resolve target '%s' until the SDK is loaded.", hashString); + + // let's not log this one to the console as it will happen at every boot, + // and it's expected. (we do keep the error marker though). + outputToConsole = false; + } + + // log the error and put the marker on the project + if (outputToConsole) { + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_ALWAYS, iProject, message); + } + IMarker marker = BaseProjectHelper.addMarker(iProject, AdtConstants.MARKER_TARGET, message, + IMarker.SEVERITY_ERROR); + + // add a marker priority as this is an more important error than the error that will + // spring from the lack of library + try { + marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); + } catch (CoreException e) { + // just log the error + AdtPlugin.log(e, "Error changing target marker priority."); + } + + // return a dummy container to replace the one we may have had before. + return new IClasspathContainer() { + public IClasspathEntry[] getClasspathEntries() { + return new IClasspathEntry[0]; + } + + public String getDescription() { + return "Unable to get system library for the project"; + } + + public int getKind() { + return IClasspathContainer.K_DEFAULT_SYSTEM; + } + + public IPath getPath() { + return null; + } + }; } /** - * Creates and returns a new {@link IClasspathEntry} object for the android framework. - * <p/>This references the OS path to the android.jar and the java doc directory. This is - * dynamically created when a project is opened, and never saved in the project itself, so - * there's no risk of storing an obsolete path. + * Creates and returns a new {@link IClasspathEntry} object for the android + * framework. <p/>This references the OS path to the android.jar and the + * java doc directory. This is dynamically created when a project is opened, + * and never saved in the project itself, so there's no risk of storing an + * obsolete path. + * + * @param target The target that contains the libraries. */ - private static IClasspathEntry createFrameworkClasspath() { + private static IClasspathEntry createFrameworkClasspath(IAndroidTarget target) { // now add the android framework to the class path. // create the path object. - IPath android_lib = new Path(AdtPlugin.getOsAbsoluteFramework()); - - IPath android_src = new Path(AdtPlugin.getOsAbsoluteAndroidSources()); + IPath android_lib = new Path(target.getPath(IAndroidTarget.ANDROID_JAR)); + IPath android_src = new Path(target.getPath(IAndroidTarget.SOURCES)); // create the java doc link. IClasspathAttribute cpAttribute = JavaCore.newClasspathAttribute( diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/properties/AndroidPropertyPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/properties/AndroidPropertyPage.java new file mode 100644 index 0000000..584dd0d --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/properties/AndroidPropertyPage.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.project.properties; + +import com.android.ide.eclipse.adt.sdk.Sdk; +import com.android.sdklib.IAndroidTarget; +import com.android.sdkuilib.SdkTargetSelector; + +import org.eclipse.core.resources.IProject; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.IWorkbenchPropertyPage; +import org.eclipse.ui.dialogs.PropertyPage; + +/** + * Property page for "Android" project. + * This is accessible from the Package Explorer when right clicking a project and choosing + * "Properties". + * + */ +public class AndroidPropertyPage extends PropertyPage implements IWorkbenchPropertyPage { + + private IProject mProject; + private SdkTargetSelector mSelector; + + public AndroidPropertyPage() { + // pass + } + + @Override + protected Control createContents(Composite parent) { + // get the element (this is not yet valid in the constructor). + mProject = (IProject)getElement(); + + Composite top = new Composite(parent, SWT.NONE); + top.setLayoutData(new GridData(GridData.FILL_BOTH)); + top.setLayout(new GridLayout(1, false)); + + Label l = new Label(top, SWT.NONE); + l.setText("Project Target"); + + // get the targets from the sdk + IAndroidTarget[] targets = null; + if (Sdk.getCurrent() != null) { + targets = Sdk.getCurrent().getTargets(); + } + + // build the UI. + mSelector = new SdkTargetSelector(top, targets, false /*allowMultipleSelection*/); + + if (Sdk.getCurrent() != null) { + IAndroidTarget target = Sdk.getCurrent().getTarget(mProject); + if (target != null) { + mSelector.setSelection(target); + } + } + + mSelector.setSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + // look for the selection and validate the page if there is a selection + IAndroidTarget target = mSelector.getFirstSelected(); + setValid(target != null); + } + }); + + return top; + } + + @Override + public boolean performOk() { + if (Sdk.getCurrent() != null) { + Sdk.getCurrent().setProject(mProject, mSelector.getFirstSelected()); + } + + return true; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/resources/AndroidJarLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidJarLoader.java index 8c37f05..fad4f19 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/resources/AndroidJarLoader.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidJarLoader.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.resources; +package com.android.ide.eclipse.adt.sdk; -import com.android.ide.eclipse.adt.resources.LayoutParamsParser.IClass; import com.android.ide.eclipse.common.AndroidConstants; import org.eclipse.core.runtime.IProgressMonitor; @@ -35,9 +34,12 @@ import javax.management.InvalidAttributeValueException; /** * Custom class loader able to load a class from the SDK jar file. */ -public class AndroidJarLoader extends ClassLoader implements IAndroidLoader { +public class AndroidJarLoader extends ClassLoader implements IAndroidClassLoader { - public final static class ClassWrapper implements IClass { + /** + * Wrapper around a {@link Class} to provide the methods of {@link IClassDescriptor}. + */ + public final static class ClassWrapper implements IClassDescriptor { private Class<?> mClass; public ClassWrapper(Class<?> clazz) { @@ -48,9 +50,9 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidLoader { return mClass.getCanonicalName(); } - public IClass[] getDeclaredClasses() { + public IClassDescriptor[] getDeclaredClasses() { Class<?>[] classes = mClass.getDeclaredClasses(); - IClass[] iclasses = new IClass[classes.length]; + IClassDescriptor[] iclasses = new IClassDescriptor[classes.length]; for (int i = 0 ; i < classes.length ; i++) { iclasses[i] = new ClassWrapper(classes[i]); } @@ -58,7 +60,7 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidLoader { return iclasses; } - public IClass getEnclosingClass() { + public IClassDescriptor getEnclosingClass() { return new ClassWrapper(mClass.getEnclosingClass()); } @@ -66,7 +68,7 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidLoader { return mClass.getSimpleName(); } - public IClass getSuperclass() { + public IClassDescriptor getSuperclass() { return new ClassWrapper(mClass.getSuperclass()); } @@ -131,17 +133,18 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidLoader { * @param packageFilter The package that contains all the class data to preload, using a fully * qualified binary name (.e.g "com.my.package."). The matching algorithm * is simple "startsWith". Use an empty string to include everything. + * @param taskLabel An optional task name for the sub monitor. Can be null. * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. * @throws IOException * @throws InvalidAttributeValueException * @throws ClassFormatError */ - public void preLoadClasses(String packageFilter, IProgressMonitor monitor) + public void preLoadClasses(String packageFilter, String taskLabel, IProgressMonitor monitor) throws IOException, InvalidAttributeValueException, ClassFormatError { // Transform the package name into a zip entry path String pathFilter = packageFilter.replaceAll("\\.", "/"); //$NON-NLS-1$ //$NON-NLS-2$ - SubMonitor progress = SubMonitor.convert(monitor, 100); + SubMonitor progress = SubMonitor.convert(monitor, taskLabel == null ? "" : taskLabel, 100); // create streams to read the intermediary archive FileInputStream fis = new FileInputStream(mOsFrameworkLocation); @@ -174,6 +177,7 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidLoader { // advance 5% of whatever is allocated on the progress bar progress.setWorkRemaining(100); progress.worked(5); + progress.subTask(String.format("Preload %1$s", className)); } } @@ -193,17 +197,18 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidLoader { * @throws InvalidAttributeValueException * @throws ClassFormatError */ - public HashMap<String, ArrayList<IClass>> findClassesDerivingFrom( + public HashMap<String, ArrayList<IClassDescriptor>> findClassesDerivingFrom( String packageFilter, String[] superClasses) throws IOException, InvalidAttributeValueException, ClassFormatError { packageFilter = packageFilter.replaceAll("\\.", "/"); //$NON-NLS-1$ //$NON-NLS-2$ - HashMap<String, ArrayList<IClass>> mClassesFound = new HashMap<String, ArrayList<IClass>>(); + HashMap<String, ArrayList<IClassDescriptor>> mClassesFound = + new HashMap<String, ArrayList<IClassDescriptor>>(); for (String className : superClasses) { - mClassesFound.put(className, new ArrayList<IClass>()); + mClassesFound.put(className, new ArrayList<IClassDescriptor>()); } // create streams to read the intermediary archive @@ -415,7 +420,7 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidLoader { * @param className the fully-qualified name of the class to return. * @throws ClassNotFoundException */ - public IClass getClass(String className) throws ClassNotFoundException { + public IClassDescriptor getClass(String className) throws ClassNotFoundException { try { return new ClassWrapper(loadClass(className)); } catch (ClassNotFoundException e) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java new file mode 100644 index 0000000..60561ab --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.sdk; + +import com.android.ide.eclipse.common.resources.IResourceRepository; +import com.android.ide.eclipse.editors.descriptors.IDescriptorProvider; +import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors; +import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors; +import com.android.ide.eclipse.editors.menu.descriptors.MenuDescriptors; +import com.android.ide.eclipse.editors.resources.descriptors.ResourcesDescriptors; +import com.android.ide.eclipse.editors.resources.manager.ProjectResources; +import com.android.ide.eclipse.editors.xml.descriptors.XmlDescriptors; +import com.android.layoutlib.api.ILayoutBridge; +import com.android.sdklib.IAndroidTarget; + +import java.util.Hashtable; +import java.util.Map; + +/** + * This class contains the data of an Android Target as loaded from the SDK. + */ +public class AndroidTargetData { + + public final static int DESCRIPTOR_MANIFEST = 1; + public final static int DESCRIPTOR_LAYOUT = 2; + public final static int DESCRIPTOR_MENU = 3; + public final static int DESCRIPTOR_XML = 4; + public final static int DESCRIPTOR_RESOURCES = 5; + public final static int DESCRIPTOR_SEARCHABLE = 6; + public final static int DESCRIPTOR_PREFERENCES = 7; + + public final static class LayoutBridge { + /** Link to the layout bridge */ + public ILayoutBridge bridge; + + public LoadStatus status = LoadStatus.LOADING; + + public ClassLoader classLoader; + } + + private final IAndroidTarget mTarget; + + /** + * mAttributeValues is a map { key => list [ values ] }. + * The key for the map is "(element-xml-name,attribute-namespace:attribute-xml-local-name)". + * The attribute namespace prefix must be: + * - "android" for AndroidConstants.NS_RESOURCES + * - "xmlns" for the XMLNS URI. + * + * This is used for attributes that do not have a unique name, but still need to be populated + * with values in the UI. Uniquely named attributes have their values in {@link #mEnumValueMap}. + */ + private final Hashtable<String, String[]> mAttributeValues = new Hashtable<String, String[]>(); + + private IResourceRepository mSystemResourceRepository; + + private final AndroidManifestDescriptors mManifestDescriptors; + private final LayoutDescriptors mLayoutDescriptors; + private final MenuDescriptors mMenuDescriptors; + private final XmlDescriptors mXmlDescriptors; + + private final Map<String, Map<String, Integer>> mEnumValueMap; + + private final ProjectResources mFrameworkResources; + private final LayoutBridge mLayoutBridge; + + private boolean mLayoutBridgeInit = false; + + /** + * Creates an AndroidTargetData object. + */ + AndroidTargetData(IAndroidTarget androidTarget, + IResourceRepository systemResourceRepository, + AndroidManifestDescriptors manifestDescriptors, + LayoutDescriptors layoutDescriptors, + MenuDescriptors menuDescriptors, + XmlDescriptors xmlDescriptors, + Map<String, Map<String, Integer>> enumValueMap, + String[] permissionValues, + String[] activityIntentActionValues, + String[] broadcastIntentActionValues, + String[] serviceIntentActionValues, + String[] intentCategoryValues, + ProjectResources resources, + LayoutBridge layoutBridge) { + + mTarget = androidTarget; + mSystemResourceRepository = systemResourceRepository; + mManifestDescriptors = manifestDescriptors; + mLayoutDescriptors = layoutDescriptors; + mMenuDescriptors = menuDescriptors; + mXmlDescriptors = xmlDescriptors; + mEnumValueMap = enumValueMap; + mFrameworkResources = resources; + mLayoutBridge = layoutBridge; + + setPermissions(permissionValues); + setIntentFilterActionsAndCategories(activityIntentActionValues, broadcastIntentActionValues, + serviceIntentActionValues, intentCategoryValues); + } + + public IResourceRepository getSystemResources() { + return mSystemResourceRepository; + } + + /** + * Returns an {@link IDescriptorProvider} from a given Id. + * The Id can be one of {@link #DESCRIPTOR_MANIFEST}, {@link #DESCRIPTOR_LAYOUT}, + * {@link #DESCRIPTOR_MENU}, or {@link #DESCRIPTOR_XML}. + * All other values will throw an {@link IllegalArgumentException}. + */ + public IDescriptorProvider getDescriptorProvider(int descriptorId) { + switch (descriptorId) { + case DESCRIPTOR_MANIFEST: + return mManifestDescriptors; + case DESCRIPTOR_LAYOUT: + return mLayoutDescriptors; + case DESCRIPTOR_MENU: + return mMenuDescriptors; + case DESCRIPTOR_XML: + return mXmlDescriptors; + case DESCRIPTOR_RESOURCES: + // FIXME: since it's hard-coded the Resources Descriptors are not platform dependent. + return ResourcesDescriptors.getInstance(); + case DESCRIPTOR_PREFERENCES: + return mXmlDescriptors.getPreferencesProvider(); + case DESCRIPTOR_SEARCHABLE: + return mXmlDescriptors.getSearchableProvider(); + default : + throw new IllegalArgumentException(); + } + } + + /** + * Returns the manifest descriptors. + */ + public AndroidManifestDescriptors getManifestDescriptors() { + return mManifestDescriptors; + } + + /** + * Returns the layout Descriptors. + */ + public LayoutDescriptors getLayoutDescriptors() { + return mLayoutDescriptors; + } + + /** + * Returns the menu descriptors. + */ + public MenuDescriptors getMenuDescriptors() { + return mMenuDescriptors; + } + + /** + * Returns the XML descriptors + */ + public XmlDescriptors getXmlDescriptors() { + return mXmlDescriptors; + } + + /** + * Returns this list of possible values for an XML attribute. + * <p/>This should only be called for attributes for which possible values depend on the + * parent element node. + * <p/>For attributes that have the same values no matter the parent node, use + * {@link #getEnumValueMap()}. + * @param elementName the name of the element containing the attribute. + * @param attributeName the name of the attribute + * @return an array of String with the possible values, or <code>null</code> if no values were + * found. + */ + public String[] getAttributeValues(String elementName, String attributeName) { + String key = String.format("(%1$s,%2$s)", elementName, attributeName); //$NON-NLS-1$ + return mAttributeValues.get(key); + } + + /** + * Returns this list of possible values for an XML attribute. + * <p/>This should only be called for attributes for which possible values depend on the + * parent and great-grand-parent element node. + * <p/>The typical example of this is for the 'name' attribute under + * activity/intent-filter/action + * <p/>For attributes that have the same values no matter the parent node, use + * {@link #getEnumValueMap()}. + * @param elementName the name of the element containing the attribute. + * @param attributeName the name of the attribute + * @param greatGrandParentElementName the great-grand-parent node. + * @return an array of String with the possible values, or <code>null</code> if no values were + * found. + */ + public String[] getAttributeValues(String elementName, String attributeName, + String greatGrandParentElementName) { + if (greatGrandParentElementName != null) { + String key = String.format("(%1$s,%2$s,%3$s)", //$NON-NLS-1$ + greatGrandParentElementName, elementName, attributeName); + String[] values = mAttributeValues.get(key); + if (values != null) { + return values; + } + } + + return getAttributeValues(elementName, attributeName); + } + + /** + * Returns the enum values map. + * <p/>The map defines the possible values for XML attributes. The key is the attribute name + * and the value is a map of (string, integer) in which the key (string) is the name of + * the value, and the Integer is the numerical value in the compiled binary XML files. + */ + public Map<String, Map<String, Integer>> getEnumValueMap() { + return mEnumValueMap; + } + + /** + * Returns the {@link ProjectResources} containing the Framework Resources. + */ + public ProjectResources getFrameworkResources() { + return mFrameworkResources; + } + + /** + * Returns a {@link LayoutBridge} object possibly containing a {@link ILayoutBridge} object. + * <p/>If {@link LayoutBridge#bridge} is <code>null</code>, {@link LayoutBridge#status} will + * contain the reason (either {@link LoadStatus#LOADING} or {@link LoadStatus#FAILED}). + * <p/>Valid {@link ILayoutBridge} objects are always initialized before being returned. + */ + public synchronized LayoutBridge getLayoutBridge() { + if (mLayoutBridgeInit == false && mLayoutBridge.bridge != null) { + mLayoutBridge.bridge.init(mTarget.getPath(IAndroidTarget.FONTS), + getEnumValueMap()); + mLayoutBridgeInit = true; + } + return mLayoutBridge; + } + + /** + * Sets the permission values + * @param permissionValues the list of permissions + */ + private void setPermissions(String[] permissionValues) { + setValues("(uses-permission,android:name)", permissionValues); //$NON-NLS-1$ + setValues("(application,android:permission)", permissionValues); //$NON-NLS-1$ + setValues("(activity,android:permission)", permissionValues); //$NON-NLS-1$ + setValues("(receiver,android:permission)", permissionValues); //$NON-NLS-1$ + setValues("(service,android:permission)", permissionValues); //$NON-NLS-1$ + setValues("(provider,android:permission)", permissionValues); //$NON-NLS-1$ + } + + private void setIntentFilterActionsAndCategories(String[] activityIntentActions, + String[] broadcastIntentActions, String[] serviceIntentActions, + String[] intentCategoryValues) { + setValues("(activity,action,android:name)", activityIntentActions); //$NON-NLS-1$ + setValues("(receiver,action,android:name)", broadcastIntentActions); //$NON-NLS-1$ + setValues("(service,action,android:name)", serviceIntentActions); //$NON-NLS-1$ + setValues("(category,android:name)", intentCategoryValues); //$NON-NLS-1$ + } + + /** + * Sets a (name, values) pair in the hash map. + * <p/> + * If the name is already present in the map, it is first removed. + * @param name the name associated with the values. + * @param values The values to add. + */ + private void setValues(String name, String[] values) { + mAttributeValues.remove(name); + mAttributeValues.put(name, values); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/resources/FrameworkResourceParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java index 703efcf..232b9e8 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/resources/FrameworkResourceParser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,19 +14,29 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.resources; +package com.android.ide.eclipse.adt.sdk; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData.LayoutBridge; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.resources.AttrsXmlParser; import com.android.ide.eclipse.common.resources.DeclareStyleableInfo; -import com.android.ide.eclipse.common.resources.FrameworkResourceManager; +import com.android.ide.eclipse.common.resources.IResourceRepository; import com.android.ide.eclipse.common.resources.ResourceItem; import com.android.ide.eclipse.common.resources.ResourceType; import com.android.ide.eclipse.common.resources.ViewClassInfo; +import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors; +import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors; +import com.android.ide.eclipse.editors.menu.descriptors.MenuDescriptors; +import com.android.ide.eclipse.editors.resources.manager.ProjectResources; +import com.android.ide.eclipse.editors.resources.manager.ResourceManager; +import com.android.ide.eclipse.editors.xml.descriptors.XmlDescriptors; +import com.android.layoutlib.api.ILayoutBridge; +import com.android.sdklib.IAndroidTarget; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import java.io.BufferedReader; @@ -34,10 +44,11 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.net.MalformedURLException; import java.net.URL; +import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -48,7 +59,7 @@ import java.util.Map; import javax.management.InvalidAttributeValueException; /** - * Parser for the framework library. + * Parser for the platform data in an SDK. * <p/> * This gather the following information: * <ul> @@ -57,14 +68,16 @@ import javax.management.InvalidAttributeValueException; * <li></li> * </ul> */ -public final class FrameworkResourceParser { +public final class AndroidTargetParser { private static final String TAG = "Framework Resource Parser"; + private final IAndroidTarget mAndroidTarget; /** - * Creates a framework resource parser. + * Creates a platform data parser. */ - public FrameworkResourceParser() { + public AndroidTargetParser(IAndroidTarget platformTarget) { + mAndroidTarget = platformTarget; } /** @@ -77,34 +90,29 @@ public final class FrameworkResourceParser { * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. * @return True if the SDK path was valid and parsing has been attempted. */ - public boolean parse(String osSdkPath, FrameworkResourceManager resourceManager, - IProgressMonitor monitor) { - if (osSdkPath == null || osSdkPath.length() == 0) { - return false; - } - + public IStatus run(IProgressMonitor monitor) { try { - SubMonitor progress = SubMonitor.convert(monitor, 100); + SubMonitor progress = SubMonitor.convert(monitor, + String.format("Parsing SDK %1$s", mAndroidTarget.getName()), + 120); AndroidJarLoader classLoader = - new AndroidJarLoader(osSdkPath + AndroidConstants.FN_FRAMEWORK_LIBRARY); + new AndroidJarLoader(mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); - progress.subTask("Preloading"); - preload(classLoader, progress.newChild(40)); - progress.setWorkRemaining(60); + preload(classLoader, progress.newChild(40, SubMonitor.SUPPRESS_NONE)); + progress.setWorkRemaining(80); if (progress.isCanceled()) { - return false; + return Status.CANCEL_STATUS; } // get the resource Ids. progress.subTask("Resource IDs"); - FrameworkResourceRepository systemResourceRepository = new FrameworkResourceRepository( - collectResourceIds(classLoader)); + IResourceRepository frameworkRepository = collectResourceIds(classLoader); progress.worked(5); if (progress.isCanceled()) { - return false; + return Status.CANCEL_STATUS; } // get the permissions @@ -113,56 +121,59 @@ public final class FrameworkResourceParser { progress.worked(5); if (progress.isCanceled()) { - return false; + return Status.CANCEL_STATUS; } - String osLibPath = osSdkPath + AndroidConstants.OS_SDK_LIBS_FOLDER; - // get the action and category values for the Intents. progress.subTask("Intents"); ArrayList<String> activity_actions = new ArrayList<String>(); ArrayList<String> broadcast_actions = new ArrayList<String>(); ArrayList<String> service_actions = new ArrayList<String>(); ArrayList<String> categories = new ArrayList<String>(); - collectIntentFilterActionsAndCategories(osLibPath, - activity_actions, broadcast_actions, service_actions, categories); + collectIntentFilterActionsAndCategories(activity_actions, broadcast_actions, + service_actions, categories); progress.worked(5); if (progress.isCanceled()) { - return false; + return Status.CANCEL_STATUS; } - progress.subTask("Layouts"); + // gather the attribute definition + progress.subTask("Attributes definitions"); AttrsXmlParser attrsXmlParser = new AttrsXmlParser( - osSdkPath + AndroidConstants.OS_SDK_ATTRS_XML); + mAndroidTarget.getPath(IAndroidTarget.ATTRIBUTES)); attrsXmlParser.preload(); + progress.subTask("Manifest definitions"); AttrsXmlParser attrsManifestXmlParser = new AttrsXmlParser( - osSdkPath + AndroidConstants.OS_SDK_ATTRS_MANIFEST_XML, + mAndroidTarget.getPath(IAndroidTarget.MANIFEST_ATTRIBUTES), attrsXmlParser); attrsManifestXmlParser.preload(); Collection<ViewClassInfo> mainList = new ArrayList<ViewClassInfo>(); Collection<ViewClassInfo> groupList = new ArrayList<ViewClassInfo>(); - collectLayoutClasses(osLibPath, classLoader, attrsXmlParser, mainList, groupList, + // collect the layout/widgets classes + progress.subTask("Widgets and layouts"); + collectLayoutClasses(classLoader, attrsXmlParser, mainList, groupList, progress.newChild(40)); if (progress.isCanceled()) { - return false; + return Status.CANCEL_STATUS; } ViewClassInfo[] layoutViewsInfo = mainList.toArray(new ViewClassInfo[mainList.size()]); ViewClassInfo[] layoutGroupsInfo = groupList.toArray( new ViewClassInfo[groupList.size()]); + // collect the preferences classes. mainList.clear(); groupList.clear(); collectPreferenceClasses(classLoader, attrsXmlParser, mainList, groupList, progress.newChild(5)); if (progress.isCanceled()) { - return false; + return Status.CANCEL_STATUS; } ViewClassInfo[] preferencesInfo = mainList.toArray(new ViewClassInfo[mainList.size()]); @@ -177,34 +188,77 @@ public final class FrameworkResourceParser { Map<String, Map<String, Integer>> enumValueMap = attrsXmlParser.getEnumFlagValues(); if (progress.isCanceled()) { - return false; + return Status.CANCEL_STATUS; } - String docBaseUrl = getDocumentationBaseUrl( - osSdkPath + AndroidConstants.OS_SDK_DOCS_FOLDER); - - FrameworkResourceManager.getInstance().setResources(systemResourceRepository, - layoutViewsInfo, - layoutGroupsInfo, - preferencesInfo, - preferenceGroupsInfo, - xmlMenuMap, - xmlSearchableMap, - manifestMap, + // From the information that was collected, create the pieces that will be put in + // the PlatformData object. + AndroidManifestDescriptors manifestDescriptors = new AndroidManifestDescriptors(); + manifestDescriptors.updateDescriptors(manifestMap); + progress.worked(10); + + if (progress.isCanceled()) { + return Status.CANCEL_STATUS; + } + + LayoutDescriptors layoutDescriptors = new LayoutDescriptors(); + layoutDescriptors.updateDescriptors(layoutViewsInfo, layoutGroupsInfo); + progress.worked(10); + + if (progress.isCanceled()) { + return Status.CANCEL_STATUS; + } + + MenuDescriptors menuDescriptors = new MenuDescriptors(); + menuDescriptors.updateDescriptors(xmlMenuMap); + progress.worked(10); + + if (progress.isCanceled()) { + return Status.CANCEL_STATUS; + } + + XmlDescriptors xmlDescriptors = new XmlDescriptors(); + xmlDescriptors.updateDescriptors(xmlSearchableMap, preferencesInfo, + preferenceGroupsInfo); + progress.worked(10); + + // load the framework resources. + ProjectResources resources = ResourceManager.getInstance().loadFrameworkResources( + mAndroidTarget); + progress.worked(10); + + // now load the layout lib bridge + LayoutBridge layoutBridge = loadLayoutBridge(); + progress.worked(10); + + // and finally create the PlatformData with all that we loaded. + AndroidTargetData targetData = new AndroidTargetData(mAndroidTarget, + frameworkRepository, + manifestDescriptors, + layoutDescriptors, + menuDescriptors, + xmlDescriptors, enumValueMap, permissionValues, activity_actions.toArray(new String[activity_actions.size()]), broadcast_actions.toArray(new String[broadcast_actions.size()]), service_actions.toArray(new String[service_actions.size()]), categories.toArray(new String[categories.size()]), - docBaseUrl); + resources, + layoutBridge); + + Sdk.getCurrent().setTargetData(mAndroidTarget, targetData); - return true; + return Status.OK_STATUS; } catch (Exception e) { AdtPlugin.logAndPrintError(e, TAG, "SDK parser failed"); //$NON-NLS-1$ + AdtPlugin.printToConsole("SDK parser failed", e.getMessage()); + return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "SDK parser failed", e); + } finally { + if (monitor != null) { + monitor.done(); + } } - - return false; } /** @@ -217,7 +271,9 @@ public final class FrameworkResourceParser { */ private void preload(AndroidJarLoader classLoader, IProgressMonitor monitor) { try { - classLoader.preLoadClasses("" /* all classes */, monitor); //$NON-NLS-1$ + classLoader.preLoadClasses("" /* all classes */, //$NON-NLS-1$ + mAndroidTarget.getName(), // monitor task label + monitor); } catch (InvalidAttributeValueException e) { AdtPlugin.log(e, "Problem preloading classes"); //$NON-NLS-1$ } catch (IOException e) { @@ -226,24 +282,27 @@ public final class FrameworkResourceParser { } /** - * Collects the resources IDs found in the SDK. + * Creates an IResourceRepository for the framework resources. * * @param classLoader The framework SDK jar classloader * @return a map of the resources, or null if it failed. */ - private Map<ResourceType, List<ResourceItem>> collectResourceIds( + private IResourceRepository collectResourceIds( AndroidJarLoader classLoader) { try { Class<?> r = classLoader.loadClass(AndroidConstants.CLASS_R); if (r != null) { - return parseRClass(r); + Map<ResourceType, List<ResourceItem>> map = parseRClass(r); + if (map != null) { + return new FrameworkResourceRepository(map); + } } } catch (ClassNotFoundException e) { AdtPlugin.logAndPrintError(e, TAG, "Collect resource IDs failed, class %1$s not found in %2$s", //$NON-NLS-1$ AndroidConstants.CLASS_R, - classLoader.getSource()); + mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); } return null; @@ -331,7 +390,7 @@ public final class FrameworkResourceParser { AdtPlugin.logAndPrintError(e, TAG, "Collect permissions failed, class %1$s not found in %2$s", //$NON-NLS-1$ AndroidConstants.CLASS_MANIFEST_PERMISSION, - classLoader.getSource()); + mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); } return new String[0]; @@ -347,13 +406,17 @@ public final class FrameworkResourceParser { * @param serviceActions the list which will receive the service action values. * @param categories the list which will receive the category values. */ - private void collectIntentFilterActionsAndCategories(String osLibPath, - ArrayList<String> activityActions, ArrayList<String> broadcastActions, + private void collectIntentFilterActionsAndCategories(ArrayList<String> activityActions, + ArrayList<String> broadcastActions, ArrayList<String> serviceActions, ArrayList<String> categories) { - collectValues(osLibPath + "activity_actions.txt" , activityActions); - collectValues(osLibPath + "broadcast_actions.txt" , broadcastActions); - collectValues(osLibPath + "service_actions.txt" , serviceActions); - collectValues(osLibPath + "categories.txt" , categories); + collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_ACTIVITY), + activityActions); + collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_BROADCAST), + broadcastActions); + collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_SERVICE), + serviceActions); + collectValues(mAndroidTarget.getPath(IAndroidTarget.CATEGORIES), + categories); } /** @@ -400,21 +463,21 @@ public final class FrameworkResourceParser { * Collects all layout classes information from the class loader and the * attrs.xml and sets the corresponding structures in the resource manager. * - * @param osLibPath The OS path to the SDK tools/lib folder, ending with a separator. - * @param classLoader The framework SDK jar classloader + * @param classLoader The framework SDK jar classloader in case we cannot get the widget from + * the platform directly * @param attrsXmlParser The parser of the attrs.xml file * @param mainList the Collection to receive the main list of {@link ViewClassInfo}. * @param groupList the Collection to receive the group list of {@link ViewClassInfo}. * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. */ - private void collectLayoutClasses(String osLibPath, - AndroidJarLoader classLoader, + private void collectLayoutClasses(AndroidJarLoader classLoader, AttrsXmlParser attrsXmlParser, Collection<ViewClassInfo> mainList, Collection<ViewClassInfo> groupList, IProgressMonitor monitor) { LayoutParamsParser ldp = null; try { - WidgetListLoader loader = new WidgetListLoader(osLibPath + "widgets.txt"); + WidgetClassLoader loader = new WidgetClassLoader( + mAndroidTarget.getPath(IAndroidTarget.WIDGETS)); if (loader.parseWidgetList(monitor)) { ldp = new LayoutParamsParser(loader, attrsXmlParser); } @@ -465,12 +528,13 @@ public final class FrameworkResourceParser { } } catch (NoClassDefFoundError e) { AdtPlugin.logAndPrintError(e, TAG, - "Collect preferences failed, class %1$s not found in %2$s", //$NON-NLS-1$ + "Collect preferences failed, class %1$s not found in %2$s", e.getMessage(), classLoader.getSource()); } catch (Throwable e) { AdtPlugin.log(e, "Android Framework Parser: failed to collect preference classes"); //$NON-NLS-1$ - AdtPlugin.printErrorToConsole("Android Framework Parser", "failed to collect preference classes"); + AdtPlugin.printErrorToConsole("Android Framework Parser", + "failed to collect preference classes"); } } @@ -537,40 +601,51 @@ public final class FrameworkResourceParser { } /** - * Returns the URL to the local documentation. - * Can return null if no documentation is found in the current SDK. - * - * @param osDocsPath Path to the documentation folder in the current SDK. - * The folder may not actually exist. - * @return A file:// URL on the local documentation folder if it exists or null. + * Loads the layout bridge from the dynamically loaded layoutlib.jar */ - private String getDocumentationBaseUrl(String osDocsPath) { - File f = new File(osDocsPath); + private LayoutBridge loadLayoutBridge() { + LayoutBridge layoutBridge = new LayoutBridge(); - if (f.isDirectory()) { - try { - // Note: to create a file:// URL, one would typically use something like - // f.toURI().toURL().toString(). However this generates a broken path on - // Windows, namely "C:\\foo" is converted to "file:/C:/foo" instead of - // "file:///C:/foo" (i.e. there should be 3 / after "file:"). So we'll - // do the correct thing manually. + try { + // get the URL for the file. + File f = new File(mAndroidTarget.getPath(IAndroidTarget.LAYOUT_LIB)); + if (f.isFile() == false) { + AdtPlugin.log(IStatus.ERROR, "layoutlib.jar is missing!"); //$NON-NLS-1$ + } else { + URL url = f.toURL(); - String path = f.getAbsolutePath(); - if (File.separatorChar != '/') { - path = path.replace(File.separatorChar, '/'); + // create a class loader. Because this jar reference interfaces + // that are in the editors plugin, it's important to provide + // a parent class loader. + layoutBridge.classLoader = new URLClassLoader(new URL[] { url }, + this.getClass().getClassLoader()); + + // load the class + Class<?> clazz = layoutBridge.classLoader.loadClass(AndroidConstants.CLASS_BRIDGE); + if (clazz != null) { + // instantiate an object of the class. + Constructor<?> constructor = clazz.getConstructor(); + if (constructor != null) { + Object bridge = constructor.newInstance(); + if (bridge instanceof ILayoutBridge) { + layoutBridge.bridge = (ILayoutBridge)bridge; + } + } } - // For some reason the URL class doesn't add the mandatory "//" after - // the "file:" protocol name, so it has to be hacked into the path. - URL url = new URL("file", null, "//" + path); //$NON-NLS-1$ //$NON-NLS-2$ - String result = url.toString(); - return result; - } catch (MalformedURLException e) { - // ignore malformed URLs + if (layoutBridge.bridge == null) { + layoutBridge.status = LoadStatus.FAILED; + AdtPlugin.log(IStatus.ERROR, "Failed to load " + AndroidConstants.CLASS_BRIDGE); //$NON-NLS-1$ + } else { + layoutBridge.status = LoadStatus.LOADED; + } } + } catch (Throwable t) { + layoutBridge.status = LoadStatus.FAILED; + // log the error. + AdtPlugin.log(t, "Failed to load the LayoutLib"); } - - return null; + + return layoutBridge; } - } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/resources/FrameworkResourceRepository.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/FrameworkResourceRepository.java index 19a7de3..f4b10df 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/resources/FrameworkResourceRepository.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/FrameworkResourceRepository.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.resources; +package com.android.ide.eclipse.adt.sdk; import com.android.ide.eclipse.common.resources.IResourceRepository; import com.android.ide.eclipse.common.resources.ResourceItem; @@ -26,9 +26,9 @@ import java.util.Set; /** * Implementation of the {@link IResourceRepository} interface to hold the system resource Ids - * parsed by {@link FrameworkResourceParser}. + * parsed by {@link AndroidTargetParser}. */ -public final class FrameworkResourceRepository implements IResourceRepository { +final class FrameworkResourceRepository implements IResourceRepository { private Map<ResourceType, List<ResourceItem>> mResourcesMap; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/resources/IAndroidLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/IAndroidClassLoader.java index f0f48ca..50d319e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/resources/IAndroidLoader.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/IAndroidClassLoader.java @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.resources; - -import com.android.ide.eclipse.adt.resources.LayoutParamsParser.IClass; +package com.android.ide.eclipse.adt.sdk; import java.io.IOException; import java.util.ArrayList; @@ -28,7 +26,25 @@ import javax.management.InvalidAttributeValueException; * Classes which implements this interface provide methods to access framework resource * data loaded from the SDK. */ -public interface IAndroidLoader { +public interface IAndroidClassLoader { + + /** + * Classes which implement this interface provide methods to describe a class. + */ + public interface IClassDescriptor { + + String getCanonicalName(); + + IClassDescriptor getSuperclass(); + + String getSimpleName(); + + IClassDescriptor getEnclosingClass(); + + IClassDescriptor[] getDeclaredClasses(); + + boolean isInstantiable(); + } /** * Finds and loads all classes that derive from a given set of super classes. @@ -43,7 +59,7 @@ public interface IAndroidLoader { * @throws InvalidAttributeValueException * @throws ClassFormatError */ - public HashMap<String, ArrayList<IClass>> findClassesDerivingFrom( + public HashMap<String, ArrayList<IClassDescriptor>> findClassesDerivingFrom( String rootPackage, String[] superClasses) throws IOException, InvalidAttributeValueException, ClassFormatError; @@ -52,7 +68,7 @@ public interface IAndroidLoader { * @param className the fully-qualified name of the class to return. * @throws ClassNotFoundException */ - public IClass getClass(String className) throws ClassNotFoundException; + public IClassDescriptor getClass(String className) throws ClassNotFoundException; /** * Returns a string indicating the source of the classes, typically for debugging diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/resources/LayoutParamsParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/LayoutParamsParser.java index ee71b60..dc600d7 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/resources/LayoutParamsParser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/LayoutParamsParser.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.resources; +package com.android.ide.eclipse.adt.sdk; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.sdk.IAndroidClassLoader.IClassDescriptor; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.CommonPlugin; import com.android.ide.eclipse.common.resources.AttrsXmlParser; import com.android.ide.eclipse.common.resources.ViewClassInfo; import com.android.ide.eclipse.common.resources.ViewClassInfo.LayoutParamsInfo; @@ -52,23 +53,10 @@ import javax.management.InvalidAttributeValueException; public class LayoutParamsParser { /** - * Classes which implement this interface provide methods to describe a class. + * Class extending {@link ViewClassInfo} by adding the notion of instantiability. + * {@link LayoutParamsParser#getViews()} and {@link LayoutParamsParser#getGroups()} should + * only return classes that can be instantiated. */ - public interface IClass { - - public String getCanonicalName(); - - public IClass getSuperclass(); - - public String getSimpleName(); - - public IClass getEnclosingClass(); - - public IClass[] getDeclaredClasses(); - - public boolean isInstantiable(); - } - final static class ExtViewClassInfo extends ViewClassInfo { private boolean mIsInstantiable; @@ -87,16 +75,16 @@ public class LayoutParamsParser { /* Note: protected members/methods are overridden in unit tests */ /** Reference to android.view.View */ - protected IClass mTopViewClass; + protected IClassDescriptor mTopViewClass; /** Reference to android.view.ViewGroup */ - protected IClass mTopGroupClass; + protected IClassDescriptor mTopGroupClass; /** Reference to android.view.ViewGroup$LayoutParams */ - protected IClass mTopLayoutParamsClass; + protected IClassDescriptor mTopLayoutParamsClass; /** Input list of all classes deriving from android.view.View */ - protected ArrayList<IClass> mViewList; + protected ArrayList<IClassDescriptor> mViewList; /** Input list of all classes deriving from android.view.ViewGroup */ - protected ArrayList<IClass> mGroupList; + protected ArrayList<IClassDescriptor> mGroupList; /** Output map of FQCN => info on View classes */ protected TreeMap<String, ExtViewClassInfo> mViewMap; @@ -109,14 +97,14 @@ public class LayoutParamsParser { protected AttrsXmlParser mAttrsXmlParser; /** The android.jar class loader */ - protected IAndroidLoader mClassLoader; + protected IAndroidClassLoader mClassLoader; /** * Instantiate a new LayoutParamsParser. * @param classLoader The android.jar class loader * @param attrsXmlParser The parser of the attrs.xml file */ - public LayoutParamsParser(IAndroidLoader classLoader, + public LayoutParamsParser(IAndroidClassLoader classLoader, AttrsXmlParser attrsXmlParser) { mClassLoader = classLoader; mAttrsXmlParser = attrsXmlParser; @@ -135,8 +123,8 @@ public class LayoutParamsParser { /** * TODO: doc here. * <p/> - * Note: on output we should have NO dependency on IClass, otherwise we wouldn't be able - * to unload the class loader later. + * Note: on output we should have NO dependency on {@link IClassDescriptor}, + * otherwise we wouldn't be able to unload the class loader later. * <p/> * Note on Vocabulary: FQCN=Fully Qualified Class Name (e.g. "my.package.class$innerClass") * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. @@ -168,8 +156,8 @@ public class LayoutParamsParser { if (paramsClassName != null) { superClasses[2] = paramsClassName; } - HashMap<String, ArrayList<IClass>> found = mClassLoader.findClassesDerivingFrom( - "android.", superClasses); + HashMap<String, ArrayList<IClassDescriptor>> found = + mClassLoader.findClassesDerivingFrom("android.", superClasses); mTopViewClass = mClassLoader.getClass(rootClassName); mTopGroupClass = mClassLoader.getClass(groupClassName); if (paramsClassName != null) { @@ -196,26 +184,26 @@ public class LayoutParamsParser { progress.setWorkRemaining(mGroupList.size() + mViewList.size()); - for (IClass groupChild : mGroupList) { + for (IClassDescriptor groupChild : mGroupList) { addGroup(groupChild); progress.worked(1); } - for (IClass viewChild : mViewList) { + for (IClassDescriptor viewChild : mViewList) { if (viewChild != mTopGroupClass) { addView(viewChild); } progress.worked(1); } } catch (ClassNotFoundException e) { - CommonPlugin.log(e, "Problem loading class %1$s or %2$s", //$NON-NLS-1$ + AdtPlugin.log(e, "Problem loading class %1$s or %2$s", //$NON-NLS-1$ rootClassName, groupClassName); } catch (InvalidAttributeValueException e) { - CommonPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$ + AdtPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$ } catch (ClassFormatError e) { - CommonPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$ + AdtPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$ } catch (IOException e) { - CommonPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$ + AdtPlugin.log(e, "Problem loading classes"); //$NON-NLS-1$ } } @@ -223,7 +211,7 @@ public class LayoutParamsParser { * Parses a View class and adds a ExtViewClassInfo for it in mViewMap. * It calls itself recursively to handle super classes which are also Views. */ - private ExtViewClassInfo addView(IClass viewClass) { + private ExtViewClassInfo addView(IClassDescriptor viewClass) { String fqcn = viewClass.getCanonicalName(); if (mViewMap.containsKey(fqcn)) { return mViewMap.get(fqcn); @@ -238,7 +226,7 @@ public class LayoutParamsParser { // All view classes derive from mTopViewClass by design. // Do not lookup the super class for mTopViewClass itself. if (viewClass.equals(mTopViewClass) == false) { - IClass superClass = viewClass.getSuperclass(); + IClassDescriptor superClass = viewClass.getSuperclass(); ExtViewClassInfo superClassInfo = addView(superClass); info.setSuperClass(superClassInfo); } @@ -251,7 +239,7 @@ public class LayoutParamsParser { * Parses a ViewGroup class and adds a ExtViewClassInfo for it in mGroupMap. * It calls itself recursively to handle super classes which are also ViewGroups. */ - private ExtViewClassInfo addGroup(IClass groupClass) { + private ExtViewClassInfo addGroup(IClassDescriptor groupClass) { String fqcn = groupClass.getCanonicalName(); if (mGroupMap.containsKey(fqcn)) { return mGroupMap.get(fqcn); @@ -265,7 +253,7 @@ public class LayoutParamsParser { // android.view.View (i.e. mTopViewClass here). So the only group that can have View as // its super class is the ViewGroup base class and we don't try to resolve it since groups // are loaded before views. - IClass superClass = groupClass.getSuperclass(); + IClassDescriptor superClass = groupClass.getSuperclass(); // Assertion: at this point, we should have // superClass != mTopViewClass || fqcn.equals(AndroidConstants.CLASS_VIEWGROUP); @@ -291,15 +279,15 @@ public class LayoutParamsParser { * * @return The {@link LayoutParamsInfo} for the ViewGroup class or null. */ - private LayoutParamsInfo addLayoutParams(IClass groupClass) { + private LayoutParamsInfo addLayoutParams(IClassDescriptor groupClass) { // Is there a LayoutParams in this group class? - IClass layoutParamsClass = findLayoutParams(groupClass); + IClassDescriptor layoutParamsClass = findLayoutParams(groupClass); // if there's no layout data in the group class, link to the one from the // super class. if (layoutParamsClass == null) { - for (IClass superClass = groupClass.getSuperclass(); + for (IClassDescriptor superClass = groupClass.getSuperclass(); layoutParamsClass == null && superClass != null && superClass.equals(mTopViewClass) == false; @@ -319,7 +307,7 @@ public class LayoutParamsParser { * Parses a LayoutParams class and returns a LayoutParamsInfo object for it. * It calls itself recursively to handle the super class of the LayoutParams. */ - private LayoutParamsInfo getLayoutParamsInfo(IClass layoutParamsClass) { + private LayoutParamsInfo getLayoutParamsInfo(IClassDescriptor layoutParamsClass) { String fqcn = layoutParamsClass.getCanonicalName(); LayoutParamsInfo layoutParamsInfo = mLayoutParamsMap.get(fqcn); @@ -330,7 +318,7 @@ public class LayoutParamsParser { // Find the link on the LayoutParams super class LayoutParamsInfo superClassInfo = null; if (layoutParamsClass.equals(mTopLayoutParamsClass) == false) { - IClass superClass = layoutParamsClass.getSuperclass(); + IClassDescriptor superClass = layoutParamsClass.getSuperclass(); superClassInfo = getLayoutParamsInfo(superClass); } @@ -355,9 +343,9 @@ public class LayoutParamsParser { * @param groupClass The ViewGroup derived class * @return The Class of the inner LayoutParams or null if none is declared. */ - private IClass findLayoutParams(IClass groupClass) { - IClass[] innerClasses = groupClass.getDeclaredClasses(); - for (IClass innerClass : innerClasses) { + private IClassDescriptor findLayoutParams(IClassDescriptor groupClass) { + IClassDescriptor[] innerClasses = groupClass.getDeclaredClasses(); + for (IClassDescriptor innerClass : innerClasses) { if (innerClass.getSimpleName().equals(AndroidConstants.CLASS_LAYOUTPARAMS)) { return innerClass; } @@ -365,12 +353,16 @@ public class LayoutParamsParser { return null; } + /** + * Computes and return a list of ViewClassInfo from a map by filtering out the class that + * cannot be instantiated. + */ private List<ViewClassInfo> getInstantiables(SortedMap<String, ExtViewClassInfo> map) { Collection<ExtViewClassInfo> values = map.values(); ArrayList<ViewClassInfo> list = new ArrayList<ViewClassInfo>(); for (ExtViewClassInfo info : values) { - if (info.mIsInstantiable) { + if (info.isInstantiable()) { list.add(info); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/LoadStatus.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/LoadStatus.java new file mode 100644 index 0000000..6bf0272 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/LoadStatus.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.sdk; + +/** + * Enum for loading status of various SDK parts. + */ +public enum LoadStatus { + LOADING, LOADED, FAILED; +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java new file mode 100644 index 0000000..3b9d10e --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.sdk; + +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerInitializer; +import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.ISdkLog; +import com.android.sdklib.SdkConstants; +import com.android.sdklib.SdkManager; +import com.android.sdklib.project.ProjectProperties; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Central point to load, manipulate and deal with the Android SDK. Only one SDK can be used + * at the same time. + * + * To start using an SDK, call {@link #loadSdk(String)} which returns the instance of + * the Sdk object. + * + * To get the list of platforms present in the SDK, call {@link #getPlatforms()}. + * To get the list of add-ons present in the SDK, call {@link #getAddons()}. + * + */ +public class Sdk { + private final static String PROPERTY_PROJECT_TARGET = "androidTarget"; //$NON-NLS-1$ + + private static Sdk sCurrentSdk = null; + + private final SdkManager mManager; + private final HashMap<IProject, IAndroidTarget> mProjectMap = + new HashMap<IProject, IAndroidTarget>(); + private final HashMap<IAndroidTarget, AndroidTargetData> mTargetMap = + new HashMap<IAndroidTarget, AndroidTargetData>(); + private final String mDocBaseUrl; + + /** + * Loads an SDK and returns an {@link Sdk} object if success. + * @param sdkLocation the OS path to the SDK. + */ + public static Sdk loadSdk(String sdkLocation) { + if (sCurrentSdk != null) { + // manual unload? + sCurrentSdk = null; + } + + final ArrayList<String> logMessages = new ArrayList<String>(); + ISdkLog log = new ISdkLog() { + public void error(String errorFormat, Object... arg) { + logMessages.add(String.format(errorFormat, arg)); + } + public void warning(String warningFormat, Object... arg) { + logMessages.add(String.format(warningFormat, arg)); + } + }; + + // get an SdkManager object for the location + SdkManager manager = SdkManager.createManager(sdkLocation, log); + if (manager != null) { + sCurrentSdk = new Sdk(manager); + return sCurrentSdk; + } else { + StringBuilder sb = new StringBuilder("Error Loading the SDK:\n"); + for (String msg : logMessages) { + sb.append('\n'); + sb.append(msg); + } + AdtPlugin.displayError("Android SDK", sb.toString()); + } + return null; + } + + /** + * Returns the current {@link Sdk} object. + */ + public static Sdk getCurrent() { + return sCurrentSdk; + } + + /** + * Returns the URL to the local documentation. + * Can return null if no documentation is found in the current SDK. + * + * @return A file:// URL on the local documentation folder if it exists or null. + */ + public String getDocumentationBaseUrl() { + return mDocBaseUrl; + } + + /** + * Returns the list of targets that are available in the SDK. + */ + public IAndroidTarget[] getTargets() { + return mManager.getTargets(); + } + + /** + * Returns a target from a hash that was generated by {@link IAndroidTarget#hashString()}. + * @param hash the hash + */ + public IAndroidTarget getTargetFromHashString(String hash) { + return mManager.getTargetFromHashString(hash); + } + + /** + * Associates an {@link IProject} and an {@link IAndroidTarget}. + */ + public void setProject(IProject project, IAndroidTarget target) { + synchronized (mProjectMap) { + // look for the current target of the project + IAndroidTarget previousTarget = mProjectMap.get(project); + + if (target != previousTarget) { + // save the target hash string in the project persistent property + setProjectTargetHashString(project, target.hashString()); + + // put it in a local map for easy access. + mProjectMap.put(project, target); + + // recompile the project if needed. + IJavaProject javaProject = JavaCore.create(project); + AndroidClasspathContainerInitializer.updateProjects( + new IJavaProject[] { javaProject }); + } + } + } + + /** + * Returns the {@link IAndroidTarget} object associated with the given {@link IProject}. + */ + public IAndroidTarget getTarget(IProject project) { + synchronized (mProjectMap) { + IAndroidTarget target = mProjectMap.get(project); + if (target == null) { + // get the value from the project persistent property. + String targetHashString = getProjectTargetHashString(project); + + if (targetHashString != null) { + target = mManager.getTargetFromHashString(targetHashString); + } + } + + return target; + } + } + + /** + * Returns the hash string uniquely identifying the target of a project. This methods reads + * the string from the project persistent preferences/properties. + * <p/>The string is equivalent to the return of {@link IAndroidTarget#hashString()}. + * @param project The project for which to return the target hash string. + * @return the hash string or null if the project does not have a target set. + */ + public static String getProjectTargetHashString(IProject project) { + // load the default.properties from the project folder. + ProjectProperties properties = ProjectProperties.load(project.getLocation().toOSString()); + if (properties == null) { + AdtPlugin.log(IStatus.ERROR, "Failed to load properties file for project '%s'", + project.getName()); + return null; + } + + return properties.getProperty(ProjectProperties.PROPERTY_TARGET); + } + + /** + * Sets a target hash string in a project's persistent preferences/property storage. + * @param project The project in which to save the hash string. + * @param targetHashString The target hash string to save. This must be the result from + * {@link IAndroidTarget#hashString()}. + */ + public static void setProjectTargetHashString(IProject project, String targetHashString) { + // because we don't want to erase other properties from default.properties, we first load + // them + ProjectProperties properties = ProjectProperties.load(project.getLocation().toOSString()); + if (properties == null) { + // doesn't exist yet? we create it. + properties = ProjectProperties.create(project.getLocation().toOSString()); + } + + // add/change the target hash string. + properties.setProperty(ProjectProperties.PROPERTY_TARGET, targetHashString); + + // and rewrite the file. + try { + properties.save(); + } catch (IOException e) { + AdtPlugin.log(e, "Failed to save default.properties for project '%s'", + project.getName()); + } + } + /** + * Return the {@link PlatformData} for a given {@link IAndroidTarget}. + */ + public AndroidTargetData getTargetData(IAndroidTarget target) { + synchronized (mTargetMap) { + return mTargetMap.get(target); + } + } + + private Sdk(SdkManager manager) { + mManager = manager; + + // pre-compute some paths + mDocBaseUrl = getDocumentationBaseUrl(mManager.getLocation() + + SdkConstants.OS_SDK_DOCS_FOLDER); + } + + void setTargetData(IAndroidTarget target, AndroidTargetData data) { + synchronized (mTargetMap) { + mTargetMap.put(target, data); + } + } + + /** + * Returns the URL to the local documentation. + * Can return null if no documentation is found in the current SDK. + * + * @param osDocsPath Path to the documentation folder in the current SDK. + * The folder may not actually exist. + * @return A file:// URL on the local documentation folder if it exists or null. + */ + private String getDocumentationBaseUrl(String osDocsPath) { + File f = new File(osDocsPath); + + if (f.isDirectory()) { + try { + // Note: to create a file:// URL, one would typically use something like + // f.toURI().toURL().toString(). However this generates a broken path on + // Windows, namely "C:\\foo" is converted to "file:/C:/foo" instead of + // "file:///C:/foo" (i.e. there should be 3 / after "file:"). So we'll + // do the correct thing manually. + + String path = f.getAbsolutePath(); + if (File.separatorChar != '/') { + path = path.replace(File.separatorChar, '/'); + } + + // For some reason the URL class doesn't add the mandatory "//" after + // the "file:" protocol name, so it has to be hacked into the path. + URL url = new URL("file", null, "//" + path); //$NON-NLS-1$ //$NON-NLS-2$ + String result = url.toString(); + return result; + } catch (MalformedURLException e) { + // ignore malformed URLs + } + } + + return null; + } + +} + + diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/resources/WidgetListLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/WidgetClassLoader.java index c85a50e..8db09f2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/resources/WidgetListLoader.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/WidgetClassLoader.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.resources; +package com.android.ide.eclipse.adt.sdk; -import com.android.ide.eclipse.adt.resources.LayoutParamsParser.IClass; import com.android.ide.eclipse.common.AndroidConstants; import org.eclipse.core.runtime.IProgressMonitor; @@ -42,18 +41,19 @@ import javax.management.InvalidAttributeValueException; * where code is a single letter (W for widget, L for layout, P for layout params), and class names * are the fully qualified name of the classes. */ -public final class WidgetListLoader implements IAndroidLoader { +public final class WidgetClassLoader implements IAndroidClassLoader { /** * Basic class containing the class descriptions found in the text file. */ - private final static class ClassDescriptor implements IClass { + private final static class ClassDescriptor implements IClassDescriptor { private String mName; private String mSimpleName; private ClassDescriptor mSuperClass; private ClassDescriptor mEnclosingClass; - private final ArrayList<IClass> mDeclaredClasses = new ArrayList<IClass>(); + private final ArrayList<IClassDescriptor> mDeclaredClasses = + new ArrayList<IClassDescriptor>(); private boolean mIsInstantiable = false; ClassDescriptor(String fqcn) { @@ -69,15 +69,15 @@ public final class WidgetListLoader implements IAndroidLoader { return mSimpleName; } - public IClass[] getDeclaredClasses() { - return mDeclaredClasses.toArray(new IClass[mDeclaredClasses.size()]); + public IClassDescriptor[] getDeclaredClasses() { + return mDeclaredClasses.toArray(new IClassDescriptor[mDeclaredClasses.size()]); } private void addDeclaredClass(ClassDescriptor declaredClass) { mDeclaredClasses.add(declaredClass); } - public IClass getEnclosingClass() { + public IClassDescriptor getEnclosingClass() { return mEnclosingClass; } @@ -93,7 +93,7 @@ public final class WidgetListLoader implements IAndroidLoader { mName = enclosingClass.mName + "$" + mName.substring(enclosingClass.mName.length() + 1); } - public IClass getSuperclass() { + public IClassDescriptor getSuperclass() { return mSuperClass; } @@ -147,7 +147,7 @@ public final class WidgetListLoader implements IAndroidLoader { * @param osFilePath the OS path of the file to load. * @throws FileNotFoundException if the file is not found. */ - WidgetListLoader(String osFilePath) throws FileNotFoundException { + WidgetClassLoader(String osFilePath) throws FileNotFoundException { mOsFilePath = osFilePath; mReader = new BufferedReader(new FileReader(osFilePath)); } @@ -301,20 +301,21 @@ public final class WidgetListLoader implements IAndroidLoader { * @throws InvalidAttributeValueException * @throws ClassFormatError */ - public HashMap<String, ArrayList<IClass>> findClassesDerivingFrom(String rootPackage, + public HashMap<String, ArrayList<IClassDescriptor>> findClassesDerivingFrom(String rootPackage, String[] superClasses) throws IOException, InvalidAttributeValueException, ClassFormatError { - HashMap<String, ArrayList<IClass>> map = new HashMap<String, ArrayList<IClass>>(); + HashMap<String, ArrayList<IClassDescriptor>> map = + new HashMap<String, ArrayList<IClassDescriptor>>(); - ArrayList<IClass> list = new ArrayList<IClass>(); + ArrayList<IClassDescriptor> list = new ArrayList<IClassDescriptor>(); list.addAll(mWidgetMap.values()); map.put(AndroidConstants.CLASS_VIEW, list); - list = new ArrayList<IClass>(); + list = new ArrayList<IClassDescriptor>(); list.addAll(mLayoutMap.values()); map.put(AndroidConstants.CLASS_VIEWGROUP, list); - list = new ArrayList<IClass>(); + list = new ArrayList<IClassDescriptor>(); list.addAll(mLayoutParamsMap.values()); map.put(AndroidConstants.CLASS_VIEWGROUP_LAYOUTPARAMS, list); @@ -326,7 +327,7 @@ public final class WidgetListLoader implements IAndroidLoader { * @param className the fully-qualified name of the class to return. * @throws ClassNotFoundException */ - public IClass getClass(String className) throws ClassNotFoundException { + public IClassDescriptor getClass(String className) throws ClassNotFoundException { return mMap.get(className); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/NewProjectCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java index d395905..8044bcb 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/NewProjectCreationPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java @@ -20,11 +20,13 @@ * org.eclipse.jdt.internal.ui.wizards.JavaProjectWizardFirstPage */ -package com.android.ide.eclipse.adt.project.internal; +package com.android.ide.eclipse.adt.wizards.newproject; -import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestHelper; +import com.android.sdklib.IAndroidTarget; +import com.android.sdkuilib.SdkTargetSelector; import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.resources.IProject; @@ -120,6 +122,7 @@ public class NewProjectCreationPage extends WizardPage { private boolean mInternalActivityNameUpdate; protected boolean mProjectNameModifiedByUser; protected boolean mApplicationNameModifiedByUser; + private SdkTargetSelector mSdkTargetSelector; /** @@ -133,7 +136,9 @@ public class NewProjectCreationPage extends WizardPage { if (sCustomLocationOsPath == null || sCustomLocationOsPath.length() == 0 || !new File(sCustomLocationOsPath).isDirectory()) { - sCustomLocationOsPath = AdtPlugin.getOsSdkSamplesFolder(); + // FIXME location of samples is pretty much impossible here. + //sCustomLocationOsPath = AdtPlugin.getOsSdkSamplesFolder(); + sCustomLocationOsPath = File.listRoots()[0].getAbsolutePath(); } } @@ -200,6 +205,11 @@ public class NewProjectCreationPage extends WizardPage { return mSourceFolder; } } + + /** Returns the current sdk target or null if none has been selected yet. */ + public IAndroidTarget getSdkTarget() { + return mSdkTargetSelector == null ? null : mSdkTargetSelector.getFirstSelected(); + } /** * Overrides @DialogPage.setVisible(boolean) to put the focus in the project name when @@ -232,15 +242,19 @@ public class NewProjectCreationPage extends WizardPage { createProjectNameGroup(composite); createLocationGroup(composite); + createTargetGroup(composite); createPropertiesGroup(composite); // Update state the first time enableLocationWidgets(); - setPageComplete(validatePage()); + // Show description the first time setErrorMessage(null); setMessage(null); setControl(composite); + + // Validate. This will complain about the first empty field. + setPageComplete(validatePage()); } /** @@ -357,6 +371,33 @@ public class NewProjectCreationPage extends WizardPage { } /** + * Creates the target group. + * It only contains an SdkTargetSelector. + */ + private void createTargetGroup(Composite parent) { + Group group = new Group(parent, SWT.SHADOW_ETCHED_IN); + // Layout has 1 column + group.setLayout(new GridLayout()); + group.setLayoutData(new GridData(GridData.FILL_BOTH)); + group.setFont(parent.getFont()); + group.setText("Target"); + + // get the targets from the sdk + IAndroidTarget[] targets = null; + if (Sdk.getCurrent() != null) { + targets = Sdk.getCurrent().getTargets(); + } + + mSdkTargetSelector = new SdkTargetSelector(group, targets, false /*multi-selection*/); + mSdkTargetSelector.setSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + setPageComplete(validatePage()); + } + }); + } + + /** * Display a directory browser and update the location path field with the selected path */ private void openDirectoryBrowser() { @@ -755,6 +796,9 @@ public class NewProjectCreationPage extends WizardPage { status |= validateLocationPath(workspace); } if ((status & MSG_ERROR) == 0) { + status |= validateSdkTarget(); + } + if ((status & MSG_ERROR) == 0) { status |= validatePackageField(); } if ((status & MSG_ERROR) == 0) { @@ -894,6 +938,18 @@ public class NewProjectCreationPage extends WizardPage { } /** + * Validates the sdk target choice. + * + * @return The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE. + */ + private int validateSdkTarget() { + if (getSdkTarget() == null) { + return setStatus("An SDK Target must be specified.", MSG_ERROR); + } + return MSG_NONE; + } + + /** * Validates the activity name field. * * @return The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE. diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/NewProjectWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java index a67f5ed..a582217 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/NewProjectWizard.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java @@ -14,12 +14,14 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.project.internal; +package com.android.ide.eclipse.adt.wizards.newproject; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.project.AndroidNature; import com.android.ide.eclipse.adt.project.ProjectHelper; +import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.AndroidConstants; +import com.android.sdklib.IAndroidTarget; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; @@ -60,6 +62,7 @@ import java.net.MalformedURLException; import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.Map.Entry; /** * A "New Android Project" Wizard. @@ -81,6 +84,7 @@ public class NewProjectWizard extends Wizard implements INewWizard { private static final String PARAM_STRING_CONTENT = "STRING_CONTENT"; //$NON-NLS-1$ private static final String PARAM_IS_NEW_PROJECT = "IS_NEW_PROJECT"; //$NON-NLS-1$ private static final String PARAM_SRC_FOLDER = "SRC_FOLDER"; //$NON-NLS-1$ + private static final String PARAM_SDK_TARGET = "SDK_TARGET"; //$NON-NLS-1$ private static final String PH_ACTIVITIES = "ACTIVITIES"; //$NON-NLS-1$ private static final String PH_INTENT_FILTERS = "INTENT_FILTERS"; //$NON-NLS-1$ @@ -223,13 +227,14 @@ public class NewProjectWizard extends Wizard implements INewWizard { final IProject project = workspace.getRoot().getProject(mMainPage.getProjectName()); final IProjectDescription description = workspace.newProjectDescription(project.getName()); - final Map<String, String> parameters = new HashMap<String, String>(); + final Map<String, Object> parameters = new HashMap<String, Object>(); parameters.put(PARAM_PROJECT, mMainPage.getProjectName()); parameters.put(PARAM_PACKAGE, mMainPage.getPackageName()); parameters.put(PARAM_APPLICATION, STRING_RSRC_PREFIX + STRING_APP_NAME); parameters.put(PARAM_SDK_TOOLS_DIR, AdtPlugin.getOsSdkToolsFolder()); - parameters.put(PARAM_IS_NEW_PROJECT, Boolean.toString(mMainPage.isNewProject())); + parameters.put(PARAM_IS_NEW_PROJECT, mMainPage.isNewProject()); parameters.put(PARAM_SRC_FOLDER, mMainPage.getSourceFolder()); + parameters.put(PARAM_SDK_TARGET, mMainPage.getSdkTarget()); if (mMainPage.isCreateActivity()) { // An activity name can be of the form ".package.Class" or ".Class". @@ -315,7 +320,7 @@ public class NewProjectWizard extends Wizard implements INewWizard { * to create or modify the project or if it is canceled by the user. */ private void createProjectAsync(IProject project, IProjectDescription description, - IProgressMonitor monitor, Map<String, String> parameters, + IProgressMonitor monitor, Map<String, Object> parameters, Map<String, String> stringDictionary) throws InvocationTargetException { monitor.beginTask("Create Android Project", 100); @@ -330,7 +335,7 @@ public class NewProjectWizard extends Wizard implements INewWizard { // Create folders in the project if they don't already exist addDefaultDirectories(project, AndroidConstants.WS_ROOT, DEFAULT_DIRECTORIES, monitor); - String[] sourceFolder = new String[] { parameters.get(PARAM_SRC_FOLDER) }; + String[] sourceFolder = new String[] { (String) parameters.get(PARAM_SRC_FOLDER) }; addDefaultDirectories(project, AndroidConstants.WS_ROOT, sourceFolder, monitor); // Create the resource folders in the project if they don't already exist. @@ -340,7 +345,7 @@ public class NewProjectWizard extends Wizard implements INewWizard { IJavaProject javaProject = JavaCore.create(project); setupSourceFolder(javaProject, sourceFolder[0], monitor); - if (Boolean.parseBoolean(parameters.get(PARAM_IS_NEW_PROJECT))) { + if (((Boolean) parameters.get(PARAM_IS_NEW_PROJECT)).booleanValue()) { // Create files in the project if they don't already exist addManifest(project, parameters, stringDictionary, monitor); @@ -360,6 +365,8 @@ public class NewProjectWizard extends Wizard implements INewWizard { monitor); } + Sdk.getCurrent().setProject(project, (IAndroidTarget) parameters.get(PARAM_SDK_TARGET)); + // Fix the project to make sure all properties are as expected. // Necessary for existing projects and good for new ones to. ProjectHelper.fixProject(project); @@ -409,7 +416,7 @@ public class NewProjectWizard extends Wizard implements INewWizard { * @throws IOException if the method fails to create the files in the * project. */ - private void addManifest(IProject project, Map<String, String> parameters, + private void addManifest(IProject project, Map<String, Object> parameters, Map<String, String> stringDictionary, IProgressMonitor monitor) throws CoreException, IOException { @@ -543,16 +550,16 @@ public class NewProjectWizard extends Wizard implements INewWizard { * project. */ private void addSampleCode(IProject project, String sourceFolder, - Map<String, String> parameters, Map<String, String> stringDictionary, + Map<String, Object> parameters, Map<String, String> stringDictionary, IProgressMonitor monitor) throws CoreException, IOException { // create the java package directories. IFolder pkgFolder = project.getFolder(sourceFolder); - String packageName = parameters.get(PARAM_PACKAGE); + String packageName = (String) parameters.get(PARAM_PACKAGE); // The PARAM_ACTIVITY key will be absent if no activity should be created, // in which case activityName will be null. - String activityName = parameters.get(PARAM_ACTIVITY); - Map<String, String> java_activity_parameters = parameters; + String activityName = (String) parameters.get(PARAM_ACTIVITY); + Map<String, Object> java_activity_parameters = parameters; if (activityName != null) { if (activityName.indexOf('.') >= 0) { // There are package names in the activity name. Transform packageName to add @@ -564,7 +571,7 @@ public class NewProjectWizard extends Wizard implements INewWizard { // Also update the values used in the JAVA_FILE_TEMPLATE below // (but not the ones from the manifest so don't change the caller's dictionary) - java_activity_parameters = new HashMap<String, String>(parameters); + java_activity_parameters = new HashMap<String, Object>(parameters); java_activity_parameters.put(PARAM_PACKAGE, packageName); java_activity_parameters.put(PARAM_ACTIVITY, activityName); } @@ -665,7 +672,7 @@ public class NewProjectWizard extends Wizard implements INewWizard { * length. */ private void copyFile(String resourceFilename, IFile destFile, - Map<String, String> parameters, IProgressMonitor monitor) + Map<String, Object> parameters, IProgressMonitor monitor) throws CoreException, IOException { // Read existing file. @@ -692,13 +699,14 @@ public class NewProjectWizard extends Wizard implements INewWizard { * Replaces placeholders found in a string with values. * * @param str the string to search for placeholders. - * @param parameters a map of <placeholder, Value> to search for in the - * string + * @param parameters a map of <placeholder, Value> to search for in the string * @return A new String object with the placeholder replaced by the values. */ - private String replaceParameters(String str, Map<String, String> parameters) { - for (String key : parameters.keySet()) { - str = str.replaceAll(key, parameters.get(key)); + private String replaceParameters(String str, Map<String, Object> parameters) { + for (Entry<String, Object> entry : parameters.entrySet()) { + if (entry.getValue() instanceof String) { + str = str.replaceAll(entry.getKey(), (String) entry.getValue()); + } } return str; diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/AndroidConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java index 02cef2d..f3f7b79 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/AndroidConstants.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java @@ -16,6 +16,8 @@ package com.android.ide.eclipse.common; +import com.android.sdklib.SdkConstants; + import java.io.File; /** @@ -38,8 +40,11 @@ import java.io.File; * */ public class AndroidConstants { - /** The Editors Plugin ID */ - public static final String EDITORS_PLUGIN_ID = "com.android.ide.eclipse.editors"; // $NON-NLS-1$ + /** + * The old Editors Plugin ID. It is still used in some places for compatibility. + * Please do not use for new features. + */ + public static final String EDITORS_NAMESPACE = "com.android.ide.eclipse.editors"; // $NON-NLS-1$ /** Nature of android projects */ public final static String NATURE = "com.android.ide.eclipse.adt.AndroidNature"; //$NON-NLS-1$ @@ -72,6 +77,8 @@ public class AndroidConstants { public final static String EXT_JAR = "jar"; //$NON-NLS-1$ /** Extension of aidl files, i.e. "aidl" */ public final static String EXT_AIDL = "aidl"; //$NON-NLS-1$ + /** Extension of native libraries, i.e. "so" */ + public final static String EXT_NATIVE_LIB = "so"; //$NON-NLS-1$ private final static String DOT = "."; //$NON-NLS-1$ @@ -90,17 +97,8 @@ public class AndroidConstants { /** Name of the manifest file, i.e. "AndroidManifest.xml". */ public static final String FN_ANDROID_MANIFEST = "AndroidManifest.xml"; //$NON-NLS-1$ + public static final String FN_PROJECT_AIDL = "project.aidl"; //$NON-NLS-1$ - /** Name of the framework library, i.e. "android.jar" */ - public static final String FN_FRAMEWORK_LIBRARY = "android.jar"; //$NON-NLS-1$ - /** Name of the layout attributes, i.e. "attrs.xml" */ - public static final String FN_ATTRS_XML = "attrs.xml"; //$NON-NLS-1$ - /** Name of the layout attributes, i.e. "attrs_manifest.xml" */ - public static final String FN_ATTRS_MANIFEST_XML = "attrs_manifest.xml"; //$NON-NLS-1$ - /** framework aidl import file */ - public static final String FN_FRAMEWORK_AIDL = "framework.aidl"; //$NON-NLS-1$ - /** layoutlib.jar file */ - public static final String FN_LAYOUTLIB_JAR = "layoutlib.jar"; //$NON-NLS-1$ /** dex.jar file */ public static final String FN_DX_JAR = "dx.jar"; //$NON-NLS-1$ /** Name of the android sources directory */ @@ -116,10 +114,6 @@ public class AndroidConstants { public final static String FN_CLASSES_DEX = "classes.dex"; //$NON-NLS-1$ /** Temporary packaged resources file name, i.e. "resources.ap_" */ public final static String FN_RESOURCES_AP_ = "resources.ap_"; //$NON-NLS-1$ - /** build properties file */ - public final static String FN_BUILD_PROP = "build.prop"; //$NON-NLS-1$ - /** plugin properties file */ - public final static String FN_PLUGIN_PROP = "plugin.prop"; //$NON-NLS-1$ public final static String FN_ADB = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? "adb.exe" : "adb"; //$NON-NLS-1$ //$NON-NLS-2$ @@ -136,15 +130,21 @@ public class AndroidConstants { public final static String FN_TRACEVIEW = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? "traceview.exe" : "traceview"; //$NON-NLS-1$ //$NON-NLS-2$ - /** Skin layout file */ - public final static String FN_LAYOUT = "layout";//$NON-NLS-1$ + /** Folder Names for Android Projects . */ - /** Resources folder name, i.e. "res". */ + /* Resources folder name, i.e. "res". */ public final static String FD_RESOURCES = "res"; //$NON-NLS-1$ /** Assets folder name, i.e. "assets" */ public final static String FD_ASSETS = "assets"; //$NON-NLS-1$ /** Default source folder name, i.e. "src" */ public final static String FD_SOURCES = "src"; //$NON-NLS-1$ + /** Default native library folder name inside the project, i.e. "libs" + * While the folder inside the .apk is "lib", we call that one libs because + * that's what we use in ant for both .jar and .so and we need to make the 2 development ways + * compatible. */ + public final static String FD_NATIVE_LIBS = "libs"; //$NON-NLS-1$ + /** Native lib folder inside the APK: "lib" */ + public final static String FD_APK_NATIVE_LIBS = "lib"; //$NON-NLS-1$ /** Default bin folder name, i.e. "bin" */ public final static String FD_BINARIES = "bin"; //$NON-NLS-1$ /** Default anim resource folder name, i.e. "anim" */ @@ -163,22 +163,6 @@ public class AndroidConstants { public final static String FD_XML = "xml"; //$NON-NLS-1$ /** Default raw resource folder name, i.e. "raw" */ public final static String FD_RAW = "raw"; //$NON-NLS-1$ - /** Name of the tools folder. */ - public final static String FD_TOOLS = "tools"; //$NON-NLS-1$ - /** Name of the libs folder. */ - public final static String FD_LIBS = "lib"; //$NON-NLS-1$ - /** Name of the docs folder. */ - public final static String FD_DOCS = "docs"; //$NON-NLS-1$ - /** Name of the images folder. */ - public final static String FD_IMAGES = "images"; //$NON-NLS-1$ - /** Name of the skins folder. */ - public final static String FD_SKINS = "skins"; //$NON-NLS-1$ - /** Name of the samples folder. */ - public final static String FD_SAMPLES = "samples"; //$NON-NLS-1$ - /** Name of the folder containing the default framework resources. */ - public final static String FD_DEFAULT_RES = "default"; //$NON-NLS-1$ - /** SDK font folder name, i.e. "fonts" */ - public final static String FD_FONTS = "fonts"; //$NON-NLS-1$ /** Absolute path of the workspace root, i.e. "/" */ public final static String WS_ROOT = WS_SEP; @@ -190,57 +174,16 @@ public class AndroidConstants { public final static String WS_ASSETS = WS_SEP + FD_ASSETS; /** Leaf of the javaDoc folder. Does not start with a separator. */ - public final static String WS_JAVADOC_FOLDER_LEAF = FD_DOCS + "/reference"; //$NON-NLS-1$ - - /** Path of the documentation directory relative to the sdk folder. - * This is an OS path, ending with a separator. */ - public final static String OS_SDK_DOCS_FOLDER = FD_DOCS + File.separator; - - /** Path of the tools directory relative to the sdk folder. - * This is an OS path, ending with a separator. */ - public final static String OS_SDK_TOOLS_FOLDER = FD_TOOLS + File.separator; + public final static String WS_JAVADOC_FOLDER_LEAF = SdkConstants.FD_DOCS + "/reference"; //$NON-NLS-1$ /** Path of the samples directory relative to the sdk folder. - * This is an OS path, ending with a separator. */ - public final static String OS_SDK_SAMPLES_FOLDER = FD_SAMPLES + File.separator; - - /** Path of the lib directory relative to the sdk folder. - * This is an OS path, ending with a separator. */ - public final static String OS_SDK_LIBS_FOLDER = - OS_SDK_TOOLS_FOLDER + FD_LIBS + File.separator; - - /** Path of the resources directory relative to the sdk folder. - * This is an OS path, ending with a separator. */ - public final static String OS_SDK_RESOURCES_FOLDER = - OS_SDK_LIBS_FOLDER + FD_RESOURCES + File.separator; - - /** Path of the resources directory relative to the sdk folder. - * This is an OS path, ending with a separator. */ - public final static String OS_SDK_FONTS_FOLDER = - OS_SDK_LIBS_FOLDER + FD_FONTS + File.separator; - - /** Path of the skin directory relative to the sdk folder. - * This is an OS path, ending with a separator. */ - public final static String OS_SDK_SKINS_FOLDER = - OS_SDK_LIBS_FOLDER + FD_IMAGES + File.separator + FD_SKINS + File.separator; - - /** Path of the attrs.xml file relative to the sdk folder. */ - public final static String OS_SDK_ATTRS_XML = - OS_SDK_RESOURCES_FOLDER + File.separator + FD_DEFAULT_RES + File.separator + - FD_VALUES + File.separator + FN_ATTRS_XML; - - /** Path of the attrs_manifest.xml file relative to the sdk folder. */ - public final static String OS_SDK_ATTRS_MANIFEST_XML = - OS_SDK_RESOURCES_FOLDER + File.separator + FD_DEFAULT_RES + File.separator + - FD_VALUES + File.separator + FN_ATTRS_MANIFEST_XML; - - /** Path of the layoutlib.jar file relative to the sdk folder. */ - public final static String OS_SDK_LIBS_LAYOUTLIB_JAR = - OS_SDK_LIBS_FOLDER + FN_LAYOUTLIB_JAR; + * This is an OS path, ending with a separator. + * FIXME: remove once the NPW is fixed. */ + public final static String OS_SDK_SAMPLES_FOLDER = SdkConstants.FD_SAMPLES + File.separator; /** Path of the dx.jar file relative to the sdk folder. */ public final static String OS_SDK_LIBS_DX_JAR = - OS_SDK_LIBS_FOLDER + FN_DX_JAR; + SdkConstants.OS_SDK_TOOLS_LIB_FOLDER + FN_DX_JAR; /** Regexp for single dot */ public final static String RE_DOT = "\\."; //$NON-NLS-1$ @@ -255,17 +198,20 @@ public class AndroidConstants { /** Namespace pattern for the custom resource XML, i.e. "http://schemas.android.com/apk/res/%s" */ public final static String NS_CUSTOM_RESOURCES = "http://schemas.android.com/apk/res/%1$s"; //$NON-NLS-1$ + /** The old common plug-in ID. Please do not use for new features. */ + public static final String COMMON_PLUGIN_ID = "com.android.ide.eclipse.common"; //$NON-NLS-1$ + /** aapt marker error. */ - public final static String MARKER_AAPT = CommonPlugin.PLUGIN_ID + ".aaptProblem"; //$NON-NLS-1$ + public final static String MARKER_AAPT = COMMON_PLUGIN_ID + ".aaptProblem"; //$NON-NLS-1$ /** XML marker error. */ - public final static String MARKER_XML = CommonPlugin.PLUGIN_ID + ".xmlProblem"; //$NON-NLS-1$ + public final static String MARKER_XML = COMMON_PLUGIN_ID + ".xmlProblem"; //$NON-NLS-1$ /** aidl marker error. */ - public final static String MARKER_AIDL = CommonPlugin.PLUGIN_ID + ".aidlProblem"; //$NON-NLS-1$ + public final static String MARKER_AIDL = COMMON_PLUGIN_ID + ".aidlProblem"; //$NON-NLS-1$ /** android marker error */ - public final static String MARKER_ANDROID = CommonPlugin.PLUGIN_ID + ".androidProblem"; //$NON-NLS-1$ + public final static String MARKER_ANDROID = COMMON_PLUGIN_ID + ".androidProblem"; //$NON-NLS-1$ /** Name for the "type" marker attribute */ public final static String MARKER_ATTR_TYPE = "android.type"; //$NON-NLS-1$ @@ -301,6 +247,7 @@ public class AndroidConstants { public final static String CLASS_PREFERENCES = "android.preference." + CLASS_PREFERENCE_SCREEN; //$NON-NLS-1$ public final static String CLASS_PREFERENCEGROUP = "android.preference.PreferenceGroup"; //$NON-NLS-1$ + public final static String CLASS_PARCELABLE = "android.os.Parcelable"; //$NON-NLS-1$ public final static String CLASS_BRIDGE = "com.android.layoutlib.bridge.Bridge"; //$NON-NLS-1$ diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/EclipseUiHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/EclipseUiHelper.java index 8dd8e60..6dc8562 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/EclipseUiHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/EclipseUiHelper.java @@ -53,7 +53,7 @@ public final class EclipseUiHelper { try { IViewPart part = page.showView(viewId, null /* secondaryId */, - activate ? page.VIEW_ACTIVATE : page.VIEW_VISIBLE); + activate ? IWorkbenchPage.VIEW_ACTIVATE : IWorkbenchPage.VIEW_VISIBLE); } catch (PartInitException e) { // ignore } diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/Messages.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/Messages.java index 3f1bde4..3f1bde4 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/Messages.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/Messages.java diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/SdkStatsHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/SdkStatsHelper.java index 345c663..345c663 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/SdkStatsHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/SdkStatsHelper.java diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/StreamHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/StreamHelper.java index 6ccf4f2..6ccf4f2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/StreamHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/StreamHelper.java diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/messages.properties b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/messages.properties index dba6edc..dba6edc 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/messages.properties +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/messages.properties diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/preferences/UsagePreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/preferences/UsagePreferencePage.java index 58c2f40..58c2f40 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/preferences/UsagePreferencePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/preferences/UsagePreferencePage.java diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/project/AndroidManifestHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestHelper.java index 2db9e9b..2db9e9b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/project/AndroidManifestHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestHelper.java diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java index 42f2a8b..cb98525 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java @@ -44,6 +44,7 @@ public class AndroidManifestParser { private final static String ATTRIBUTE_NAME = "name"; //$NON-NLS-1$ private final static String ATTRIBUTE_PROCESS = "process"; //$NON-NLS-$ private final static String ATTRIBUTE_DEBUGGABLE = "debuggable"; //$NON-NLS-$ + private final static String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion"; //$NON-NLS-$ private final static String NODE_MANIFEST = "manifest"; //$NON-NLS-1$ private final static String NODE_APPLICATION = "application"; //$NON-NLS-1$ private final static String NODE_ACTIVITY = "activity"; //$NON-NLS-1$ @@ -53,6 +54,7 @@ public class AndroidManifestParser { private final static String NODE_INTENT = "intent-filter"; //$NON-NLS-1$ private final static String NODE_ACTION = "action"; //$NON-NLS-1$ private final static String NODE_CATEGORY = "category"; //$NON-NLS-1$ + private final static String NODE_USES_SDK = "uses-sdk"; //$NON-NLS-1$ private final static int LEVEL_MANIFEST = 0; private final static int LEVEL_APPLICATION = 1; @@ -77,6 +79,8 @@ public class AndroidManifestParser { private Set<String> mProcesses = null; /** debuggable attribute value. If null, the attribute is not present. */ private Boolean mDebuggable = null; + /** API level requirement. if 0 the attribute was not present. */ + private int mApiLevelRequirement = 0; //--- temporary data/flags used during parsing private IJavaProject mJavaProject; @@ -142,12 +146,19 @@ public class AndroidManifestParser { } /** - * Returns the debuggable attribute value or null if it is not set. + * Returns the <code>debuggable</code> attribute value or null if it is not set. */ Boolean getDebuggable() { return mDebuggable; } + /** + * Returns the <code>minSdkVersion</code> attribute, or 0 if it's not set. + */ + int getApiLevelRequirement() { + return mApiLevelRequirement; + } + /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#setDocumentLocator(org.xml.sax.Locator) */ @@ -171,7 +182,7 @@ public class AndroidManifestParser { // if we're at a valid level if (mValidLevel == mCurrentLevel) { - String processName; + String value; switch (mValidLevel) { case LEVEL_MANIFEST: if (NODE_MANIFEST.equals(localName)) { @@ -183,19 +194,28 @@ public class AndroidManifestParser { break; case LEVEL_APPLICATION: if (NODE_APPLICATION.equals(localName)) { - processName = getAttributeValue(attributes, ATTRIBUTE_PROCESS, + value = getAttributeValue(attributes, ATTRIBUTE_PROCESS, true /* hasNamespace */); - if (processName != null) { - addProcessName(processName); + if (value != null) { + addProcessName(value); } - String debuggable = getAttributeValue(attributes, - ATTRIBUTE_DEBUGGABLE, true /* hasNamespace*/); - if (debuggable != null) { - mDebuggable = Boolean.parseBoolean(debuggable); + value = getAttributeValue(attributes, ATTRIBUTE_DEBUGGABLE, + true /* hasNamespace*/); + if (value != null) { + mDebuggable = Boolean.parseBoolean(value); } mValidLevel++; + } else if (NODE_USES_SDK.equals(localName)) { + value = getAttributeValue(attributes, ATTRIBUTE_MIN_SDK_VERSION, + true /* hasNamespace */); + + try { + mApiLevelRequirement = Integer.parseInt(value); + } catch (NumberFormatException e) { + handleError(e, -1 /* lineNumber */); + } } break; case LEVEL_ACTIVITY: @@ -301,7 +321,7 @@ public class AndroidManifestParser { @Override public void error(SAXParseException e) throws SAXException { if (mMarkErrors) { - super.error(e); + handleError(e, e.getLineNumber()); } } @@ -311,7 +331,7 @@ public class AndroidManifestParser { @Override public void fatalError(SAXParseException e) throws SAXException { if (mMarkErrors) { - super.fatalError(e); + handleError(e, e.getLineNumber()); } } @@ -457,6 +477,7 @@ public class AndroidManifestParser { private final String mLauncherActivity; private final String[] mProcesses; private final Boolean mDebuggable; + private final int mApiLevelRequirement; static { sParserFactory = SAXParserFactory.newInstance(); @@ -491,7 +512,8 @@ public class AndroidManifestParser { return new AndroidManifestParser(manifestHandler.getPackage(), manifestHandler.getActivities(), manifestHandler.getLauncherActivity(), - manifestHandler.getProcesses(), manifestHandler.getDebuggable()); + manifestHandler.getProcesses(), manifestHandler.getDebuggable(), + manifestHandler.getApiLevelRequirement()); } catch (ParserConfigurationException e) { } catch (SAXException e) { } catch (IOException e) { @@ -530,7 +552,8 @@ public class AndroidManifestParser { // get the result from the handler return new AndroidManifestParser(manifestHandler.getPackage(), manifestHandler.getActivities(), manifestHandler.getLauncherActivity(), - manifestHandler.getProcesses(), manifestHandler.getDebuggable()); + manifestHandler.getProcesses(), manifestHandler.getDebuggable(), + manifestHandler.getApiLevelRequirement()); } } catch (ParserConfigurationException e) { } catch (SAXException e) { @@ -610,6 +633,14 @@ public class AndroidManifestParser { } /** + * Returns the <code>minSdkVersion</code> attribute, or 0 if it's not set. + */ + public int getApiLevelRequirement() { + return mApiLevelRequirement; + } + + + /** * Private constructor to enforce using * {@link #parse(IJavaProject, XmlErrorListener, boolean, boolean)}, * {@link #parse(IJavaProject, IFile, XmlErrorListener, boolean, boolean)}, @@ -619,14 +650,17 @@ public class AndroidManifestParser { * @param activities the list of activities parsed from the manifest. * @param launcherActivity the launcher activity parser from the manifest. * @param processes the list of custom processes declared in the manifest. - * @param debuggable the debuggable attribute. + * @param debuggable the debuggable attribute, or null if not set. + * @param apiLevelRequirement the minSdkVersion attribute value or 0 if not set. */ private AndroidManifestParser(String javaPackage, String[] activities, - String launcherActivity, String[] processes, Boolean debuggable) { + String launcherActivity, String[] processes, Boolean debuggable, + int apiLevelRequirement) { mJavaPackage = javaPackage; mActivities = activities; mLauncherActivity = launcherActivity; mProcesses = processes; mDebuggable = debuggable; + mApiLevelRequirement = apiLevelRequirement; } } diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/project/AndroidXPathFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidXPathFactory.java index 530c89e..530c89e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/project/AndroidXPathFactory.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidXPathFactory.java diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/project/BaseProjectHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/BaseProjectHelper.java index 57ca496..c69e875 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/project/BaseProjectHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/BaseProjectHelper.java @@ -16,8 +16,8 @@ package com.android.ide.eclipse.common.project; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.CommonPlugin; import com.android.ide.eclipse.common.project.XmlErrorHandler.XmlErrorListener; import org.eclipse.core.resources.IFile; @@ -108,7 +108,7 @@ public final class BaseProjectHelper { return marker; } catch (CoreException e) { - CommonPlugin.log(e, "Failed to add marker '%1$s' to '%2$s'", + AdtPlugin.log(e, "Failed to add marker '%1$s' to '%2$s'", markerId, file.getFullPath()); } @@ -117,10 +117,9 @@ public final class BaseProjectHelper { /** * Adds a marker to a resource. - * @param file the file to be marked + * @param resource the file to be marked * @param markerId The id of the marker to add. * @param message the message associated with the mark - * @param lineNumber the line number where to put the mark * @param severity the severity of the marker. * @return the IMarker that was added or null if it failed to add one. */ @@ -138,7 +137,7 @@ public final class BaseProjectHelper { return marker; } catch (CoreException e) { - CommonPlugin.log(e, "Failed to add marker '%1$s' to '%2$s'", + AdtPlugin.log(e, "Failed to add marker '%1$s' to '%2$s'", markerId, resource.getFullPath()); } diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/project/ExportHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/ExportHelper.java index 4b169a1..4b169a1 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/project/ExportHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/ExportHelper.java diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/project/ProjectChooserHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/ProjectChooserHelper.java index 0c43499..0c43499 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/project/ProjectChooserHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/ProjectChooserHelper.java diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/project/XmlErrorHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/XmlErrorHandler.java index 3492cb4..26fbf42 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/project/XmlErrorHandler.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/XmlErrorHandler.java @@ -65,11 +65,7 @@ public class XmlErrorHandler extends DefaultHandler { */ @Override public void error(SAXParseException exception) throws SAXException { - if (mErrorListener != null) { - mErrorListener.errorFound(); - } - BaseProjectHelper.addMarker(mFile, AndroidConstants.MARKER_XML, exception.getMessage(), - exception.getLineNumber(), IMarker.SEVERITY_ERROR); + handleError(exception, exception.getLineNumber()); } /** @@ -77,13 +73,8 @@ public class XmlErrorHandler extends DefaultHandler { * @param exception the parsing exception */ @Override - public void fatalError(SAXParseException exception) - throws SAXException { - if (mErrorListener != null) { - mErrorListener.errorFound(); - } - BaseProjectHelper.addMarker(mFile, AndroidConstants.MARKER_XML, exception.getMessage(), - exception.getLineNumber(), IMarker.SEVERITY_ERROR); + public void fatalError(SAXParseException exception) throws SAXException { + handleError(exception, exception.getLineNumber()); } /** @@ -99,4 +90,23 @@ public class XmlErrorHandler extends DefaultHandler { protected final IFile getFile() { return mFile; } + + /** + * Handles a parsing error and an optional line number. + * @param exception + * @param lineNumber + */ + protected void handleError(Exception exception, int lineNumber) { + if (mErrorListener != null) { + mErrorListener.errorFound(); + } + + if (lineNumber != -1) { + BaseProjectHelper.addMarker(mFile, AndroidConstants.MARKER_XML, exception.getMessage(), + lineNumber, IMarker.SEVERITY_ERROR); + } else { + BaseProjectHelper.addMarker(mFile, AndroidConstants.MARKER_XML, exception.getMessage(), + IMarker.SEVERITY_ERROR); + } + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/AttrsXmlParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/AttrsXmlParser.java index 43260c0..3176c8e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/AttrsXmlParser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/AttrsXmlParser.java @@ -16,7 +16,7 @@ package com.android.ide.eclipse.common.resources; -import com.android.ide.eclipse.common.CommonPlugin; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo; import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo.Format; import com.android.ide.eclipse.common.resources.ViewClassInfo.LayoutParamsInfo; @@ -106,7 +106,7 @@ public final class AttrsXmlParser { Document doc = getDocument(); if (doc == null) { - CommonPlugin.log(IStatus.WARNING, "Failed to find %1$s", //$NON-NLS-1$ + AdtPlugin.log(IStatus.WARNING, "Failed to find %1$s", //$NON-NLS-1$ mOsAttrsXmlPath); return this; } @@ -119,7 +119,7 @@ public final class AttrsXmlParser { } if (res == null) { - CommonPlugin.log(IStatus.WARNING, "Failed to find a <resources> node in %1$s", //$NON-NLS-1$ + AdtPlugin.log(IStatus.WARNING, "Failed to find a <resources> node in %1$s", //$NON-NLS-1$ mOsAttrsXmlPath); return this; } @@ -189,13 +189,13 @@ public final class AttrsXmlParser { DocumentBuilder builder = factory.newDocumentBuilder(); mDocument = builder.parse(new File(mOsAttrsXmlPath)); } catch (ParserConfigurationException e) { - CommonPlugin.log(e, "Failed to create XML document builder for %1$s", //$NON-NLS-1$ + AdtPlugin.log(e, "Failed to create XML document builder for %1$s", //$NON-NLS-1$ mOsAttrsXmlPath); } catch (SAXException e) { - CommonPlugin.log(e, "Failed to parse XML document %1$s", //$NON-NLS-1$ + AdtPlugin.log(e, "Failed to parse XML document %1$s", //$NON-NLS-1$ mOsAttrsXmlPath); } catch (IOException e) { - CommonPlugin.log(e, "Failed to read XML document %1$s", //$NON-NLS-1$ + AdtPlugin.log(e, "Failed to read XML document %1$s", //$NON-NLS-1$ mOsAttrsXmlPath); } } @@ -340,7 +340,7 @@ public final class AttrsXmlParser { formats.add(format); } } catch (IllegalArgumentException e) { - CommonPlugin.log(e, "Unknown format name '%s' in <attr name=\"%s\">, file '%s'.", //$NON-NLS-1$ + AdtPlugin.log(e, "Unknown format name '%s' in <attr name=\"%s\">, file '%s'.", //$NON-NLS-1$ f, name, getOsAttrsXmlPath()); } } @@ -389,7 +389,7 @@ public final class AttrsXmlParser { if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals(filter)) { Node nameNode = child.getAttributes().getNamedItem("name"); //$NON-NLS-1$ if (nameNode == null) { - CommonPlugin.log(IStatus.WARNING, + AdtPlugin.log(IStatus.WARNING, "Missing name attribute in <attr name=\"%s\"><%s></attr>", //$NON-NLS-1$ attrName, filter); } else { @@ -401,7 +401,7 @@ public final class AttrsXmlParser { Node valueNode = child.getAttributes().getNamedItem("value"); //$NON-NLS-1$ if (valueNode == null) { - CommonPlugin.log(IStatus.WARNING, + AdtPlugin.log(IStatus.WARNING, "Missing value attribute in <attr name=\"%s\"><%s name=\"%s\"></attr>", //$NON-NLS-1$ attrName, filter, name); } else { @@ -419,7 +419,7 @@ public final class AttrsXmlParser { map.put(name, Integer.valueOf(i)); } catch(NumberFormatException e) { - CommonPlugin.log(e, + AdtPlugin.log(e, "Value in <attr name=\"%s\"><%s name=\"%s\" value=\"%s\"></attr> is not a valid decimal or hexadecimal", //$NON-NLS-1$ attrName, filter, name, value); } diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java index 6cff62c..6cff62c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/IIdResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/IIdResourceItem.java index 38b7e03..38b7e03 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/IIdResourceItem.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/IIdResourceItem.java diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/IPathChangedListener.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/IPathChangedListener.java index 53d9077..53d9077 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/IPathChangedListener.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/IPathChangedListener.java diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/IResourceRepository.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/IResourceRepository.java index 3819997..3819997 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/IResourceRepository.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/IResourceRepository.java diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/ResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/ResourceItem.java index 83527f3..83527f3 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/ResourceItem.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/ResourceItem.java diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/ResourceType.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/ResourceType.java index 3d64e5d..60c471e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/ResourceType.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/ResourceType.java @@ -21,7 +21,7 @@ package com.android.ide.eclipse.common.resources; */ public enum ResourceType { ANIM("anim", "Animation"), //$NON-NLS-1$ - ARRAY("array", "Array"), //$NON-NLS-1$ + ARRAY("array", "Array", "string-array", "integer-array"), //$NON-NLS-1$ //$NON-NLS-3$ //$NON-NLS-4$ ATTR("attr", "Attr"), //$NON-NLS-1$ COLOR("color", "Color"), //$NON-NLS-1$ DIMEN("dimen", "Dimension"), //$NON-NLS-1$ @@ -35,12 +35,14 @@ public enum ResourceType { STYLEABLE("styleable", "Styleable"), //$NON-NLS-1$ XML("xml", "XML"); //$NON-NLS-1$ - private String mName; - private String mDisplayName; + private final String mName; + private final String mDisplayName; + private final String[] mAlternateXmlNames; - ResourceType(String name, String displayName) { + ResourceType(String name, String displayName, String... alternateXmlNames) { mName = name; mDisplayName = displayName; + mAlternateXmlNames = alternateXmlNames; } /** @@ -66,6 +68,13 @@ public enum ResourceType { for (ResourceType rType : values()) { if (rType.mName.equals(name)) { return rType; + } else if (rType.mAlternateXmlNames != null) { + // if there are alternate Xml Names, we test those too + for (String alternate : rType.mAlternateXmlNames) { + if (alternate.equals(name)) { + return rType; + } + } } } return null; diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/ViewClassInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/ViewClassInfo.java index 619e3cc..619e3cc 100644 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/ViewClassInfo.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/ViewClassInfo.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/AndroidContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidContentAssist.java index 1dd0c27..d1b4547 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/AndroidContentAssist.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidContentAssist.java @@ -16,11 +16,12 @@ package com.android.ide.eclipse.editors; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.resources.FrameworkResourceManager; import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.editors.descriptors.IDescriptorProvider; import com.android.ide.eclipse.editors.descriptors.SeparatorAttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.TextValueDescriptor; @@ -80,15 +81,20 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor { protected final static String ROOT_ELEMENT = ""; /** Descriptor of the root of the XML hierarchy. This a "fake" ElementDescriptor which - * is used to list all the possible roots given by actual implementations. */ + * is used to list all the possible roots given by actual implementations. + * DO NOT USE DIRECTLY. Call {@link #getRootDescriptor()} instead. */ private ElementDescriptor mRootDescriptor; + private final int mDescriptorId; + + private AndroidEditor mEditor; + /** * Constructor for AndroidContentAssist * @param rootElementDescriptors The valid root elements of the XML hierarchy */ - public AndroidContentAssist(ElementDescriptor[] rootElementDescriptors) { - mRootDescriptor = new ElementDescriptor("", rootElementDescriptors); + public AndroidContentAssist(int descriptorId) { + mDescriptorId = descriptorId; } /** @@ -104,8 +110,11 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor { */ public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { - AndroidEditor editor = getAndroidEditor(viewer); - UiElementNode rootUiNode = editor.getUiRootNode(); + if (mEditor == null) { + mEditor = getAndroidEditor(viewer); + } + + UiElementNode rootUiNode = mEditor.getUiRootNode(); Object[] choices = null; /* An array of ElementDescriptor, or AttributeDescriptor or String or null */ @@ -245,7 +254,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor { if (current_node.getParentNode().getNodeType() == Node.ELEMENT_NODE) { grandparent = getDescriptor(current_node.getParentNode().getNodeName()); } else if (current_node.getParentNode().getNodeType() == Node.DOCUMENT_NODE) { - grandparent = mRootDescriptor; + grandparent = getRootDescriptor(); } if (grandparent != null) { for (ElementDescriptor e : grandparent.getChildren()) { @@ -339,8 +348,11 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor { greatGrandParentName = greatGrandParent.getLocalName(); } } - choices = FrameworkResourceManager.getInstance().getValues( - parent, attrInfo.name, greatGrandParentName); + + AndroidTargetData data = mEditor.getTargetData(); + if (data != null) { + choices = data.getAttributeValues(parent, attrInfo.name, greatGrandParentName); + } } } else { // Editing an attribute's name... Get attributes valid for the parent node. @@ -378,7 +390,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor { } else if (parent_node.getNodeType() == Node.DOCUMENT_NODE) { // We're editing a text node at the first level (i.e. root node). // Limit content assist to the only valid root elements. - choices = mRootDescriptor.getChildren(); + choices = getRootDescriptor().getChildren(); } return choices; } @@ -509,7 +521,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor { * is returned. */ private ElementDescriptor getDescriptor(String nodeName) { - return mRootDescriptor.findChildrenDescriptor(nodeName, true /* recursive */); + return getRootDescriptor().findChildrenDescriptor(nodeName, true /* recursive */); } public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { @@ -539,8 +551,8 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor { public String getErrorMessage() { return null; - } - + } + /** * Heuristically extracts the prefix used for determining template relevance * from the viewer's document. The default implementation returns the String from @@ -710,6 +722,26 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor { } /** + * Computes (if needed) and returns the root descriptor. + * @return + */ + private ElementDescriptor getRootDescriptor() { + if (mRootDescriptor == null) { + AndroidTargetData data = mEditor.getTargetData(); + if (data != null) { + IDescriptorProvider descriptorProvider = data.getDescriptorProvider(mDescriptorId); + + if (descriptorProvider != null) { + mRootDescriptor = new ElementDescriptor("", + descriptorProvider.getRootElementDescriptors()); + } + } + } + + return mRootDescriptor; + } + + /** * Returns the active {@link AndroidEditor} matching this source viewer. */ private AndroidEditor getAndroidEditor(ITextViewer viewer) { @@ -729,5 +761,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor { return null; } + + } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/AndroidEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidEditor.java index 74eca96..78e0401 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/AndroidEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidEditor.java @@ -16,10 +16,14 @@ package com.android.ide.eclipse.editors; -import com.android.ide.eclipse.common.AndroidConstants; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; +import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.editors.uimodel.UiElementNode; +import com.android.sdklib.IAndroidTarget; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; @@ -29,15 +33,18 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.Status; +import org.eclipse.jface.action.IAction; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PartInitException; +import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.browser.IWorkbenchBrowserSupport; import org.eclipse.ui.forms.IManagedForm; import org.eclipse.ui.forms.editor.FormEditor; @@ -71,7 +78,7 @@ import java.net.URL; * source editor. This can be a no-op if desired. */ public abstract class AndroidEditor extends FormEditor implements IResourceChangeListener { - + /** Preference name for the current page of this file */ private static final String PREF_CURRENT_PAGE = "_current_page"; @@ -87,9 +94,11 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang /** Page index of the text editor (always the last page) */ private int mTextPageIndex; /** The text editor */ - private StructuredTextEditor mEditor; + private StructuredTextEditor mTextEditor; /** Listener for the XML model from the StructuredEditor */ private XmlModelStateListener mXmlModelStateListener; + /** Listener to update the root node if the resource framework changes */ + private Runnable mResourceRefreshListener; /** * Creates a form editor. @@ -97,6 +106,16 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang public AndroidEditor() { super(); ResourcesPlugin.getWorkspace().addResourceChangeListener(this); + + mResourceRefreshListener = new Runnable() { + public void run() { + commitPages(false /* onSave */); + + // recreate the ui root node always + initUiRootNode(true /*force*/); + } + }; + AdtPlugin.getDefault().addResourceChangedListener(mResourceRefreshListener); } // ---- Abstract Methods ---- @@ -113,6 +132,12 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang * Derived classes must implement this to add their own specific tabs. */ abstract protected void createFormPages(); + + /** + * Creates the initial UI Root Node, including the known mandatory elements. + * @param force if true, a new UiManifestNode is recreated even if it already exists. + */ + abstract protected void initUiRootNode(boolean force); /** * Subclasses should override this method to process the new XML Model, which XML @@ -143,6 +168,26 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang protected void createAndroidPages() { createFormPages(); createTextEditor(); + + createUndoRedoActions(); + } + + /** + * Creates undo redo actions for the editor site (so that it works for any page of this + * multi-page editor) by re-using the actions defined by the {@link StructuredTextEditor} + * (aka the XML text editor.) + */ + private void createUndoRedoActions() { + IActionBars bars = getEditorSite().getActionBars(); + if (bars != null) { + IAction action = mTextEditor.getAction(ActionFactory.UNDO.getId()); + bars.setGlobalActionHandler(ActionFactory.UNDO.getId(), action); + + action = mTextEditor.getAction(ActionFactory.REDO.getId()); + bars.setGlobalActionHandler(ActionFactory.REDO.getId(), action); + + bars.updateActionBars(); + } } /** @@ -155,7 +200,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang if (getEditorInput() instanceof IFileEditorInput) { IFile file = ((IFileEditorInput) getEditorInput()).getFile(); - QualifiedName qname = new QualifiedName(AndroidConstants.EDITORS_PLUGIN_ID, + QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID, getClass().getSimpleName() + PREF_CURRENT_PAGE); String pageId; try { @@ -177,7 +222,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang // AssertionError from setActivePage when the index is out of bounds. // Generally speaking we just want to ignore any exception and fall back on the // first page rather than crash the editor load. Logging the error is enough. - EditorsPlugin.log(e, "Selecting page '%s' in AndroidEditor failed", defaultPageId); + AdtPlugin.log(e, "Selecting page '%s' in AndroidEditor failed", defaultPageId); } } } @@ -224,7 +269,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang if (getEditorInput() instanceof IFileEditorInput) { IFile file = ((IFileEditorInput) getEditorInput()).getFile(); - QualifiedName qname = new QualifiedName(AndroidConstants.EDITORS_PLUGIN_ID, + QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID, getClass().getSimpleName() + PREF_CURRENT_PAGE); try { file.setPersistentProperty(qname, Integer.toString(newPageIndex)); @@ -248,10 +293,10 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang IWorkbenchPage[] pages = getSite().getWorkbenchWindow() .getPages(); for (int i = 0; i < pages.length; i++) { - if (((FileEditorInput)mEditor.getEditorInput()) + if (((FileEditorInput)mTextEditor.getEditorInput()) .getFile().getProject().equals( event.getResource())) { - IEditorPart editorPart = pages[i].findEditor(mEditor + IEditorPart editorPart = pages[i].findEditor(mTextEditor .getEditorInput()); pages[i].closeEditor(editorPart, true); } @@ -294,6 +339,12 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang } } ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); + + if (mResourceRefreshListener != null) { + AdtPlugin.getDefault().removeResourceChangedListener(mResourceRefreshListener); + mResourceRefreshListener = null; + } + super.dispose(); } @@ -447,13 +498,13 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang */ private void createTextEditor() { try { - mEditor = new StructuredTextEditor(); - int index = addPage(mEditor, getEditorInput()); + mTextEditor = new StructuredTextEditor(); + int index = addPage(mTextEditor, getEditorInput()); mTextPageIndex = index; - setPageText(index, mEditor.getTitle()); + setPageText(index, mTextEditor.getTitle()); - if (!(mEditor.getTextViewer().getDocument() instanceof IStructuredDocument)) { - Status status = new Status(IStatus.ERROR, AndroidConstants.EDITORS_PLUGIN_ID, + if (!(mTextEditor.getTextViewer().getDocument() instanceof IStructuredDocument)) { + Status status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "Error opening the Android XML editor. Is the document an XML file?"); throw new RuntimeException("Android XML Editor Error", new CoreException(status)); } @@ -464,6 +515,8 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang mXmlModelStateListener = new XmlModelStateListener(); xml_model.addModelStateListener(mXmlModelStateListener); mXmlModelStateListener.modelChanged(xml_model); + } catch (Exception e) { + AdtPlugin.log(e, "Error while loading editor"); //$NON-NLS-1$ } finally { xml_model.releaseFromRead(); } @@ -478,11 +531,11 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang * Returns the ISourceViewer associated with the Structured Text editor. */ public final ISourceViewer getStructuredSourceViewer() { - if (mEditor != null) { + if (mTextEditor != null) { // We can't access mEditor.getSourceViewer() because it is protected, // however getTextViewer simply returns the SourceViewer casted, so we // can use it instead. - return mEditor.getTextViewer(); + return mTextEditor.getTextViewer(); } return null; } @@ -492,8 +545,8 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang * Editor) or null if not available. */ public final IStructuredDocument getStructuredDocument() { - if (mEditor != null && mEditor.getTextViewer() != null) { - return (IStructuredDocument) mEditor.getTextViewer().getDocument(); + if (mTextEditor != null && mTextEditor.getTextViewer() != null) { + return (IStructuredDocument) mTextEditor.getTextViewer().getDocument(); } return null; } @@ -539,14 +592,17 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang /** * Helper class to perform edits on the XML model whilst making sure the * model has been prepared to be changed. + * <p/> + * It first gets a model for edition using {@link #getModelForEdit()}, + * then calls {@link IStructuredModel#aboutToChangeModel()}, + * then performs the requested action + * and finally calls {@link IStructuredModel#changedModel()} + * and {@link IStructuredModel#releaseFromEdit()}. + * <p/> + * The method is synchronous. As soon as the {@link IStructuredModel#changedModel()} method + * is called, XML model listeners will be triggered. * - * It first gets a model for edition, then calls aboutToChangeModel, then performs the - * requested action and finally calls changedModel and releaseFromEdit. - * - * The method is synchronous. As soon as the changedModel method is called, XML model - * listeners will be triggered. - * - * @param edit_action Something that will change + * @param edit_action Something that will change the XML. */ public final void editXmlModel(Runnable edit_action) { IStructuredModel model = getModelForEdit(); @@ -561,11 +617,81 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang } /** + * Starts an "undo recording" session. This is managed by the underlying undo manager + * associated to the structured XML model. + * <p/> + * There <em>must</em> be a corresponding call to {@link #endUndoRecording()}. + * <p/> + * beginUndoRecording/endUndoRecording calls can be nested (inner calls are ignored, only one + * undo operation is recorded.) + * + * @param label The label for the undo operation. Can be null but we should really try to put + * something meaningful if possible. + * @return True if the undo recording actually started, false if any kind of error occured. + * {@link #endUndoRecording()} should only be called if True is returned. + */ + private final boolean beginUndoRecording(String label) { + IStructuredDocument document = getStructuredDocument(); + if (document != null) { + IModelManager mm = StructuredModelManager.getModelManager(); + if (mm != null) { + IStructuredModel model = mm.getModelForEdit(document); + if (model != null) { + model.beginRecording(this, label); + return true; + } + } + } + return false; + } + + /** + * Ends an "undo recording" session. + * <p/> + * This is the counterpart call to {@link #beginUndoRecording(String)} and should only be + * used if the initial call returned true. + */ + private final void endUndoRecording() { + IStructuredDocument document = getStructuredDocument(); + if (document != null) { + IModelManager mm = StructuredModelManager.getModelManager(); + if (mm != null) { + IStructuredModel model = mm.getModelForEdit(document); + if (model != null) { + model.endRecording(this); + } + } + } + } + + /** + * Creates an "undo recording" session by calling the undoableAction runnable + * using {@link #beginUndoRecording(String)} and {@link #endUndoRecording()}. + * <p> + * You can nest several calls to {@link #wrapUndoRecording(String, Runnable)}, only one + * recording session will be created. + * + * @param label The label for the undo operation. Can be null. Ideally we should really try + * to put something meaningful if possible. + */ + public void wrapUndoRecording(String label, Runnable undoableAction) { + boolean recording = false; + try { + recording = beginUndoRecording(label); + undoableAction.run(); + } finally { + if (recording) { + endUndoRecording(); + } + } + } + + /** * Returns the XML {@link Document} or null if we can't get it */ protected final Document getXmlDocument(IStructuredModel model) { if (model == null) { - EditorsPlugin.log(IStatus.WARNING, "Android Editor: No XML model for root node."); //$NON-NLS-1$ + AdtPlugin.log(IStatus.WARNING, "Android Editor: No XML model for root node."); //$NON-NLS-1$ return null; } @@ -577,6 +703,45 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang } /** + * Returns the {@link IProject} for the edited file. + */ + public IProject getProject() { + if (mTextEditor != null) { + IEditorInput input = mTextEditor.getEditorInput(); + if (input instanceof FileEditorInput) { + FileEditorInput fileInput = (FileEditorInput)input; + IFile inputFile = fileInput.getFile(); + + if (inputFile != null) { + return inputFile.getProject(); + } + } + } + + return null; + } + + /** + * Returns the {@link PlatformData} for the edited file. + */ + public AndroidTargetData getTargetData() { + IProject project = getProject(); + if (project != null) { + Sdk currentSdk = Sdk.getCurrent(); + if (currentSdk != null) { + IAndroidTarget target = currentSdk.getTarget(project); + + if (target != null) { + return currentSdk.getTargetData(target); + } + } + } + + return null; + } + + + /** * Listen to changes in the underlying XML model in the structured editor. */ private class XmlModelStateListener implements IModelStateListener { diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/AndroidSourceViewerConfig.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidSourceViewerConfig.java index ab17bef..ab17bef 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/AndroidSourceViewerConfig.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidSourceViewerConfig.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/FirstElementParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/FirstElementParser.java index bb0996b..bb0996b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/FirstElementParser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/FirstElementParser.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/IconFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/IconFactory.java index 9e3b733..e3de3af 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/IconFactory.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/IconFactory.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.editors; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.AndroidConstants; import org.eclipse.jface.resource.ImageDescriptor; @@ -100,8 +101,6 @@ public class IconFactory { * one of SHAPE_DEFAULT, SHAPE_CIRCLE or SHAPE_RECT. */ public Image getIcon(String osName, int color, int shape) { - EditorsPlugin plugin = EditorsPlugin.getDefault(); - String key = Character.toString((char) shape) + Integer.toString(color) + osName; Image icon = mIconMap.get(key); if (icon == null && !mIconMap.containsKey(key)) { @@ -143,13 +142,11 @@ public class IconFactory { * one of SHAPE_DEFAULT, SHAPE_CIRCLE or SHAPE_RECT. */ public ImageDescriptor getImageDescriptor(String osName, int color, int shape) { - EditorsPlugin plugin = EditorsPlugin.getDefault(); - String key = Character.toString((char) shape) + Integer.toString(color) + osName; ImageDescriptor id = mImageDescMap.get(key); if (id == null && !mImageDescMap.containsKey(key)) { - id = plugin.imageDescriptorFromPlugin( - AndroidConstants.EDITORS_PLUGIN_ID, + id = AdtPlugin.imageDescriptorFromPlugin( + AdtPlugin.PLUGIN_ID, String.format("/icons/%1$s.png", osName)); //$NON-NLS-1$ if (id == null) { diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptor.java index 48fa903..2c779b2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptor.java @@ -16,8 +16,8 @@ package com.android.ide.eclipse.editors.descriptors; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.IconFactory; import com.android.ide.eclipse.editors.uimodel.UiAttributeNode; import com.android.ide.eclipse.editors.uimodel.UiElementNode; @@ -61,30 +61,6 @@ public abstract class AttributeDescriptor { public final String getNamespaceUri() { return mNsUri; } - - /** - * Returns the XML qualified name of the attribute (case sensitive, with namespace prefix - * if present) - * - * @deprecated - */ - private final String getXmlName() { - return mXmlLocalName; - } - - /** - * Returns the namespace of the attribute. - * - * @deprecated - */ - private final String getNamespace() { - // For now we hard-code the prefix as being "android" - if (mXmlLocalName.startsWith("android:")) { //$NON-NLs-1$ - return AndroidConstants.NS_RESOURCES; - } - - return ""; //$NON-NLs-1$ - } final void setParent(ElementDescriptor parent) { mParent = parent; @@ -107,7 +83,7 @@ public abstract class AttributeDescriptor { IconFactory factory = IconFactory.getInstance(); Image icon; icon = factory.getIcon(getXmlLocalName(), IconFactory.COLOR_RED, IconFactory.SHAPE_CIRCLE); - return icon != null ? icon : EditorsPlugin.getAndroidLogo(); + return icon != null ? icon : AdtPlugin.getAndroidLogo(); } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptorLabelProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptorLabelProvider.java index 05ae922..05ae922 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptorLabelProvider.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptorLabelProvider.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/BooleanAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/BooleanAttributeDescriptor.java index 9ebf5f1..9ebf5f1 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/BooleanAttributeDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/BooleanAttributeDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/DescriptorsUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/DescriptorsUtils.java index f848d79..c84bf57 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/DescriptorsUtils.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/DescriptorsUtils.java @@ -20,6 +20,8 @@ import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.resources.ResourceType; import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo; import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo.Format; +import com.android.ide.eclipse.editors.layout.LayoutConstants; +import com.android.ide.eclipse.editors.uimodel.UiDocumentNode; import com.android.ide.eclipse.editors.uimodel.UiElementNode; import org.eclipse.swt.graphics.Image; @@ -29,6 +31,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -39,6 +42,8 @@ import java.util.regex.Pattern; */ public final class DescriptorsUtils { + private static final String DEFAULT_WIDGET_PREFIX = "widget"; + private static final int JAVADOC_BREAK_LENGTH = 60; /** @@ -458,7 +463,8 @@ public final class DescriptorsUtils { Image icon = elementDescriptor.getIcon(); if (icon != null) { - sb.append("<form><li style=\"image\" value=\"" + IMAGE_KEY + "\">"); //$NON-NLS-1$ //$NON-NLS-2$ + sb.append("<form><li style=\"image\" value=\"" + //$NON-NLS-1$ + IMAGE_KEY + "\">"); //$NON-NLS-1$ } else { sb.append("<form><p>"); //$NON-NLS-1$ } @@ -573,13 +579,13 @@ public final class DescriptorsUtils { // Detects {@link <base>#<name> <text>} where all 3 are optional Pattern p_link = Pattern.compile("\\{@link\\s+([^#\\}\\s]*)(?:#([^\\s\\}]*))?(?:\\s*([^\\}]*))?\\}(.*)"); //$NON-NLS-1$ // Detects <code>blah</code> - Pattern p_code = Pattern.compile("<code>(.+?)</code>(.*)"); //$NON-NLS-1$ + Pattern p_code = Pattern.compile("<code>(.+?)</code>(.*)"); //$NON-NLS-1$ // Detects @blah@, used in hard-coded tooltip descriptors - Pattern p_elem = Pattern.compile("@([\\w -]+)@(.*)"); //$NON-NLS-1$ + Pattern p_elem = Pattern.compile("@([\\w -]+)@(.*)"); //$NON-NLS-1$ // Detects a buffer that starts by @ < or { (one that was not matched above) - Pattern p_open = Pattern.compile("([@<\\{])(.*)"); //$NON-NLS-1$ + Pattern p_open = Pattern.compile("([@<\\{])(.*)"); //$NON-NLS-1$ // Detects everything till the next potential separator, i.e. @ < or { - Pattern p_text = Pattern.compile("([^@<\\{]+)(.*)"); //$NON-NLS-1$ + Pattern p_text = Pattern.compile("([^@<\\{]+)(.*)"); //$NON-NLS-1$ int currentLength = 0; String text = null; @@ -668,28 +674,55 @@ public final class DescriptorsUtils { * <p/> * This does not override attributes which are not empty. */ - public static void setDefaultLayoutAttributes(UiElementNode ui_node) { - ui_node.setAttributeValue("layout_width", "wrap_content", false /* override */); //$NON-NLS-1$ $NON-NLS-2$ - ui_node.setAttributeValue("layout_height", "wrap_content", false /* override */); //$NON-NLS-1$ $NON-NLS-2$ + public static void setDefaultLayoutAttributes(UiElementNode ui_node, boolean updateLayout) { + // if this ui_node is a layout and we're adding it to a document, use fill_parent for + // both W/H. Otherwise default to wrap_layout. + boolean fill = ui_node.getDescriptor().hasChildren() && + ui_node.getUiParent() instanceof UiDocumentNode; + ui_node.setAttributeValue(LayoutConstants.ATTR_LAYOUT_WIDTH, + fill ? LayoutConstants.VALUE_FILL_PARENT : LayoutConstants.VALUE_WRAP_CONTENT, + false /* override */); + ui_node.setAttributeValue(LayoutConstants.ATTR_LAYOUT_HEIGHT, + fill ? LayoutConstants.VALUE_FILL_PARENT : LayoutConstants.VALUE_WRAP_CONTENT, + false /* override */); String widget_id = getFreeWidgetId(ui_node.getUiRoot(), - new Object[] {ui_node.getDescriptor().getXmlLocalName(), 1, null }); + new Object[] { ui_node.getDescriptor().getXmlLocalName(), null, null, null }); if (widget_id != null) { - ui_node.setAttributeValue("id", "@+id/" + widget_id, false /* override */); //$NON-NLS-1$ $NON-NLS-2$ + ui_node.setAttributeValue(LayoutConstants.ATTR_ID, "@+id/" + widget_id, //$NON-NLS-1$ + false /* override */); } + + ui_node.setAttributeValue(LayoutConstants.ATTR_TEXT, widget_id, false /*override*/); - UiElementNode ui_parent = ui_node.getUiParent(); - if (ui_parent != null && ui_parent.getDescriptor().getXmlLocalName().equals("RelativeLayout")) { //$NON-NLS-1$ - UiElementNode ui_previous = ui_node.getUiPreviousSibling(); - if (ui_previous != null) { - String id = ui_previous.getAttributeValue("id"); //$NON-NLS-1$ - if (id != null && id.length() > 0) { - id = id.replace("@+", "@"); - ui_node.setAttributeValue("layout_below", id, false /* override */); + if (updateLayout) { + UiElementNode ui_parent = ui_node.getUiParent(); + if (ui_parent != null && + ui_parent.getDescriptor().getXmlLocalName().equals( + LayoutConstants.RELATIVE_LAYOUT)) { + UiElementNode ui_previous = ui_node.getUiPreviousSibling(); + if (ui_previous != null) { + String id = ui_previous.getAttributeValue(LayoutConstants.ATTR_ID); + if (id != null && id.length() > 0) { + id = id.replace("@+", "@"); //$NON-NLS-1$ //$NON-NLS-2$ + ui_node.setAttributeValue(LayoutConstants.ATTR_LAYOUT_BELOW, id, + false /* override */); + } } } } - + } + + /** + * Given a UI root node, returns the first available id that matches the + * pattern "prefix%02d". + * + * @param uiNode The UI node that gives the prefix to match. + * @return A suitable generated id + */ + public static String getFreeWidgetId(UiElementNode uiNode) { + return getFreeWidgetId(uiNode.getUiRoot(), + new Object[] { uiNode.getDescriptor().getXmlLocalName(), null, null, null }); } /** @@ -698,19 +731,34 @@ public final class DescriptorsUtils { * * For recursion purposes, a "context" is given. Since Java doesn't have in-out parameters * in methods and we're not going to do a dedicated type, we just use an object array which - * must contain the following items: - * - prefix(String): The prefix of the generated id, i.e. "widget" - * - index(Integer): The minimum index of the generated id - * - generated(String) The generated widget currently being searched. Must start with null. + * must contain one initial item and several are built on the fly just for internal storage: + * <ul> + * <li> prefix(String): The prefix of the generated id, i.e. "widget". Cannot be null. + * <li> index(Integer): The minimum index of the generated id. Must start with null. + * <li> generated(String): The generated widget currently being searched. Must start with null. + * <li> map(Set<String>): A set of the ids collected so far when walking through the widget + * hierarchy. Must start with null. + * </ul> * - * @param uiRoot The Ui root node where to start searching recusrively. - * @param params An in-out context of parameters used during recursion, as explaine above. + * @param uiRoot The Ui root node where to start searching recursively. For the initial call + * you want to pass the document root. + * @param params An in-out context of parameters used during recursion, as explained above. * @return A suitable generated id */ - private static String getFreeWidgetId(UiElementNode uiRoot, Object[] params) { + @SuppressWarnings("unchecked") + private static String getFreeWidgetId(UiElementNode uiRoot, + Object[] params) { + + Set<String> map = (Set<String>)params[3]; + if (map == null) { + params[3] = map = new HashSet<String>(); + } + + int num = params[1] == null ? 0 : ((Integer)params[1]).intValue(); + String generated = (String) params[2]; + String prefix = (String) params[0]; if (generated == null) { - String prefix = (String) params[0]; int pos = prefix.indexOf('.'); if (pos >= 0) { prefix = prefix.substring(pos + 1); @@ -719,32 +767,42 @@ public final class DescriptorsUtils { if (pos >= 0) { prefix = prefix.substring(pos + 1); } - prefix = prefix.replaceAll("[^a-zA-Z]", ""); //$NON-NLS-1$ $NON-NLS-2$ + prefix = prefix.replaceAll("[^a-zA-Z]", ""); //$NON-NLS-1$ $NON-NLS-2$ if (prefix.length() == 0) { - prefix = "widget"; + prefix = DEFAULT_WIDGET_PREFIX; } - generated = String.format("%1$s%2$02d", prefix, ((Integer)params[1]).intValue()); + + do { + num++; + generated = String.format("%1$s%2$02d", prefix, num); //$NON-NLS-1$ + } while (map.contains(generated)); + params[0] = prefix; + params[1] = num; params[2] = generated; } - String id = uiRoot.getAttributeValue("id"); //$NON-NLS-1$ + String id = uiRoot.getAttributeValue(LayoutConstants.ATTR_ID); if (id != null) { - id = id.replace("@+id/", ""); //$NON-NLS-1$ $NON-NLS-2$ - id = id.replace("@id/", ""); //$NON-NLS-1$ $NON-NLS-2$ - if (id.equals(generated)) { - // switch to next value - int num = ((Integer)params[1]).intValue() + 1; - generated = String.format("%1$s%2$02d", params[0], num); + id = id.replace("@+id/", ""); //$NON-NLS-1$ $NON-NLS-2$ + id = id.replace("@id/", ""); //$NON-NLS-1$ $NON-NLS-2$ + if (map.add(id) && map.contains(generated)) { + + do { + num++; + generated = String.format("%1$s%2$02d", prefix, num); //$NON-NLS-1$ + } while (map.contains(generated)); + params[1] = num; params[2] = generated; } } - + for (UiElementNode uiChild : uiRoot.getUiChildren()) { getFreeWidgetId(uiChild, params); } + // Note: return params[2] (not "generated") since it could have changed during recursion. return (String) params[2]; } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/DocumentDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/DocumentDescriptor.java index 7d296f7..7d296f7 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/DocumentDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/DocumentDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/ElementDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/ElementDescriptor.java index 12ebc38..7d7b1c9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/ElementDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/ElementDescriptor.java @@ -16,8 +16,8 @@ package com.android.ide.eclipse.editors.descriptors; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.IconFactory; import com.android.ide.eclipse.editors.uimodel.UiElementNode; @@ -173,7 +173,7 @@ public class ElementDescriptor { int color = hasChildren() ? IconFactory.COLOR_BLUE : IconFactory.COLOR_GREEN; int shape = hasChildren() ? IconFactory.SHAPE_RECT : IconFactory.SHAPE_CIRCLE; Image icon = factory.getIcon(mXmlName, color, shape); - return icon != null ? icon : EditorsPlugin.getAndroidLogo(); + return icon != null ? icon : AdtPlugin.getAndroidLogo(); } /** @@ -190,7 +190,7 @@ public class ElementDescriptor { int color = hasChildren() ? IconFactory.COLOR_BLUE : IconFactory.COLOR_GREEN; int shape = hasChildren() ? IconFactory.SHAPE_RECT : IconFactory.SHAPE_CIRCLE; ImageDescriptor id = factory.getImageDescriptor(mXmlName, color, shape); - return id != null ? id : EditorsPlugin.getAndroidLogoDesc(); + return id != null ? id : AdtPlugin.getAndroidLogoDesc(); } /* Returns the list of allowed attributes. */ diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/EnumAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/EnumAttributeDescriptor.java index cab9883..cab9883 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/EnumAttributeDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/EnumAttributeDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/FlagAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/FlagAttributeDescriptor.java index 903417b..903417b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/FlagAttributeDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/FlagAttributeDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/IDescriptorProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/IDescriptorProvider.java new file mode 100644 index 0000000..4c115e9 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/IDescriptorProvider.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.editors.descriptors; + +public interface IDescriptorProvider { + + ElementDescriptor[] getRootElementDescriptors(); + + ElementDescriptor getDescriptor(); +} diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/ListAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/ListAttributeDescriptor.java index 93969e9..93969e9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/ListAttributeDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/ListAttributeDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/ReferenceAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/ReferenceAttributeDescriptor.java index 3d3ff29..3d3ff29 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/ReferenceAttributeDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/ReferenceAttributeDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/SeparatorAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/SeparatorAttributeDescriptor.java index 8fb1c7c..8fb1c7c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/SeparatorAttributeDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/SeparatorAttributeDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/TextAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/TextAttributeDescriptor.java index 632471d..632471d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/TextAttributeDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/TextAttributeDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/TextValueDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/TextValueDescriptor.java index 2015d71..2015d71 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/TextValueDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/TextValueDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/XmlnsAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/XmlnsAttributeDescriptor.java index ed9c897..ed9c897 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/descriptors/XmlnsAttributeDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/XmlnsAttributeDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/BasePullParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/BasePullParser.java new file mode 100644 index 0000000..c512625 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/BasePullParser.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.editors.layout; + +import com.android.layoutlib.api.IXmlPullParser; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +/** + * Base implementation of an {@link IXmlPullParser} for cases where the parser is not sitting + * on top of an actual XML file. + * <p/>It's designed to work on layout files, and will most likely not work on other resource + * files. + */ +public abstract class BasePullParser implements IXmlPullParser { + + protected int mParsingState = START_DOCUMENT; + + public BasePullParser() { + } + + // --- new methods to override --- + + public abstract void onNextFromStartDocument(); + public abstract void onNextFromStartTag(); + public abstract void onNextFromEndTag(); + + // --- basic implementation of IXmlPullParser --- + + public void setFeature(String name, boolean state) throws XmlPullParserException { + if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) { + return; + } + if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) { + return; + } + throw new XmlPullParserException("Unsupported feature: " + name); + } + + public boolean getFeature(String name) { + if (FEATURE_PROCESS_NAMESPACES.equals(name)) { + return true; + } + if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) { + return true; + } + return false; + } + + public void setProperty(String name, Object value) throws XmlPullParserException { + throw new XmlPullParserException("setProperty() not supported"); + } + + public Object getProperty(String name) { + return null; + } + + public void setInput(Reader in) throws XmlPullParserException { + throw new XmlPullParserException("setInput() not supported"); + } + + public void setInput(InputStream inputStream, String inputEncoding) + throws XmlPullParserException { + throw new XmlPullParserException("setInput() not supported"); + } + + public void defineEntityReplacementText(String entityName, String replacementText) + throws XmlPullParserException { + throw new XmlPullParserException("defineEntityReplacementText() not supported"); + } + + public String getNamespacePrefix(int pos) throws XmlPullParserException { + throw new XmlPullParserException("getNamespacePrefix() not supported"); + } + + public String getInputEncoding() { + return null; + } + + public String getNamespace(String prefix) { + throw new RuntimeException("getNamespace() not supported"); + } + + public int getNamespaceCount(int depth) throws XmlPullParserException { + throw new XmlPullParserException("getNamespaceCount() not supported"); + } + + public String getNamespaceUri(int pos) throws XmlPullParserException { + throw new XmlPullParserException("getNamespaceUri() not supported"); + } + + public int getColumnNumber() { + return -1; + } + + public int getLineNumber() { + return -1; + } + + public String getAttributeType(int arg0) { + return "CDATA"; + } + + public int getEventType() { + return mParsingState; + } + + public String getText() { + return null; + } + + public char[] getTextCharacters(int[] arg0) { + return null; + } + + public boolean isAttributeDefault(int arg0) { + return false; + } + + public boolean isWhitespace() { + return false; + } + + public int next() throws XmlPullParserException { + switch (mParsingState) { + case END_DOCUMENT: + throw new XmlPullParserException("Nothing after the end"); + case START_DOCUMENT: + onNextFromStartDocument(); + break; + case START_TAG: + onNextFromStartTag(); + break; + case END_TAG: + onNextFromEndTag(); + break; + case TEXT: + // not used + break; + case CDSECT: + // not used + break; + case ENTITY_REF: + // not used + break; + case IGNORABLE_WHITESPACE: + // not used + break; + case PROCESSING_INSTRUCTION: + // not used + break; + case COMMENT: + // not used + break; + case DOCDECL: + // not used + break; + } + + return mParsingState; + } + + public int nextTag() throws XmlPullParserException, IOException { + int eventType = next(); + if (eventType != START_TAG && eventType != END_TAG) { + throw new XmlPullParserException("expected start or end tag", this, null); + } + return eventType; + } + + public String nextText() throws XmlPullParserException, IOException { + if (getEventType() != START_TAG) { + throw new XmlPullParserException("parser must be on START_TAG to read next text", this, + null); + } + int eventType = next(); + if (eventType == TEXT) { + String result = getText(); + eventType = next(); + if (eventType != END_TAG) { + throw new XmlPullParserException( + "event TEXT it must be immediately followed by END_TAG", this, null); + } + return result; + } else if (eventType == END_TAG) { + return ""; + } else { + throw new XmlPullParserException("parser must be on START_TAG or TEXT to read text", + this, null); + } + } + + public int nextToken() throws XmlPullParserException, IOException { + return next(); + } + + public void require(int type, String namespace, String name) throws XmlPullParserException { + if (type != getEventType() || (namespace != null && !namespace.equals(getNamespace())) + || (name != null && !name.equals(getName()))) + throw new XmlPullParserException("expected " + TYPES[type] + getPositionDescription()); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java index 278b921..77467cd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java @@ -16,12 +16,18 @@ package com.android.ide.eclipse.editors.layout; -import com.android.ide.eclipse.common.AndroidConstants; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; +import com.android.ide.eclipse.adt.sdk.LoadStatus; +import com.android.ide.eclipse.adt.sdk.Sdk; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData.LayoutBridge; import com.android.ide.eclipse.common.resources.ResourceType; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.IconFactory; -import com.android.ide.eclipse.editors.EditorsPlugin.LayoutBridge; +import com.android.ide.eclipse.editors.layout.LayoutEditor.UiEditorActions; import com.android.ide.eclipse.editors.layout.LayoutReloadMonitor.ILayoutReloadListener; +import com.android.ide.eclipse.editors.layout.descriptors.ViewElementDescriptor; +import com.android.ide.eclipse.editors.layout.parts.ElementCreateCommand; +import com.android.ide.eclipse.editors.layout.parts.UiElementEditPart; import com.android.ide.eclipse.editors.layout.parts.UiElementsEditPartFactory; import com.android.ide.eclipse.editors.resources.configurations.CountryCodeQualifier; import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration; @@ -44,6 +50,8 @@ import com.android.ide.eclipse.editors.resources.manager.ProjectResources; import com.android.ide.eclipse.editors.resources.manager.ResourceFile; import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType; import com.android.ide.eclipse.editors.resources.manager.ResourceManager; +import com.android.ide.eclipse.editors.ui.tree.CopyCutAction; +import com.android.ide.eclipse.editors.ui.tree.PasteAction; import com.android.ide.eclipse.editors.uimodel.UiDocumentNode; import com.android.ide.eclipse.editors.uimodel.UiElementNode; import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.DensityVerifier; @@ -55,6 +63,7 @@ import com.android.layoutlib.api.ILayoutResult; import com.android.layoutlib.api.IResourceValue; import com.android.layoutlib.api.IStyleResourceValue; import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo; +import com.android.sdklib.IAndroidTarget; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; @@ -67,14 +76,22 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.gef.DefaultEditDomain; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.EditPartViewer; import org.eclipse.gef.GraphicalViewer; import org.eclipse.gef.SelectionManager; import org.eclipse.gef.dnd.TemplateTransferDragSourceListener; import org.eclipse.gef.dnd.TemplateTransferDropTargetListener; import org.eclipse.gef.editparts.ScalableFreeformRootEditPart; import org.eclipse.gef.palette.PaletteRoot; -import org.eclipse.gef.ui.parts.GraphicalEditor; +import org.eclipse.gef.requests.CreationFactory; +import org.eclipse.gef.ui.parts.GraphicalEditorWithPalette; import org.eclipse.gef.ui.parts.SelectionSynchronizer; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.viewers.ISelection; import org.eclipse.swt.SWT; @@ -85,6 +102,8 @@ import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.PaletteData; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; @@ -98,6 +117,9 @@ import org.eclipse.ui.PartInitException; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.part.FileEditorInput; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.awt.image.Raster; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -105,6 +127,7 @@ import java.io.InputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -112,20 +135,25 @@ import java.util.Set; /** * Graphical layout editor, based on GEF. + * <p/> + * To understand GEF: http://www.ibm.com/developerworks/opensource/library/os-gef/ + * <p/> + * To understand Drag'n'drop: http://www.eclipse.org/articles/Article-Workbench-DND/drag_drop.html */ -public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ +public class GraphicalLayoutEditor extends GraphicalEditorWithPalette implements ILayoutReloadListener { private final static String THEME_SEPARATOR = "----------"; //$NON-NLS-1$ /** Reference to the layout editor */ private final LayoutEditor mLayoutEditor; - + /** reference to the file being edited. */ private IFile mEditedFile; private Clipboard mClipboard; private Composite mParent; + private PaletteRoot mPaletteRoot; private Text mCountry; private Text mNetwork; @@ -141,7 +169,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ private Text mSize2; private Combo mThemeCombo; private Button mCreateButton; - + private Label mCountryIcon; private Label mNetworkIcon; private Label mLanguageIcon; @@ -153,18 +181,18 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ private Label mTextInputIcon; private Label mNavigationIcon; private Label mSizeIcon; - + private Label mCurrentLayoutLabel; private Image mWarningImage; private Image mMatchImage; private Image mErrorImage; - + /** The {@link FolderConfiguration} representing the state of the UI controls */ private FolderConfiguration mCurrentConfig = new FolderConfiguration(); /** The {@link FolderConfiguration} being edited. */ private FolderConfiguration mEditedConfig; - + private Map<String, Map<String, IResourceValue>> mConfiguredFrameworkRes; private Map<String, Map<String, IResourceValue>> mConfiguredProjectRes; private ProjectCallback mProjectCallback; @@ -173,6 +201,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ private boolean mNeedsRecompute = false; private int mPlatformThemeCount = 0; private boolean mDisableUpdates = false; + private boolean mActive = false; private Runnable mFrameworkResourceChangeListener = new Runnable() { public void run() { @@ -180,6 +209,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ mConfiguredFrameworkRes = null; updateUIFromResources(); + mThemeCombo.getParent().layout(); // updateUiFromFramework will reset language/region combo, so we must call @@ -191,21 +221,21 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ // make sure we remove the custom view loader, since its parent class loader is the // bridge class loader. mProjectCallback = null; - + recomputeLayout(); } }; - + private final Runnable mConditionalRecomputeRunnable = new Runnable() { public void run() { - if (mLayoutEditor.isGraphicalEditorActive()) { + if (mActive) { recomputeLayout(); } else { mNeedsRecompute = true; } } }; - + private final Runnable mUiUpdateFromResourcesRunnable = new Runnable() { public void run() { updateUIFromResources(); @@ -217,19 +247,19 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ mLayoutEditor = layoutEditor; setEditDomain(new DefaultEditDomain(this)); setPartName("Layout"); - + IconFactory factory = IconFactory.getInstance(); mWarningImage = factory.getIcon("warning"); //$NON-NLS-1$ mMatchImage = factory.getIcon("match"); //$NON-NLS-1$ mErrorImage = factory.getIcon("error"); //$NON-NLS-1$ - - EditorsPlugin.getDefault().addResourceChangedListener(mFrameworkResourceChangeListener); + + AdtPlugin.getDefault().addResourceChangedListener(mFrameworkResourceChangeListener); } - + // ------------------------------------ // Methods overridden from base classes //------------------------------------ - + @Override public void createPartControl(Composite parent) { mParent = parent; @@ -237,13 +267,13 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ GridData gd; mClipboard = new Clipboard(parent.getDisplay()); - + parent.setLayout(gl = new GridLayout(1, false)); gl.marginHeight = gl.marginWidth = 0; - + // create the top part for the configuration control int cols = 10; - + Composite topParent = new Composite(parent, SWT.NONE); topParent.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); topParent.setLayout(gl = new GridLayout(cols, false)); @@ -339,9 +369,9 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ @Override public void widgetSelected(SelectionEvent e) { onOrientationChange(); - } + } }); - + new Label(topParent, SWT.NONE).setText("Density"); mDensityIcon = createControlComposite(topParent, true /* grab_horizontal */); mDensity = new Text(mDensityIcon.getParent(), SWT.BORDER); @@ -437,7 +467,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ gl.marginWidth = gl.marginHeight = 0; labelParent.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); gd.horizontalSpan = cols; - + new Label(labelParent, SWT.NONE).setText("Editing config:"); mCurrentLayoutLabel = new Label(labelParent, SWT.NONE); mCurrentLayoutLabel.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); @@ -449,7 +479,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ sizeParent.setLayout(gl = new GridLayout(3, false)); gl.marginWidth = gl.marginHeight = 0; gl.horizontalSpacing = 0; - + mSize1 = new Text(sizeParent, SWT.BORDER); mSize1.setLayoutData(gd = new GridData()); gd.widthHint = 30; @@ -457,11 +487,11 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ mSize2 = new Text(sizeParent, SWT.BORDER); mSize2.setLayoutData(gd = new GridData()); gd.widthHint = 30; - + DimensionVerifier verifier = new DimensionVerifier(); mSize1.addVerifyListener(verifier); mSize2.addVerifyListener(verifier); - + SelectionListener sl = new SelectionListener() { public void widgetDefaultSelected(SelectionEvent e) { onSizeChange(); @@ -470,7 +500,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ onSizeChange(); } }; - + mSize1.addSelectionListener(sl); mSize2.addSelectionListener(sl); @@ -488,7 +518,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ separator.setLayoutData(gd = new GridData( GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL)); gd.heightHint = 0; - + mThemeCombo = new Combo(labelParent, SWT.READ_ONLY | SWT.DROP_DOWN); mThemeCombo.setEnabled(false); updateUIFromResources(); @@ -521,7 +551,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } } }); - + // create a new composite that will contain the standard editor controls. Composite editorParent = new Composite(parent, SWT.NONE); editorParent.setLayoutData(new GridData(GridData.FILL_BOTH)); @@ -532,28 +562,31 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ @Override public void dispose() { if (mFrameworkResourceChangeListener != null) { - EditorsPlugin.getDefault().removeResourceChangedListener( + AdtPlugin.getDefault().removeResourceChangedListener( mFrameworkResourceChangeListener); mFrameworkResourceChangeListener = null; } - + LayoutReloadMonitor.getMonitor().removeListener(mEditedFile.getProject(), this); if (mClipboard != null) { mClipboard.dispose(); mClipboard = null; } - + super.dispose(); } - + /* (non-Javadoc) * Creates the palette root. */ + @Override protected PaletteRoot getPaletteRoot() { - return PaletteFactory.createPaletteRoot(); + mPaletteRoot = PaletteFactory.createPaletteRoot(mPaletteRoot, + mLayoutEditor.getTargetData()); + return mPaletteRoot; } - + public Clipboard getClipboard() { return mClipboard; } @@ -577,22 +610,60 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ firePropertyChange(PROP_DIRTY); } + @Override + protected void configurePaletteViewer() { + super.configurePaletteViewer(); + + // Create a drag source listener on an edit part that is a viewer. + // What this does is use DND with a TemplateTransfer type which is actually + // the PaletteTemplateEntry held in the PaletteRoot. + TemplateTransferDragSourceListener dragSource = + new TemplateTransferDragSourceListener(getPaletteViewer()); + + // Create a drag source on the palette viewer. + // See the drag target associated with the GraphicalViewer in configureGraphicalViewer. + getPaletteViewer().addDragSourceListener(dragSource); + } + /* (non-javadoc) * Configure the graphical viewer before it receives its contents. */ @Override protected void configureGraphicalViewer() { super.configureGraphicalViewer(); - + GraphicalViewer viewer = getGraphicalViewer(); viewer.setEditPartFactory(new UiElementsEditPartFactory(mParent.getDisplay())); viewer.setRootEditPart(new ScalableFreeformRootEditPart()); - // TODO: viewer.setKeyHandler() - // TODO: custom ContextMenuProvider => viewer.setContextMenu & registerContextMenu + // Disable the following -- we don't drag *from* the GraphicalViewer yet: + // viewer.addDragSourceListener(new TemplateTransferDragSourceListener(viewer)); + + viewer.addDropTargetListener(new DropListener(viewer)); + } + + class DropListener extends TemplateTransferDropTargetListener { + public DropListener(EditPartViewer viewer) { + super(viewer); + } + + // TODO explain + @Override + protected CreationFactory getFactory(final Object template) { + return new CreationFactory() { + public Object getNewObject() { + // We don't know the newly created EditPart since "creating" new + // elements is done by ElementCreateCommand.execute() directly by + // manipulating the XML elements.. + return null; + } - viewer.addDragSourceListener(new TemplateTransferDragSourceListener(viewer)); - viewer.addDropTargetListener(new TemplateTransferDropTargetListener(viewer)); + public Object getObjectType() { + return template; + } + + }; + } } /* (non-javadoc) @@ -602,31 +673,184 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ protected void initializeGraphicalViewer() { GraphicalViewer viewer = getGraphicalViewer(); viewer.setContents(getModel()); - + IEditorInput input = getEditorInput(); if (input instanceof FileEditorInput) { FileEditorInput fileInput = (FileEditorInput)input; mEditedFile = fileInput.getFile(); - + updateUIFromResources(); LayoutReloadMonitor.getMonitor().addListener(mEditedFile.getProject(), this); } else { // really this shouldn't happen! Log it in case it happens mEditedFile = null; - EditorsPlugin.log(IStatus.ERROR, "Input is not of type FileEditorInput: %1$s", + AdtPlugin.log(IStatus.ERROR, "Input is not of type FileEditorInput: %1$s", input.toString()); } } + /* (non-javadoc) + * Sets the graphicalViewer for this EditorPart. + * @param viewer the graphical viewer + */ + @Override + protected void setGraphicalViewer(GraphicalViewer viewer) { + super.setGraphicalViewer(viewer); + + // TODO: viewer.setKeyHandler() + viewer.setContextMenu(createContextMenu(viewer)); + } + + /** + * Used by LayoutEditor.UiEditorActions.selectUiNode to select a new UI Node + * created by {@link ElementCreateCommand#execute()}. + * + * @param uiNodeModel The {@link UiElementNode} to select. + */ + public void selectModel(UiElementNode uiNodeModel) { + GraphicalViewer viewer = getGraphicalViewer(); + + // Give focus to the graphical viewer (in case the outline has it) + viewer.getControl().forceFocus(); + + Object editPart = viewer.getEditPartRegistry().get(uiNodeModel); + + if (editPart instanceof EditPart) { + viewer.select((EditPart)editPart); + } + } + + //-------------- // Local methods //-------------- - + public LayoutEditor getLayoutEditor() { return mLayoutEditor; } + private MenuManager createContextMenu(GraphicalViewer viewer) { + MenuManager menuManager = new MenuManager(); + menuManager.setRemoveAllWhenShown(true); + menuManager.addMenuListener(new ActionMenuListener(viewer)); + + return menuManager; + } + + private class ActionMenuListener implements IMenuListener { + private final GraphicalViewer mViewer; + + public ActionMenuListener(GraphicalViewer viewer) { + mViewer = viewer; + } + + /** + * The menu is about to be shown. The menu manager has already been + * requested to remove any existing menu item. This method gets the + * tree selection and if it is of the appropriate type it re-creates + * the necessary actions. + */ + public void menuAboutToShow(IMenuManager manager) { + ArrayList<UiElementNode> selected = new ArrayList<UiElementNode>(); + + // filter selected items and only keep those we can handle + for (Object obj : mViewer.getSelectedEditParts()) { + if (obj instanceof UiElementEditPart) { + UiElementEditPart part = (UiElementEditPart) obj; + UiElementNode uiNode = part.getUiNode(); + if (uiNode != null) { + selected.add(uiNode); + } + } + } + + if (selected.size() > 0) { + doCreateMenuAction(manager, mViewer, selected); + } + } + } + + private void doCreateMenuAction(IMenuManager manager, + final GraphicalViewer viewer, + final ArrayList<UiElementNode> selected) { + if (selected != null) { + boolean hasXml = false; + for (UiElementNode uiNode : selected) { + if (uiNode.getXmlNode() != null) { + hasXml = true; + break; + } + } + + if (hasXml) { + manager.add(new CopyCutAction(mLayoutEditor, getClipboard(), + null, selected, true /* cut */)); + manager.add(new CopyCutAction(mLayoutEditor, getClipboard(), + null, selected, false /* cut */)); + + // Can't paste with more than one element selected (the selection is the target) + if (selected.size() <= 1) { + // Paste is not valid if it would add a second element on a terminal element + // which parent is a document -- an XML document can only have one child. This + // means paste is valid if the current UI node can have children or if the + // parent is not a document. + UiElementNode ui_root = selected.get(0).getUiRoot(); + if (ui_root.getDescriptor().hasChildren() || + !(ui_root.getUiParent() instanceof UiDocumentNode)) { + manager.add(new PasteAction(mLayoutEditor, getClipboard(), + selected.get(0))); + } + } + manager.add(new Separator()); + } + } + + // Append "add" and "remove" actions. They do the same thing as the add/remove + // buttons on the side. + IconFactory factory = IconFactory.getInstance(); + + final UiEditorActions uiActions = mLayoutEditor.getUiEditorActions(); + + // "Add" makes sense only if there's 0 or 1 item selected since the + // one selected item becomes the target. + if (selected == null || selected.size() <= 1) { + manager.add(new Action("Add...", factory.getImageDescriptor("add")) { //$NON-NLS-2$ + @Override + public void run() { + UiElementNode node = selected != null && selected.size() > 0 ? selected.get(0) + : null; + uiActions.doAdd(node, viewer.getControl().getShell()); + } + }); + } + + if (selected != null) { + manager.add(new Action("Remove", factory.getImageDescriptor("delete")) { //$NON-NLS-2$ + @Override + public void run() { + uiActions.doRemove(selected, viewer.getControl().getShell()); + } + }); + + manager.add(new Separator()); + + manager.add(new Action("Up", factory.getImageDescriptor("up")) { //$NON-NLS-2$ + @Override + public void run() { + uiActions.doUp(selected); + } + }); + manager.add(new Action("Down", factory.getImageDescriptor("down")) { //$NON-NLS-2$ + @Override + public void run() { + uiActions.doDown(selected); + } + }); + } + + } + /** * Sets the UI for the edition of a new file. * @param configuration the configuration of the new file. @@ -669,7 +893,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ s1 = s2; s2 = tmp; } - + switch (orientation) { default: case PORTRAIT: @@ -680,6 +904,105 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ return new Rectangle(0, 0, s1, s1); } } + + /** + * Renders an Android View described by a {@link ViewElementDescriptor}. + * <p/>This uses the <code>wrap_content</code> mode for both <code>layout_width</code> and + * <code>layout_height</code>, and use the class name for the <code>text</code> attribute. + * @param descriptor the descriptor for the class to render. + * @return an ImageData containing the rendering or <code>null</code> if rendering failed. + */ + public ImageData renderWidget(ViewElementDescriptor descriptor) { + if (mEditedFile == null) { + return null; + } + + IAndroidTarget target = Sdk.getCurrent().getTarget(mEditedFile.getProject()); + if (target == null) { + return null; + } + + AndroidTargetData data = Sdk.getCurrent().getTargetData(target); + if (data == null) { + return null; + } + + LayoutBridge bridge = data.getLayoutBridge(); + + if (bridge.bridge != null) { // bridge can never be null. + ResourceManager resManager = ResourceManager.getInstance(); + + ProjectCallback projectCallback = null; + Map<String, Map<String, IResourceValue>> configuredProjectResources = null; + if (mEditedFile != null) { + ProjectResources projectRes = resManager.getProjectResources( + mEditedFile.getProject()); + projectCallback = new ProjectCallback(bridge.classLoader, + projectRes, mEditedFile.getProject()); + + // get the configured resources for the project + // get the resources of the file's project. + if (mConfiguredProjectRes == null && projectRes != null) { + // make sure they are loaded + projectRes.loadAll(); + + // get the project resource values based on the current config + mConfiguredProjectRes = projectRes.getConfiguredResources(mCurrentConfig); + } + + configuredProjectResources = mConfiguredProjectRes; + } else { + // we absolutely need a Map of configured project resources. + configuredProjectResources = new HashMap<String, Map<String, IResourceValue>>(); + } + + // get the framework resources + Map<String, Map<String, IResourceValue>> frameworkResources = + getConfiguredFrameworkResources(); + + if (configuredProjectResources != null && frameworkResources != null) { + // get the selected theme + int themeIndex = mThemeCombo.getSelectionIndex(); + if (themeIndex != -1) { + String theme = mThemeCombo.getItem(themeIndex); + + // change the string if it's a custom theme to make sure we can + // differentiate them + if (themeIndex >= mPlatformThemeCount) { + theme = "*" + theme; //$NON-NLS-1$ + } + + // Render a single object as described by the ViewElementDescriptor. + WidgetPullParser parser = new WidgetPullParser(descriptor); + ILayoutResult result = bridge.bridge.computeLayout(parser, + null /* projectKey */, + 300 /* width */, 300 /* height */, theme, + configuredProjectResources, frameworkResources, projectCallback, + null /* logger */); + + // update the UiElementNode with the layout info. + if (result.getSuccess() == ILayoutResult.SUCCESS) { + BufferedImage largeImage = result.getImage(); + + // we need to resize it to the actual widget size, and convert it into + // an SWT image object. + int width = result.getRootView().getRight(); + int height = result.getRootView().getBottom(); + Raster raster = largeImage.getData(new java.awt.Rectangle(width, height)); + int[] imageDataBuffer = ((DataBufferInt)raster.getDataBuffer()).getData(); + + ImageData imageData = new ImageData(width, height, 32, + new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF)); + + imageData.setPixels(0, 0, imageDataBuffer.length, imageDataBuffer, 0); + + return imageData; + } + } + } + } + return null; + } /** * Reloads this editor, by getting the new model from the {@link LayoutEditor}. @@ -690,41 +1013,41 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ IEditorInput input = mLayoutEditor.getEditorInput(); setInput(input); - + if (input instanceof FileEditorInput) { FileEditorInput fileInput = (FileEditorInput)input; mEditedFile = fileInput.getFile(); } else { // really this shouldn't happen! Log it in case it happens mEditedFile = null; - EditorsPlugin.log(IStatus.ERROR, "Input is not of type FileEditorInput: %1$s", + AdtPlugin.log(IStatus.ERROR, "Input is not of type FileEditorInput: %1$s", input.toString()); } } - + /** * Update the layout editor when the Xml model is changed. */ void onXmlModelChanged() { GraphicalViewer viewer = getGraphicalViewer(); - + // try to preserve the selection before changing the content SelectionManager selMan = viewer.getSelectionManager(); ISelection selection = selMan.getSelection(); - + try { viewer.setContents(getModel()); } finally { selMan.setSelection(selection); - } - + } + if (mLayoutEditor.isGraphicalEditorActive()) { recomputeLayout(); } else { mNeedsRecompute = true; } } - + /** * Update the UI controls state with a given {@link FolderConfiguration}. * <p/>If a qualifier is not present in the {@link FolderConfiguration} object, the UI control @@ -745,7 +1068,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else if (mCountry.getText().length() > 0) { mCountryIcon.setImage(mWarningImage); } - + mNetworkIcon.setImage(mMatchImage); NetworkCodeQualifier networkQualifier = config.getNetworkCodeQualifier(); if (networkQualifier != null) { @@ -754,7 +1077,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else if (mNetwork.getText().length() > 0) { mNetworkIcon.setImage(mWarningImage); } - + mLanguageIcon.setImage(mMatchImage); LanguageQualifier languageQualifier = config.getLanguageQualifier(); if (languageQualifier != null) { @@ -763,7 +1086,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else if (mLanguage.getText().length() > 0) { mLanguageIcon.setImage(mWarningImage); } - + mRegionIcon.setImage(mMatchImage); RegionQualifier regionQualifier = config.getRegionQualifier(); if (regionQualifier != null) { @@ -772,7 +1095,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else if (mRegion.getText().length() > 0) { mRegionIcon.setImage(mWarningImage); } - + mOrientationIcon.setImage(mMatchImage); ScreenOrientationQualifier orientationQualifier = config.getScreenOrientationQualifier(); if (orientationQualifier != null) { @@ -782,7 +1105,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else if (mOrientation.getSelectionIndex() != 0) { mOrientationIcon.setImage(mWarningImage); } - + mDensityIcon.setImage(mMatchImage); PixelDensityQualifier densityQualifier = config.getPixelDensityQualifier(); if (densityQualifier != null) { @@ -791,7 +1114,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else if (mDensity.getText().length() > 0) { mDensityIcon.setImage(mWarningImage); } - + mTouchIcon.setImage(mMatchImage); TouchScreenQualifier touchQualifier = config.getTouchTypeQualifier(); if (touchQualifier != null) { @@ -800,7 +1123,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else if (mTouch.getSelectionIndex() != 0) { mTouchIcon.setImage(mWarningImage); } - + mKeyboardIcon.setImage(mMatchImage); KeyboardStateQualifier keyboardQualifier = config.getKeyboardStateQualifier(); if (keyboardQualifier != null) { @@ -818,7 +1141,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else if (mTextInput.getSelectionIndex() != 0) { mTextInputIcon.setImage(mWarningImage); } - + mNavigationIcon.setImage(mMatchImage); NavigationMethodQualifier navigationQualifiter = config.getNavigationMethodQualifier(); if (navigationQualifiter != null) { @@ -828,7 +1151,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else if (mNavigation.getSelectionIndex() != 0) { mNavigationIcon.setImage(mWarningImage); } - + mSizeIcon.setImage(mMatchImage); ScreenDimensionQualifier sizeQualifier = config.getScreenDimensionQualifier(); if (sizeQualifier != null) { @@ -838,7 +1161,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else if (mSize1.getText().length() > 0 && mSize2.getText().length() > 0) { mSizeIcon.setImage(mWarningImage); } - + // update the string showing the folder name String current = config.toDisplayString(); mCurrentLayoutLabel.setText(current != null ? current : "(Default)"); @@ -927,6 +1250,10 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ return mLayoutEditor.getUiRootNode(); } + void reloadPalette() { + PaletteFactory.createPaletteRoot(mPaletteRoot, mLayoutEditor.getTargetData()); + } + private void onCountryCodeChange() { // because mCountry triggers onCountryCodeChange at each modification, calling setText() // will trigger notifications, and we don't want that. @@ -936,7 +1263,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ // update the current config String value = mCountry.getText(); - + // empty string, means no qualifier. if (value.length() == 0) { mCurrentConfig.setCountryCodeQualifier(null); @@ -959,7 +1286,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ mCountryIcon.setImage(mErrorImage); } } - + // look for a file to open/create onConfigurationChange(); } @@ -973,7 +1300,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ // update the current config String value = mNetwork.getText(); - + // empty string, means no qualifier. if (value.length() == 0) { mCurrentConfig.setNetworkCodeQualifier(null); @@ -996,7 +1323,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ mNetworkIcon.setImage(mErrorImage); } } - + // look for a file to open/create onConfigurationChange(); } @@ -1013,9 +1340,9 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ // update the current config String value = mLanguage.getText(); - + updateRegionUi(null /* projectResources */, null /* frameworkResources */); - + // empty string, means no qualifier. if (value.length() == 0) { mCurrentConfig.setLanguageQualifier(null); @@ -1034,11 +1361,11 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ mLanguageIcon.setImage(mErrorImage); } } - + // look for a file to open/create onConfigurationChange(); } - + private void onRegionChange() { // because mRegion triggers onRegionChange at each modification, the filling // of the combo with data will trigger notifications, and we don't want that. @@ -1048,7 +1375,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ // update the current config String value = mRegion.getText(); - + // empty string, means no qualifier. if (value.length() == 0) { mCurrentConfig.setRegionQualifier(null); @@ -1067,11 +1394,11 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ mRegionIcon.setImage(mErrorImage); } } - + // look for a file to open/create onConfigurationChange(); } - + private void onOrientationChange() { // update the current config int index = mOrientation.getSelectionIndex(); @@ -1081,11 +1408,11 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else { mCurrentConfig.setScreenOrientationQualifier(null); } - + // look for a file to open/create onConfigurationChange(); } - + private void onDensityChange() { // because mDensity triggers onDensityChange at each modification, calling setText() // will trigger notifications, and we don't want that. @@ -1095,7 +1422,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ // update the current config String value = mDensity.getText(); - + // empty string, means no qualifier. if (value.length() == 0) { mCurrentConfig.setPixelDensityQualifier(null); @@ -1122,7 +1449,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ // look for a file to open/create onConfigurationChange(); } - + private void onTouchChange() { // update the current config int index = mTouch.getSelectionIndex(); @@ -1132,7 +1459,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else { mCurrentConfig.setTouchTypeQualifier(null); } - + // look for a file to open/create onConfigurationChange(); } @@ -1146,11 +1473,11 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else { mCurrentConfig.setKeyboardStateQualifier(null); } - + // look for a file to open/create onConfigurationChange(); } - + private void onTextInputChange() { // update the current config int index = mTextInput.getSelectionIndex(); @@ -1160,11 +1487,11 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else { mCurrentConfig.setTextInputMethodQualifier(null); } - + // look for a file to open/create onConfigurationChange(); } - + private void onNavigationChange() { // update the current config int index = mNavigation.getSelectionIndex(); @@ -1174,11 +1501,11 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } else { mCurrentConfig.setNavigationMethodQualifier(null); } - + // look for a file to open/create onConfigurationChange(); } - + private void onSizeChange() { // because mSize1 and mSize2 trigger onSizeChange at each modification, calling setText() // will trigger notifications, and we don't want that. @@ -1189,7 +1516,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ // update the current config String size1 = mSize1.getText(); String size2 = mSize2.getText(); - + // if only one of the strings is empty, do nothing if ((size1.length() == 0) ^ (size2.length() == 0)) { mSizeIcon.setImage(mErrorImage); @@ -1209,12 +1536,12 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ return; } } - + // look for a file to open/create onConfigurationChange(); } - + /** * Looks for a file matching the new {@link FolderConfiguration} and attempts to open it. * <p/>If there is no match, notify the user. @@ -1237,7 +1564,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ ResourceFolderType.LAYOUT, mCurrentConfig); } - + if (match != null) { if (match.getFile().equals(mEditedFile) == false) { try { @@ -1279,7 +1606,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ showErrorInEditor(message); } } - + private void onThemeChange() { int themeIndex = mThemeCombo.getSelectionIndex(); if (themeIndex != -1) { @@ -1314,10 +1641,10 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ // create the label Label icon = new Label(composite, SWT.NONE); icon.setImage(mMatchImage); - + return icon; } - + /** * Recomputes the layout with the help of layoutlib. */ @@ -1328,7 +1655,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ // return false; if (mEditedFile.exists() == false) { String message = String.format("Resource '%1$s' does not exist.", - mEditedFile.getFullPath().toString()); + mEditedFile.getFullPath().toString()); showErrorInEditor(message); @@ -1339,126 +1666,156 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ if (mEditedFile.isSynchronized(IResource.DEPTH_ZERO) == false) { String message = String.format("%1$s is out of sync. Please refresh.", - mEditedFile.getName()); + mEditedFile.getName()); showErrorInEditor(message); // also print it in the error console. - EditorsPlugin.printErrorToConsole(iProject.getName(), message); - return; - } - - // check there is actually a model (maybe the file is empty). - UiDocumentNode model = getModel(); - - if (model.getUiChildren().size() == 0) { - showErrorInEditor("No Xml content. Go to the Outline view and add nodes."); + AdtPlugin.printErrorToConsole(iProject.getName(), message); return; } - EditorsPlugin plugin = EditorsPlugin.getDefault(); - LayoutBridge bridge = plugin.getLayoutBridge(); - - if (bridge.bridge != null) { // bridge can never be null. - ResourceManager resManager = ResourceManager.getInstance(); - - ProjectResources projectRes = resManager.getProjectResources(iProject); - if (projectRes == null) { + Sdk currentSdk = Sdk.getCurrent(); + if (currentSdk != null) { + IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject()); + if (target == null) { + showErrorInEditor("The project target is not set."); + return; + } + + AndroidTargetData data = currentSdk.getTargetData(target); + if (data == null) { + // It can happen that the workspace refreshes while the SDK is loading its + // data, which could trigger a redraw of the opened layout if some resources + // changed while Eclipse is closed. + // In this case data could be null, but this is not an error. + // We can just silently return, as all the opened editors are automatically + // refreshed once the SDK finishes loading. + if (AdtPlugin.getDefault().getSdkLoadStatus(null) != LoadStatus.LOADING) { + showErrorInEditor(String.format( + "The project target (%s) was not properly loaded.", + target.getName())); + } return; } - // get the resources of the file's project. - if (mConfiguredProjectRes == null) { - // make sure they are loaded - projectRes.loadAll(); + // check there is actually a model (maybe the file is empty). + UiDocumentNode model = getModel(); - // get the project resource values based on the current config - mConfiguredProjectRes = projectRes.getConfiguredResources(mCurrentConfig); + if (model.getUiChildren().size() == 0) { + showErrorInEditor("No Xml content. Go to the Outline view and add nodes."); + return; } - // get the framework resources - Map<String, Map<String, IResourceValue>> frameworkResources = - getConfiguredFrameworkResources(); + LayoutBridge bridge = data.getLayoutBridge(); - if (mConfiguredProjectRes != null && frameworkResources != null) { - if (mProjectCallback == null) { - mProjectCallback = new ProjectCallback( - plugin.getLayoutlibBridgeClassLoader(), projectRes, iProject); + if (bridge.bridge != null) { // bridge can never be null. + ResourceManager resManager = ResourceManager.getInstance(); + + ProjectResources projectRes = resManager.getProjectResources(iProject); + if (projectRes == null) { + return; } - - if (mLogger == null) { - mLogger = new ILayoutLog() { - public void error(String message) { - EditorsPlugin.printErrorToConsole(mEditedFile.getName(), message); - } - - public void error(Throwable error) { - String message = error.getMessage(); - if (message == null) { - message = error.getClass().getName(); - } - - PrintStream ps = new PrintStream(EditorsPlugin.getErrorStream()); - error.printStackTrace(ps); - } - - public void warning(String message) { - EditorsPlugin.printToConsole(mEditedFile.getName(), message); - } - }; + + // get the resources of the file's project. + if (mConfiguredProjectRes == null) { + // make sure they are loaded + projectRes.loadAll(); + + // get the project resource values based on the current config + mConfiguredProjectRes = projectRes.getConfiguredResources(mCurrentConfig); } - - // get the selected theme - int themeIndex = mThemeCombo.getSelectionIndex(); - if (themeIndex != -1) { - String theme = mThemeCombo.getItem(themeIndex); - - // change the string if it's a custom theme to make sure we can - // differentiate them - if (themeIndex >= mPlatformThemeCount) { - theme = "*" + theme; //$NON-NLS-1$ + + // get the framework resources + Map<String, Map<String, IResourceValue>> frameworkResources = + getConfiguredFrameworkResources(); + + if (mConfiguredProjectRes != null && frameworkResources != null) { + if (mProjectCallback == null) { + mProjectCallback = new ProjectCallback( + bridge.classLoader, projectRes, iProject); } - - // Compute the layout - UiElementPullParser parser = new UiElementPullParser(getModel()); - Rectangle rect = getBounds(); - ILayoutResult result = bridge.bridge.computeLayout(parser, iProject, - rect.width, rect.height, theme, - mConfiguredProjectRes, frameworkResources, mProjectCallback, - mLogger); - - // update the UiElementNode with the layout info. - if (result.getSuccess() == ILayoutResult.SUCCESS) { - model.setEditData(result.getImage()); - - updateNodeWithBounds(result.getRootView()); - } else { - String message = result.getErrorMessage(); - - // Reset the edit data for all the nodes. - resetNodeBounds(model); - - if (message != null) { - // set the error in the top element. - model.setEditData(message); + + if (mLogger == null) { + mLogger = new ILayoutLog() { + public void error(String message) { + AdtPlugin.printErrorToConsole(mEditedFile.getName(), message); + } + + public void error(Throwable error) { + String message = error.getMessage(); + if (message == null) { + message = error.getClass().getName(); + } + + PrintStream ps = new PrintStream(AdtPlugin.getErrorStream()); + error.printStackTrace(ps); + } + + public void warning(String message) { + AdtPlugin.printToConsole(mEditedFile.getName(), message); + } + }; + } + + // get the selected theme + int themeIndex = mThemeCombo.getSelectionIndex(); + if (themeIndex != -1) { + String theme = mThemeCombo.getItem(themeIndex); + + // change the string if it's a custom theme to make sure we can + // differentiate them + if (themeIndex >= mPlatformThemeCount) { + theme = "*" + theme; //$NON-NLS-1$ } + + // Compute the layout + UiElementPullParser parser = new UiElementPullParser(getModel()); + Rectangle rect = getBounds(); + ILayoutResult result = bridge.bridge.computeLayout(parser, + iProject /* projectKey */, + rect.width, rect.height, theme, + mConfiguredProjectRes, frameworkResources, mProjectCallback, + mLogger); + + // update the UiElementNode with the layout info. + if (result.getSuccess() == ILayoutResult.SUCCESS) { + model.setEditData(result.getImage()); + + updateNodeWithBounds(result.getRootView()); + } else { + String message = result.getErrorMessage(); + + // Reset the edit data for all the nodes. + resetNodeBounds(model); + + if (message != null) { + // set the error in the top element. + model.setEditData(message); + } + } + + model.refreshUi(); } - - model.refreshUi(); } + } else { + // SDK is loaded but not the layout library! + String message = null; + // check whether the bridge managed to load, or not + if (bridge.status == LoadStatus.LOADING) { + message = String.format( + "Eclipse is loading framework information and the Layout library from the SDK folder.\n%1$s will refresh automatically once the process is finished.", + mEditedFile.getName()); + } else { + message = String.format("Eclipse failed to load the framework information and the Layout library!"); + } + showErrorInEditor(message); } } else { - String message = null; + String message = String.format( + "Eclipse is loading the SDK.\n%1$s will refresh automatically once the process is finished.", + mEditedFile.getName()); - // check whether the bridge managed to load, or not - if (bridge.status == LayoutBridge.LoadStatus.LOADING) { - message = String.format( - "Eclipse is loading framework information and the Layout library from the SDK folder.\n%1$s will refresh automatically once the process is finished.", - mEditedFile.getName()); - } else { - message = String.format("Eclipse failed to load the framework information and the Layout library!"); - } - showErrorInEditor(message); } } finally { @@ -1482,10 +1839,10 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ model.refreshUi(); } - + private void resetNodeBounds(UiElementNode node) { node.setEditData(null); - + List<UiElementNode> children = node.getUiChildren(); for (UiElementNode child : children) { resetNodeBounds(child); @@ -1499,10 +1856,10 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ if (viewKey instanceof UiElementNode) { Rectangle bounds = new Rectangle(r.getLeft(), r.getTop(), r.getRight()-r.getLeft(), r.getBottom() - r.getTop()); - + ((UiElementNode)viewKey).setEditData(bounds); } - + // and then its children. ILayoutViewInfo[] children = r.getChildren(); if (children != null) { @@ -1512,11 +1869,11 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } } } - + /* * (non-Javadoc) * @see com.android.ide.eclipse.editors.layout.LayoutReloadMonitor.ILayoutReloadListener#reloadLayout(boolean, boolean, boolean) - * + * * Called when the file changes triggered a redraw of the layout */ public void reloadLayout(boolean codeChange, boolean rChange, boolean resChange) { @@ -1525,19 +1882,25 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ if (resChange) { recompute = true; - // TODO: differentiate between single and multi resource file changed, and whether the resource change affects the cache. + // TODO: differentiate between single and multi resource file changed, and whether the resource change affects the cache. // force a reparse in case a value XML file changed. mConfiguredProjectRes = null; - - // clear the cache in the bridge in case a bitmap/9-patch changed. - EditorsPlugin plugin = EditorsPlugin.getDefault(); - LayoutBridge bridge = plugin.getLayoutBridge(); - if (bridge.bridge != null) { - bridge.bridge.clearCaches(mEditedFile.getProject()); + // clear the cache in the bridge in case a bitmap/9-patch changed. + IAndroidTarget target = Sdk.getCurrent().getTarget(mEditedFile.getProject()); + if (target != null) { + + AndroidTargetData data = Sdk.getCurrent().getTargetData(target); + if (data != null) { + LayoutBridge bridge = data.getLayoutBridge(); + + if (bridge.bridge != null) { + bridge.bridge.clearCaches(mEditedFile.getProject()); + } + } } - + mParent.getDisplay().asyncExec(mUiUpdateFromResourcesRunnable); } @@ -1548,7 +1911,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ recompute = true; } } - + if (recompute) { mParent.getDisplay().asyncExec(mConditionalRecomputeRunnable); } @@ -1558,11 +1921,19 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ * Responds to a page change that made the Graphical editor page the activated page. */ void activated() { + mActive = true; if (mNeedsRecompute) { recomputeLayout(); } } - + + /** + * Responds to a page change that made the Graphical editor page the deactivated page + */ + void deactivated() { + mActive = false; + } + /** * Updates the UI from values in the resources, such as languages, regions, themes, etc... * This must be called from the UI thread. @@ -1570,8 +1941,8 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ private void updateUIFromResources() { ResourceManager manager = ResourceManager.getInstance(); - - ProjectResources frameworkProject = manager.getFrameworkResources(); + + ProjectResources frameworkProject = getFrameworkResources(); mDisableUpdates = true; @@ -1615,7 +1986,6 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ mPlatformThemeCount = themes.size(); themes.clear(); } - // now get the languages from the framework. Set<String> frameworkLanguages = frameworkProject.getLanguages(); if (frameworkLanguages != null) { @@ -1627,7 +1997,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ ProjectResources project = null; if (mEditedFile != null) { project = manager.getProjectResources(mEditedFile.getProject()); - + // in cases where the opened file is not linked to a project, this could be null. if (project != null) { // get the configured resources for the project @@ -1706,7 +2076,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ * This is done by making sure the parent is a theme. * @param value the style to check * @param styleMap the map of styles for the current project. Key is the style name. - * @return + * @return True if the given <var>style</var> is a theme. */ private boolean isTheme(IResourceValue value, Map<String, IResourceValue> styleMap) { if (value instanceof IStyleResourceValue) { @@ -1767,15 +2137,15 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ projectResources = ResourceManager.getInstance().getProjectResources( mEditedFile.getProject()); } - + if (frameworkResources == null) { - frameworkResources = ResourceManager.getInstance().getFrameworkResources(); + frameworkResources = getFrameworkResources(); } - + String currentLanguage = mLanguage.getText(); - + Set<String> set = null; - + if (projectResources != null) { set = projectResources.getRegions(currentLanguage); } @@ -1803,10 +2173,10 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ private Map<String, Map<String, IResourceValue>> getConfiguredFrameworkResources() { if (mConfiguredFrameworkRes == null) { - ProjectResources frameworkRes = ResourceManager.getInstance().getFrameworkResources(); + ProjectResources frameworkRes = getFrameworkResources(); if (frameworkRes == null) { - EditorsPlugin.log(IStatus.ERROR, "Failed to get ProjectResource for the framework"); + AdtPlugin.log(IStatus.ERROR, "Failed to get ProjectResource for the framework"); } // get the framework resource values based on the current config @@ -1821,19 +2191,19 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ * The synchronizer can be used to sync the selection of 2 or more EditPartViewers. * <p/> * This is changed from protected to public so that the outline can use it. - * + * * @return the synchronizer */ @Override public SelectionSynchronizer getSelectionSynchronizer() { return super.getSelectionSynchronizer(); } - + /** * Returns the edit domain. * <p/> * This is changed from protected to public so that the outline can use it. - * + * * @return the edit domain */ @Override @@ -1865,10 +2235,9 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ String message = String.format("File 'res/%1$s' is in the way!", folderName); - EditorsPlugin.displayError("Layout Creation", message); + AdtPlugin.displayError("Layout Creation", message); - return new Status(IStatus.ERROR, - AndroidConstants.EDITORS_PLUGIN_ID, message); + return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, message); } else if (newLayoutFolder.exists() == false) { // create it. newLayoutFolder.mkdir(); @@ -1903,7 +2272,7 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ mParent.getDisplay().asyncExec(new Runnable() { public void run() { onConfigurationChange(); - }; + } }); } @@ -1941,16 +2310,16 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ "Failed to create File 'res/%1$s/%2$s' : %3$s", folderName, mEditedFile.getName(), e2.getMessage()); - EditorsPlugin.displayError("Layout Creation", message); + AdtPlugin.displayError("Layout Creation", message); - return new Status(IStatus.ERROR, AndroidConstants.EDITORS_PLUGIN_ID, + return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, message, e2); } catch (CoreException e2) { String message = String.format( "Failed to create File 'res/%1$s/%2$s' : %3$s", folderName, mEditedFile.getName(), e2.getMessage()); - EditorsPlugin.displayError("Layout Creation", message); + AdtPlugin.displayError("Layout Creation", message); return e2.getStatus(); } @@ -1960,4 +2329,27 @@ public class GraphicalLayoutEditor extends GraphicalEditor/*WithPalette*/ } }.schedule(); } + + /** + * Returns a {@link ProjectResources} for the framework resources. + * @return the framework resources or null if not found. + */ + private ProjectResources getFrameworkResources() { + if (mEditedFile != null) { + Sdk currentSdk = Sdk.getCurrent(); + if (currentSdk != null) { + IAndroidTarget target = currentSdk.getTarget(mEditedFile.getProject()); + + if (target != null) { + AndroidTargetData data = currentSdk.getTargetData(target); + + if (data != null) { + return data.getFrameworkResources(); + } + } + } + } + + return null; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutConstants.java new file mode 100644 index 0000000..d4ec5e1 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutConstants.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.editors.layout; + +/** + * A bunch of constants that map to either: + * <ul> + * <li>Android Layouts XML element names (Linear, Relative, Absolute, etc.) + * <li>Attributes for layout XML elements. + * <li>Values for attributes. + * </ul> + */ +public class LayoutConstants { + + public static final String RELATIVE_LAYOUT = "RelativeLayout"; //$NON-NLS-1$ + public static final String LINEAR_LAYOUT = "LinearLayout"; //$NON-NLS-1$ + public static final String ABSOLUTE_LAYOUT = "AbsoluteLayout"; //$NON-NLS-1$ + + public static final String ATTR_TEXT = "text"; //$NON-NLS-1$ + public static final String ATTR_ID = "id"; //$NON-NLS-1$ + + public static final String ATTR_LAYOUT_HEIGHT = "layout_height"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_WIDTH = "layout_width"; //$NON-NLS-1$ + + public static final String ATTR_LAYOUT_ALIGN_PARENT_TOP = "layout_alignParentTop"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_PARENT_BOTTOM = "layout_alignParentBottom"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_PARENT_LEFT = "layout_alignParentLeft";//$NON-NLS-1$ + public static final String ATTR_LAYOUT_ALIGN_PARENT_RIGHT = "layout_alignParentRight"; //$NON-NLS-1$ + + public static final String ATTR_LAYOUT_ALIGN_BASELINE = "layout_alignBaseline"; //$NON-NLS-1$ + + public static final String ATTR_LAYOUT_CENTER_VERTICAL = "layout_centerVertical"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_CENTER_HORIZONTAL = "layout_centerHorizontal"; //$NON-NLS-1$ + + public static final String ATTR_LAYOUT_TO_RIGHT_OF = "layout_toRightOf"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_TO_LEFT_OF = "layout_toLeftOf"; //$NON-NLS-1$ + + public static final String ATTR_LAYOUT_BELOW = "layout_below"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_ABOVE = "layout_above"; //$NON-NLS-1$ + + public static final String ATTR_LAYOUT_Y = "layout_y"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_X = "layout_x"; //$NON-NLS-1$ + + public static final String VALUE_WRAP_CONTENT = "wrap_content"; //$NON-NLS-1$ + public static final String VALUE_FILL_PARENT = "fill_parent"; //$NON-NLS-1$ + public static final String VALUE_TRUE = "true"; //$NON-NLS-1$ + public static final String VALUE_N_DIP = "%ddip"; //$NON-NLS-1$ + + private LayoutConstants() { + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/LayoutContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutContentAssist.java index dfec17a..9f39495 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/LayoutContentAssist.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutContentAssist.java @@ -16,8 +16,8 @@ package com.android.ide.eclipse.editors.layout; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; import com.android.ide.eclipse.editors.AndroidContentAssist; -import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors; /** * Content Assist Processor for /res/layout XML files @@ -28,6 +28,6 @@ class LayoutContentAssist extends AndroidContentAssist { * Constructor for LayoutContentAssist */ public LayoutContentAssist() { - super(LayoutDescriptors.getInstance().getDescriptor().getChildren()); + super(AndroidTargetData.DESCRIPTOR_LAYOUT); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java index c4a8f5c..c4a8f5c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java index f1bedcb..880ee2b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java @@ -16,14 +16,17 @@ package com.android.ide.eclipse.editors.layout; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; +import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.EclipseUiHelper; import com.android.ide.eclipse.editors.AndroidEditor; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor; -import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors; import com.android.ide.eclipse.editors.resources.manager.ResourceFolder; import com.android.ide.eclipse.editors.resources.manager.ResourceManager; +import com.android.ide.eclipse.editors.ui.tree.UiActions; import com.android.ide.eclipse.editors.uimodel.UiDocumentNode; +import com.android.ide.eclipse.editors.uimodel.UiElementNode; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.IProgressMonitor; @@ -47,12 +50,10 @@ import org.w3c.dom.Document; */ public class LayoutEditor extends AndroidEditor implements IShowEditorInput, IPartListener { - public static final String ID = "com.android.ide.eclipse.editors.layout.LayoutEditor"; //$NON-NLS-1$ + public static final String ID = AndroidConstants.EDITORS_NAMESPACE + ".layout.LayoutEditor"; //$NON-NLS-1$ /** Root node of the UI element hierarchy */ private UiDocumentNode mUiRootNode; - /** Listener to update the root node if the resource framework changes */ - private Runnable mResourceRefreshListener; private GraphicalLayoutEditor mGraphicalEditor; private int mGraphicalEditorIndex; @@ -61,13 +62,13 @@ public class LayoutEditor extends AndroidEditor implements IShowEditorInput, IPa /** Custom implementation of {@link IPropertySheetPage} for this editor */ private UiPropertySheetPage mPropertyPage; + private UiEditorActions mUiEditorActions; /** * Creates the form editor for resources XML files. */ public LayoutEditor() { super(); - initUiRootNode(); } /** @@ -82,10 +83,6 @@ public class LayoutEditor extends AndroidEditor implements IShowEditorInput, IPa @Override public void dispose() { - if (mResourceRefreshListener != null) { - EditorsPlugin.getDefault().removeResourceChangedListener(mResourceRefreshListener); - mResourceRefreshListener = null; - } getSite().getPage().removePartListener(this); super.dispose(); @@ -155,7 +152,7 @@ public class LayoutEditor extends AndroidEditor implements IShowEditorInput, IPa getSite().getPage().addPartListener(this); } } catch (PartInitException e) { - EditorsPlugin.log(e, "Error creating nested page"); //$NON-NLS-1$ + AdtPlugin.log(e, "Error creating nested page"); //$NON-NLS-1$ } } @@ -222,6 +219,9 @@ public class LayoutEditor extends AndroidEditor implements IShowEditorInput, IPa */ @Override protected void xmlModelChanged(Document xml_doc) { + // init the ui root on demand + initUiRootNode(false /*force*/); + mUiRootNode.loadFromXmlNode(xml_doc); // update the model first, since it is used by the viewers. @@ -293,7 +293,11 @@ public class LayoutEditor extends AndroidEditor implements IShowEditorInput, IPa } public void partDeactivated(IWorkbenchPart part) { - // pass + if (part == this) { + if (mGraphicalEditor != null && getActivePage() == mGraphicalEditorIndex) { + mGraphicalEditor.deactivated(); + } + } } public void partOpened(IWorkbenchPart part) { @@ -301,6 +305,32 @@ public class LayoutEditor extends AndroidEditor implements IShowEditorInput, IPa EclipseUiHelper.showView(EclipseUiHelper.PROPERTY_SHEET_VIEW_ID, false /* activate */); } + public class UiEditorActions extends UiActions { + + @Override + protected UiDocumentNode getRootNode() { + return mUiRootNode; + } + + // Select the new item + @Override + protected void selectUiNode(UiElementNode uiNodeToSelect) { + mGraphicalEditor.selectModel(uiNodeToSelect); + } + + @Override + public void commitPendingXmlChanges() { + // Pass. There is nothing to commit before the XML is changed here. + } + } + + public UiEditorActions getUiEditorActions() { + if (mUiEditorActions == null) { + mUiEditorActions = new UiEditorActions(); + } + return mUiEditorActions; + } + // ---- Local Methods ---- /** @@ -321,38 +351,50 @@ public class LayoutEditor extends AndroidEditor implements IShowEditorInput, IPa return false; } - /** - * Creates the initial UI Root Node, including the known mandatory elements. - */ - private void initUiRootNode() { + @Override + protected void initUiRootNode(boolean force) { // The root UI node is always created, even if there's no corresponding XML node. - if (mUiRootNode == null) { - DocumentDescriptor desc = LayoutDescriptors.getInstance().getDescriptor(); + if (mUiRootNode == null || force) { + // get the target data from the opened file (and its project) + AndroidTargetData data = getTargetData(); + + Document doc = null; + if (mUiRootNode != null) { + doc = mUiRootNode.getXmlDocument(); + } + + DocumentDescriptor desc; + if (data == null) { + desc = new DocumentDescriptor("temp", null /*children*/); + } else { + desc = data.getLayoutDescriptors().getDescriptor(); + } + + // get the descriptors from the data. mUiRootNode = (UiDocumentNode) desc.createUiNode(); mUiRootNode.setEditor(this); - // Add a listener to refresh the root node if the resource framework changes - // by forcing it to parse its own XML - mResourceRefreshListener = new Runnable() { - public void run() { - commitPages(false /* onSave */); - - mUiRootNode.reloadFromXmlNode(mUiRootNode.getXmlDocument()); - - if (mOutline != null) { - mOutline.reloadModel(); - } - - if (mGraphicalEditor != null) { - mGraphicalEditor.recomputeLayout(); - } - } - }; - EditorsPlugin.getDefault().addResourceChangedListener(mResourceRefreshListener); - mResourceRefreshListener.run(); + onDescriptorsChanged(doc); } } + private void onDescriptorsChanged(Document document) { + if (document != null) { + mUiRootNode.loadFromXmlNode(document); + } else { + mUiRootNode.reloadFromXmlNode(mUiRootNode.getXmlDocument()); + } + + if (mOutline != null) { + mOutline.reloadModel(); + } + + if (mGraphicalEditor != null) { + mGraphicalEditor.reloadEditor(); + mGraphicalEditor.reloadPalette(); + mGraphicalEditor.recomputeLayout(); + } + } /** * Handles a new input, and update the part name. diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/LayoutReloadMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutReloadMonitor.java index cf20288..cf20288 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/LayoutReloadMonitor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutReloadMonitor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/LayoutSourceViewerConfig.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutSourceViewerConfig.java index 1aa1f4c..1aa1f4c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/LayoutSourceViewerConfig.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutSourceViewerConfig.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/MatchingStrategy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/MatchingStrategy.java index bb075c2..bb075c2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/MatchingStrategy.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/MatchingStrategy.java diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/PaletteFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/PaletteFactory.java new file mode 100644 index 0000000..94df28f --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/PaletteFactory.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.editors.layout; + +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; +import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; + +import org.eclipse.gef.palette.PaletteDrawer; +import org.eclipse.gef.palette.PaletteGroup; +import org.eclipse.gef.palette.PaletteRoot; +import org.eclipse.gef.palette.PaletteTemplateEntry; + +import java.util.List; + +/** + * Factory that creates the palette for the {@link GraphicalLayoutEditor}. + */ +public class PaletteFactory { + + /** Static factory, nothing to instantiate here. */ + private PaletteFactory() { + } + + public static PaletteRoot createPaletteRoot(PaletteRoot currentPalette, + AndroidTargetData targetData) { + + if (currentPalette == null) { + currentPalette = new PaletteRoot(); + } + + for (int n = currentPalette.getChildren().size() - 1; n >= 0; n--) { + currentPalette.getChildren().remove(n); + } + + if (targetData != null) { + addTools(currentPalette); + addViews(currentPalette, "Layouts", + targetData.getLayoutDescriptors().getLayoutDescriptors()); + addViews(currentPalette, "Views", + targetData.getLayoutDescriptors().getViewDescriptors()); + } + + return currentPalette; + } + + private static void addTools(PaletteRoot paletteRoot) { + PaletteGroup group = new PaletteGroup("Tools"); + + // Default tools: selection. + // Do not use the MarqueeToolEntry since we don't support multiple selection. + /* -- Do not put the selection tool. It's the unique tool so it looks useless. + Leave this piece of code here in case we want it back later. + PanningSelectionToolEntry entry = new PanningSelectionToolEntry(); + group.add(entry); + paletteRoot.setDefaultEntry(entry); + */ + + paletteRoot.add(group); + } + + private static void addViews(PaletteRoot paletteRoot, String groupName, + List<ElementDescriptor> descriptors) { + PaletteDrawer group = new PaletteDrawer(groupName); + + for (ElementDescriptor desc : descriptors) { + PaletteTemplateEntry entry = new PaletteTemplateEntry( + desc.getUiName(), // label + desc.getTooltip(), // short description + desc, // template + desc.getImageDescriptor(), // small icon + desc.getImageDescriptor() // large icon + ); + + group.add(entry); + } + + paletteRoot.add(group); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/ProjectCallback.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/ProjectCallback.java index d91ecbc..81fd2ed 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/ProjectCallback.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/ProjectCallback.java @@ -16,6 +16,7 @@ package com.android.ide.eclipse.editors.layout; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestHelper; import com.android.ide.eclipse.editors.resources.manager.ProjectClassLoader; @@ -65,12 +66,17 @@ public final class ProjectCallback implements IProjectCallback { // load the class. ProjectClassLoader loader = new ProjectClassLoader(mParentClassLoader, mProject); - clazz = loader.loadClass(className); - - if (clazz != null) { - mUsed = true; - mLoadedClasses.put(className, clazz); - return instantiateClass(clazz, constructorSignature, constructorParameters); + try { + clazz = loader.loadClass(className); + + if (clazz != null) { + mUsed = true; + mLoadedClasses.put(className, clazz); + return instantiateClass(clazz, constructorSignature, constructorParameters); + } + } catch (Error e) { + // Log this error with the class name we're trying to load and abort. + AdtPlugin.log(e, "ProjectCallback.loadView failed to find class %1$s", className); //$NON-NLS-1$ } return null; @@ -142,7 +148,7 @@ public final class ProjectCallback implements IProjectCallback { * @param clazz the class to instantiate * @param constructorSignature the signature of the constructor to use * @param constructorParameters the parameters to use in the constructor. - * @return + * @return A new class object, created using a specific constructor and parameters. * @throws Exception */ @SuppressWarnings("unchecked") diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java index 05f8370..3e0f5d8 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java @@ -60,7 +60,10 @@ import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.IActionBars; +import java.util.ArrayList; +import java.util.Iterator; import java.util.LinkedList; +import java.util.List; /** * Implementation of the {@link ContentOutlinePage} to display {@link UiElementNode}. @@ -84,7 +87,8 @@ class UiContentOutlinePage extends ContentOutlinePage { mAddAction = new Action("Add...") { @Override public void run() { - UiElementNode node = getModelSelection(); + List<UiElementNode> nodes = getModelSelections(); + UiElementNode node = nodes != null && nodes.size() > 0 ? nodes.get(0) : null; mUiActions.doAdd(node, viewer.getControl().getShell()); } @@ -95,9 +99,9 @@ class UiContentOutlinePage extends ContentOutlinePage { mDeleteAction = new Action("Remove...") { @Override public void run() { - UiElementNode node = getModelSelection(); + List<UiElementNode> nodes = getModelSelections(); - mUiActions.doRemove(node, viewer.getControl().getShell()); + mUiActions.doRemove(nodes, viewer.getControl().getShell()); } }; mDeleteAction.setToolTipText("Removes an existing selected element."); @@ -106,9 +110,9 @@ class UiContentOutlinePage extends ContentOutlinePage { mUpAction = new Action("Up") { @Override public void run() { - UiElementNode node = getModelSelection(); + List<UiElementNode> nodes = getModelSelections(); - mUiActions.doUp(node); + mUiActions.doUp(nodes); } }; mUpAction.setToolTipText("Moves the selected element up"); @@ -117,9 +121,9 @@ class UiContentOutlinePage extends ContentOutlinePage { mDownAction = new Action("Down") { @Override public void run() { - UiElementNode node = getModelSelection(); + List<UiElementNode> nodes = getModelSelections(); - mUiActions.doDown(node); + mUiActions.doDown(nodes); } }; mDownAction.setToolTipText("Moves the selected element down"); @@ -250,7 +254,7 @@ class UiContentOutlinePage extends ContentOutlinePage { * the necessary actions. */ public void menuAboutToShow(IMenuManager manager) { - UiElementNode selected = getModelSelection(); + List<UiElementNode> selected = getModelSelections(); if (selected != null) { doCreateMenuAction(manager, selected); @@ -269,49 +273,74 @@ class UiContentOutlinePage extends ContentOutlinePage { * the tree view. * * @param manager The context menu manager - * @param ui_node The UI node selected in the tree. Can be null, in which case the root + * @param selected The UI node selected in the tree. Can be null, in which case the root * is to be modified. */ - private void doCreateMenuAction(IMenuManager manager, UiElementNode ui_node) { - if (ui_node != null && ui_node.getXmlNode() != null) { - manager.add(new CopyCutAction(mEditor.getLayoutEditor(), mEditor.getClipboard(), - null, ui_node, true /* cut */)); - manager.add(new CopyCutAction(mEditor.getLayoutEditor(), mEditor.getClipboard(), - null, ui_node, false /* cut */)); - // Paste is not valid if it would add a second element on a terminal element - // which parent is a document -- an XML document can only have one child. This - // means paste is valid if the current UI node can have children or if the parent - // is not a document. - UiElementNode ui_root = ui_node.getUiRoot(); - if (ui_root.getDescriptor().hasChildren() || - !(ui_root.getUiParent() instanceof UiDocumentNode)) { - manager.add(new PasteAction(mEditor.getLayoutEditor(), mEditor.getClipboard(), - ui_node)); + private void doCreateMenuAction(IMenuManager manager, List<UiElementNode> selected) { + + if (selected != null) { + boolean hasXml = false; + for (UiElementNode uiNode : selected) { + if (uiNode.getXmlNode() != null) { + hasXml = true; + break; + } + } + + if (hasXml) { + manager.add(new CopyCutAction(mEditor.getLayoutEditor(), mEditor.getClipboard(), + null, selected, true /* cut */)); + manager.add(new CopyCutAction(mEditor.getLayoutEditor(), mEditor.getClipboard(), + null, selected, false /* cut */)); + + // Can't paste with more than one element selected (the selection is the target) + if (selected.size() <= 1) { + // Paste is not valid if it would add a second element on a terminal element + // which parent is a document -- an XML document can only have one child. This + // means paste is valid if the current UI node can have children or if the parent + // is not a document. + UiElementNode ui_root = selected.get(0).getUiRoot(); + if (ui_root.getDescriptor().hasChildren() || + !(ui_root.getUiParent() instanceof UiDocumentNode)) { + manager.add(new PasteAction(mEditor.getLayoutEditor(), + mEditor.getClipboard(), + selected.get(0))); + } + } + manager.add(new Separator()); } - manager.add(new Separator()); } // Append "add" and "remove" actions. They do the same thing as the add/remove // buttons on the side. - manager.add(mAddAction); - manager.add(mDeleteAction); + // + // "Add" makes sense only if there's 0 or 1 item selected since the + // one selected item becomes the target. + if (selected == null || selected.size() <= 1) { + manager.add(mAddAction); + } - manager.add(new Separator()); - - manager.add(mUpAction); - manager.add(mDownAction); - - manager.add(new Separator()); - - Action propertiesAction = new Action("Properties") { - @Override - public void run() { - EclipseUiHelper.showView(EclipseUiHelper.PROPERTY_SHEET_VIEW_ID, - true /* activate */); - } - }; - propertiesAction.setToolTipText("Displays properties of the selected element."); - manager.add(propertiesAction); + if (selected != null) { + manager.add(mDeleteAction); + manager.add(new Separator()); + + manager.add(mUpAction); + manager.add(mDownAction); + } + + if (selected != null && selected.size() == 1) { + manager.add(new Separator()); + + Action propertiesAction = new Action("Properties") { + @Override + public void run() { + EclipseUiHelper.showView(EclipseUiHelper.PROPERTY_SHEET_VIEW_ID, + true /* activate */); + } + }; + propertiesAction.setToolTipText("Displays properties of the selected element."); + manager.add(propertiesAction); + } } /** @@ -321,18 +350,20 @@ class UiContentOutlinePage extends ContentOutlinePage { */ public void reloadModel() { // Attemps to preserve the UiNode selection, if any - UiElementNode uiNode = null; + List<UiElementNode> uiNodes = null; try { // get current selection using the model rather than the edit part as // reloading the content may change the actual edit part. - uiNode = getModelSelection(); + uiNodes = getModelSelections(); // perform the update getViewer().setContents(mEditor.getModel()); } finally { // restore selection - setModelSelection(uiNode); + if (uiNodes != null) { + setModelSelection(uiNodes.get(0)); + } } } @@ -344,17 +375,24 @@ class UiContentOutlinePage extends ContentOutlinePage { * When there is no actual selection, this might still return the root node, * which is of type {@link UiDocumentTreeEditPart}. */ - private UiElementTreeEditPart getViewerSelection() { + @SuppressWarnings("unchecked") + private List<UiElementTreeEditPart> getViewerSelections() { ISelection selection = getSelection(); if (selection instanceof StructuredSelection) { StructuredSelection structuredSelection = (StructuredSelection)selection; - if (structuredSelection.size() == 1) { - Object selectedObj = structuredSelection.getFirstElement(); + if (structuredSelection.size() > 0) { + ArrayList<UiElementTreeEditPart> selected = new ArrayList<UiElementTreeEditPart>(); - if (selectedObj instanceof UiElementTreeEditPart) { - return (UiElementTreeEditPart) selectedObj; + for (Iterator it = structuredSelection.iterator(); it.hasNext(); ) { + Object selectedObj = it.next(); + + if (selectedObj instanceof UiElementTreeEditPart) { + selected.add((UiElementTreeEditPart) selectedObj); + } } + + return selected.size() > 0 ? selected : null; } } @@ -368,13 +406,22 @@ class UiContentOutlinePage extends ContentOutlinePage { * Returns null if there is no selection or if the implicit root is "selected" * (which actually represents the lack of a real element selection.) */ - private UiElementNode getModelSelection() { + private List<UiElementNode> getModelSelections() { - UiElementTreeEditPart part = getViewerSelection(); + List<UiElementTreeEditPart> parts = getViewerSelections(); - if (part instanceof UiViewTreeEditPart || part instanceof UiLayoutTreeEditPart) { - return (UiElementNode) part.getModel(); + if (parts != null) { + ArrayList<UiElementNode> selected = new ArrayList<UiElementNode>(); + + for (UiElementTreeEditPart part : parts) { + if (part instanceof UiViewTreeEditPart || part instanceof UiLayoutTreeEditPart) { + selected.add((UiElementNode) part.getModel()); + } + } + + return selected.size() > 0 ? selected : null; } + return null; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiElementPullParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiElementPullParser.java new file mode 100644 index 0000000..b0e6fdb --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiElementPullParser.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.editors.layout; + +import com.android.ide.eclipse.editors.uimodel.UiElementNode; +import com.android.layoutlib.api.IXmlPullParser; + +import org.w3c.dom.Node; +import org.xmlpull.v1.XmlPullParserException; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@link IXmlPullParser} implementation on top of {@link UiElementNode}. + * <p/>It's designed to work on layout files, and will most likely not work on other resource + * files. + */ +public final class UiElementPullParser extends BasePullParser { + + private final ArrayList<UiElementNode> mNodeStack = new ArrayList<UiElementNode>(); + private UiElementNode mRoot; + + public UiElementPullParser(UiElementNode top) { + super(); + mRoot = top; + push(mRoot); + } + + private UiElementNode getCurrentNode() { + if (mNodeStack.size() > 0) { + return mNodeStack.get(mNodeStack.size()-1); + } + + return null; + } + + private Node getAttribute(int i) { + if (mParsingState != START_TAG) { + throw new IndexOutOfBoundsException(); + } + + // get the current uiNode + UiElementNode uiNode = getCurrentNode(); + + // get its xml node + Node xmlNode = uiNode.getXmlNode(); + + if (xmlNode != null) { + return xmlNode.getAttributes().item(i); + } + + return null; + } + + private void push(UiElementNode node) { + mNodeStack.add(node); + } + + private UiElementNode pop() { + return mNodeStack.remove(mNodeStack.size()-1); + } + + // ------------- IXmlPullParser -------- + + /** + * {@inheritDoc} + * + * This implementation returns the underlying DOM node. + */ + public Object getViewKey() { + return getCurrentNode(); + } + + // ------------- XmlPullParser -------- + + public String getPositionDescription() { + return "XML DOM element depth:" + mNodeStack.size(); + } + + public int getAttributeCount() { + UiElementNode node = getCurrentNode(); + if (node != null) { + return node.getUiAttributes().size(); + } + + return 0; + } + + public String getAttributeName(int i) { + Node attribute = getAttribute(i); + if (attribute != null) { + return attribute.getLocalName(); + } + + return null; + } + + public String getAttributeNamespace(int i) { + Node attribute = getAttribute(i); + if (attribute != null) { + return attribute.getNamespaceURI(); + } + return ""; //$NON-NLS-1$ + } + + public String getAttributePrefix(int i) { + Node attribute = getAttribute(i); + if (attribute != null) { + return attribute.getPrefix(); + } + return null; + } + + public String getAttributeValue(int i) { + Node attribute = getAttribute(i); + if (attribute != null) { + return attribute.getNodeValue(); + } + + return null; + } + + public String getAttributeValue(String namespace, String localName) { + // get the current uiNode + UiElementNode uiNode = getCurrentNode(); + + // get its xml node + Node xmlNode = uiNode.getXmlNode(); + + if (xmlNode != null) { + Node attribute = xmlNode.getAttributes().getNamedItemNS(namespace, localName); + if (attribute != null) { + return attribute.getNodeValue(); + } + } + + return null; + } + + public int getDepth() { + return mNodeStack.size(); + } + + public String getName() { + if (mParsingState == START_TAG || mParsingState == END_TAG) { + return getCurrentNode().getDescriptor().getXmlLocalName(); + } + + return null; + } + + public String getNamespace() { + if (mParsingState == START_TAG || mParsingState == END_TAG) { + return getCurrentNode().getDescriptor().getNamespace(); + } + + return null; + } + + public String getPrefix() { + if (mParsingState == START_TAG || mParsingState == END_TAG) { + // FIXME will NEVER work + if (getCurrentNode().getDescriptor().getXmlLocalName().startsWith("android:")) { //$NON-NLS-1$ + return "android"; //$NON-NLS-1$ + } + } + + return null; + } + + public boolean isEmptyElementTag() throws XmlPullParserException { + if (mParsingState == START_TAG) { + return getCurrentNode().getUiChildren().size() == 0; + } + + throw new XmlPullParserException("Call to isEmptyElementTag while not in START_TAG", + this, null); + } + + @Override + public void onNextFromStartDocument() { + onNextFromStartTag(); + } + + @Override + public void onNextFromStartTag() { + // get the current node, and look for text or children (children first) + UiElementNode node = getCurrentNode(); + List<UiElementNode> children = node.getUiChildren(); + if (children.size() > 0) { + // move to the new child, and don't change the state. + push(children.get(0)); + + // in case the current state is CURRENT_DOC, we set the proper state. + mParsingState = START_TAG; + } else { + if (mParsingState == START_DOCUMENT) { + // this handles the case where there's no node. + mParsingState = END_DOCUMENT; + } else { + mParsingState = END_TAG; + } + } + } + + @Override + public void onNextFromEndTag() { + // look for a sibling. if no sibling, go back to the parent + UiElementNode node = getCurrentNode(); + node = node.getUiNextSibling(); + if (node != null) { + // to go to the sibling, we need to remove the current node, + pop(); + // and add its sibling. + push(node); + mParsingState = START_TAG; + } else { + // move back to the parent + pop(); + + // we have only one element left (mRoot), then we're done with the document. + if (mNodeStack.size() == 1) { + mParsingState = END_DOCUMENT; + } else { + mParsingState = END_TAG; + } + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/UiPropertySheetPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiPropertySheetPage.java index 8093c90..8093c90 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/UiPropertySheetPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiPropertySheetPage.java diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/WidgetPullParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/WidgetPullParser.java new file mode 100644 index 0000000..75d10ed --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/WidgetPullParser.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.editors.layout; + +import com.android.ide.eclipse.common.AndroidConstants; +import com.android.ide.eclipse.editors.layout.descriptors.ViewElementDescriptor; +import com.android.layoutlib.api.IXmlPullParser; + +import org.xmlpull.v1.XmlPullParserException; + +/** + * {@link IXmlPullParser} implementation to render android widget bitmap. + * <p/>The parser emulates a layout that contains just one widget, described by the + * {@link ViewElementDescriptor} passed in the constructor. + */ +public class WidgetPullParser extends BasePullParser { + + private final ViewElementDescriptor mDescriptor; + private String[][] mAttributes = new String[][] { + { "text", null }, + { "layout_width", "wrap_content" }, + { "layout_height", "wrap_content" }, + }; + + public WidgetPullParser(ViewElementDescriptor descriptor) { + mDescriptor = descriptor; + + String[] segments = mDescriptor.getCanonicalClassName().split(AndroidConstants.RE_DOT); + mAttributes[0][1] = segments[segments.length-1]; + } + + public Object getViewKey() { + // we need a viewKey or the ILayoutResult will not contain any ILayoutViewInfo + return mDescriptor; + } + + public int getAttributeCount() { + return mAttributes.length; // text attribute + } + + public String getAttributeName(int index) { + if (index < mAttributes.length) { + return mAttributes[index][0]; + } + + return null; + } + + public String getAttributeNamespace(int index) { + return AndroidConstants.NS_RESOURCES; + } + + public String getAttributePrefix(int index) { + // pass + return null; + } + + public String getAttributeValue(int index) { + if (index < mAttributes.length) { + return mAttributes[index][1]; + } + + return null; + } + + public String getAttributeValue(String ns, String name) { + if (AndroidConstants.NS_RESOURCES.equals(ns)) { + for (String[] attribute : mAttributes) { + if (name.equals(attribute[0])) { + return attribute[1]; + } + } + } + + return null; + } + + public int getDepth() { + // pass + return 0; + } + + public String getName() { + return mDescriptor.getXmlLocalName(); + } + + public String getNamespace() { + // pass + return null; + } + + public String getPositionDescription() { + // pass + return null; + } + + public String getPrefix() { + // pass + return null; + } + + public boolean isEmptyElementTag() throws XmlPullParserException { + if (mParsingState == START_TAG) { + return true; + } + + throw new XmlPullParserException("Call to isEmptyElementTag while not in START_TAG", + this, null); + } + + @Override + public void onNextFromStartDocument() { + // just go to start_tag + mParsingState = START_TAG; + } + + @Override + public void onNextFromStartTag() { + // since we have no children, just go to end_tag + mParsingState = END_TAG; + } + + @Override + public void onNextFromEndTag() { + // just one tag. we are done. + mParsingState = END_DOCUMENT; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/descriptors/CustomViewDescriptorService.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/CustomViewDescriptorService.java index c3e9b70..0f388f4 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/descriptors/CustomViewDescriptorService.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/CustomViewDescriptorService.java @@ -16,9 +16,12 @@ package com.android.ide.eclipse.editors.layout.descriptors; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; +import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.resources.ViewClassInfo; import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; +import com.android.sdklib.IAndroidTarget; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.NullProgressMonitor; @@ -192,15 +195,24 @@ public final class CustomViewDescriptorService { private ViewElementDescriptor getDescriptor(IType type, IProject project, ITypeHierarchy typeHierarchy) { // check if the type is a built-in View class. - List<ElementDescriptor> builtInList = LayoutDescriptors.getInstance().getViewDescriptors(); - + List<ElementDescriptor> builtInList = null; + + Sdk currentSdk = Sdk.getCurrent(); + IAndroidTarget target = currentSdk.getTarget(project); + if (target != null) { + AndroidTargetData data = currentSdk.getTargetData(target); + builtInList = data.getLayoutDescriptors().getViewDescriptors(); + } + String canonicalName = type.getFullyQualifiedName(); - for (ElementDescriptor desc : builtInList) { - if (desc instanceof ViewElementDescriptor) { - ViewElementDescriptor viewDescriptor = (ViewElementDescriptor)desc; - if (canonicalName.equals(viewDescriptor.getCanonicalClassName())) { - return viewDescriptor; + if (builtInList != null) { + for (ElementDescriptor desc : builtInList) { + if (desc instanceof ViewElementDescriptor) { + ViewElementDescriptor viewDescriptor = (ViewElementDescriptor)desc; + if (canonicalName.equals(viewDescriptor.getCanonicalClassName())) { + return viewDescriptor; + } } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java index 8ad2382..cad9ccf 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java @@ -24,6 +24,7 @@ import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.editors.descriptors.IDescriptorProvider; import com.android.ide.eclipse.editors.descriptors.SeparatorAttributeDescriptor; import java.util.ArrayList; @@ -34,31 +35,26 @@ import java.util.List; /** * Complete description of the layout structure. */ -public class LayoutDescriptors { +public final class LayoutDescriptors implements IDescriptorProvider { // Public attributes names, attributes descriptors and elements descriptors public static final String ID_ATTR = "id"; //$NON-NLS-1$ - /** Singleton instance */ - private static LayoutDescriptors sThis; - /** The document descriptor. Contains all layouts and views linked together. */ private DocumentDescriptor mDescriptor = new DocumentDescriptor("layout_doc", null); //$NON-NLS-1$ /** The list of all known ViewLayout descriptors. */ private ArrayList<ElementDescriptor> mLayoutDescriptors = new ArrayList<ElementDescriptor>(); - + + /** Read-Only list of View Descriptors. */ + private List<ElementDescriptor> mROLayoutDescriptors; + /** The list of all known View (not ViewLayout) descriptors. */ private ArrayList<ElementDescriptor> mViewDescriptors = new ArrayList<ElementDescriptor>(); - /** Returns a singleton instance of the {@link LayoutDescriptors}. */ - public static synchronized LayoutDescriptors getInstance() { - if (sThis == null) { - sThis = new LayoutDescriptors(); - } - return sThis; - } + /** Read-Only list of View Descriptors. */ + private List<ElementDescriptor> mROViewDescriptors; /** @return the document descriptor. Contains all layouts and views linked together. */ public DocumentDescriptor getDescriptor() { @@ -67,12 +63,16 @@ public class LayoutDescriptors { /** @return The read-only list of all known ViewLayout descriptors. */ public List<ElementDescriptor> getLayoutDescriptors() { - return Collections.unmodifiableList(mLayoutDescriptors); + return mROLayoutDescriptors; } /** @return The read-only list of all known View (not ViewLayout) descriptors. */ public List<ElementDescriptor> getViewDescriptors() { - return Collections.unmodifiableList(mViewDescriptors); + return mROViewDescriptors; + } + + public ElementDescriptor[] getRootElementDescriptors() { + return mDescriptor.getChildren(); } /** @@ -118,6 +118,9 @@ public class LayoutDescriptors { mViewDescriptors = newViews; mLayoutDescriptors = newLayouts; mDescriptor.setChildren(newArray); + + mROLayoutDescriptors = Collections.unmodifiableList(mLayoutDescriptors); + mROViewDescriptors = Collections.unmodifiableList(mViewDescriptors); } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/descriptors/ViewElementDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/ViewElementDescriptor.java index d718ebd..d718ebd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/descriptors/ViewElementDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/ViewElementDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/DropFeedback.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/DropFeedback.java new file mode 100644 index 0000000..6e79d64 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/DropFeedback.java @@ -0,0 +1,761 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.editors.layout.parts; + +import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; +import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.editors.layout.LayoutConstants; +import com.android.ide.eclipse.editors.layout.LayoutEditor.UiEditorActions; +import com.android.ide.eclipse.editors.layout.parts.UiLayoutEditPart.HighlightInfo; +import com.android.ide.eclipse.editors.uimodel.UiElementNode; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; + +import java.util.HashMap; +import java.util.Map.Entry; + +/** + * Utility methods used when dealing with dropping EditPart on the GLE. + * <p/> + * This class uses some temporary static storage to avoid excessive allocations during + * drop operations. It is expected to only be invoked from the main UI thread with no + * concurrent access. + */ +class DropFeedback { + + private static final int TOP = 0; + private static final int LEFT = 1; + private static final int BOTTOM = 2; + private static final int RIGHT = 3; + private static final int MAX_DIR = RIGHT; + + private static final int sOppositeDirection[] = { BOTTOM, RIGHT, TOP, LEFT }; + + private static final UiElementEditPart sTempClosests[] = new UiElementEditPart[4]; + private static final int sTempMinDists[] = new int[4]; + + + /** + * Target information computed from a drop on a RelativeLayout. + * We need only one instance of this and it is sRelativeInfo. + */ + private static class RelativeInfo { + /** The two target parts 0 and 1. They can be null, meaning a border is used. + * The direction from part 0 to 1 is always to-the-right or to-the-bottom. */ + final UiElementEditPart targetParts[] = new UiElementEditPart[2]; + /** Direction from the anchor part to the drop point. */ + int direction; + /** The index of the "anchor" part, i.e. the closest one selected by the drop. + * This can be either 0 or 1. The corresponding part can be null. */ + int anchorIndex; + } + + /** The single RelativeInfo used to compute results from a drop on a RelativeLayout */ + private static final RelativeInfo sRelativeInfo = new RelativeInfo(); + /** A temporary array of 2 {@link UiElementEditPart} to avoid allocations. */ + private static final UiElementEditPart sTempTwoParts[] = new UiElementEditPart[2]; + + + private DropFeedback() { + } + + + //----- Package methods called by users of this helper class ----- + + + /** + * This method is used by {@link ElementCreateCommand#execute()} when a new item + * needs to be "dropped" in the current XML document. It creates the new item using + * the given descriptor as a child of the given parent part. + * + * @param parentPart The parent part. + * @param descriptor The descriptor for the new XML element. + * @param where The drop location (in parent coordinates) + * @param actions The helper that actually modifies the XML model. + */ + static void addElementToXml(UiElementEditPart parentPart, + ElementDescriptor descriptor, Point where, + UiEditorActions actions) { + + String layoutXmlName = getXmlLocalName(parentPart); + RelativeInfo info = null; + UiElementEditPart sibling = null; + + if (LayoutConstants.LINEAR_LAYOUT.equals(layoutXmlName)) { + sibling = findLinearTarget(parentPart, where)[1]; + + } else if (LayoutConstants.RELATIVE_LAYOUT.equals(layoutXmlName)) { + info = findRelativeTarget(parentPart, where, sRelativeInfo); + if (info != null) { + sibling = info.targetParts[info.anchorIndex]; + sibling = getNextUiSibling(sibling); + } + } + + if (actions != null) { + UiElementNode uiSibling = sibling != null ? sibling.getUiNode() : null; + UiElementNode uiParent = parentPart.getUiNode(); + UiElementNode uiNode = actions.addElement(uiParent, uiSibling, descriptor, + false /*updateLayout*/); + + if (LayoutConstants.ABSOLUTE_LAYOUT.equals(layoutXmlName)) { + adjustAbsoluteAttributes(uiNode, where); + } else if (LayoutConstants.RELATIVE_LAYOUT.equals(layoutXmlName)) { + adustRelativeAttributes(uiNode, info); + } + } + } + + /** + * This method is used by {@link UiLayoutEditPart#showDropTarget(Point)} to compute + * highlight information when a drop target is moved over a valid drop area. + * <p/> + * Since there are no "out" parameters in Java, all the information is returned + * via the {@link HighlightInfo} structure passed as parameter. + * + * @param parentPart The parent part, always a layout. + * @param highlightInfo A structure where result is stored to perform highlight. + * @param where The target drop point, in parent's coordinates + * @return The {@link HighlightInfo} structured passed as a parameter, for convenience. + */ + static HighlightInfo computeDropFeedback(UiLayoutEditPart parentPart, + HighlightInfo highlightInfo, + Point where) { + String layoutType = getXmlLocalName(parentPart); + + if (LayoutConstants.ABSOLUTE_LAYOUT.equals(layoutType)) { + highlightInfo.anchorPoint = where; + + } else if (LayoutConstants.LINEAR_LAYOUT.equals(layoutType)) { + boolean isVertical = isVertical(parentPart); + + highlightInfo.childParts = findLinearTarget(parentPart, where); + computeLinearLine(parentPart, isVertical, highlightInfo); + + } else if (LayoutConstants.RELATIVE_LAYOUT.equals(layoutType)) { + + RelativeInfo info = findRelativeTarget(parentPart, where, sRelativeInfo); + if (info != null) { + highlightInfo.childParts = sRelativeInfo.targetParts; + computeRelativeLine(parentPart, info, highlightInfo); + } + } + + return highlightInfo; + } + + + //----- Misc utilities ----- + + /** + * Returns the next UI sibling of this part, i.e. the element which is just after in + * the UI/XML order in the same parent. Returns null if there's no such part. + * <p/> + * Note: by "UI sibling" here we mean the sibling in the UiNode hierarchy. By design the + * UiNode model has the <em>exact</em> same order as the XML model. This has nothing to do + * with the "user interface" order that you see on the rendered Android layouts (e.g. for + * LinearLayout they are the same but for AbsoluteLayout or RelativeLayout the UI/XML model + * order can be vastly different from the user interface order.) + */ + private static UiElementEditPart getNextUiSibling(UiElementEditPart part) { + if (part != null) { + UiElementNode uiNode = part.getUiNode(); + if (uiNode != null) { + uiNode = uiNode.getUiNextSibling(); + } + if (uiNode != null) { + for (Object childPart : part.getParent().getChildren()) { + if (childPart instanceof UiElementEditPart && + ((UiElementEditPart) childPart).getUiNode() == uiNode) { + return (UiElementEditPart) childPart; + } + } + } + } + return null; + } + + /** + * Returns the XML local name of the ui node associated with this edit part or null. + */ + private static String getXmlLocalName(UiElementEditPart editPart) { + UiElementNode uiNode = editPart.getUiNode(); + if (uiNode != null) { + ElementDescriptor desc = uiNode.getDescriptor(); + if (desc != null) { + return desc.getXmlLocalName(); + } + } + return null; + } + + /** + * Adjusts the attributes of a new node dropped in an AbsoluteLayout. + * + * @param uiNode The new node being dropped. + * @param where The drop location (in parent coordinates) + */ + private static void adjustAbsoluteAttributes(final UiElementNode uiNode, final Point where) { + if (where == null) { + return; + } + uiNode.getEditor().editXmlModel(new Runnable() { + public void run() { + uiNode.setAttributeValue(LayoutConstants.ATTR_LAYOUT_X, + String.format(LayoutConstants.VALUE_N_DIP, where.x), + false /* override */); + uiNode.setAttributeValue(LayoutConstants.ATTR_LAYOUT_Y, + String.format(LayoutConstants.VALUE_N_DIP, where.y), + false /* override */); + + uiNode.commitDirtyAttributesToXml(); + } + }); + } + + /** + * Adjusts the attributes of a new node dropped in a RelativeLayout: + * <ul> + * <li> anchor part: the one the user selected (or the closest) and to which the new one + * will "attach". The anchor part can be null, either because the layout is currently + * empty or the user is attaching to an existing empty border. + * <li> direction: the direction from the anchor part to the drop point. That's also the + * direction from the anchor part to the new part. + * <li> the new node; it is created either after the anchor for right or top directions + * or before the anchor for left or bottom directions. This means the new part can + * reference the id of the anchor part. + * </ul> + * + * Several cases: + * <ul> + * <li> set: layout_above/below/toLeftOf/toRightOf to point to the anchor. + * <li> copy: layout_centerHorizontal for top/bottom directions + * <li> copy: layout_centerVertical for left/right directions. + * <li> copy: layout_above/below/toLeftOf/toRightOf for the orthogonal direction + * (i.e. top/bottom or left/right.) + * </ul> + * + * @param uiNode The new node being dropped. + * @param info The context computed by {@link #findRelativeTarget(UiElementEditPart, Point, RelativeInfo)}. + */ + private static void adustRelativeAttributes(final UiElementNode uiNode, RelativeInfo info) { + if (uiNode == null || info == null) { + return; + } + + final UiElementEditPart anchorPart = info.targetParts[info.anchorIndex]; // can be null + final int direction = info.direction; + + uiNode.getEditor().editXmlModel(new Runnable() { + public void run() { + HashMap<String, String> map = new HashMap<String, String>(); + + UiElementNode anchorUiNode = anchorPart != null ? anchorPart.getUiNode() : null; + String anchorId = anchorUiNode != null + ? anchorUiNode.getAttributeValue("id") //$NON-NLS-1$ + : null; + + if (anchorId == null) { + anchorId = DescriptorsUtils.getFreeWidgetId(anchorUiNode); + anchorUiNode.setAttributeValue("id", anchorId, true /*override*/); //$NON-NLS-1$ + } + + if (anchorId != null) { + switch(direction) { + case TOP: + map.put(LayoutConstants.ATTR_LAYOUT_ABOVE, anchorId); + break; + case BOTTOM: + map.put(LayoutConstants.ATTR_LAYOUT_BELOW, anchorId); + break; + case LEFT: + map.put(LayoutConstants.ATTR_LAYOUT_TO_LEFT_OF, anchorId); + break; + case RIGHT: + map.put(LayoutConstants.ATTR_LAYOUT_TO_RIGHT_OF, anchorId); + break; + } + + switch(direction) { + case TOP: + case BOTTOM: + map.put(LayoutConstants.ATTR_LAYOUT_CENTER_HORIZONTAL, + anchorUiNode.getAttributeValue( + LayoutConstants.ATTR_LAYOUT_CENTER_HORIZONTAL)); + + map.put(LayoutConstants.ATTR_LAYOUT_TO_LEFT_OF, + anchorUiNode.getAttributeValue( + LayoutConstants.ATTR_LAYOUT_TO_LEFT_OF)); + map.put(LayoutConstants.ATTR_LAYOUT_TO_RIGHT_OF, + anchorUiNode.getAttributeValue( + LayoutConstants.ATTR_LAYOUT_TO_RIGHT_OF)); + break; + case LEFT: + case RIGHT: + map.put(LayoutConstants.ATTR_LAYOUT_CENTER_VERTICAL, + anchorUiNode.getAttributeValue( + LayoutConstants.ATTR_LAYOUT_CENTER_VERTICAL)); + map.put(LayoutConstants.ATTR_LAYOUT_ALIGN_BASELINE, + anchorUiNode.getAttributeValue( + LayoutConstants.ATTR_LAYOUT_ALIGN_BASELINE)); + + map.put(LayoutConstants.ATTR_LAYOUT_ABOVE, + anchorUiNode.getAttributeValue(LayoutConstants.ATTR_LAYOUT_ABOVE)); + map.put(LayoutConstants.ATTR_LAYOUT_BELOW, + anchorUiNode.getAttributeValue(LayoutConstants.ATTR_LAYOUT_BELOW)); + break; + } + } else { + // We don't have an anchor node. Assume we're targeting a border and align + // to the parent. + switch(direction) { + case TOP: + map.put(LayoutConstants.ATTR_LAYOUT_ALIGN_PARENT_TOP, + LayoutConstants.VALUE_TRUE); + break; + case BOTTOM: + map.put(LayoutConstants.ATTR_LAYOUT_ALIGN_PARENT_BOTTOM, + LayoutConstants.VALUE_TRUE); + break; + case LEFT: + map.put(LayoutConstants.ATTR_LAYOUT_ALIGN_PARENT_LEFT, + LayoutConstants.VALUE_TRUE); + break; + case RIGHT: + map.put(LayoutConstants.ATTR_LAYOUT_ALIGN_PARENT_RIGHT, + LayoutConstants.VALUE_TRUE); + break; + } + } + + for (Entry<String, String> entry : map.entrySet()) { + uiNode.setAttributeValue(entry.getKey(), entry.getValue(), true /* override */); + } + uiNode.commitDirtyAttributesToXml(); + } + }); + } + + + //----- LinearLayout -------- + + /** + * For a given parent edit part that MUST represent a LinearLayout, finds the + * element before which the location points. + * <p/> + * This computes the edit part that corresponds to what will be the "next sibling" of the new + * element. + * <p/> + * It returns null if it can't be determined, in which case the element will be added at the + * end of the parent child list. + * + * @return The edit parts that correspond to what will be the "prev" and "next sibling" of the + * new element. The previous sibling can be null if adding before the first element. + * The next sibling can be null if adding after the last element. + */ + private static UiElementEditPart[] findLinearTarget(UiElementEditPart parent, Point point) { + // default orientation is horizontal + boolean isVertical = isVertical(parent); + + int target = isVertical ? point.y : point.x; + + UiElementEditPart prev = null; + UiElementEditPart next = null; + + for (Object child : parent.getChildren()) { + if (child instanceof UiElementEditPart) { + UiElementEditPart childPart = (UiElementEditPart) child; + Point p = childPart.getBounds().getCenter(); + int middle = isVertical ? p.y : p.x; + if (target < middle) { + next = childPart; + break; + } + prev = childPart; + } + } + + sTempTwoParts[0] = prev; + sTempTwoParts[1] = next; + return sTempTwoParts; + } + + /** + * Computes the highlight line between two parts. + * <p/> + * The two parts are listed in HighlightInfo.childParts[2]. Any of the parts + * can be null. + * The result is stored in HighlightInfo. + * <p/> + * Caller must clear the HighlightInfo as appropriate before this call. + * + * @param parentPart The parent part, always a layout. + * @param isVertical True for vertical parts, thus computing an horizontal line. + * @param highlightInfo The in-out highlight info. + */ + private static void computeLinearLine(UiLayoutEditPart parentPart, + boolean isVertical, HighlightInfo highlightInfo) { + Rectangle r = parentPart.getBounds(); + + if (isVertical) { + Point p = null; + UiElementEditPart part = highlightInfo.childParts[0]; + if (part != null) { + p = part.getBounds().getBottom(); + } else { + part = highlightInfo.childParts[1]; + if (part != null) { + p = part.getBounds().getTop(); + } + } + if (p != null) { + // horizontal line with middle anchor point + highlightInfo.tempPoints[0].setLocation(0, p.y); + highlightInfo.tempPoints[1].setLocation(r.width, p.y); + highlightInfo.linePoints = highlightInfo.tempPoints; + highlightInfo.anchorPoint = p.setLocation(r.width / 2, p.y); + } + } else { + Point p = null; + UiElementEditPart part = highlightInfo.childParts[0]; + if (part != null) { + p = part.getBounds().getRight(); + } else { + part = highlightInfo.childParts[1]; + if (part != null) { + p = part.getBounds().getLeft(); + } + } + if (p != null) { + // vertical line with middle anchor point + highlightInfo.tempPoints[0].setLocation(p.x, 0); + highlightInfo.tempPoints[1].setLocation(p.x, r.height); + highlightInfo.linePoints = highlightInfo.tempPoints; + highlightInfo.anchorPoint = p.setLocation(p.x, r.height / 2); + } + } + } + + /** + * Returns true if the linear layout is marked as vertical. + * + * @param parent The a layout part that must be a LinearLayout + * @return True if the linear layout has a vertical orientation attribute. + */ + private static boolean isVertical(UiElementEditPart parent) { + String orientation = parent.getStringAttr("orientation"); //$NON-NLS-1$ + boolean isVertical = "vertical".equals(orientation) || //$NON-NLS-1$ + "1".equals(orientation); //$NON-NLS-1$ + return isVertical; + } + + + //----- RelativeLayout -------- + + /** + * Finds the "target" relative layout item for the drop operation & feedback. + * <p/> + * If the drop point is exactly on a current item, simply returns the side the drop will occur + * compared to the center of that element. For the actual XML, we'll need to insert *after* + * that element to make sure that referenced are defined in the right order. + * In that case the result contains two elements, the second one always being on the right or + * bottom side of the first one. When insert in XML, we want to insert right before that + * second element or at the end of the child list if the second element is null. + * <p/> + * If the drop point is not exactly on a current element, find the closest in each + * direction and align with the two closest of these. + * + * @return null if we fail to find anything (such as there are currently no items to compare + * with); otherwise fills the {@link RelativeInfo} and return it. + */ + private static RelativeInfo findRelativeTarget(UiElementEditPart parent, + Point point, + RelativeInfo outInfo) { + + for (int i = 0; i < 4; i++) { + sTempMinDists[i] = Integer.MAX_VALUE; + sTempClosests[i] = null; + } + + + for (Object child : parent.getChildren()) { + if (child instanceof UiElementEditPart) { + UiElementEditPart childPart = (UiElementEditPart) child; + Rectangle r = childPart.getBounds(); + if (r.contains(point)) { + + float rx = ((float)(point.x - r.x) / (float)r.width ) - 0.5f; + float ry = ((float)(point.y - r.y) / (float)r.height) - 0.5f; + + /* TOP + * \ / + * \ / + * L X R + * / \ + * / \ + * BOT + */ + + int index = 0; + if (Math.abs(rx) >= Math.abs(ry)) { + if (rx < 0) { + outInfo.direction = LEFT; + index = 1; + } else { + outInfo.direction = RIGHT; + } + } else { + if (ry < 0) { + outInfo.direction = TOP; + index = 1; + } else { + outInfo.direction = BOTTOM; + } + } + + outInfo.anchorIndex = index; + outInfo.targetParts[index] = childPart; + outInfo.targetParts[1 - index] = findClosestPart(childPart, + outInfo.direction); + + return outInfo; + } + + computeClosest(point, childPart, sTempClosests, sTempMinDists, TOP); + computeClosest(point, childPart, sTempClosests, sTempMinDists, LEFT); + computeClosest(point, childPart, sTempClosests, sTempMinDists, BOTTOM); + computeClosest(point, childPart, sTempClosests, sTempMinDists, RIGHT); + } + } + + UiElementEditPart closest = null; + int minDist = Integer.MAX_VALUE; + int minDir = -1; + + for (int i = 0; i <= MAX_DIR; i++) { + if (sTempClosests[i] != null && sTempMinDists[i] < minDist) { + closest = sTempClosests[i]; + minDist = sTempMinDists[i]; + minDir = i; + } + } + + if (closest != null) { + int index = 0; + switch(minDir) { + case TOP: + case LEFT: + index = 0; + break; + case BOTTOM: + case RIGHT: + index = 1; + break; + } + outInfo.anchorIndex = index; + outInfo.targetParts[index] = closest; + outInfo.targetParts[1 - index] = findClosestPart(closest, sOppositeDirection[minDir]); + outInfo.direction = sOppositeDirection[minDir]; + return outInfo; + } + + return null; + } + + /** + * Computes the highlight line for a drop on a RelativeLayout. + * <p/> + * The line is always placed on the side of the anchor part indicated by the + * direction. The direction always point from the anchor part to the drop point. + * <p/> + * If there's no anchor part, use the other one with a reversed direction. + * <p/> + * On output, this updates the {@link HighlightInfo}. + */ + private static void computeRelativeLine(UiLayoutEditPart parentPart, + RelativeInfo relInfo, + HighlightInfo highlightInfo) { + + UiElementEditPart[] parts = relInfo.targetParts; + int dir = relInfo.direction; + int index = relInfo.anchorIndex; + UiElementEditPart part = parts[index]; + + if (part == null) { + dir = sOppositeDirection[dir]; + part = parts[1 - index]; + } + if (part == null) { + // give up if both parts are null + return; + } + + Rectangle r = part.getBounds(); + Point p = null; + switch(dir) { + case TOP: + p = r.getTop(); + break; + case BOTTOM: + p = r.getBottom(); + break; + case LEFT: + p = r.getLeft(); + break; + case RIGHT: + p = r.getRight(); + break; + } + + highlightInfo.anchorPoint = p; + + r = parentPart.getBounds(); + switch(dir) { + case TOP: + case BOTTOM: + // horizontal line with middle anchor point + highlightInfo.tempPoints[0].setLocation(0, p.y); + highlightInfo.tempPoints[1].setLocation(r.width, p.y); + highlightInfo.linePoints = highlightInfo.tempPoints; + highlightInfo.anchorPoint = p; + break; + case LEFT: + case RIGHT: + // vertical line with middle anchor point + highlightInfo.tempPoints[0].setLocation(p.x, 0); + highlightInfo.tempPoints[1].setLocation(p.x, r.height); + highlightInfo.linePoints = highlightInfo.tempPoints; + highlightInfo.anchorPoint = p; + break; + } + } + + /** + * Given a certain reference point (drop point), computes the distance to the given + * part in the given direction. For example if direction is top, only accepts parts which + * bottom is above the reference point, computes their distance and then updates the + * current minimal distances and current closest parts arrays accordingly. + */ + private static void computeClosest(Point refPoint, + UiElementEditPart compareToPart, + UiElementEditPart[] currClosests, + int[] currMinDists, + int direction) { + Rectangle r = compareToPart.getBounds(); + + Point p = null; + boolean usable = false; + + switch(direction) { + case TOP: + p = r.getBottom(); + usable = p.y <= refPoint.y; + break; + case BOTTOM: + p = r.getTop(); + usable = p.y >= refPoint.y; + break; + case LEFT: + p = r.getRight(); + usable = p.x <= refPoint.x; + break; + case RIGHT: + p = r.getLeft(); + usable = p.x >= refPoint.x; + break; + } + + if (usable) { + int d = p.getDistance2(refPoint); + if (d < currMinDists[direction]) { + currMinDists[direction] = d; + currClosests[direction] = compareToPart; + } + } + } + + /** + * Given a reference parts, finds the closest part in the parent in the given direction. + * For example if direction is top, finds the closest sibling part which is above the + * reference part and non-overlapping (they can touch.) + */ + private static UiElementEditPart findClosestPart(UiElementEditPart referencePart, + int direction) { + if (referencePart == null || referencePart.getParent() == null) { + return null; + } + + Rectangle r = referencePart.getBounds(); + Point ref = null; + switch(direction) { + case TOP: + ref = r.getTop(); + break; + case BOTTOM: + ref = r.getBottom(); + break; + case LEFT: + ref = r.getLeft(); + break; + case RIGHT: + ref = r.getRight(); + break; + } + + int minDist = Integer.MAX_VALUE; + UiElementEditPart closestPart = null; + + for (Object childPart : referencePart.getParent().getChildren()) { + if (childPart != referencePart && childPart instanceof UiElementEditPart) { + r = ((UiElementEditPart) childPart).getBounds(); + Point p = null; + boolean usable = false; + + switch(direction) { + case TOP: + p = r.getBottom(); + usable = p.y <= ref.y; + break; + case BOTTOM: + p = r.getTop(); + usable = p.y >= ref.y; + break; + case LEFT: + p = r.getRight(); + usable = p.x <= ref.x; + break; + case RIGHT: + p = r.getLeft(); + usable = p.x >= ref.x; + break; + } + + if (usable) { + int d = p.getDistance2(ref); + if (d < minDist) { + minDist = d; + closestPart = (UiElementEditPart) childPart; + } + } + } + } + + return closestPart; + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/ElementCreateCommand.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/ElementCreateCommand.java new file mode 100644 index 0000000..d36d9f7 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/ElementCreateCommand.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.editors.layout.parts; + +import com.android.ide.eclipse.editors.AndroidEditor; +import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.editors.layout.LayoutEditor; +import com.android.ide.eclipse.editors.layout.LayoutEditor.UiEditorActions; +import com.android.ide.eclipse.editors.uimodel.UiElementNode; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.gef.commands.Command; + +/** + * A command that knows how to instantiate a new element based on a given {@link ElementDescriptor}, + * the parent {@link UiElementEditPart} and an optional target location. + */ +public class ElementCreateCommand extends Command { + + /** Descriptor of the new element to create */ + private final ElementDescriptor mDescriptor; + /** The edit part that hosts the new edit part */ + private final UiElementEditPart mParentPart; + /** The drop location in parent coordinates */ + private final Point mTargetPoint; + + /** + * Creates a new {@link ElementCreateCommand}. + * + * @param descriptor Descriptor of the new element to create + * @param targetPart The edit part that hosts the new edit part + * @param targetPoint The drop location in parent coordinates + */ + public ElementCreateCommand(ElementDescriptor descriptor, + UiElementEditPart targetPart, Point targetPoint) { + mDescriptor = descriptor; + mParentPart = targetPart; + mTargetPoint = targetPoint; + } + + // --- Methods inherited from Command --- + + @Override + public boolean canExecute() { + return mDescriptor != null && + mParentPart != null && + mParentPart.getUiNode() != null && + mParentPart.getUiNode().getEditor() instanceof LayoutEditor; + } + + @Override + public void execute() { + super.execute(); + UiElementNode uiParent = mParentPart.getUiNode(); + if (uiParent != null) { + final AndroidEditor editor = uiParent.getEditor(); + if (editor instanceof LayoutEditor) { + ((LayoutEditor) editor).wrapUndoRecording( + String.format("Create %1$s", mDescriptor.getXmlLocalName()), + new Runnable() { + public void run() { + UiEditorActions actions = ((LayoutEditor) editor).getUiEditorActions(); + if (actions != null) { + DropFeedback.addElementToXml(mParentPart, mDescriptor, mTargetPoint, + actions); + } + } + }); + } + } + } + + @Override + public void redo() { + throw new UnsupportedOperationException("redo not supported by this command"); //$NON-NLS-1$ + } + + @Override + public void undo() { + throw new UnsupportedOperationException("undo not supported by this command"); //$NON-NLS-1$ + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/ElementFigure.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/ElementFigure.java new file mode 100644 index 0000000..f863037 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/ElementFigure.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.editors.layout.parts; + +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.Figure; +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.swt.SWT; + + +/** + * The figure used to draw basic elements. + * <p/> + * The figure is totally empty and transparent except for the selection border. + */ +class ElementFigure extends Figure { + + private boolean mIsSelected; + private Rectangle mInnerBounds; + + public ElementFigure() { + setOpaque(false); + } + + public void setSelected(boolean isSelected) { + if (isSelected != mIsSelected) { + mIsSelected = isSelected; + repaint(); + } + } + + @Override + public void setBounds(Rectangle rect) { + super.setBounds(rect); + + mInnerBounds = getBounds().getCopy(); + if (mInnerBounds.width > 0) { + mInnerBounds.width--; + } + if (mInnerBounds.height > 0) { + mInnerBounds.height--; + } + } + + public Rectangle getInnerBounds() { + return mInnerBounds; + } + + @Override + protected void paintBorder(Graphics graphics) { + super.paintBorder(graphics); + + if (mIsSelected) { + graphics.setLineWidth(1); + graphics.setLineStyle(SWT.LINE_SOLID); + graphics.setForegroundColor(ColorConstants.red); + graphics.drawRectangle(getInnerBounds()); + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/LayoutFigure.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/LayoutFigure.java new file mode 100644 index 0000000..55ed39b --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/LayoutFigure.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.editors.layout.parts; + +import com.android.ide.eclipse.editors.layout.parts.UiLayoutEditPart.HighlightInfo; + +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.Figure; +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.swt.SWT; + +/** + * The figure used to draw the feedback on a layout. + * <p/> + * By default the figure is transparent and empty. + * The base {@link ElementFigure} knows how to draw the selection border. + * This figure knows how to draw the drop feedback. + */ +class LayoutFigure extends ElementFigure { + + private HighlightInfo mHighlightInfo; + + public LayoutFigure() { + super(); + } + + public void setHighlighInfo(HighlightInfo highlightInfo) { + mHighlightInfo = highlightInfo; + repaint(); + } + + /** + * Paints the "border" for this figure. + * <p/> + * The parent {@link Figure#paint(Graphics)} calls {@link #paintFigure(Graphics)} then + * {@link #paintClientArea(Graphics)} then {@link #paintBorder(Graphics)}. Here we thus + * draw the actual highlight border but also the highlight anchor lines and points so that + * we can make sure they are all drawn on top of the border. + * <p/> + * Note: This method doesn't really need to restore its graphic state. The parent + * Figure will do it for us. + * <p/> + * + * @param graphics The Graphics object used for painting + */ + @Override + protected void paintBorder(Graphics graphics) { + super.paintBorder(graphics); + + if (mHighlightInfo == null) { + return; + } + + // Draw the border. We want other highlighting to be drawn on top of the border. + if (mHighlightInfo.drawDropBorder) { + graphics.setLineWidth(3); + graphics.setLineStyle(SWT.LINE_SOLID); + graphics.setForegroundColor(ColorConstants.green); + graphics.drawRectangle(getInnerBounds().getCopy().shrink(1, 1)); + } + + Rectangle bounds = getBounds(); + int bx = bounds.x; + int by = bounds.y; + int w = bounds.width; + int h = bounds.height; + + // Draw frames of target child parts, if any + if (mHighlightInfo.childParts != null) { + graphics.setLineWidth(2); + graphics.setLineStyle(SWT.LINE_DOT); + graphics.setForegroundColor(ColorConstants.lightBlue); + for (UiElementEditPart part : mHighlightInfo.childParts) { + if (part != null) { + graphics.drawRectangle(part.getBounds().getCopy().translate(bx, by)); + } + } + } + + // Draw the target line, if any + if (mHighlightInfo.linePoints != null) { + int x1 = mHighlightInfo.linePoints[0].x; + int y1 = mHighlightInfo.linePoints[0].y; + int x2 = mHighlightInfo.linePoints[1].x; + int y2 = mHighlightInfo.linePoints[1].y; + + // if the line is right to the edge, draw it one pixel more inside so that the + // full 2-pixel width be visible. + if (x1 <= 0) x1++; + if (x2 <= 0) x2++; + if (y1 <= 0) y1++; + if (y2 <= 0) y2++; + + if (x1 >= w - 1) x1--; + if (x2 >= w - 1) x2--; + if (y1 >= h - 1) y1--; + if (y2 >= h - 1) y2--; + + x1 += bx; + x2 += bx; + y1 += by; + y2 += by; + + graphics.setLineWidth(2); + graphics.setLineStyle(SWT.LINE_DASH); + graphics.setLineCap(SWT.CAP_ROUND); + graphics.setForegroundColor(ColorConstants.orange); + graphics.drawLine(x1, y1, x2, y2); + } + + // Draw the anchor point, if any + if (mHighlightInfo.anchorPoint != null) { + int x = mHighlightInfo.anchorPoint.x; + int y = mHighlightInfo.anchorPoint.y; + + // If the point is right on the edge, draw it one pixel inside so that it + // matches the highlight line. It makes it slightly more visible that way. + if (x <= 0) x++; + if (y <= 0) y++; + if (x >= w - 1) x--; + if (y >= h - 1) y--; + x += bx; + y += by; + + graphics.setLineWidth(2); + graphics.setLineStyle(SWT.LINE_SOLID); + graphics.setLineCap(SWT.CAP_ROUND); + graphics.setForegroundColor(ColorConstants.orange); + graphics.drawLine(x-5, y-5, x+5, y+5); + graphics.drawLine(x-5, y+5, x+5, y-5); + // 7 * cos(45) == 5 so we use 8 for the circle radius (it looks slightly better than 7) + graphics.setLineWidth(1); + graphics.drawOval(x-8, y-8, 16, 16); + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiDocumentEditPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiDocumentEditPart.java index 53a87f5..2f7636d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiDocumentEditPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiDocumentEditPart.java @@ -27,7 +27,14 @@ import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.Label; import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gef.Request; +import org.eclipse.gef.RequestConstants; +import org.eclipse.gef.editpolicies.RootComponentEditPolicy; +import org.eclipse.gef.requests.DropRequest; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.PaletteData; @@ -149,4 +156,57 @@ public class UiDocumentEditPart extends UiElementEditPart { protected void showSelection() { // no selection at this level. } + + @Override + protected void createEditPolicies() { + super.createEditPolicies(); + + // This policy indicates this a root component that cannot be removed + installEditPolicy(EditPolicy.COMPONENT_ROLE, new RootComponentEditPolicy()); + + installLayoutEditPolicy(this); + } + + /** + * Returns the EditPart that should be used as the target for the specified Request. + * For instance this is called during drag'n'drop with a CreateRequest. + * <p/> + * For the root document, we want the first child edit part to the be the target + * since an XML document can have only one root element. + * + * {@inheritDoc} + */ + @Override + public EditPart getTargetEditPart(Request request) { + if (request != null && request.getType() == RequestConstants.REQ_CREATE) { + // We refuse the drop if it's not in the bounds of the document. + if (request instanceof DropRequest) { + Point where = ((DropRequest) request).getLocation().getCopy(); + UiElementNode uiNode = getUiNode(); + if (uiNode instanceof UiDocumentNode) { + // Take the bounds of the background image as the valid drop zone + Object editData = uiNode.getEditData(); + if (editData instanceof BufferedImage) { + BufferedImage image = (BufferedImage)editData; + int w = image.getWidth(); + int h = image.getHeight(); + if (where.x > w || where.y > h) { + return null; + } + } + + } + } + + // For the root document, we want the first child edit part to the be the target + // since an XML document can have only one root element. + if (getChildren().size() > 0) { + Object o = getChildren().get(0); + if (o instanceof EditPart) { + return ((EditPart) o).getTargetEditPart(request); + } + } + } + return super.getTargetEditPart(request); + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiDocumentTreeEditPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiDocumentTreeEditPart.java index af22afb..af22afb 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiDocumentTreeEditPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiDocumentTreeEditPart.java diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiElementEditPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiElementEditPart.java new file mode 100644 index 0000000..d873005 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiElementEditPart.java @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.editors.layout.parts; + +import com.android.ide.eclipse.common.AndroidConstants; +import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.editors.uimodel.IUiUpdateListener; +import com.android.ide.eclipse.editors.uimodel.UiElementNode; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.DragTracker; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gef.GraphicalEditPart; +import org.eclipse.gef.Request; +import org.eclipse.gef.RequestConstants; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.editparts.AbstractGraphicalEditPart; +import org.eclipse.gef.editpolicies.LayoutEditPolicy; +import org.eclipse.gef.editpolicies.SelectionEditPolicy; +import org.eclipse.gef.requests.CreateRequest; +import org.eclipse.gef.requests.DropRequest; +import org.eclipse.gef.tools.SelectEditPartTracker; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import java.util.List; + +/** + * An {@link EditPart} for a {@link UiElementNode}. + */ +public abstract class UiElementEditPart extends AbstractGraphicalEditPart + implements IUiUpdateListener { + + public UiElementEditPart(UiElementNode uiElementNode) { + setModel(uiElementNode); + } + + //------------------------- + // Derived classes must define these + + abstract protected void hideSelection(); + abstract protected void showSelection(); + + //------------------------- + // Base class overrides + + @Override + public DragTracker getDragTracker(Request request) { + return new SelectEditPartTracker(this); + } + + @Override + protected void createEditPolicies() { + /* + * This is no longer needed, as a selection edit policy is set by the parent layout. + * Leave this code commented out right now, I'll want to play with this later. + * + installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE, + new NonResizableSelectionEditPolicy(this)); + */ + } + + /* (non-javadoc) + * Returns a List containing the children model objects. + * Must not return null, instead use the super which returns an empty list. + */ + @SuppressWarnings("unchecked") + @Override + protected List getModelChildren() { + return getUiNode().getUiChildren(); + } + + @Override + public void activate() { + super.activate(); + getUiNode().addUpdateListener(this); + } + + @Override + public void deactivate() { + super.deactivate(); + getUiNode().removeUpdateListener(this); + } + + @Override + protected void refreshVisuals() { + if (getFigure().getParent() != null) { + ((GraphicalEditPart) getParent()).setLayoutConstraint(this, getFigure(), getBounds()); + } + + // update the visuals of the children as well + refreshChildrenVisuals(); + } + + protected void refreshChildrenVisuals() { + if (children != null) { + for (Object child : children) { + if (child instanceof UiElementEditPart) { + UiElementEditPart childPart = (UiElementEditPart)child; + childPart.refreshVisuals(); + } + } + } + } + + //------------------------- + // IUiUpdateListener implementation + + public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) { + // TODO: optimize by refreshing only when needed + switch(state) { + case ATTR_UPDATED: + refreshVisuals(); + break; + case CHILDREN_CHANGED: + refreshChildren(); + + // new children list, need to update the layout + refreshVisuals(); + break; + case CREATED: + refreshVisuals(); + break; + case DELETED: + // pass + break; + } + } + + //------------------------- + // Local methods + + /** @return The object model casted to an {@link UiElementNode} */ + public final UiElementNode getUiNode() { + return (UiElementNode) getModel(); + } + + protected final ElementDescriptor getDescriptor() { + return getUiNode().getDescriptor(); + } + + protected final UiElementEditPart getEditPartParent() { + EditPart parent = getParent(); + if (parent instanceof UiElementEditPart) { + return (UiElementEditPart)parent; + } + return null; + } + + /** + * Returns a given XML attribute. + * @param attrName The local name of the attribute. + * @return the attribute as a {@link String}, if it exists, or <code>null</code> + */ + protected final String getStringAttr(String attrName) { + UiElementNode uiNode = getUiNode(); + if (uiNode.getXmlNode() != null) { + Node xmlNode = uiNode.getXmlNode(); + if (xmlNode != null) { + NamedNodeMap nodeAttributes = xmlNode.getAttributes(); + if (nodeAttributes != null) { + Node attr = nodeAttributes.getNamedItemNS( + AndroidConstants.NS_RESOURCES, attrName); + if (attr != null) { + return attr.getNodeValue(); + } + } + } + } + return null; + } + + protected final Rectangle getBounds() { + UiElementNode model = (UiElementNode)getModel(); + + Object editData = model.getEditData(); + + if (editData != null) { + // assert with fully qualified class name to prevent import changes to another + // Rectangle class. + assert (editData instanceof org.eclipse.draw2d.geometry.Rectangle); + + return (Rectangle)editData; + } + + // return a dummy rect + return new Rectangle(0, 0, 0, 0); + } + + /** + * Returns the EditPart that should be used as the target for the specified Request. + * <p/> + * For instance this is called during drag'n'drop with a CreateRequest. + * <p/> + * Reject being a target for elements which descriptor does not allow children. + * + * {@inheritDoc} + */ + @Override + public EditPart getTargetEditPart(Request request) { + if (request != null && request.getType() == RequestConstants.REQ_CREATE) { + // Reject being a target for elements which descriptor does not allow children. + if (!getUiNode().getDescriptor().hasChildren()) { + return null; + } + } + return super.getTargetEditPart(request); + } + + /** + * Used by derived classes {@link UiDocumentEditPart} and {@link UiLayoutEditPart} + * to accept drag'n'drop of new items from the palette. + * + * @param layoutEditPart The layout edit part where this policy is installed. It can + * be either a {@link UiDocumentEditPart} or a {@link UiLayoutEditPart}. + */ + protected void installLayoutEditPolicy(final UiElementEditPart layoutEditPart) { + // This policy indicates how elements can be constrained by the layout. + // TODO Right now we use the XY layout policy since our constraints are + // handled by the android rendering engine rather than GEF. Tweak as + // appropriate. + installEditPolicy(EditPolicy.LAYOUT_ROLE, new LayoutEditPolicy() { + + /** + * We don't allow layout children to be resized yet. + * <p/> + * Typical choices would be: + * <ul> + * <li> ResizableEditPolicy, to allow for selection, move and resize. + * <li> NonResizableEditPolicy, to allow for selection, move but not resize. + * <li> SelectionEditPolicy to allow for only selection. + * </ul> + * <p/> + * TODO: make this depend on the part layout. For an AbsoluteLayout we should + * probably use a NonResizableEditPolicy and SelectionEditPolicy for the rest. + * Whether to use ResizableEditPolicy or NonResizableEditPolicy should depend + * on the child in an AbsoluteLayout. + */ + @Override + protected EditPolicy createChildEditPolicy(EditPart child) { + if (child instanceof UiElementEditPart) { + return new NonResizableSelectionEditPolicy((UiElementEditPart) child); + } + return null; + } + + @Override + protected Command getCreateCommand(CreateRequest request) { + // We store the ElementDescriptor in the request.factory.type + Object newType = request.getNewObjectType(); + if (newType instanceof ElementDescriptor) { + Point where = request.getLocation().getCopy(); + Point origin = getLayoutContainer().getClientArea().getLocation(); + where.translate(origin.getNegated()); + + // The host is the EditPart where this policy is installed, + // e.g. this UiElementEditPart. + EditPart host = getHost(); + if (host instanceof UiElementEditPart) { + + return new ElementCreateCommand((ElementDescriptor) newType, + (UiElementEditPart) host, + where); + } + } + + return null; + } + + @Override + protected Command getMoveChildrenCommand(Request request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void showLayoutTargetFeedback(Request request) { + super.showLayoutTargetFeedback(request); + + // for debugging + // System.out.println("target: " + request.toString() + " -- " + layoutEditPart.getUiNode().getBreadcrumbTrailDescription(false)); + + if (layoutEditPart instanceof UiLayoutEditPart && + request instanceof DropRequest) { + Point where = ((DropRequest) request).getLocation().getCopy(); + Point origin = getLayoutContainer().getClientArea().getLocation(); + where.translate(origin.getNegated()); + + ((UiLayoutEditPart) layoutEditPart).showDropTarget(where); + } + } + + @Override + protected void eraseLayoutTargetFeedback(Request request) { + super.eraseLayoutTargetFeedback(request); + if (layoutEditPart instanceof UiLayoutEditPart) { + ((UiLayoutEditPart) layoutEditPart).hideDropTarget(); + } + } + + @Override + protected IFigure createSizeOnDropFeedback(CreateRequest createRequest) { + // TODO understand if this is useful for us or remove + return super.createSizeOnDropFeedback(createRequest); + } + + }); + } + + protected static class NonResizableSelectionEditPolicy extends SelectionEditPolicy { + + private final UiElementEditPart mEditPart; + + public NonResizableSelectionEditPolicy(UiElementEditPart editPart) { + mEditPart = editPart; + } + + @Override + protected void hideSelection() { + mEditPart.hideSelection(); + } + + @Override + protected void showSelection() { + mEditPart.showSelection(); + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiElementTreeEditPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiElementTreeEditPart.java index fd788dd..fd788dd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiElementTreeEditPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiElementTreeEditPart.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiElementTreeEditPartFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiElementTreeEditPartFactory.java index de6c404..de6c404 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiElementTreeEditPartFactory.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiElementTreeEditPartFactory.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiElementsEditPartFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiElementsEditPartFactory.java index 18dcd9c..18dcd9c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiElementsEditPartFactory.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiElementsEditPartFactory.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiLayoutEditPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiLayoutEditPart.java index d9433ca..43a70a5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiLayoutEditPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiLayoutEditPart.java @@ -18,11 +18,9 @@ package com.android.ide.eclipse.editors.layout.parts; import com.android.ide.eclipse.editors.uimodel.UiElementNode; -import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.IFigure; -import org.eclipse.draw2d.Label; -import org.eclipse.draw2d.LineBorder; import org.eclipse.draw2d.XYLayout; +import org.eclipse.draw2d.geometry.Point; import org.eclipse.gef.EditPolicy; import org.eclipse.gef.commands.Command; import org.eclipse.gef.editpolicies.ContainerEditPolicy; @@ -35,6 +33,24 @@ import org.eclipse.gef.requests.CreateRequest; */ public final class UiLayoutEditPart extends UiElementEditPart { + static class HighlightInfo { + public boolean drawDropBorder; + public UiElementEditPart[] childParts; + public Point anchorPoint; + public Point linePoints[]; + + public final Point tempPoints[] = new Point[] { new Point(), new Point() }; + + public void clear() { + drawDropBorder = false; + childParts = null; + anchorPoint = null; + linePoints = null; + } + } + + private final HighlightInfo mHighlightInfo = new HighlightInfo(); + public UiLayoutEditPart(UiElementNode uiElementNode) { super(uiElementNode); } @@ -49,39 +65,51 @@ public final class UiLayoutEditPart extends UiElementEditPart { return null; } }); + + installLayoutEditPolicy(this); } @Override protected IFigure createFigure() { - Label f = new Label(); + IFigure f = new LayoutFigure(); f.setLayoutManager(new XYLayout()); return f; } @Override - protected void hideSelection() { + protected void showSelection() { IFigure f = getFigure(); - if (f instanceof Label) { - f.setBorder(null); + if (f instanceof ElementFigure) { + ((ElementFigure) f).setSelected(true); } } @Override - protected void showSelection() { + protected void hideSelection() { IFigure f = getFigure(); - if (f instanceof Label) { - f.setBorder(new LineBorder(ColorConstants.red, 1)); + if (f instanceof ElementFigure) { + ((ElementFigure) f).setSelected(false); } } - - public void showDropTarget() { - IFigure f = getFigure(); - if (f instanceof Label) { - f.setBorder(new LineBorder(ColorConstants.blue, 1)); + + public void showDropTarget(Point where) { + if (where != null) { + mHighlightInfo.clear(); + mHighlightInfo.drawDropBorder = true; + DropFeedback.computeDropFeedback(this, mHighlightInfo, where); + + IFigure f = getFigure(); + if (f instanceof LayoutFigure) { + ((LayoutFigure) f).setHighlighInfo(mHighlightInfo); + } } } public void hideDropTarget() { - hideSelection(); + mHighlightInfo.clear(); + IFigure f = getFigure(); + if (f instanceof LayoutFigure) { + ((LayoutFigure) f).setHighlighInfo(mHighlightInfo); + } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiLayoutTreeEditPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiLayoutTreeEditPart.java index 4359e23..4359e23 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiLayoutTreeEditPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiLayoutTreeEditPart.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiViewEditPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiViewEditPart.java index b427ead..05329f3 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiViewEditPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiViewEditPart.java @@ -18,10 +18,8 @@ package com.android.ide.eclipse.editors.layout.parts; import com.android.ide.eclipse.editors.uimodel.UiElementNode; -import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.IFigure; -import org.eclipse.draw2d.Label; -import org.eclipse.draw2d.LineBorder; +import org.eclipse.draw2d.XYLayout; /** * Graphical edit part for an {@link UiElementNode} that represents a View. @@ -31,26 +29,27 @@ public class UiViewEditPart extends UiElementEditPart { public UiViewEditPart(UiElementNode uiElementNode) { super(uiElementNode); } - + @Override protected IFigure createFigure() { - Label f = new Label(); + IFigure f = new ElementFigure(); + f.setLayoutManager(new XYLayout()); return f; } - + @Override - protected void hideSelection() { + protected void showSelection() { IFigure f = getFigure(); - if (f instanceof Label) { - f.setBorder(null); + if (f instanceof ElementFigure) { + ((ElementFigure) f).setSelected(true); } } @Override - protected void showSelection() { + protected void hideSelection() { IFigure f = getFigure(); - if (f instanceof Label) { - f.setBorder(new LineBorder(ColorConstants.red, 1)); + if (f instanceof ElementFigure) { + ((ElementFigure) f).setSelected(false); } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiViewTreeEditPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiViewTreeEditPart.java index 62b5e8a..62b5e8a 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiViewTreeEditPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/UiViewTreeEditPart.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/uimodel/UiViewElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/uimodel/UiViewElementNode.java index 75cf4b6..45cbc77 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/uimodel/UiViewElementNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/uimodel/UiViewElementNode.java @@ -16,14 +16,20 @@ package com.android.ide.eclipse.editors.layout.uimodel; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; +import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor; -import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors; import com.android.ide.eclipse.editors.layout.descriptors.ViewElementDescriptor; import com.android.ide.eclipse.editors.uimodel.UiDocumentNode; import com.android.ide.eclipse.editors.uimodel.UiElementNode; +import com.android.sdklib.IAndroidTarget; + +import org.eclipse.core.resources.IProject; + +import java.util.List; /** * Specialized version of {@link UiElementNode} for the {@link ViewElementDescriptor}s. @@ -59,12 +65,26 @@ public class UiViewElementNode extends UiElementNode { // Limitation: right now the layout behaves as if everything was // owned by a FrameLayout. // TODO replace by something user-configurable. - for (ElementDescriptor desc : LayoutDescriptors.getInstance().getLayoutDescriptors()) { - if (desc instanceof ViewElementDescriptor && - desc.getXmlName().equals(AndroidConstants.CLASS_FRAMELAYOUT)) { - layout_attrs = ((ViewElementDescriptor) desc).getLayoutAttributes(); - need_xmlns = true; - break; + + List<ElementDescriptor> layoutDescriptors = null; + IProject project = getEditor().getProject(); + if (project != null) { + Sdk currentSdk = Sdk.getCurrent(); + IAndroidTarget target = currentSdk.getTarget(project); + if (target != null) { + AndroidTargetData data = currentSdk.getTargetData(target); + layoutDescriptors = data.getLayoutDescriptors().getLayoutDescriptors(); + } + } + + if (layoutDescriptors != null) { + for (ElementDescriptor desc : layoutDescriptors) { + if (desc instanceof ViewElementDescriptor && + desc.getXmlName().equals(AndroidConstants.CLASS_FRAMELAYOUT)) { + layout_attrs = ((ViewElementDescriptor) desc).getLayoutAttributes(); + need_xmlns = true; + break; + } } } } else if (ui_parent instanceof UiViewElementNode){ diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/ManifestContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestContentAssist.java index e43c984..b40e458 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/ManifestContentAssist.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestContentAssist.java @@ -16,9 +16,8 @@ package com.android.ide.eclipse.editors.manifest; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; import com.android.ide.eclipse.editors.AndroidContentAssist; -import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors; /** * Content Assist Processor for AndroidManifest.xml @@ -29,6 +28,6 @@ final class ManifestContentAssist extends AndroidContentAssist { * Constructor for ManifestContentAssist */ public ManifestContentAssist() { - super(new ElementDescriptor[] { AndroidManifestDescriptors.MANIFEST_ELEMENT }); + super(AndroidTargetData.DESCRIPTOR_MANIFEST); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/ManifestEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestEditor.java index 666a066..d0f8d7b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/ManifestEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestEditor.java @@ -16,10 +16,11 @@ package com.android.ide.eclipse.editors.manifest; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidXPathFactory; import com.android.ide.eclipse.editors.AndroidEditor; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors; import com.android.ide.eclipse.editors.manifest.pages.ApplicationPage; @@ -59,19 +60,30 @@ public final class ManifestEditor extends AndroidEditor { /** Root node of the UI element hierarchy */ private UiElementNode mUiManifestNode; - /** Listener to update the root node if the resource framework changes */ - private Runnable mResourceRefreshListener; /** The Application Page tab */ private ApplicationPage mAppPage; /** The Overview Manifest Page tab */ private OverviewPage mOverviewPage; + /** The Permission Page tab */ + private PermissionPage mPermissionPage; + /** The Instrumentation Page tab */ + private InstrumentationPage mInstrumentationPage; + + private IFileListener mMarkerMonitor; + /** * Creates the form editor for AndroidManifest.xml. */ public ManifestEditor() { super(); - initUiManifestNode(); + } + + @Override + public void dispose() { + super.dispose(); + + ResourceMonitor.getMonitor().removeFileListener(mMarkerMonitor); } /** @@ -83,16 +95,19 @@ public final class ManifestEditor extends AndroidEditor { return mUiManifestNode; } - // ---- Base Class Overrides ---- - - @Override - public void dispose() { - if (mResourceRefreshListener != null) { - EditorsPlugin.getDefault().removeResourceChangedListener(mResourceRefreshListener); - mResourceRefreshListener = null; + /** + * Returns the Manifest descriptors for the file being edited. + */ + public AndroidManifestDescriptors getManifestDescriptors() { + AndroidTargetData data = getTargetData(); + if (data != null) { + return data.getManifestDescriptors(); } - super.dispose(); + + return null; } + + // ---- Base Class Overrides ---- /** * Returns whether the "save as" operation is supported by this editor. @@ -115,10 +130,10 @@ public final class ManifestEditor extends AndroidEditor { try { addPage(mOverviewPage = new OverviewPage(this)); addPage(mAppPage = new ApplicationPage(this)); - addPage(new PermissionPage(this)); - addPage(new InstrumentationPage(this)); + addPage(mPermissionPage = new PermissionPage(this)); + addPage(mInstrumentationPage = new InstrumentationPage(this)); } catch (PartInitException e) { - EditorsPlugin.log(e, "Error creating nested page"); //$NON-NLS-1$ + AdtPlugin.log(e, "Error creating nested page"); //$NON-NLS-1$ } } @@ -130,6 +145,7 @@ public final class ManifestEditor extends AndroidEditor { super.setInput(input); IFile inputFile = getInputFile(); if (inputFile != null) { + startMonitoringMarkers(); setPartName(String.format("%1$s Manifest", inputFile.getProject().getName())); } } @@ -141,29 +157,54 @@ public final class ManifestEditor extends AndroidEditor { */ @Override protected void xmlModelChanged(Document xml_doc) { - mUiManifestNode.setXmlDocument(xml_doc); - if (xml_doc != null) { + // create the ui root node on demand. + initUiRootNode(false /*force*/); + + loadFromXml(xml_doc); + + super.xmlModelChanged(xml_doc); + } + + private void loadFromXml(Document xmlDoc) { + mUiManifestNode.setXmlDocument(xmlDoc); + if (xmlDoc != null) { ElementDescriptor manifest_desc = mUiManifestNode.getDescriptor(); try { XPath xpath = AndroidXPathFactory.newXPath(); Node node = (Node) xpath.evaluate("/" + manifest_desc.getXmlName(), //$NON-NLS-1$ - xml_doc, + xmlDoc, XPathConstants.NODE); assert node != null && node.getNodeName().equals(manifest_desc.getXmlName()); // Refresh the manifest UI node and all its descendants mUiManifestNode.loadFromXmlNode(node); - - startMonitoringMarkers(); } catch (XPathExpressionException e) { - EditorsPlugin.log(e, "XPath error when trying to find '%s' element in XML.", //$NON-NLS-1$ + AdtPlugin.log(e, "XPath error when trying to find '%s' element in XML.", //$NON-NLS-1$ manifest_desc.getXmlName()); } } - - super.xmlModelChanged(xml_doc); } + private void onDescriptorsChanged(UiElementNode oldManifestNode) { + mUiManifestNode.reloadFromXmlNode(oldManifestNode.getXmlNode()); + + if (mOverviewPage != null) { + mOverviewPage.refreshUiApplicationNode(); + } + + if (mAppPage != null) { + mAppPage.refreshUiApplicationNode(); + } + + if (mPermissionPage != null) { + mPermissionPage.refreshUiNode(); + } + + if (mInstrumentationPage != null) { + mInstrumentationPage.refreshUiNode(); + } + } + /** * Reads and processes the current markers and adds a listener for marker changes. */ @@ -172,13 +213,15 @@ public final class ManifestEditor extends AndroidEditor { if (inputFile != null) { updateFromExistingMarkers(inputFile); - ResourceMonitor.getMonitor().addFileListener(new IFileListener() { + mMarkerMonitor = new IFileListener() { public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) { if (file.equals(inputFile)) { processMarkerChanges(markerDeltas); } } - }, IResourceDelta.CHANGED); + }; + + ResourceMonitor.getMonitor().addFileListener(mMarkerMonitor, IResourceDelta.CHANGED); } } @@ -192,14 +235,22 @@ public final class ManifestEditor extends AndroidEditor { // get the markers for the file IMarker[] markers = inputFile.findMarkers(AndroidConstants.MARKER_ANDROID, true, IResource.DEPTH_ZERO); + + AndroidManifestDescriptors desc = getManifestDescriptors(); + if (desc != null) { + ElementDescriptor appElement = desc.getApplicationElement(); + + if (appElement != null) { + UiElementNode app_ui_node = mUiManifestNode.findUiChildNode( + appElement.getXmlName()); + List<UiElementNode> children = app_ui_node.getUiChildren(); - UiElementNode app_ui_node = mUiManifestNode.findUiChildNode( - AndroidManifestDescriptors.APPLICATION_ELEMENT.getXmlName()); - List<UiElementNode> children = app_ui_node.getUiChildren(); - - for (IMarker marker : markers) { - processMarker(marker, children, IResourceDelta.ADDED); + for (IMarker marker : markers) { + processMarker(marker, children, IResourceDelta.ADDED); + } + } } + } catch (CoreException e) { // findMarkers can throw an exception, in which case, we'll do nothing. } @@ -210,12 +261,15 @@ public final class ManifestEditor extends AndroidEditor { * @param markerDeltas the list of {@link IMarkerDelta} */ private void processMarkerChanges(IMarkerDelta[] markerDeltas) { - UiElementNode app_ui_node = mUiManifestNode.findUiChildNode( - AndroidManifestDescriptors.APPLICATION_ELEMENT.getXmlName()); - List<UiElementNode> children = app_ui_node.getUiChildren(); - - for (IMarkerDelta markerDelta : markerDeltas) { - processMarker(markerDelta.getMarker(), children, markerDelta.getKind()); + AndroidManifestDescriptors descriptors = getManifestDescriptors(); + if (descriptors != null && descriptors.getApplicationElement() != null) { + UiElementNode app_ui_node = mUiManifestNode.findUiChildNode( + descriptors.getApplicationElement().getXmlName()); + List<UiElementNode> children = app_ui_node.getUiChildren(); + + for (IMarkerDelta markerDelta : markerDeltas) { + processMarker(markerDelta.getMarker(), children, markerDelta.getKind()); + } } } @@ -259,56 +313,62 @@ public final class ManifestEditor extends AndroidEditor { /** * Creates the initial UI Root Node, including the known mandatory elements. + * @param force if true, a new UiManifestNode is recreated even if it already exists. */ - private void initUiManifestNode() { + @Override + protected void initUiRootNode(boolean force) { // The manifest UI node is always created, even if there's no corresponding XML node. - if (mUiManifestNode == null) { - ElementDescriptor manifest_desc = AndroidManifestDescriptors.MANIFEST_ELEMENT; - mUiManifestNode = manifest_desc.createUiNode(); + if (mUiManifestNode != null && force == false) { + return; + } + + + AndroidManifestDescriptors manifestDescriptor = getManifestDescriptors(); + + if (manifestDescriptor != null) { + // save the old manifest node if it exists + UiElementNode oldManifestNode = mUiManifestNode; + + ElementDescriptor manifestElement = manifestDescriptor.getManifestElement(); + mUiManifestNode = manifestElement.createUiNode(); mUiManifestNode.setEditor(this); // Similarly, always create the /manifest/application and /manifest/uses-sdk nodes - ElementDescriptor app_desc = AndroidManifestDescriptors.APPLICATION_ELEMENT; + ElementDescriptor appElement = manifestDescriptor.getApplicationElement(); boolean present = false; for (UiElementNode ui_node : mUiManifestNode.getUiChildren()) { - if (ui_node.getDescriptor() == app_desc) { + if (ui_node.getDescriptor() == appElement) { present = true; break; } } if (!present) { - mUiManifestNode.appendNewUiChild(app_desc); + mUiManifestNode.appendNewUiChild(appElement); } - app_desc = AndroidManifestDescriptors.USES_SDK_ELEMENT; + appElement = manifestDescriptor.getUsesSdkElement(); present = false; for (UiElementNode ui_node : mUiManifestNode.getUiChildren()) { - if (ui_node.getDescriptor() == app_desc) { + if (ui_node.getDescriptor() == appElement) { present = true; break; } } if (!present) { - mUiManifestNode.appendNewUiChild(app_desc); + mUiManifestNode.appendNewUiChild(appElement); } - // Add a listener to refresh the root node if the resource framework changes - // by forcing it to parse its own XML - mResourceRefreshListener = new Runnable() { - public void run() { - commitPages(false /* onSave */); - - mUiManifestNode.reloadFromXmlNode(mUiManifestNode.getXmlNode()); - if (mOverviewPage != null) { - mOverviewPage.refreshUiApplicationNode(); - } - if (mAppPage != null) { - mAppPage.refreshUiApplicationNode(); - } - } - }; - EditorsPlugin.getDefault().addResourceChangedListener(mResourceRefreshListener); - mResourceRefreshListener.run(); + if (oldManifestNode != null) { + onDescriptorsChanged(oldManifestNode); + } + } else { + // create a dummy descriptor/uinode until we have real descriptors + ElementDescriptor desc = new ElementDescriptor("manifest", //$NON-NLS-1$ + "temporary descriptors due to missing decriptors", //$NON-NLS-1$ + null /*tooltip*/, null /*sdk_url*/, null /*attributes*/, + null /*children*/, false /*mandatory*/); + mUiManifestNode = desc.createUiNode(); + mUiManifestNode.setEditor(this); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/ManifestEditorContributor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestEditorContributor.java index 911faa1..911faa1 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/ManifestEditorContributor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestEditorContributor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/ManifestSourceViewerConfig.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestSourceViewerConfig.java index e33e1ef..e33e1ef 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/ManifestSourceViewerConfig.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/ManifestSourceViewerConfig.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java index 171eaee..87a14ad 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java @@ -16,17 +16,18 @@ package com.android.ide.eclipse.editors.manifest.descriptors; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.resources.DeclareStyleableInfo; import com.android.ide.eclipse.common.resources.ResourceType; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor; +import com.android.ide.eclipse.editors.descriptors.IDescriptorProvider; import com.android.ide.eclipse.editors.descriptors.ListAttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.ReferenceAttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor; +import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor; import org.eclipse.core.runtime.IStatus; @@ -45,7 +46,7 @@ import java.util.Map.Entry; * However their sub-elements and attributes are created only when the SDK changes or is * loaded the first time. */ -public class AndroidManifestDescriptors { +public final class AndroidManifestDescriptors implements IDescriptorProvider { private static final String MANIFEST_NODE_NAME = "manifest"; //$NON-NLS-1$ private static final String ANDROID_MANIFEST_STYLEABLE = "AndroidManifest"; //$NON-NLS-1$ @@ -57,28 +58,28 @@ public class AndroidManifestDescriptors { public static final String PACKAGE_ATTR = "package"; //$NON-NLS-1$ /** The {@link ElementDescriptor} for the root Manifest element. */ - public static final ElementDescriptor MANIFEST_ELEMENT; + private final ElementDescriptor MANIFEST_ELEMENT; /** The {@link ElementDescriptor} for the root Application element. */ - public static final ElementDescriptor APPLICATION_ELEMENT; + private final ElementDescriptor APPLICATION_ELEMENT; /** The {@link ElementDescriptor} for the root Instrumentation element. */ - public static final ElementDescriptor INTRUMENTATION_ELEMENT; + private final ElementDescriptor INTRUMENTATION_ELEMENT; /** The {@link ElementDescriptor} for the root Permission element. */ - public static final ElementDescriptor PERMISSION_ELEMENT; + private final ElementDescriptor PERMISSION_ELEMENT; /** The {@link ElementDescriptor} for the root UsesPermission element. */ - public static final ElementDescriptor USES_PERMISSION_ELEMENT; + private final ElementDescriptor USES_PERMISSION_ELEMENT; /** The {@link ElementDescriptor} for the root UsesSdk element. */ - public static final ElementDescriptor USES_SDK_ELEMENT; + private final ElementDescriptor USES_SDK_ELEMENT; /** The {@link ElementDescriptor} for the root PermissionGroup element. */ - public static final ElementDescriptor PERMISSION_GROUP_ELEMENT; + private final ElementDescriptor PERMISSION_GROUP_ELEMENT; /** The {@link ElementDescriptor} for the root PermissionTree element. */ - public static final ElementDescriptor PERMISSION_TREE_ELEMENT; + private final ElementDescriptor PERMISSION_TREE_ELEMENT; /** Private package attribute for the manifest element. Needs to be handled manually. */ - private static final TextAttributeDescriptor PACKAGE_ATTR_DESC; + private final TextAttributeDescriptor PACKAGE_ATTR_DESC; - static { + public AndroidManifestDescriptors() { APPLICATION_ELEMENT = createElement("application", null, true); //$NON-NLS-1$ + no child & mandatory INTRUMENTATION_ELEMENT = createElement("instrumentation"); //$NON-NLS-1$ @@ -109,6 +110,46 @@ public class AndroidManifestDescriptors { null /* nsUri */, "This attribute gives a unique name for the package, using a Java-style naming convention to avoid name collisions.\nFor example, applications published by Google could have names of the form com.google.app.appname"); } + + public ElementDescriptor[] getRootElementDescriptors() { + return new ElementDescriptor[] { MANIFEST_ELEMENT }; + } + + public ElementDescriptor getDescriptor() { + return getManifestElement(); + } + + public ElementDescriptor getApplicationElement() { + return APPLICATION_ELEMENT; + } + + public ElementDescriptor getManifestElement() { + return MANIFEST_ELEMENT; + } + + public ElementDescriptor getUsesSdkElement() { + return USES_SDK_ELEMENT; + } + + public ElementDescriptor getInstrumentationElement() { + return INTRUMENTATION_ELEMENT; + } + + public ElementDescriptor getPermissionElement() { + return PERMISSION_ELEMENT; + } + + public ElementDescriptor getUsesPermissionElement() { + return USES_PERMISSION_ELEMENT; + } + + public ElementDescriptor getPermissionGroupElement() { + return PERMISSION_GROUP_ELEMENT; + } + + public ElementDescriptor getPermissionTreeElement() { + return PERMISSION_TREE_ELEMENT; + } /** * Updates the document descriptor. @@ -118,7 +159,7 @@ public class AndroidManifestDescriptors { * * @param manifestMap The map style => attributes from the attrs_manifest.xml file */ - public static synchronized void updateDescriptors( + public synchronized void updateDescriptors( Map<String, DeclareStyleableInfo> manifestMap) { XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor( @@ -177,7 +218,7 @@ public class AndroidManifestDescriptors { sanityCheck(manifestMap, MANIFEST_ELEMENT); } - + /** * Sets up an attribute override for ANDROID_NAME_ATTR using a ClassAttributeDescriptor * with the specified class name. @@ -223,7 +264,7 @@ public class AndroidManifestDescriptors { * <p/> * Creates an element with no attribute overrides. */ - private static ElementDescriptor createElement( + private ElementDescriptor createElement( String xmlName, ElementDescriptor[] childrenElements, boolean mandatory) { @@ -243,7 +284,7 @@ public class AndroidManifestDescriptors { * <p/> * This version creates an element not mandatory. */ - private static ElementDescriptor createElement(String xmlName) { + private ElementDescriptor createElement(String xmlName) { // Creates an element with no child and not mandatory return createElement(xmlName, null, false); } @@ -253,7 +294,7 @@ public class AndroidManifestDescriptors { * (based on the attribute XML name.) * The attribute is inserted at the beginning of the attribute list. */ - private static void insertAttribute(ElementDescriptor element, AttributeDescriptor newAttr) { + private void insertAttribute(ElementDescriptor element, AttributeDescriptor newAttr) { AttributeDescriptor[] attributes = element.getAttributes(); for (AttributeDescriptor attr : attributes) { if (attr.getXmlLocalName().equals(newAttr.getXmlLocalName())) { @@ -285,7 +326,7 @@ public class AndroidManifestDescriptors { * @param styleName The name of the {@link ElementDescriptor} to inflate. Its XML local name * will be guessed automatically from the style name. */ - private static void inflateElement( + private void inflateElement( Map<String, DeclareStyleableInfo> styleMap, Map<String, Object> overrides, HashMap<String, ElementDescriptor> existingElementDescs, @@ -343,7 +384,7 @@ public class AndroidManifestDescriptors { * <p/> * Capitalizes the first letter and replace non-alphabet by a space followed by a capital. */ - private static String getUiName(String xmlName) { + private String getUiName(String xmlName) { StringBuilder sb = new StringBuilder(); boolean capitalize = true; @@ -376,7 +417,7 @@ public class AndroidManifestDescriptors { * - application => AndroidManifestApplication * - uses-permission => AndroidManifestUsesPermission */ - private static String guessStyleName(String xmlName) { + private String guessStyleName(String xmlName) { StringBuilder sb = new StringBuilder(); if (!xmlName.equals(MANIFEST_NODE_NAME)) { @@ -403,7 +444,7 @@ public class AndroidManifestDescriptors { * manifestMap are actually defined in the actual element descriptors and reachable from * the manifestElement root node. */ - private static void sanityCheck(Map<String, DeclareStyleableInfo> manifestMap, + private void sanityCheck(Map<String, DeclareStyleableInfo> manifestMap, ElementDescriptor manifestElement) { TreeSet<String> elementsDeclared = new TreeSet<String>(); findAllElementNames(manifestElement, elementsDeclared); @@ -434,8 +475,8 @@ public class AndroidManifestDescriptors { } } - EditorsPlugin.log(IStatus.WARNING, "%s", sb.toString()); - EditorsPlugin.printToConsole(null, sb); + AdtPlugin.log(IStatus.WARNING, "%s", sb.toString()); + AdtPlugin.printToConsole((String)null, sb); sb.setLength(0); } @@ -448,8 +489,8 @@ public class AndroidManifestDescriptors { } } - EditorsPlugin.log(IStatus.WARNING, "%s", sb.toString()); - EditorsPlugin.printToConsole(null, sb); + AdtPlugin.log(IStatus.WARNING, "%s", sb.toString()); + AdtPlugin.printToConsole((String)null, sb); } } @@ -459,7 +500,7 @@ public class AndroidManifestDescriptors { * * @return The XML local name for a given style name. */ - private static String guessXmlName(String name) { + private String guessXmlName(String name) { StringBuilder sb = new StringBuilder(); if (ANDROID_MANIFEST_STYLEABLE.equals(name)) { sb.append(MANIFEST_NODE_NAME); @@ -486,7 +527,7 @@ public class AndroidManifestDescriptors { * <p/> * Note: this assumes no circular reference in the tree of {@link ElementDescriptor}s. */ - private static void findAllElementNames(ElementDescriptor element, TreeSet<String> declared) { + private void findAllElementNames(ElementDescriptor element, TreeSet<String> declared) { declared.add(element.getXmlName()); for (ElementDescriptor desc : element.getChildren()) { findAllElementNames(desc, declared); diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/ApplicationAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ApplicationAttributeDescriptor.java index eab7f09..eab7f09 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/ApplicationAttributeDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ApplicationAttributeDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/ClassAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ClassAttributeDescriptor.java index 1144006..1144006 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/ClassAttributeDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ClassAttributeDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/InstrumentationAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/InstrumentationAttributeDescriptor.java index 6e589f7..6e589f7 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/InstrumentationAttributeDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/InstrumentationAttributeDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/ManifestElementDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ManifestElementDescriptor.java index d89292b..d89292b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/ManifestElementDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ManifestElementDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/PackageAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/PackageAttributeDescriptor.java index 34c5d0d..34c5d0d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/PackageAttributeDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/PackageAttributeDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/PostActivityCreationAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/PostActivityCreationAction.java index 3442c24..3442c24 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/PostActivityCreationAction.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/PostActivityCreationAction.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/PostReceiverCreationAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/PostReceiverCreationAction.java index 5a8137d..5a8137d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/PostReceiverCreationAction.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/PostReceiverCreationAction.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/ThemeAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ThemeAttributeDescriptor.java index 4219007..4219007 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/descriptors/ThemeAttributeDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ThemeAttributeDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/model/UiClassAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiClassAttributeNode.java index f8aac1d..f8aac1d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/model/UiClassAttributeNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiClassAttributeNode.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/model/UiManifestElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiManifestElementNode.java index 10ce50d..79295a8 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/model/UiManifestElementNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiManifestElementNode.java @@ -64,12 +64,15 @@ public final class UiManifestElementNode extends UiElementNode { getXmlNode() instanceof Element && getXmlNode().hasAttributes()) { + AndroidManifestDescriptors manifestDescriptors = + getAndroidTarget().getManifestDescriptors(); + // Application and Manifest nodes have a special treatment: they are unique nodes // so we don't bother trying to differentiate their strings and we fall back to // just using the UI name below. ElementDescriptor desc = getDescriptor(); - if (desc != AndroidManifestDescriptors.MANIFEST_ELEMENT && - desc != AndroidManifestDescriptors.APPLICATION_ELEMENT) { + if (desc != manifestDescriptors.getManifestElement() && + desc != manifestDescriptors.getApplicationElement()) { Element elem = (Element) getXmlNode(); String attr = elem.getAttributeNS(AndroidConstants.NS_RESOURCES, AndroidManifestDescriptors.ANDROID_NAME_ATTR); diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/model/UiPackageAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiPackageAttributeNode.java index 5e02273..02fb44f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/model/UiPackageAttributeNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiPackageAttributeNode.java @@ -16,8 +16,8 @@ package com.android.ide.eclipse.editors.manifest.model; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.AndroidEditor; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor; import com.android.ide.eclipse.editors.ui.SectionHelper; @@ -184,7 +184,7 @@ public class UiPackageAttributeNode extends UiTextAttributeNode { JavaCore.create(project), 0); dlg.setTitle("Select Android Package"); dlg.setMessage("Select the package for the Android project."); - dlg.setDefaultImage(EditorsPlugin.getAndroidLogo()); + SelectionDialog.setDefaultImage(AdtPlugin.getAndroidLogo()); if (dlg.open() == Window.OK) { Object[] results = dlg.getResult(); @@ -212,13 +212,13 @@ public class UiPackageAttributeNode extends UiTextAttributeNode { IProject project = getProject(); if (project == null) { - EditorsPlugin.log(IStatus.ERROR, "Failed to get project for UiPackageAttribute"); //$NON-NLS-1$ + AdtPlugin.log(IStatus.ERROR, "Failed to get project for UiPackageAttribute"); //$NON-NLS-1$ return; } IWorkbenchPartSite site = getUiParent().getEditor().getSite(); if (site == null) { - EditorsPlugin.log(IStatus.ERROR, "Failed to get editor site for UiPackageAttribute"); //$NON-NLS-1$ + AdtPlugin.log(IStatus.ERROR, "Failed to get editor site for UiPackageAttribute"); //$NON-NLS-1$ return; } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/ApplicationAttributesPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/ApplicationAttributesPart.java index 25bdb0e..01b0f8f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/ApplicationAttributesPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/ApplicationAttributesPart.java @@ -16,7 +16,7 @@ package com.android.ide.eclipse.editors.manifest.pages; -import com.android.ide.eclipse.editors.EditorsPlugin; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor; import com.android.ide.eclipse.editors.manifest.ManifestEditor; @@ -130,7 +130,7 @@ final class ApplicationAttributesPart extends UiElementPart { } else { // The XML has an extra attribute which wasn't declared in // AndroidManifestDescriptors. This is not a problem, we just ignore it. - EditorsPlugin.log(IStatus.WARNING, + AdtPlugin.log(IStatus.WARNING, "Attribute %1$s not declared in node %2$s, ignored.", //$NON-NLS-1$ attr_desc.getXmlLocalName(), uiElementNode.getDescriptor().getXmlName()); diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/ApplicationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/ApplicationPage.java index 1823278..77527f0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/ApplicationPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/ApplicationPage.java @@ -16,7 +16,7 @@ package com.android.ide.eclipse.editors.manifest.pages; -import com.android.ide.eclipse.editors.EditorsPlugin; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.manifest.ManifestEditor; import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors; @@ -67,7 +67,7 @@ public final class ApplicationPage extends FormPage { super.createFormContent(managedForm); ScrolledForm form = managedForm.getForm(); form.setText("Android Manifest Application"); - form.setImage(EditorsPlugin.getAndroidLogo()); + form.setImage(AdtPlugin.getAndroidLogo()); UiElementNode appUiNode = getUiApplicationNode(); @@ -96,8 +96,14 @@ public final class ApplicationPage extends FormPage { * exists, even if there is no matching XML node. */ private UiElementNode getUiApplicationNode() { - ElementDescriptor desc = AndroidManifestDescriptors.APPLICATION_ELEMENT; - return mEditor.getUiRootNode().findUiChildNode(desc.getXmlName()); + AndroidManifestDescriptors manifestDescriptor = mEditor.getManifestDescriptors(); + if (manifestDescriptor != null) { + ElementDescriptor desc = manifestDescriptor.getApplicationElement(); + return mEditor.getUiRootNode().findUiChildNode(desc.getXmlName()); + } else { + // return the ui root node, as a dummy application root node. + return mEditor.getUiRootNode(); + } } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/ApplicationToggle.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/ApplicationToggle.java index daec98c..139575d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/ApplicationToggle.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/ApplicationToggle.java @@ -16,8 +16,8 @@ package com.android.ide.eclipse.editors.manifest.pages; -import com.android.ide.eclipse.common.resources.FrameworkResourceManager; -import com.android.ide.eclipse.editors.EditorsPlugin; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; import com.android.ide.eclipse.editors.manifest.ManifestEditor; import com.android.ide.eclipse.editors.ui.UiElementPart; @@ -130,10 +130,10 @@ final class ApplicationToggle extends UiElementPart { if (tooltip != null) { tooltip = DescriptorsUtils.formatFormText(tooltip, getUiElementNode().getDescriptor(), - FrameworkResourceManager.getInstance().getDocumentationBaseUrl()); + Sdk.getCurrent().getDocumentationBaseUrl()); mTooltipFormText.setText(tooltip, true /* parseTags */, true /* expandURLs */); - mTooltipFormText.setImage(DescriptorsUtils.IMAGE_KEY, EditorsPlugin.getAndroidLogo()); + mTooltipFormText.setImage(DescriptorsUtils.IMAGE_KEY, AdtPlugin.getAndroidLogo()); mTooltipFormText.addHyperlinkListener(getEditor().createHyperlinkListener()); isVisible = true; } @@ -156,23 +156,32 @@ final class ApplicationToggle extends UiElementPart { public void widgetSelected(SelectionEvent e) { super.widgetSelected(e); if (!mInternalModification && getUiElementNode() != null) { - getUiElementNode().getEditor().editXmlModel(new Runnable() { - public void run() { - if (mCheckbox.getSelection()) { - // The user wants an <application> node. Either restore a previous one - // or create a full new one. - boolean create = true; - if (mUndoXmlNode != null) { - create = !restoreApplicationNode(); + getUiElementNode().getEditor().wrapUndoRecording( + mCheckbox.getSelection() + ? "Create or restore Application node" + : "Remove Application node", + new Runnable() { + public void run() { + getUiElementNode().getEditor().editXmlModel(new Runnable() { + public void run() { + if (mCheckbox.getSelection()) { + // The user wants an <application> node. + // Either restore a previous one + // or create a full new one. + boolean create = true; + if (mUndoXmlNode != null) { + create = !restoreApplicationNode(); + } + if (create) { + getUiElementNode().createXmlNode(); + } + } else { + // Users no longer wants the <application> node. + removeApplicationNode(); + } + } + }); } - if (create) { - getUiElementNode().createXmlNode(); - } - } else { - // Users no longer wants the <application> node. - removeApplicationNode(); - } - } }); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/InstrumentationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/InstrumentationPage.java index 8eb6765..86d0dd1 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/InstrumentationPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/InstrumentationPage.java @@ -16,7 +16,7 @@ package com.android.ide.eclipse.editors.manifest.pages; -import com.android.ide.eclipse.editors.EditorsPlugin; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.manifest.ManifestEditor; import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors; @@ -37,6 +37,8 @@ public final class InstrumentationPage extends FormPage { /** Container editor */ ManifestEditor mEditor; + private UiTreeBlock mTreeBlock; + public InstrumentationPage(ManifestEditor editor) { super(editor, PAGE_ID, "Instrumentation"); // tab's label, keep it short mEditor = editor; @@ -52,14 +54,39 @@ public final class InstrumentationPage extends FormPage { super.createFormContent(managedForm); ScrolledForm form = managedForm.getForm(); form.setText("Android Manifest Instrumentation"); - form.setImage(EditorsPlugin.getAndroidLogo()); + form.setImage(AdtPlugin.getAndroidLogo()); UiElementNode manifest = mEditor.getUiRootNode(); - UiTreeBlock block = new UiTreeBlock(mEditor, manifest, + AndroidManifestDescriptors manifestDescriptor = mEditor.getManifestDescriptors(); + + ElementDescriptor[] descriptorFilters = null; + if (manifestDescriptor != null) { + descriptorFilters = new ElementDescriptor[] { + manifestDescriptor.getInstrumentationElement(), + }; + } + + mTreeBlock = new UiTreeBlock(mEditor, manifest, true /* autoCreateRoot */, - new ElementDescriptor[] { AndroidManifestDescriptors.INTRUMENTATION_ELEMENT }, + descriptorFilters, "Instrumentation", "List of instrumentations defined in the manifest"); - block.createContent(managedForm); + mTreeBlock.createContent(managedForm); + } + + /** + * Changes and refreshes the Application UI node handled by the sub parts. + */ + public void refreshUiNode() { + if (mTreeBlock != null) { + UiElementNode manifest = mEditor.getUiRootNode(); + AndroidManifestDescriptors manifestDescriptor = mEditor.getManifestDescriptors(); + + mTreeBlock.changeRootAndDescriptors(manifest, + new ElementDescriptor[] { + manifestDescriptor.getInstrumentationElement() + }, + true /* refresh */); + } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/OverviewExportPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/OverviewExportPart.java index 4e6521c..66af84c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/OverviewExportPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/OverviewExportPart.java @@ -49,9 +49,12 @@ final class OverviewExportPart extends ManifestSectionPart { StringBuffer buf = new StringBuffer(); buf.append("<form><li><a href=\"wizard\">"); //$NON-NLS-1$ - buf.append("Use the export wizard"); - buf.append("</a></li><li><a href=\"manual\">"); //$NON-NLS-1$ - buf.append("Export an unsigned apk"); + buf.append("Use the Export Wizard"); + buf.append("</a>"); //$NON-NLS-1$ + buf.append(" to export and sign an APK"); + buf.append("</li>"); //$NON-NLS-1$ + buf.append("<li><a href=\"manual\">"); //$NON-NLS-1$ + buf.append("Export an unsigned APK"); buf.append("</a>"); //$NON-NLS-1$ buf.append(" and sign it manually"); buf.append("</li></form>"); //$NON-NLS-1$ diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/OverviewInfoPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/OverviewInfoPart.java index 182c6f3..026b760 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/OverviewInfoPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/OverviewInfoPart.java @@ -49,12 +49,19 @@ final class OverviewInfoPart extends UiElementPart { * and can't be null, by design, because it's a mandatory node. */ private static UiElementNode getManifestUiNode(ManifestEditor editor) { - ElementDescriptor desc = AndroidManifestDescriptors.MANIFEST_ELEMENT; - if (editor.getUiRootNode().getDescriptor() == desc) { - return editor.getUiRootNode(); - } else { - return editor.getUiRootNode().findUiChildNode(desc.getXmlName()); + AndroidManifestDescriptors manifestDescriptors = editor.getManifestDescriptors(); + if (manifestDescriptors != null) { + ElementDescriptor desc = manifestDescriptors.getManifestElement(); + if (editor.getUiRootNode().getDescriptor() == desc) { + return editor.getUiRootNode(); + } else { + return editor.getUiRootNode().findUiChildNode(desc.getXmlName()); + } } + + // No manifest descriptor: we have a dummy UiRootNode, so we return that. + // The editor will be reloaded once we have the proper descriptors anyway. + return editor.getUiRootNode(); } /** @@ -62,8 +69,15 @@ final class OverviewInfoPart extends UiElementPart { * exists, even if there is no matching XML node. */ private UiElementNode getUsesSdkUiNode(ManifestEditor editor) { - ElementDescriptor desc = AndroidManifestDescriptors.USES_SDK_ELEMENT; - return editor.getUiRootNode().findUiChildNode(desc.getXmlName()); + AndroidManifestDescriptors manifestDescriptors = editor.getManifestDescriptors(); + if (manifestDescriptors != null) { + ElementDescriptor desc = manifestDescriptors.getUsesSdkElement(); + return editor.getUiRootNode().findUiChildNode(desc.getXmlName()); + } + + // No manifest descriptor: we have a dummy UiRootNode, so we return that. + // The editor will be reloaded once we have the proper descriptors anyway. + return editor.getUiRootNode(); } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/OverviewLinksPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/OverviewLinksPart.java index 7675fa2..d637a8f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/OverviewLinksPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/OverviewLinksPart.java @@ -16,7 +16,7 @@ package com.android.ide.eclipse.editors.manifest.pages; -import com.android.ide.eclipse.editors.EditorsPlugin; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.manifest.ManifestEditor; import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors; @@ -33,8 +33,12 @@ import org.eclipse.ui.forms.widgets.Section; */ final class OverviewLinksPart extends ManifestSectionPart { + private final ManifestEditor mEditor; + private FormText mFormText; + public OverviewLinksPart(Composite body, FormToolkit toolkit, ManifestEditor editor) { super(body, toolkit, Section.TWISTIE | Section.EXPANDED, true /* description */); + mEditor = editor; Section section = getSection(); section.setText("Links"); section.setDescription("The content of the Android Manifest is made up of three sections. You can also edit the XML directly."); @@ -75,13 +79,40 @@ final class OverviewLinksPart extends ManifestSectionPart { buf.append("</li>"); //$NON-NLS-1$ buf.append("</form>"); //$NON-NLS-1$ - FormText text = createFormText(table, toolkit, true, buf.toString(), + mFormText = createFormText(table, toolkit, true, buf.toString(), false /* setupLayoutData */); - text.setImage("android_img", EditorsPlugin.getAndroidLogo()); - text.setImage("app_img", getIcon(AndroidManifestDescriptors.APPLICATION_ELEMENT)); - text.setImage("perm_img", getIcon(AndroidManifestDescriptors.PERMISSION_ELEMENT)); - text.setImage("inst_img", getIcon(AndroidManifestDescriptors.INTRUMENTATION_ELEMENT)); - text.addHyperlinkListener(editor.createHyperlinkListener()); + + AndroidManifestDescriptors manifestDescriptor = editor.getManifestDescriptors(); + + Image androidLogo = AdtPlugin.getAndroidLogo(); + mFormText.setImage("android_img", androidLogo); //$NON-NLS-1$ + + if (manifestDescriptor != null) { + mFormText.setImage("app_img", getIcon(manifestDescriptor.getApplicationElement())); //$NON-NLS-1$ + mFormText.setImage("perm_img", getIcon(manifestDescriptor.getPermissionElement())); //$NON-NLS-1$ + mFormText.setImage("inst_img", getIcon(manifestDescriptor.getInstrumentationElement())); //$NON-NLS-1$ + } else { + mFormText.setImage("app_img", androidLogo); //$NON-NLS-1$ + mFormText.setImage("perm_img", androidLogo); //$NON-NLS-1$ + mFormText.setImage("inst_img", androidLogo); //$NON-NLS-1$ + } + mFormText.addHyperlinkListener(editor.createHyperlinkListener()); + } + + /** + * Update the UI with information from the new descriptors. + * <p/>At this point, this only refreshes the icons. + * <p/> + * This is called by {@link OverviewPage#refreshUiApplicationNode()} when the + * SDK has changed. + */ + public void onSdkChanged() { + AndroidManifestDescriptors manifestDescriptor = mEditor.getManifestDescriptors(); + if (manifestDescriptor != null) { + mFormText.setImage("app_img", getIcon(manifestDescriptor.getApplicationElement())); //$NON-NLS-1$ + mFormText.setImage("perm_img", getIcon(manifestDescriptor.getPermissionElement())); //$NON-NLS-1$ + mFormText.setImage("inst_img", getIcon(manifestDescriptor.getInstrumentationElement())); //$NON-NLS-1$ + } } private Image getIcon(ElementDescriptor desc) { @@ -89,6 +120,6 @@ final class OverviewLinksPart extends ManifestSectionPart { return desc.getIcon(); } - return EditorsPlugin.getAndroidLogo(); + return AdtPlugin.getAndroidLogo(); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/OverviewPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/OverviewPage.java index 9e8925a..62954bd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/OverviewPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/OverviewPage.java @@ -16,7 +16,7 @@ package com.android.ide.eclipse.editors.manifest.pages; -import com.android.ide.eclipse.editors.EditorsPlugin; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.manifest.ManifestEditor; import org.eclipse.swt.widgets.Composite; @@ -43,6 +43,8 @@ public final class OverviewPage extends FormPage { ManifestEditor mEditor; /** Overview part (attributes for manifest) */ private OverviewInfoPart mOverviewPart; + /** Overview link part */ + private OverviewLinksPart mOverviewLinkPart; public OverviewPage(ManifestEditor editor) { super(editor, PAGE_ID, "Overview"); // tab's label, user visible, keep it short @@ -59,7 +61,7 @@ public final class OverviewPage extends FormPage { super.createFormContent(managedForm); ScrolledForm form = managedForm.getForm(); form.setText("Android Manifest Overview"); - form.setImage(EditorsPlugin.getAndroidLogo()); + form.setImage(AdtPlugin.getAndroidLogo()); Composite body = form.getBody(); FormToolkit toolkit = managedForm.getToolkit(); @@ -69,7 +71,8 @@ public final class OverviewPage extends FormPage { mOverviewPart = new OverviewInfoPart(body, toolkit, mEditor); managedForm.addPart(mOverviewPart); managedForm.addPart(new OverviewExportPart(this, body, toolkit, mEditor)); - managedForm.addPart(new OverviewLinksPart(body, toolkit, mEditor)); + mOverviewLinkPart = new OverviewLinksPart(body, toolkit, mEditor); + managedForm.addPart(mOverviewLinkPart); } /** @@ -79,5 +82,9 @@ public final class OverviewPage extends FormPage { if (mOverviewPart != null) { mOverviewPart.onSdkChanged(); } + + if (mOverviewLinkPart != null) { + mOverviewLinkPart.onSdkChanged(); + } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/PermissionPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/PermissionPage.java index a7c0ad5..41ba22e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/manifest/pages/PermissionPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/pages/PermissionPage.java @@ -16,7 +16,7 @@ package com.android.ide.eclipse.editors.manifest.pages; -import com.android.ide.eclipse.editors.EditorsPlugin; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.manifest.ManifestEditor; import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors; @@ -41,6 +41,8 @@ public final class PermissionPage extends FormPage { /** Container editor */ ManifestEditor mEditor; + private UiTreeBlock mTreeBlock; + public PermissionPage(ManifestEditor editor) { super(editor, PAGE_ID, "Permissions"); // tab label, keep it short mEditor = editor; @@ -56,19 +58,44 @@ public final class PermissionPage extends FormPage { super.createFormContent(managedForm); ScrolledForm form = managedForm.getForm(); form.setText("Android Manifest Permissions"); - form.setImage(EditorsPlugin.getAndroidLogo()); + form.setImage(AdtPlugin.getAndroidLogo()); UiElementNode manifest = mEditor.getUiRootNode(); - UiTreeBlock block = new UiTreeBlock(mEditor, manifest, + AndroidManifestDescriptors manifestDescriptor = mEditor.getManifestDescriptors(); + + ElementDescriptor[] descriptorFilters = null; + if (manifestDescriptor != null) { + descriptorFilters = new ElementDescriptor[] { + manifestDescriptor.getPermissionElement(), + manifestDescriptor.getUsesPermissionElement(), + manifestDescriptor.getPermissionGroupElement(), + manifestDescriptor.getPermissionTreeElement() + }; + } + mTreeBlock = new UiTreeBlock(mEditor, manifest, true /* autoCreateRoot */, - new ElementDescriptor[] { - AndroidManifestDescriptors.PERMISSION_ELEMENT, - AndroidManifestDescriptors.USES_PERMISSION_ELEMENT, - AndroidManifestDescriptors.PERMISSION_GROUP_ELEMENT, - AndroidManifestDescriptors.PERMISSION_TREE_ELEMENT - }, + descriptorFilters, "Permissions", "List of permissions defined and used by the manifest"); - block.createContent(managedForm); + mTreeBlock.createContent(managedForm); + } + + /** + * Changes and refreshes the Application UI node handled by the sub parts. + */ + public void refreshUiNode() { + if (mTreeBlock != null) { + UiElementNode manifest = mEditor.getUiRootNode(); + AndroidManifestDescriptors manifestDescriptor = mEditor.getManifestDescriptors(); + + mTreeBlock.changeRootAndDescriptors(manifest, + new ElementDescriptor[] { + manifestDescriptor.getPermissionElement(), + manifestDescriptor.getUsesPermissionElement(), + manifestDescriptor.getPermissionGroupElement(), + manifestDescriptor.getPermissionTreeElement() + }, + true /* refresh */); + } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/menu/MenuContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/menu/MenuContentAssist.java index 57b9a42..bf76d53 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/menu/MenuContentAssist.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/menu/MenuContentAssist.java @@ -16,8 +16,8 @@ package com.android.ide.eclipse.editors.menu; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; import com.android.ide.eclipse.editors.AndroidContentAssist; -import com.android.ide.eclipse.editors.menu.descriptors.MenuDescriptors; /** * Content Assist Processor for /res/menu XML files @@ -28,6 +28,6 @@ class MenuContentAssist extends AndroidContentAssist { * Constructor for LayoutContentAssist */ public MenuContentAssist() { - super(MenuDescriptors.getInstance().getDescriptor().getChildren()); + super(AndroidTargetData.DESCRIPTOR_MENU); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/menu/MenuEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/menu/MenuEditor.java index 4bf02fa..cff1746 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/menu/MenuEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/menu/MenuEditor.java @@ -16,11 +16,12 @@ package com.android.ide.eclipse.editors.menu; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; +import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidXPathFactory; import com.android.ide.eclipse.editors.AndroidEditor; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.editors.menu.descriptors.MenuDescriptors; import com.android.ide.eclipse.editors.uimodel.UiElementNode; import org.eclipse.core.resources.IFile; @@ -40,19 +41,16 @@ import javax.xml.xpath.XPathExpressionException; */ public class MenuEditor extends AndroidEditor { - public static final String ID = "com.android.ide.eclipse.editors.menu.MenuEditor"; //$NON-NLS-1$ + public static final String ID = AndroidConstants.EDITORS_NAMESPACE + ".menu.MenuEditor"; //$NON-NLS-1$ /** Root node of the UI element hierarchy */ private UiElementNode mUiRootNode; - /** Listener to update the root node if the resource framework changes */ - private Runnable mResourceRefreshListener; /** * Creates the form editor for resources XML files. */ public MenuEditor() { super(); - initUiRootNode(); } /** @@ -66,15 +64,6 @@ public class MenuEditor extends AndroidEditor { // ---- Base Class Overrides ---- - @Override - public void dispose() { - if (mResourceRefreshListener != null) { - EditorsPlugin.getDefault().removeResourceChangedListener(mResourceRefreshListener); - mResourceRefreshListener = null; - } - super.dispose(); - } - /** * Returns whether the "save as" operation is supported by this editor. * <p/> @@ -96,7 +85,7 @@ public class MenuEditor extends AndroidEditor { try { addPage(new MenuTreePage(this)); } catch (PartInitException e) { - EditorsPlugin.log(e, "Error creating nested page"); //$NON-NLS-1$ + AdtPlugin.log(e, "Error creating nested page"); //$NON-NLS-1$ } } @@ -121,6 +110,9 @@ public class MenuEditor extends AndroidEditor { */ @Override protected void xmlModelChanged(Document xml_doc) { + // init the ui root on demand + initUiRootNode(false /*force*/); + mUiRootNode.setXmlDocument(xml_doc); if (xml_doc != null) { ElementDescriptor root_desc = mUiRootNode.getDescriptor(); @@ -139,38 +131,54 @@ public class MenuEditor extends AndroidEditor { // TODO ? startMonitoringMarkers(); } catch (XPathExpressionException e) { - EditorsPlugin.log(e, "XPath error when trying to find '%s' element in XML.", //$NON-NLS-1$ + AdtPlugin.log(e, "XPath error when trying to find '%s' element in XML.", //$NON-NLS-1$ root_desc.getXmlName()); } } super.xmlModelChanged(xml_doc); } - - // ---- Local Methods ---- - /** * Creates the initial UI Root Node, including the known mandatory elements. + * @param force if true, a new UiRootNode is recreated even if it already exists. */ - private void initUiRootNode() { + @Override + protected void initUiRootNode(boolean force) { // The root UI node is always created, even if there's no corresponding XML node. - if (mUiRootNode == null) { - ElementDescriptor desc = MenuDescriptors.getInstance().getDescriptor(); + if (mUiRootNode == null || force) { + Document doc = null; + if (mUiRootNode != null) { + doc = mUiRootNode.getXmlDocument(); + } + + // get the target data from the opened file (and its project) + AndroidTargetData data = getTargetData(); + + ElementDescriptor desc; + if (data == null) { + desc = new ElementDescriptor("temp", null /*children*/); + } else { + desc = data.getMenuDescriptors().getDescriptor(); + } + mUiRootNode = desc.createUiNode(); mUiRootNode.setEditor(this); - // Add a listener to refresh the root node if the resource framework changes - // by forcing it to parse its own XML - mResourceRefreshListener = new Runnable() { - public void run() { - commitPages(false /* onSave */); + onDescriptorsChanged(doc); + } + } - mUiRootNode.reloadFromXmlNode(mUiRootNode.getXmlNode()); - } - }; - EditorsPlugin.getDefault().addResourceChangedListener(mResourceRefreshListener); - mResourceRefreshListener.run(); + // ---- Local Methods ---- + + /** + * Reloads the UI manifest node from the XML, and calls the pages to update. + */ + private void onDescriptorsChanged(Document document) { + if (document != null) { + mUiRootNode.loadFromXmlNode(document); + } else { + mUiRootNode.reloadFromXmlNode(mUiRootNode.getXmlNode()); } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/menu/MenuSourceViewerConfig.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/menu/MenuSourceViewerConfig.java index a5e3b09..a5e3b09 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/menu/MenuSourceViewerConfig.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/menu/MenuSourceViewerConfig.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/menu/MenuTreePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/menu/MenuTreePage.java index 994074e..edbfa5e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/menu/MenuTreePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/menu/MenuTreePage.java @@ -16,7 +16,7 @@ package com.android.ide.eclipse.editors.menu; -import com.android.ide.eclipse.editors.EditorsPlugin; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.ui.tree.UiTreeBlock; import com.android.ide.eclipse.editors.uimodel.UiElementNode; @@ -49,7 +49,7 @@ public final class MenuTreePage extends FormPage { super.createFormContent(managedForm); ScrolledForm form = managedForm.getForm(); form.setText("Android Menu"); - form.setImage(EditorsPlugin.getAndroidLogo()); + form.setImage(AdtPlugin.getAndroidLogo()); UiElementNode rootNode = mEditor.getUiRootNode(); UiTreeBlock block = new UiTreeBlock(mEditor, rootNode, diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/menu/descriptors/MenuDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/menu/descriptors/MenuDescriptors.java index 941f736..34c7bb2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/menu/descriptors/MenuDescriptors.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/menu/descriptors/MenuDescriptors.java @@ -21,6 +21,7 @@ import com.android.ide.eclipse.common.resources.DeclareStyleableInfo; import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.editors.descriptors.IDescriptorProvider; import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor; import java.util.ArrayList; @@ -30,32 +31,22 @@ import java.util.Map; /** * Complete description of the menu structure. */ -public class MenuDescriptors { +public final class MenuDescriptors implements IDescriptorProvider { public static final String MENU_ROOT_ELEMENT = "menu"; //$NON-NLS-1$ - - - /** Singleton instance */ - private static MenuDescriptors sThis; - /** The root element descriptor. */ private ElementDescriptor mDescriptor = null; - /** Returns a singleton instance of the {@link MenuDescriptors}. */ - public static synchronized MenuDescriptors getInstance() { - if (sThis == null) { - sThis = new MenuDescriptors(); - sThis.updateDescriptors(null); - } - return sThis; - } - /** @return the root descriptor. */ public ElementDescriptor getDescriptor() { return mDescriptor; } - + + public ElementDescriptor[] getRootElementDescriptors() { + return mDescriptor.getChildren(); + } + /** * Updates the document descriptor. * <p/> diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/ResourcesContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/ResourcesContentAssist.java index 9fe15ab..c9c8e17 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/ResourcesContentAssist.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/ResourcesContentAssist.java @@ -16,9 +16,8 @@ package com.android.ide.eclipse.editors.resources; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; import com.android.ide.eclipse.editors.AndroidContentAssist; -import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.editors.resources.descriptors.ResourcesDescriptors; /** * Content Assist Processor for /res/values and /res/drawable XML files @@ -29,6 +28,6 @@ class ResourcesContentAssist extends AndroidContentAssist { * Constructor for ResourcesContentAssist */ public ResourcesContentAssist() { - super(new ElementDescriptor[] { ResourcesDescriptors.RESOURCES_ELEMENT }); + super(AndroidTargetData.DESCRIPTOR_RESOURCES); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/ResourcesEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/ResourcesEditor.java index bad5699..46a9112 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/ResourcesEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/ResourcesEditor.java @@ -16,9 +16,10 @@ package com.android.ide.eclipse.editors.resources; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidXPathFactory; import com.android.ide.eclipse.editors.AndroidEditor; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.resources.descriptors.ResourcesDescriptors; import com.android.ide.eclipse.editors.uimodel.UiElementNode; @@ -41,7 +42,7 @@ import javax.xml.xpath.XPathExpressionException; */ public class ResourcesEditor extends AndroidEditor { - public static final String ID = "com.android.ide.eclipse.editors.resources.ResourcesEditor"; //$NON-NLS-1$ + public static final String ID = AndroidConstants.EDITORS_NAMESPACE + ".resources.ResourcesEditor"; //$NON-NLS-1$ /** Root node of the UI element hierarchy */ private UiElementNode mUiResourcesNode; @@ -52,7 +53,6 @@ public class ResourcesEditor extends AndroidEditor { */ public ResourcesEditor() { super(); - initUiResourcesNode(); } /** @@ -87,8 +87,8 @@ public class ResourcesEditor extends AndroidEditor { try { addPage(new ResourcesTreePage(this)); } catch (PartInitException e) { - EditorsPlugin.log(IStatus.ERROR, "Error creating nested page"); //$NON-NLS-1$ - EditorsPlugin.getDefault().getLog().log(e.getStatus()); + AdtPlugin.log(IStatus.ERROR, "Error creating nested page"); //$NON-NLS-1$ + AdtPlugin.getDefault().getLog().log(e.getStatus()); } } @@ -113,9 +113,13 @@ public class ResourcesEditor extends AndroidEditor { */ @Override protected void xmlModelChanged(Document xml_doc) { + // init the ui root on demand + initUiRootNode(false /*force*/); + mUiResourcesNode.setXmlDocument(xml_doc); if (xml_doc != null) { - ElementDescriptor resources_desc = ResourcesDescriptors.RESOURCES_ELEMENT; + ElementDescriptor resources_desc = + ResourcesDescriptors.getInstance().getElementDescriptor(); try { XPath xpath = AndroidXPathFactory.newXPath(); Node node = (Node) xpath.evaluate("/" + resources_desc.getXmlName(), //$NON-NLS-1$ @@ -126,27 +130,35 @@ public class ResourcesEditor extends AndroidEditor { // Refresh the manifest UI node and all its descendants mUiResourcesNode.loadFromXmlNode(node); } catch (XPathExpressionException e) { - EditorsPlugin.log(e, "XPath error when trying to find '%s' element in XML.", //$NON-NLS-1$ + AdtPlugin.log(e, "XPath error when trying to find '%s' element in XML.", //$NON-NLS-1$ resources_desc.getXmlName()); } } super.xmlModelChanged(xml_doc); } - - // ---- Local Methods ---- - /** * Creates the initial UI Root Node, including the known mandatory elements. + * @param force if true, a new UiRootNode is recreated even if it already exists. */ - private void initUiResourcesNode() { + @Override + protected void initUiRootNode(boolean force) { // The manifest UI node is always created, even if there's no corresponding XML node. - if (mUiResourcesNode == null) { - ElementDescriptor resources_desc = ResourcesDescriptors.RESOURCES_ELEMENT; + if (mUiResourcesNode == null || force) { + ElementDescriptor resources_desc = + ResourcesDescriptors.getInstance().getElementDescriptor(); mUiResourcesNode = resources_desc.createUiNode(); mUiResourcesNode.setEditor(this); + + onDescriptorsChanged(); } } + // ---- Local Methods ---- + + private void onDescriptorsChanged() { + // nothing to be done, as the descriptor are static for now. + // FIXME Update when the descriptors are not static + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/ResourcesSourceViewerConfig.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/ResourcesSourceViewerConfig.java index 1804312..1804312 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/ResourcesSourceViewerConfig.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/ResourcesSourceViewerConfig.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/ResourcesTreePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/ResourcesTreePage.java index 8cabeca..5c1b0e1 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/ResourcesTreePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/ResourcesTreePage.java @@ -16,7 +16,7 @@ package com.android.ide.eclipse.editors.resources; -import com.android.ide.eclipse.editors.EditorsPlugin; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.resources.manager.ResourceFolder; import com.android.ide.eclipse.editors.resources.manager.ResourceManager; import com.android.ide.eclipse.editors.ui.tree.UiTreeBlock; @@ -72,7 +72,7 @@ public final class ResourcesTreePage extends FormPage { form.setText("Android Resources"); } - form.setImage(EditorsPlugin.getAndroidLogo()); + form.setImage(AdtPlugin.getAndroidLogo()); UiElementNode resources = mEditor.getUiRootNode(); UiTreeBlock block = new UiTreeBlock(mEditor, resources, diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/CountryCodeQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/CountryCodeQualifier.java index 7670fa2..1d01260 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/CountryCodeQualifier.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/CountryCodeQualifier.java @@ -92,7 +92,7 @@ public final class CountryCodeQualifier extends ResourceQualifier { @Override public Image getIcon() { - return IconFactory.getInstance().getIcon("world"); //$NON-NLS-1$ + return IconFactory.getInstance().getIcon("mcc"); //$NON-NLS-1$ } @Override diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/FolderConfiguration.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/FolderConfiguration.java index 3c3e11f..3c3e11f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/FolderConfiguration.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/FolderConfiguration.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/KeyboardStateQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/KeyboardStateQualifier.java index ad232ed..ad232ed 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/KeyboardStateQualifier.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/KeyboardStateQualifier.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/LanguageQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/LanguageQualifier.java index 99c3a43..99c3a43 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/LanguageQualifier.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/LanguageQualifier.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/NavigationMethodQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/NavigationMethodQualifier.java index 1a2cf53..1a2cf53 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/NavigationMethodQualifier.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/NavigationMethodQualifier.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/NetworkCodeQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/NetworkCodeQualifier.java index ce527a4..ce527a4 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/NetworkCodeQualifier.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/NetworkCodeQualifier.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/PixelDensityQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/PixelDensityQualifier.java index 0fd05bf..0fd05bf 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/PixelDensityQualifier.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/PixelDensityQualifier.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/RegionQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/RegionQualifier.java index dc4d5fa..dc4d5fa 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/RegionQualifier.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/RegionQualifier.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/ResourceQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/ResourceQualifier.java index 0257afa..0257afa 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/ResourceQualifier.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/ResourceQualifier.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/ScreenDimensionQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/ScreenDimensionQualifier.java index a2cc789..a2cc789 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/ScreenDimensionQualifier.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/ScreenDimensionQualifier.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/ScreenOrientationQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/ScreenOrientationQualifier.java index e30930f..e30930f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/ScreenOrientationQualifier.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/ScreenOrientationQualifier.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/TextInputMethodQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/TextInputMethodQualifier.java index de40138..de40138 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/TextInputMethodQualifier.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/TextInputMethodQualifier.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/TouchScreenQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/TouchScreenQualifier.java index 2390e2c..2390e2c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/configurations/TouchScreenQualifier.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/TouchScreenQualifier.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/descriptors/ColorValueDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/descriptors/ColorValueDescriptor.java index 92288ba..92288ba 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/descriptors/ColorValueDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/descriptors/ColorValueDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/descriptors/ItemElementDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/descriptors/ItemElementDescriptor.java index bf83d52..bf83d52 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/descriptors/ItemElementDescriptor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/descriptors/ItemElementDescriptor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/descriptors/ResourcesDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/descriptors/ResourcesDescriptors.java index 4769cef..1075897 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/descriptors/ResourcesDescriptors.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/descriptors/ResourcesDescriptors.java @@ -20,16 +20,16 @@ import com.android.ide.eclipse.common.resources.ResourceType; import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.descriptors.FlagAttributeDescriptor; +import com.android.ide.eclipse.editors.descriptors.IDescriptorProvider; import com.android.ide.eclipse.editors.descriptors.ListAttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.TextValueDescriptor; /** - * Complete description of the AndroidManifest.xml structure. + * Complete description of the structure for resources XML files (under res/values/) */ -public class ResourcesDescriptors { - +public class ResourcesDescriptors implements IDescriptorProvider { // Public attributes names, attributes descriptors and elements descriptors @@ -38,14 +38,33 @@ public class ResourcesDescriptors { public static final String NAME_ATTR = "name"; //$NON-NLS-1$ public static final String TYPE_ATTR = "type"; //$NON-NLS-1$ - /** The {@link ElementDescriptor} for the root Manifest element. */ - public static final ElementDescriptor RESOURCES_ELEMENT; - + private static final ResourcesDescriptors sThis = new ResourcesDescriptors(); + + /** The {@link ElementDescriptor} for the root Resources element. */ + public final ElementDescriptor mResourcesElement; - static { + public static ResourcesDescriptors getInstance() { + return sThis; + } + + /* + * @see com.android.ide.eclipse.editors.descriptors.IDescriptorProvider#getRootElementDescriptors() + */ + public ElementDescriptor[] getRootElementDescriptors() { + return new ElementDescriptor[] { mResourcesElement }; + } + + public ElementDescriptor getDescriptor() { + return mResourcesElement; + } + + public ElementDescriptor getElementDescriptor() { + return mResourcesElement; + } + + private ResourcesDescriptors() { // Common attributes used in many placed - // Elements @@ -189,7 +208,61 @@ public class ResourcesDescriptors { }, false /* not mandatory */); - RESOURCES_ELEMENT = new ElementDescriptor( + ElementDescriptor string_array_element = new ElementDescriptor( + "string-array", //$NON-NLS-1$ + "String Array", + "An array of strings. Strings are added as underlying item elements to the array.", + null, // tooltips + new AttributeDescriptor[] { + new TextAttributeDescriptor(NAME_ATTR, + "Name*", + null /* nsUri */, + "The mandatory name used in referring to this string array."), + }, + new ElementDescriptor[] { + new ElementDescriptor( + "item", //$NON-NLS-1$ + "Item", + "A string value to use in this string array.", + null, // tooltip + new AttributeDescriptor[] { + new TextValueDescriptor( + "Value*", + "A mandatory string.") + }, + null, // no child nodes + false /* not mandatory */) + }, + false /* not mandatory */); + + ElementDescriptor integer_array_element = new ElementDescriptor( + "integer-array", //$NON-NLS-1$ + "Integer Array", + "An array of integers. Integers are added as underlying item elements to the array.", + null, // tooltips + new AttributeDescriptor[] { + new TextAttributeDescriptor(NAME_ATTR, + "Name*", + null /* nsUri */, + "The mandatory name used in referring to this integer array."), + }, + new ElementDescriptor[] { + new ElementDescriptor( + "item", //$NON-NLS-1$ + "Item", + "An integer value to use in this integer array.", + null, // tooltip + new AttributeDescriptor[] { + new TextValueDescriptor( + "Value*", + "A mandatory integer.") + }, + null, // no child nodes + false /* not mandatory */) + }, + false /* not mandatory */); + + mResourcesElement = new ElementDescriptor( ROOT_ELEMENT, "Resources", null, @@ -201,7 +274,9 @@ public class ResourcesDescriptors { dimen_element, drawable_element, style_element, - item_element + item_element, + string_array_element, + integer_array_element, }, true /* mandatory */); } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java index cc7bec8..845db32 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java @@ -16,8 +16,8 @@ package com.android.ide.eclipse.editors.resources.explorer; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.resources.manager.ProjectResourceItem; import com.android.ide.eclipse.editors.resources.manager.ProjectResources; import com.android.ide.eclipse.editors.resources.manager.ResourceFile; @@ -73,10 +73,12 @@ import java.util.Iterator; public class ResourceExplorerView extends ViewPart implements ISelectionListener, IResourceEventListener { + // Note: keep using the obsolete AndroidConstants.EDITORS_NAMESPACE (which used + // to be the Editors Plugin ID) to keep existing preferences functional. private final static String PREFS_COLUMN_RES = - AndroidConstants.EDITORS_PLUGIN_ID + "ResourceExplorer.Col1"; //$NON-NLS-1$ + AndroidConstants.EDITORS_NAMESPACE + "ResourceExplorer.Col1"; //$NON-NLS-1$ private final static String PREFS_COLUMN_2 = - AndroidConstants.EDITORS_PLUGIN_ID + "ResourceExplorer.Col2"; //$NON-NLS-1$ + AndroidConstants.EDITORS_NAMESPACE + "ResourceExplorer.Col2"; //$NON-NLS-1$ private Tree mTree; private TreeViewer mTreeViewer; @@ -93,7 +95,7 @@ public class ResourceExplorerView extends ViewPart implements ISelectionListener mTree.setHeaderVisible(true); mTree.setLinesVisible(true); - final IPreferenceStore store = EditorsPlugin.getDefault().getPreferenceStore(); + final IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore(); // create 2 columns. The main one with the resources, and an "info" column. createTreeColumn(mTree, "Resources", SWT.LEFT, @@ -156,7 +158,7 @@ public class ResourceExplorerView extends ViewPart implements ISelectionListener }); // set up the resource manager to send us resource change notification - EditorsPlugin.getDefault().getResourceMonitor().addResourceEventListener(this); + AdtPlugin.getDefault().getResourceMonitor().addResourceEventListener(this); } @Override diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/CompiledResourcesMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/CompiledResourcesMonitor.java index 366f4fd..455c825 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/CompiledResourcesMonitor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/CompiledResourcesMonitor.java @@ -16,6 +16,7 @@ package com.android.ide.eclipse.editors.resources.manager; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestHelper; import com.android.ide.eclipse.common.resources.ResourceType; @@ -124,24 +125,29 @@ public final class CompiledResourcesMonitor implements IFileListener, IProjectLi ProjectClassLoader loader = new ProjectClassLoader(null /* parentClassLoader */, project); - Class<?> clazz = loader.loadClass(className); - - if (clazz != null) { - // create the maps to store the result of the parsing - Map<String, Map<String, Integer>> resourceValueMap = - new HashMap<String, Map<String, Integer>>(); - Map<Integer, String[]> genericValueToNameMap = - new HashMap<Integer, String[]>(); - Map<IntArrayWrapper, String> styleableValueToNameMap = - new HashMap<IntArrayWrapper, String>(); + try { + Class<?> clazz = loader.loadClass(className); - // parse the class - if (parseClass(clazz, genericValueToNameMap, styleableValueToNameMap, - resourceValueMap)) { - // now we associate the maps to the project. - projectResources.setCompiledResources(genericValueToNameMap, - styleableValueToNameMap, resourceValueMap); + if (clazz != null) { + // create the maps to store the result of the parsing + Map<String, Map<String, Integer>> resourceValueMap = + new HashMap<String, Map<String, Integer>>(); + Map<Integer, String[]> genericValueToNameMap = + new HashMap<Integer, String[]>(); + Map<IntArrayWrapper, String> styleableValueToNameMap = + new HashMap<IntArrayWrapper, String>(); + + // parse the class + if (parseClass(clazz, genericValueToNameMap, styleableValueToNameMap, + resourceValueMap)) { + // now we associate the maps to the project. + projectResources.setCompiledResources(genericValueToNameMap, + styleableValueToNameMap, resourceValueMap); + } } + } catch (Error e) { + // Log this error with the class name we're trying to load and abort. + AdtPlugin.log(e, "loadAndParseRClass failed to find class %1$s", className); //$NON-NLS-1$ } } } catch (ClassNotFoundException e) { @@ -155,7 +161,7 @@ public final class CompiledResourcesMonitor implements IFileListener, IProjectLi * @param genericValueToNameMap * @param styleableValueToNameMap * @param resourceValueMap - * @return + * @return True if we managed to parse the R class. */ private boolean parseClass(Class<?> rClass, Map<Integer, String[]> genericValueToNameMap, Map<IntArrayWrapper, String> styleableValueToNameMap, Map<String, diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ConfigurableResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ConfigurableResourceItem.java index 57c17fc..57c17fc 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ConfigurableResourceItem.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ConfigurableResourceItem.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/FolderTypeRelationship.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/FolderTypeRelationship.java index a9f80bd..a9f80bd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/FolderTypeRelationship.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/FolderTypeRelationship.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/IdResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/IdResourceItem.java index 552aec9..552aec9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/IdResourceItem.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/IdResourceItem.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/IntArrayWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/IntArrayWrapper.java index 25eb112..25eb112 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/IntArrayWrapper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/IntArrayWrapper.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/MultiResourceFile.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/MultiResourceFile.java index 72438a6..72438a6 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/MultiResourceFile.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/MultiResourceFile.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ProjectClassLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectClassLoader.java index 5658224..183af27 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ProjectClassLoader.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectClassLoader.java @@ -215,12 +215,6 @@ public final class ProjectClassLoader extends ClassLoader { // get the IPath IPath path = e.getPath(); - // get the file name. if it's the framework jar, we ignore that file. - // since we now use classpath container, this is here for legacy purpose only. - if (AndroidConstants.FN_FRAMEWORK_LIBRARY.equals(path.lastSegment())) { - continue; - } - // check the name ends with .jar if (AndroidConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) { boolean local = false; diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ProjectResourceItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectResourceItem.java index ba770b2..ba770b2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ProjectResourceItem.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectResourceItem.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ProjectResources.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectResources.java index b0881fa..b0881fa 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ProjectResources.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectResources.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/Resource.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/Resource.java index dd8d080..dd8d080 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/Resource.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/Resource.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ResourceFile.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFile.java index f927a9a..f927a9a 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ResourceFile.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFile.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolder.java index 6db0d94..6db0d94 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolder.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolderType.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolderType.java index bd93301..bd93301 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolderType.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolderType.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ResourceManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceManager.java index 64942ed..9c5f0fc 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ResourceManager.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceManager.java @@ -29,6 +29,7 @@ import com.android.ide.eclipse.editors.resources.manager.files.IAbstractFile; import com.android.ide.eclipse.editors.resources.manager.files.IAbstractFolder; import com.android.ide.eclipse.editors.resources.manager.files.IFileWrapper; import com.android.ide.eclipse.editors.resources.manager.files.IFolderWrapper; +import com.android.sdklib.IAndroidTarget; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; @@ -57,8 +58,6 @@ public final class ResourceManager implements IProjectListener, IFolderListener, private final HashMap<IProject, ProjectResources> mMap = new HashMap<IProject, ProjectResources>(); - private ProjectResources mFrameworkResources = null; - /** * Sets up the resource manager with the global resource monitor. * @param monitor The global resource monitor @@ -89,14 +88,6 @@ public final class ResourceManager implements IProjectListener, IFolderListener, } /** - * Returns the resources of the framework. - * <p/>This could be <code>null</code> if the parsing failed. - */ - public ProjectResources getFrameworkResources() { - return mFrameworkResources; - } - - /** * Processes folder event. */ public void folderChanged(IFolder folder, int kind) { @@ -277,24 +268,23 @@ public final class ResourceManager implements IProjectListener, IFolderListener, } /** - * Loads the framework resources. + * Loads and returns the resources for a given {@link IAndroidTarget} * @param osFilePath the path to the folder containing all the versions of the framework * resources */ - public void loadFrameworkResources(String osResourcesPath) { - // for now only load the default framework resources - osResourcesPath += AndroidConstants.FD_DEFAULT_RES; + public ProjectResources loadFrameworkResources(IAndroidTarget androidTarget) { + String osResourcesPath = androidTarget.getPath(IAndroidTarget.RESOURCES); File frameworkRes = new File(osResourcesPath); if (frameworkRes.isDirectory()) { - mFrameworkResources = new ProjectResources(true /* isFrameworkRepository */); + ProjectResources resources = new ProjectResources(true /* isFrameworkRepository */); try { File[] files = frameworkRes.listFiles(); for (File file : files) { if (file.isDirectory()) { ResourceFolder resFolder = processFolder(new FolderWrapper(file), - mFrameworkResources); + resources); if (resFolder != null) { // now we process the content of the folder @@ -311,13 +301,17 @@ public final class ResourceManager implements IProjectListener, IFolderListener, } // now that we have loaded the files, we need to force load the resources from them - mFrameworkResources.loadAll(); + resources.loadAll(); + + return resources; } catch (IOException e) { // since we test that folders are folders, and files are files, this shouldn't // happen. We can ignore it. } } + + return null; } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ResourceMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceMonitor.java index 45a020c..dc0f505 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/ResourceMonitor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceMonitor.java @@ -257,6 +257,20 @@ public class ResourceMonitor implements IResourceChangeListener { mFileListeners.add(bundle); } + + /** + * Removes an existing file listener. + * @param listener the listener to remove. + */ + public synchronized void removeFileListener(IFileListener listener) { + for (int i = 0 ; i < mFileListeners.size() ; i++) { + FileListenerBundle bundle = mFileListeners.get(i); + if (bundle.listener == listener) { + mFileListeners.remove(i); + return; + } + } + } /** * Adds a folder listener. diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/SingleResourceFile.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/SingleResourceFile.java index 1211236..1211236 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/SingleResourceFile.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/SingleResourceFile.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/files/FileWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/FileWrapper.java index 0a14214..0a14214 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/files/FileWrapper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/FileWrapper.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/files/FolderWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/FolderWrapper.java index 8afea33..8afea33 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/files/FolderWrapper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/FolderWrapper.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/files/IAbstractFile.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IAbstractFile.java index 7e807f9..7e807f9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/files/IAbstractFile.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IAbstractFile.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/files/IAbstractFolder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IAbstractFolder.java index b35283d..b35283d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/files/IAbstractFolder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IAbstractFolder.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/files/IAbstractResource.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IAbstractResource.java index daf243d..daf243d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/files/IAbstractResource.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IAbstractResource.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/files/IFileWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IFileWrapper.java index 441c65b..441c65b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/files/IFileWrapper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IFileWrapper.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/files/IFolderWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IFolderWrapper.java index 92b5c07..92b5c07 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/manager/files/IFolderWrapper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IFolderWrapper.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/uimodel/UiColorValueNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/uimodel/UiColorValueNode.java index 29453e9..29453e9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/uimodel/UiColorValueNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/uimodel/UiColorValueNode.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/uimodel/UiItemElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/uimodel/UiItemElementNode.java index 89649f5..89649f5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/resources/uimodel/UiItemElementNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/uimodel/UiItemElementNode.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/EditableDialogCellEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/EditableDialogCellEditor.java index 5fb479f..5fb479f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/EditableDialogCellEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/EditableDialogCellEditor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/ErrorImageComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/ErrorImageComposite.java index d095376..d095376 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/ErrorImageComposite.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/ErrorImageComposite.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/FlagValueCellEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/FlagValueCellEditor.java index ccae099..ccae099 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/FlagValueCellEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/FlagValueCellEditor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/ListValueCellEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/ListValueCellEditor.java index 304dd14..304dd14 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/ListValueCellEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/ListValueCellEditor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/ResourceValueCellEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/ResourceValueCellEditor.java index 4fc0ab3..4fc0ab3 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/ResourceValueCellEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/ResourceValueCellEditor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/SectionHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/SectionHelper.java index 7942024..409e92f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/SectionHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/SectionHelper.java @@ -16,8 +16,8 @@ package com.android.ide.eclipse.editors.ui; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.AndroidEditor; -import com.android.ide.eclipse.editors.EditorsPlugin; import org.eclipse.jface.text.DefaultInformationControl; import org.eclipse.swt.events.MouseEvent; @@ -173,7 +173,7 @@ public final class SectionHelper { reflow.setAccessible(true); reflow.invoke(section); } catch (Exception e) { - EditorsPlugin.log(e, "Error when invoking Section.reflow"); + AdtPlugin.log(e, "Error when invoking Section.reflow"); } section.layout(true /* changed */, true /* all */); diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/TextValueCellEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/TextValueCellEditor.java index 2fe5783..2fe5783 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/TextValueCellEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/TextValueCellEditor.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/UiElementPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/UiElementPart.java index 69adebd..66773bd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/UiElementPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/UiElementPart.java @@ -16,7 +16,7 @@ package com.android.ide.eclipse.editors.ui; -import com.android.ide.eclipse.editors.EditorsPlugin; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor; import com.android.ide.eclipse.editors.manifest.ManifestEditor; @@ -60,7 +60,7 @@ public class UiElementPart extends ManifestSectionPart { if (uiElementNode == null) { // This is serious and should never happen. Instead of crashing, simply abort. // There will be no UI, which will prevent further damage. - EditorsPlugin.log(IStatus.ERROR, "Missing node to edit!"); //$NON-NLS-1$ + AdtPlugin.log(IStatus.ERROR, "Missing node to edit!"); //$NON-NLS-1$ return; } } @@ -222,7 +222,7 @@ public class UiElementPart extends ManifestSectionPart { } else { // The XML has an extra attribute which wasn't declared in // AndroidManifestDescriptors. This is not a problem, we just ignore it. - EditorsPlugin.log(IStatus.WARNING, + AdtPlugin.log(IStatus.WARNING, "Attribute %1$s not declared in node %2$s, ignored.", //$NON-NLS-1$ attr_desc.getXmlLocalName(), uiNode.getDescriptor().getXmlName()); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/CopyCutAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/CopyCutAction.java new file mode 100644 index 0000000..2aad217 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/CopyCutAction.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.editors.ui.tree; + +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.editors.AndroidEditor; +import com.android.ide.eclipse.editors.uimodel.UiElementNode; + +import org.apache.xml.serialize.Method; +import org.apache.xml.serialize.OutputFormat; +import org.apache.xml.serialize.XMLSerializer; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.xml.core.internal.document.NodeContainer; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + + +/** + * Provides Cut and Copy actions for the tree nodes. + */ +public class CopyCutAction extends Action { + private List<UiElementNode> mUiNodes; + private boolean mPerformCut; + private final AndroidEditor mEditor; + private final Clipboard mClipboard; + private final ICommitXml mXmlCommit; + + /** + * Creates a new Copy or Cut action. + * + * @param selected The UI node to cut or copy. It *must* have a non-null XML node. + * @param perform_cut True if the operation is cut, false if it is copy. + */ + public CopyCutAction(AndroidEditor editor, Clipboard clipboard, ICommitXml xmlCommit, + UiElementNode selected, boolean perform_cut) { + this(editor, clipboard, xmlCommit, toList(selected), perform_cut); + } + + /** + * Creates a new Copy or Cut action. + * + * @param selected The UI nodes to cut or copy. They *must* have a non-null XML node. + * The list becomes owned by the {@link CopyCutAction}. + * @param perform_cut True if the operation is cut, false if it is copy. + */ + public CopyCutAction(AndroidEditor editor, Clipboard clipboard, ICommitXml xmlCommit, + List<UiElementNode> selected, boolean perform_cut) { + super(perform_cut ? "Cut" : "Copy"); + mEditor = editor; + mClipboard = clipboard; + mXmlCommit = xmlCommit; + + ISharedImages images = PlatformUI.getWorkbench().getSharedImages(); + if (perform_cut) { + setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_CUT)); + setHoverImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_CUT)); + setDisabledImageDescriptor( + images.getImageDescriptor(ISharedImages.IMG_TOOL_CUT_DISABLED)); + } else { + setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_COPY)); + setHoverImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_COPY)); + setDisabledImageDescriptor( + images.getImageDescriptor(ISharedImages.IMG_TOOL_COPY_DISABLED)); + } + + mUiNodes = selected; + mPerformCut = perform_cut; + } + + /** + * Performs the cut or copy action. + * First an XML serializer is used to turn the existing XML node into a valid + * XML fragment, which is added as text to the clipboard. + */ + @Override + public void run() { + super.run(); + if (mUiNodes == null || mUiNodes.size() < 1) { + return; + } + + // Commit the current pages first, to make sure the XML is in sync. + // Committing may change the XML structure. + if (mXmlCommit != null) { + mXmlCommit.commitPendingXmlChanges(); + } + + StringBuilder allText = new StringBuilder(); + ArrayList<UiElementNode> nodesToCut = mPerformCut ? new ArrayList<UiElementNode>() : null; + + for (UiElementNode uiNode : mUiNodes) { + try { + Node xml_node = uiNode.getXmlNode(); + if (xml_node == null) { + return; + } + + String data = getXmlTextFromEditor(xml_node); + + // In the unlikely event that IStructuredDocument failed to extract the text + // directly from the editor, try to fall back on a direct XML serialization + // of the XML node. This uses the generic Node interface with no SSE tricks. + if (data == null) { + data = getXmlTextFromSerialization(xml_node); + } + + if (data != null) { + allText.append(data); + if (mPerformCut) { + // only remove notes to cut if we actually got some XML text from them + nodesToCut.add(uiNode); + } + } + + } catch (Exception e) { + AdtPlugin.log(e, "CopyCutAction failed for UI node %1$s", //$NON-NLS-1$ + uiNode.getBreadcrumbTrailDescription(true)); + } + } // for uiNode + + if (allText != null && allText.length() > 0) { + mClipboard.setContents( + new Object[] { allText.toString() }, + new Transfer[] { TextTransfer.getInstance() }); + if (mPerformCut) { + for (UiElementNode uiNode : nodesToCut) { + uiNode.deleteXmlNode(); + } + } + } + } + + /** Get the data directly from the editor. */ + private String getXmlTextFromEditor(Node xml_node) { + String data = null; + IStructuredModel model = mEditor.getModelForRead(); + try { + IStructuredDocument sse_doc = mEditor.getStructuredDocument(); + if (xml_node instanceof NodeContainer) { + // The easy way to get the source of an SSE XML node. + data = ((NodeContainer) xml_node).getSource(); + } else if (xml_node instanceof IndexedRegion && sse_doc != null) { + // Try harder. + IndexedRegion region = (IndexedRegion) xml_node; + int start = region.getStartOffset(); + int end = region.getEndOffset(); + + if (end > start) { + data = sse_doc.get(start, end - start); + } + } + } catch (BadLocationException e) { + // the region offset was invalid. ignore. + } finally { + model.releaseFromRead(); + } + return data; + } + + /** + * Direct XML serialization of the XML node. + * <p/> + * This uses the generic Node interface with no SSE tricks. It's however slower + * and doesn't respect formatting (since serialization is involved instead of reading + * the actual text buffer.) + */ + private String getXmlTextFromSerialization(Node xml_node) throws IOException { + String data; + StringWriter sw = new StringWriter(); + XMLSerializer serializer = new XMLSerializer(sw, + new OutputFormat(Method.XML, + OutputFormat.Defaults.Encoding /* utf-8 */, + true /* indent */)); + // Serialize will throw an IOException if it fails. + serializer.serialize((Element) xml_node); + data = sw.toString(); + return data; + } + + /** + * Static helper class to wrap on node into a list for the constructors. + */ + private static ArrayList<UiElementNode> toList(UiElementNode selected) { + ArrayList<UiElementNode> list = null; + if (selected != null) { + list = new ArrayList<UiElementNode>(1); + list.add(selected); + } + return list; + } +} + diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/ICommitXml.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/ICommitXml.java index 8b6aa0e..8b6aa0e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/ICommitXml.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/ICommitXml.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java index 00e44ab..772fb52 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java @@ -16,6 +16,7 @@ package com.android.ide.eclipse.editors.ui.tree; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.layout.descriptors.ViewElementDescriptor; @@ -90,19 +91,19 @@ public class NewItemSelectionDialog extends AbstractElementListSelectionDialog { public IStatus validate(Object[] selection) { if (selection.length == 1 && selection[0] instanceof ViewElementDescriptor) { return new Status(IStatus.OK, // severity - AndroidConstants.EDITORS_PLUGIN_ID, //plugin id + AdtPlugin.PLUGIN_ID, //plugin id IStatus.OK, // code ((ViewElementDescriptor) selection[0]).getCanonicalClassName(), //msg null); // exception } else if (selection.length == 1 && selection[0] instanceof ElementDescriptor) { return new Status(IStatus.OK, // severity - AndroidConstants.EDITORS_PLUGIN_ID, //plugin id + AdtPlugin.PLUGIN_ID, //plugin id IStatus.OK, // code "", //$NON-NLS-1$ // msg null); // exception } else { return new Status(IStatus.ERROR, // severity - AndroidConstants.EDITORS_PLUGIN_ID, //plugin id + AdtPlugin.PLUGIN_ID, //plugin id IStatus.ERROR, // code "Invalid selection", // msg, translatable null); // exception diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/PasteAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/PasteAction.java index 68580b0..8bb4ad2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/PasteAction.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/PasteAction.java @@ -16,8 +16,8 @@ package com.android.ide.eclipse.editors.ui.tree; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.AndroidEditor; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.uimodel.UiDocumentNode; import com.android.ide.eclipse.editors.uimodel.UiElementNode; @@ -113,7 +113,7 @@ public class PasteAction extends Action { } } catch (BadLocationException e) { - EditorsPlugin.log(e, "ParseAction failed for UI Node %2$s, content '%1$s'", //$NON-NLS-1$ + AdtPlugin.log(e, "ParseAction failed for UI Node %2$s, content '%1$s'", //$NON-NLS-1$ mUiNode.getBreadcrumbTrailDescription(true), data); } finally { model.releaseFromEdit(); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiActions.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiActions.java new file mode 100644 index 0000000..21180b1 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiActions.java @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.ide.eclipse.editors.ui.tree; + +import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; +import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.editors.uimodel.UiDocumentNode; +import com.android.ide.eclipse.editors.uimodel.UiElementNode; + +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.swt.widgets.Shell; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import java.util.List; + +/** + * Performs basic actions on an XML tree: add node, remove node, move up/down. + */ +public abstract class UiActions implements ICommitXml { + + public UiActions() { + } + + //--------------------- + // Actual implementations must override these to provide specific hooks + + /** Returns the UiDocumentNode for the current model. */ + abstract protected UiElementNode getRootNode(); + + /** Commits pending data before the XML model is modified. */ + abstract public void commitPendingXmlChanges(); + + /** + * Utility method to select an outline item based on its model node + * + * @param uiNode The node to select. Can be null (in which case nothing should happen) + */ + abstract protected void selectUiNode(UiElementNode uiNode); + + //--------------------- + + /** + * Called when the "Add..." button next to the tree view is selected. + * <p/> + * This simplified version of doAdd does not support descriptor filters and creates + * a new {@link UiModelTreeLabelProvider} for each call. + */ + public void doAdd(UiElementNode uiNode, Shell shell) { + doAdd(uiNode, null /* descriptorFilters */, shell, new UiModelTreeLabelProvider()); + } + + /** + * Called when the "Add..." button next to the tree view is selected. + * + * Displays a selection dialog that lets the user select which kind of node + * to create, depending on the current selection. + */ + public void doAdd(UiElementNode uiNode, + ElementDescriptor[] descriptorFilters, + Shell shell, ILabelProvider labelProvider) { + // If the root node is a document with already a root, use it as the root node + UiElementNode rootNode = getRootNode(); + if (rootNode instanceof UiDocumentNode && rootNode.getUiChildren().size() > 0) { + rootNode = rootNode.getUiChildren().get(0); + } + + NewItemSelectionDialog dlg = new NewItemSelectionDialog( + shell, + labelProvider, + descriptorFilters, + uiNode, rootNode); + dlg.open(); + Object[] results = dlg.getResult(); + if (results != null && results.length > 0) { + addElement(dlg.getChosenRootNode(), null, (ElementDescriptor) results[0], + true /*updateLayout*/); + } + } + + /** + * Adds a new XML element based on the {@link ElementDescriptor} to the given parent + * {@link UiElementNode}, and then select it. + * <p/> + * If the parent is a document root which already contains a root element, the inner + * root element is used as the actual parent. This ensure you can't create a broken + * XML file with more than one root element. + * <p/> + * If a sibling is given and that sibling has the same parent, the new node is added + * right after that sibling. Otherwise the new node is added at the end of the parent + * child list. + * + * @param uiParent An existing UI node or null to add to the tree root + * @param uiSibling An existing UI node before which to insert the new node. Can be null. + * @param descriptor The descriptor of the element to add + * @param updateLayout True if layout attributes should be set + * @return The new {@link UiElementNode} or null. + */ + public UiElementNode addElement(UiElementNode uiParent, + UiElementNode uiSibling, + ElementDescriptor descriptor, + boolean updateLayout) { + if (uiParent instanceof UiDocumentNode && uiParent.getUiChildren().size() > 0) { + uiParent = uiParent.getUiChildren().get(0); + } + if (uiSibling != null && uiSibling.getUiParent() != uiParent) { + uiSibling = null; + } + + UiElementNode uiNew = addNewTreeElement(uiParent, uiSibling, descriptor, updateLayout); + selectUiNode(uiNew); + + return uiNew; + } + + /** + * Called when the "Remove" button is selected. + * + * If the tree has a selection, remove it. + * This simply deletes the XML node attached to the UI node: when the XML model fires the + * update event, the tree will get refreshed. + */ + public void doRemove(final List<UiElementNode> nodes, Shell shell) { + + if (nodes == null || nodes.size() == 0) { + return; + } + + final int len = nodes.size(); + + StringBuilder sb = new StringBuilder(); + for (UiElementNode node : nodes) { + sb.append("\n- "); //$NON-NLS-1$ + sb.append(node.getBreadcrumbTrailDescription(false /* include_root */)); + } + + if (MessageDialog.openQuestion(shell, + len > 1 ? "Remove elements from Android XML" // title + : "Remove element from Android XML", + String.format("Do you really want to remove %1$s?", sb.toString()))) { + commitPendingXmlChanges(); + getRootNode().getEditor().editXmlModel(new Runnable() { + public void run() { + UiElementNode previous = null; + UiElementNode parent = null; + + for (int i = len - 1; i >= 0; i--) { + UiElementNode node = nodes.get(i); + previous = node.getUiPreviousSibling(); + parent = node.getUiParent(); + + // delete node + node.deleteXmlNode(); + } + + // try to select the last previous sibling or the last parent + if (previous != null) { + selectUiNode(previous); + } else if (parent != null) { + selectUiNode(parent); + } + } + }); + } + } + + /** + * Called when the "Up" button is selected. + * <p/> + * If the tree has a selection, move it up, either in the child list or as the last child + * of the previous parent. + */ + public void doUp(final List<UiElementNode> nodes) { + if (nodes == null || nodes.size() < 1) { + return; + } + + final Node[] select_xml_node = { null }; + UiElementNode last_node = null; + UiElementNode search_root = null; + + for (int i = 0; i < nodes.size(); i++) { + final UiElementNode node = last_node = nodes.get(i); + + // the node will move either up to its parent or grand-parent + search_root = node.getUiParent(); + if (search_root != null && search_root.getUiParent() != null) { + search_root = search_root.getUiParent(); + } + + commitPendingXmlChanges(); + getRootNode().getEditor().editXmlModel(new Runnable() { + public void run() { + Node xml_node = node.getXmlNode(); + if (xml_node != null) { + Node xml_parent = xml_node.getParentNode(); + if (xml_parent != null) { + UiElementNode ui_prev = node.getUiPreviousSibling(); + if (ui_prev != null && ui_prev.getXmlNode() != null) { + // This node is not the first one of the parent, so it can be + // removed and then inserted before its previous sibling. + // If the previous sibling can have children, though, then it + // is inserted at the end of the children list. + Node xml_prev = ui_prev.getXmlNode(); + if (ui_prev.getDescriptor().hasChildren()) { + xml_prev.appendChild(xml_parent.removeChild(xml_node)); + select_xml_node[0] = xml_node; + } else { + xml_parent.insertBefore( + xml_parent.removeChild(xml_node), + xml_prev); + select_xml_node[0] = xml_node; + } + } else if (!(xml_parent instanceof Document) && + xml_parent.getParentNode() != null && + !(xml_parent.getParentNode() instanceof Document)) { + // If the node is the first one of the child list of its + // parent, move it up in the hierarchy as previous sibling + // to the parent. This is only possible if the parent of the + // parent is not a document. + Node grand_parent = xml_parent.getParentNode(); + grand_parent.insertBefore(xml_parent.removeChild(xml_node), + xml_parent); + select_xml_node[0] = xml_node; + } + } + } + } + }); + } + + if (select_xml_node[0] == null) { + // The XML node has not been moved, we can just select the same UI node + selectUiNode(last_node); + } else { + // The XML node has moved. At this point the UI model has been reloaded + // and the XML node has been affected to a new UI node. Find that new UI + // node and select it. + if (search_root == null) { + search_root = last_node.getUiRoot(); + } + if (search_root != null) { + selectUiNode(search_root.findXmlNode(select_xml_node[0])); + } + } + } + + /** + * Called when the "Down" button is selected. + * + * If the tree has a selection, move it down, either in the same child list or as the + * first child of the next parent. + */ + public void doDown(final List<UiElementNode> nodes) { + if (nodes == null || nodes.size() < 1) { + return; + } + + final Node[] select_xml_node = { null }; + UiElementNode last_node = null; + UiElementNode search_root = null; + + for (int i = nodes.size() - 1; i >= 0; i--) { + final UiElementNode node = last_node = nodes.get(i); + // the node will move either down to its parent or grand-parent + search_root = node.getUiParent(); + if (search_root != null && search_root.getUiParent() != null) { + search_root = search_root.getUiParent(); + } + + commitPendingXmlChanges(); + getRootNode().getEditor().editXmlModel(new Runnable() { + public void run() { + Node xml_node = node.getXmlNode(); + if (xml_node != null) { + Node xml_parent = xml_node.getParentNode(); + if (xml_parent != null) { + UiElementNode uiNext = node.getUiNextSibling(); + if (uiNext != null && uiNext.getXmlNode() != null) { + // This node is not the last one of the parent, so it can be + // removed and then inserted after its next sibling. + // If the next sibling is a node that can have children, though, + // then the node is inserted as the first child. + Node xml_next = uiNext.getXmlNode(); + if (uiNext.getDescriptor().hasChildren()) { + // Note: insertBefore works as append if the ref node is + // null, i.e. when the node doesn't have children yet. + xml_next.insertBefore(xml_parent.removeChild(xml_node), + xml_next.getFirstChild()); + select_xml_node[0] = xml_node; + } else { + // Insert "before after next" ;-) + xml_parent.insertBefore(xml_parent.removeChild(xml_node), + xml_next.getNextSibling()); + select_xml_node[0] = xml_node; + } + } else if (!(xml_parent instanceof Document) && + xml_parent.getParentNode() != null && + !(xml_parent.getParentNode() instanceof Document)) { + // This node is the last node of its parent. + // If neither the parent nor the grandparent is a document, + // then the node can be insert right after the parent. + Node grand_parent = xml_parent.getParentNode(); + grand_parent.insertBefore(xml_parent.removeChild(xml_node), + xml_parent.getNextSibling()); + select_xml_node[0] = xml_node; + } + } + } + } + }); + } + + if (select_xml_node[0] == null) { + // The XML node has not been moved, we can just select the same UI node + selectUiNode(last_node); + } else { + // The XML node has moved. At this point the UI model has been reloaded + // and the XML node has been affected to a new UI node. Find that new UI + // node and select it. + if (search_root == null) { + search_root = last_node.getUiRoot(); + } + if (search_root != null) { + selectUiNode(search_root.findXmlNode(select_xml_node[0])); + } + } + } + + //--------------------- + + /** + * Adds a new element of the given descriptor's type to the given UI parent node. + * + * This actually creates the corresponding XML node in the XML model, which in turn + * will refresh the current tree view. + * + * @param uiParent An existing UI node or null to add to the tree root + * @param uiSibling An existing UI node to insert right before. Can be null. + * @param descriptor The descriptor of the element to add + * @param updateLayout True if layout attributes should be set + * @return The {@link UiElementNode} that has been added to the UI tree. + */ + private UiElementNode addNewTreeElement(UiElementNode uiParent, + final UiElementNode uiSibling, + ElementDescriptor descriptor, + final boolean updateLayout) { + commitPendingXmlChanges(); + + int index = 0; + for (UiElementNode uiChild : uiParent.getUiChildren()) { + if (uiChild == uiSibling) { + break; + } + index++; + } + + final UiElementNode uiNew = uiParent.insertNewUiChild(index, descriptor); + UiElementNode rootNode = getRootNode(); + + rootNode.getEditor().editXmlModel(new Runnable() { + public void run() { + DescriptorsUtils.setDefaultLayoutAttributes(uiNew, updateLayout); + Node xmlNode = uiNew.createXmlNode(); + } + }); + return uiNew; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/UiElementDetail.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiElementDetail.java index 010e30e..15c67c3 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/UiElementDetail.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiElementDetail.java @@ -16,14 +16,14 @@ package com.android.ide.eclipse.editors.ui.tree; -import com.android.ide.eclipse.common.resources.FrameworkResourceManager; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.editors.AndroidEditor; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.SeparatorAttributeDescriptor; +import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor; import com.android.ide.eclipse.editors.ui.SectionHelper; import com.android.ide.eclipse.editors.ui.SectionHelper.ManifestSectionPart; import com.android.ide.eclipse.editors.uimodel.IUiUpdateListener; @@ -130,7 +130,7 @@ class UiElementDetail implements IDetailsPage { // Finally reset the dirty flag if everything was saved properly mIsDirty = false; } catch (Exception e) { - EditorsPlugin.log(e, "Detail node failed to commit XML attribute!"); //$NON-NLS-1$ + AdtPlugin.log(e, "Detail node failed to commit XML attribute!"); //$NON-NLS-1$ } finally { // Notify the model we're done modifying it. This must *always* be executed. model.changedModel(); @@ -260,9 +260,15 @@ class UiElementDetail implements IDetailsPage { mCurrentUiElementNode = ui_node; if (elem_desc.getTooltip() != null) { - String tooltip = DescriptorsUtils.formatFormText(elem_desc.getTooltip(), - elem_desc, - FrameworkResourceManager.getInstance().getDocumentationBaseUrl()); + String tooltip; + if (Sdk.getCurrent() != null && + Sdk.getCurrent().getDocumentationBaseUrl() != null) { + tooltip = DescriptorsUtils.formatFormText(elem_desc.getTooltip(), + elem_desc, + Sdk.getCurrent().getDocumentationBaseUrl()); + } else { + tooltip = elem_desc.getTooltip(); + } try { FormText text = SectionHelper.createFormText(masterTable, toolkit, @@ -275,7 +281,7 @@ class UiElementDetail implements IDetailsPage { } catch(Exception e) { // The FormText parser is really really basic and will fail as soon as the // HTML javadoc is ever so slightly malformatted. - EditorsPlugin.log(e, + AdtPlugin.log(e, "Malformed javadoc, rejected by FormText for node %1$s: '%2$s'", //$NON-NLS-1$ ui_node.getDescriptor().getXmlName(), tooltip); @@ -320,7 +326,7 @@ class UiElementDetail implements IDetailsPage { } else { // The XML has an extra unknown attribute. // This is not expected to happen so it is ignored. - EditorsPlugin.log(IStatus.INFO, + AdtPlugin.log(IStatus.INFO, "Attribute %1$s not declared in node %2$s, ignored.", //$NON-NLS-1$ attr_desc.getXmlLocalName(), ui_node.getDescriptor().getXmlName()); diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/UiModelTreeContentProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiModelTreeContentProvider.java index 9f34d9e..9f34d9e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/UiModelTreeContentProvider.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiModelTreeContentProvider.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/UiModelTreeLabelProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiModelTreeLabelProvider.java index ff5f24c..273a30b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/UiModelTreeLabelProvider.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiModelTreeLabelProvider.java @@ -16,7 +16,7 @@ package com.android.ide.eclipse.editors.ui.tree; -import com.android.ide.eclipse.editors.EditorsPlugin; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.ui.ErrorImageComposite; import com.android.ide.eclipse.editors.uimodel.UiElementNode; @@ -62,7 +62,7 @@ public class UiModelTreeLabelProvider implements ILabelProvider { } } } - return EditorsPlugin.getAndroidLogo(); + return AdtPlugin.getAndroidLogo(); } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/UiTreeBlock.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiTreeBlock.java index 7e7bd68..e3255d9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/UiTreeBlock.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiTreeBlock.java @@ -16,8 +16,8 @@ package com.android.ide.eclipse.editors.ui.tree; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.AndroidEditor; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.IconFactory; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.ui.SectionHelper; @@ -42,6 +42,7 @@ import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.events.DisposeEvent; @@ -64,6 +65,8 @@ import org.eclipse.ui.forms.MasterDetailsBlock; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.Section; +import java.util.ArrayList; +import java.util.Iterator; import java.util.LinkedList; /** @@ -117,9 +120,15 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml private IUiUpdateListener mUiRefreshListener; /** Listener to enable/disable the UI based on the application node's presence */ private IUiUpdateListener mUiEnableListener; - + /** An adapter/wrapper to use the add/remove/up/down tree edit actions. */ private UiTreeActions mUiTreeActions; - + /** + * True if the root node can be created on-demand (i.e. as needed as + * soon as children exist). False if an external entity controls the existence of the + * root node. In practise, this is false for the manifest application page (the actual + * "application" node is managed by the ApplicationToggle part) whereas it is true + * for all other tree pages. + */ private final boolean mAutoCreateRoot; @@ -192,10 +201,24 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml private void createSectionActions(Section section, FormToolkit toolkit) { ToolBarManager manager = new ToolBarManager(SWT.FLAT); + manager.removeAll(); + ToolBar toolbar = manager.createControl(section); section.setTextClient(toolbar); + ElementDescriptor[] descs = mDescriptorFilters; + if (descs == null && mUiRootNode != null) { + descs = mUiRootNode.getDescriptor().getChildren(); + } + + if (descs != null && descs.length > 1) { + for (ElementDescriptor desc : descs) { + manager.add(new DescriptorFilterAction(desc)); + } + } + manager.add(new TreeSortAction()); + manager.update(true /*force*/); } @@ -207,7 +230,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml final IManagedForm managedForm) { // Note: we *could* use a FilteredTree instead of the Tree+TreeViewer here. // However the class must be adapted to create an adapted toolkit tree. - final Tree tree = toolkit.createTree(grid, SWT.SINGLE); + final Tree tree = toolkit.createTree(grid, SWT.MULTI); GridData gd = new GridData(GridData.FILL_BOTH); gd.widthHint = AndroidEditor.TEXT_WIDTH_HINT; gd.heightHint = TREE_HEIGHT_HINT; @@ -283,7 +306,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml changeRootAndDescriptors(mUiRootNode, mDescriptorFilters, false /* refresh */); // Listen on resource framework changes to refresh the tree - EditorsPlugin.getDefault().addResourceChangedListener(resourceRefreshListener); + AdtPlugin.getDefault().addResourceChangedListener(resourceRefreshListener); // Remove listeners when the tree widget gets disposed. tree.addDisposeListener(new DisposeListener() { @@ -295,7 +318,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml node.removeUpdateListener(mUiRefreshListener); mUiRootNode.removeUpdateListener(mUiEnableListener); - EditorsPlugin.getDefault().removeResourceChangedListener(resourceRefreshListener); + AdtPlugin.getDefault().removeResourceChangedListener(resourceRefreshListener); if (mClipboard != null) { mClipboard.dispose(); mClipboard = null; @@ -350,6 +373,8 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml if (forceRefresh) { mTreeViewer.refresh(); } + + createSectionActions(mMasterPart.getSection(), mManagedForm.getToolkit()); } /** @@ -426,13 +451,9 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml public void menuAboutToShow(IMenuManager manager) { ISelection selection = mTreeViewer.getSelection(); if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ITreeSelection tree_selection = (ITreeSelection) selection; - Object first = tree_selection.getFirstElement(); - if (first != null && first instanceof UiElementNode) { - UiElementNode ui_node = (UiElementNode) first; - doCreateMenuAction(manager, ui_node); - return; - } + ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); + doCreateMenuAction(manager, selected); + return; } doCreateMenuAction(manager, null /* ui_node */); } @@ -446,63 +467,78 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml * the tree view. * * @param manager The context menu manager - * @param ui_node The UI node selected in the tree. Can be null, in which case the root + * @param selected The UI nodes selected in the tree. Can be null, in which case the root * is to be modified. */ - private void doCreateMenuAction(IMenuManager manager, UiElementNode ui_node) { - Action action; - - if (ui_node != null && ui_node.getXmlNode() != null) { - manager.add(new CopyCutAction(getEditor(), getClipboard(), - this, ui_node, true /* cut */)); - manager.add(new CopyCutAction(getEditor(), getClipboard(), - this, ui_node, false /* cut */)); - // Paste is not valid if it would add a second element on a terminal element - // which parent is a document -- an XML document can only have one child. This - // means paste is valid if the current UI node can have children or if the parent - // is not a document. - if (mUiRootNode.getDescriptor().hasChildren() || - !(mUiRootNode.getUiParent() instanceof UiDocumentNode)) { - manager.add(new PasteAction(getEditor(), getClipboard(), ui_node)); + private void doCreateMenuAction(IMenuManager manager, ArrayList<UiElementNode> selected) { + if (selected != null) { + boolean hasXml = false; + for (UiElementNode uiNode : selected) { + if (uiNode.getXmlNode() != null) { + hasXml = true; + break; + } + } + + if (hasXml) { + manager.add(new CopyCutAction(getEditor(), getClipboard(), + null, selected, true /* cut */)); + manager.add(new CopyCutAction(getEditor(), getClipboard(), + null, selected, false /* cut */)); + + // Can't paste with more than one element selected (the selection is the target) + if (selected.size() <= 1) { + // Paste is not valid if it would add a second element on a terminal element + // which parent is a document -- an XML document can only have one child. This + // means paste is valid if the current UI node can have children or if the + // parent is not a document. + UiElementNode ui_root = selected.get(0).getUiRoot(); + if (ui_root.getDescriptor().hasChildren() || + !(ui_root.getUiParent() instanceof UiDocumentNode)) { + manager.add(new PasteAction(getEditor(), getClipboard(), selected.get(0))); + } + } + manager.add(new Separator()); } - manager.add(new Separator()); } // Append "add" and "remove" actions. They do the same thing as the add/remove // buttons on the side. - - manager.add(new Action("Add...", EditorsPlugin.getAndroidLogoDesc()) { - @Override - public void run() { - super.run(); - doTreeAdd(); - } - }); - - if (ui_node != null) { - manager.add(new Action("Remove", EditorsPlugin.getAndroidLogoDesc()) { + Action action; + IconFactory factory = IconFactory.getInstance(); + + // "Add" makes sense only if there's 0 or 1 item selected since the + // one selected item becomes the target. + if (selected == null || selected.size() <= 1) { + manager.add(new Action("Add...", factory.getImageDescriptor("add")) { //$NON-NLS-1$ @Override public void run() { super.run(); - doTreeRemove(); + doTreeAdd(); } }); } - - manager.add(new Separator()); - if (ui_node != null) { - manager.add(new Action("Up", EditorsPlugin.getAndroidLogoDesc()) { + if (selected != null) { + if (selected != null) { + manager.add(new Action("Remove", factory.getImageDescriptor("delete")) { //$NON-NLS-1$ + @Override + public void run() { + super.run(); + doTreeRemove(); + } + }); + } + manager.add(new Separator()); + + manager.add(new Action("Up", factory.getImageDescriptor("up")) { //$NON-NLS-1$ @Override public void run() { super.run(); doTreeUp(); } }); - } - - if (ui_node != null) { - manager.add(new Action("Down", EditorsPlugin.getAndroidLogoDesc()) { + manager.add(new Action("Down", factory.getImageDescriptor("down")) { //$NON-NLS-1$ @Override public void run() { super.run(); @@ -555,6 +591,27 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml } /** + * Filters an ITreeSelection to only keep the {@link UiElementNode}s (in case there's + * something else in there). + * + * @return A new list of {@link UiElementNode} with at least one item or null. + */ + @SuppressWarnings("unchecked") + private ArrayList<UiElementNode> filterSelection(ITreeSelection selection) { + ArrayList<UiElementNode> selected = new ArrayList<UiElementNode>(); + + for (Iterator it = selection.iterator(); it.hasNext(); ) { + Object selectedObj = it.next(); + + if (selectedObj instanceof UiElementNode) { + selected.add((UiElementNode) selectedObj); + } + } + + return selected.size() > 0 ? selected : null; + } + + /** * Called when the "Add..." button next to the tree view is selected. * * Displays a selection dialog that lets the user select which kind of node @@ -588,16 +645,11 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml protected void doTreeRemove() { ISelection selection = mTreeViewer.getSelection(); if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ITreeSelection tree_selection = (ITreeSelection) selection; - Object first = tree_selection.getFirstElement(); - if (first instanceof UiElementNode) { - final UiElementNode ui_node = (UiElementNode) first; - - mUiTreeActions.doRemove(ui_node, mTreeViewer.getControl().getShell()); - } + ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); + mUiTreeActions.doRemove(selected, mTreeViewer.getControl().getShell()); } } - + /** * Called when the "Up" button is selected. * <p/> @@ -607,13 +659,8 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml protected void doTreeUp() { ISelection selection = mTreeViewer.getSelection(); if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ITreeSelection tree_selection = (ITreeSelection) selection; - Object first = tree_selection.getFirstElement(); - if (first instanceof UiElementNode) { - final UiElementNode ui_node = (UiElementNode) first; - - mUiTreeActions.doUp(ui_node); - } + ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); + mUiTreeActions.doUp(selected); } } @@ -626,13 +673,8 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml protected void doTreeDown() { ISelection selection = mTreeViewer.getSelection(); if (!selection.isEmpty() && selection instanceof ITreeSelection) { - ITreeSelection tree_selection = (ITreeSelection) selection; - Object first = tree_selection.getFirstElement(); - if (first instanceof UiElementNode) { - final UiElementNode ui_node = (UiElementNode) first; - - mUiTreeActions.doDown(ui_node); - } + ArrayList<UiElementNode> selected = filterSelection((ITreeSelection) selection); + mUiTreeActions.doDown(selected); } } @@ -653,7 +695,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml @Override protected void createToolBarActions(IManagedForm managedForm) { - // pass + // Pass. Not used, toolbar actions are defined by createSectionActions(). } @Override @@ -686,14 +728,14 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml } /** - * Adds a sort action to the tree viewer. + * An alphabetic sort action for the tree viewer. */ private class TreeSortAction extends Action { private ViewerComparator mComparator; public TreeSortAction() { - setToolTipText("Sorts elements alphabetically."); + super("Sorts elements alphabetically.", AS_CHECK_BOX); setImageDescriptor(IconFactory.getInstance().getImageDescriptor("az_sort")); //$NON-NLS-1$ if (mTreeViewer != null) { @@ -703,7 +745,7 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml } /** - * Called when the button is selected. Toggle the tree viewer comparator. + * Called when the button is selected. Toggles the tree viewer comparator. */ @Override public void run() { @@ -730,4 +772,97 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml notifyResult(true /*success*/); } } + + /** + * A filter on descriptor for the tree viewer. + * <p/> + * The tree viewer will contain many of these actions and only one can be enabled at a + * given time. When no action is selected, everything is displayed. + * <p/> + * Since "radio"-like actions do not allow for unselecting all of them, we manually + * handle the exclusive radio button-like property: when an action is selected, it manually + * removes all other actions as needed. + */ + private class DescriptorFilterAction extends Action { + + private final ElementDescriptor mDescriptor; + private ViewerFilter mFilter; + + public DescriptorFilterAction(ElementDescriptor descriptor) { + super(String.format("Displays only %1$s elements.", descriptor.getUiName()), + AS_CHECK_BOX); + + mDescriptor = descriptor; + setImageDescriptor(descriptor.getImageDescriptor()); + } + + /** + * Called when the button is selected. + * <p/> + * Find any existing {@link DescriptorFilter}s and remove them. Install ours. + */ + @Override + public void run() { + super.run(); + + if (isChecked()) { + if (mFilter == null) { + // create filter when required + mFilter = new DescriptorFilter(this); + } + + // we add our filter first, otherwise the UI might show the full list + mTreeViewer.addFilter(mFilter); + + // Then remove the any other filters except ours. There should be at most + // one other filter, since that's how the actions are made to look like + // exclusive radio buttons. + for (ViewerFilter filter : mTreeViewer.getFilters()) { + if (filter instanceof DescriptorFilter && filter != mFilter) { + DescriptorFilterAction action = ((DescriptorFilter) filter).getAction(); + action.setChecked(false); + mTreeViewer.removeFilter(filter); + } + } + } else if (mFilter != null){ + mTreeViewer.removeFilter(mFilter); + } + } + + /** + * Filters the tree viewer for the given descriptor. + * <p/> + * The filter is linked to the action so that an action can iterate through the list + * of filters and un-select the actions. + */ + private class DescriptorFilter extends ViewerFilter { + + private final DescriptorFilterAction mAction; + + public DescriptorFilter(DescriptorFilterAction action) { + mAction = action; + } + + public DescriptorFilterAction getAction() { + return mAction; + } + + /** + * Returns true if an element should be displayed, that if the element or + * any of its parent matches the requested descriptor. + */ + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + while (element instanceof UiElementNode) { + UiElementNode uiNode = (UiElementNode)element; + if (uiNode.getDescriptor() == mDescriptor) { + return true; + } + element = uiNode.getUiParent(); + } + return false; + } + } + } + } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/IUiSettableAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/IUiSettableAttributeNode.java index 4c368d9..7fe44da 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/IUiSettableAttributeNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/IUiSettableAttributeNode.java @@ -26,7 +26,7 @@ public interface IUiSettableAttributeNode { /** Returns the current value of the node. */ public String getCurrentValue(); - /** Sets the current value of the node. */ + /** Sets the current value of the node. Cannot be null (use an empty string). */ public void setCurrentValue(String value); } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/IUiUpdateListener.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/IUiUpdateListener.java index 12cb31b..12cb31b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/IUiUpdateListener.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/IUiUpdateListener.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiAbstractTextAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiAbstractTextAttributeNode.java index 17b077a..5908574 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiAbstractTextAttributeNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiAbstractTextAttributeNode.java @@ -37,7 +37,7 @@ public abstract class UiAbstractTextAttributeNode extends UiAttributeNode /** Prevent internal listener from firing when internally modifying the text */ private boolean mInternalTextModification; - /** Last value read from the XML model */ + /** Last value read from the XML model. Cannot be null. */ private String mCurrentValue = DEFAULT_VALUE; public UiAbstractTextAttributeNode(AttributeDescriptor attributeDescriptor, @@ -51,7 +51,7 @@ public abstract class UiAbstractTextAttributeNode extends UiAttributeNode return mCurrentValue; } - /** Sets the current value of the node. */ + /** Sets the current value of the node. Cannot be null (use an empty string). */ public final void setCurrentValue(String value) { mCurrentValue = value; } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiAttributeNode.java index 5972f22..5972f22 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiAttributeNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiAttributeNode.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiDocumentNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiDocumentNode.java index 113738f..113738f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiDocumentNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiDocumentNode.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiElementNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiElementNode.java index 22b68b3..e0e9a40 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiElementNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiElementNode.java @@ -16,9 +16,10 @@ package com.android.ide.eclipse.editors.uimodel; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.editors.AndroidEditor; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.descriptors.SeparatorAttributeDescriptor; @@ -29,6 +30,7 @@ import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors; import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors; import com.android.ide.eclipse.editors.resources.descriptors.ResourcesDescriptors; import com.android.ide.eclipse.editors.uimodel.IUiUpdateListener.UiUpdateState; +import com.android.ide.eclipse.editors.xml.descriptors.XmlDescriptors; import org.eclipse.core.runtime.IStatus; import org.eclipse.ui.IEditorInput; @@ -206,6 +208,10 @@ public class UiElementNode implements IPropertySource { AndroidManifestDescriptors.ANDROID_LABEL_ATTR); } if (attr == null || attr.length() == 0) { + attr = elem.getAttributeNS(AndroidConstants.NS_RESOURCES, + XmlDescriptors.PREF_KEY_ATTR); + } + if (attr == null || attr.length() == 0) { attr = elem.getAttribute(ResourcesDescriptors.NAME_ATTR); } if (attr == null || attr.length() == 0) { @@ -426,6 +432,13 @@ public class UiElementNode implements IPropertySource { public AndroidEditor getEditor() { return mUiParent == null ? mEditor : mUiParent.getEditor(); } + + /** + * Returns the Android target data for the file being edited. + */ + public AndroidTargetData getAndroidTarget() { + return getEditor().getTargetData(); + } /** * @return A read-only version of the children collection. @@ -447,7 +460,7 @@ public class UiElementNode implements IPropertySource { } return mReadOnlyUiAttributes; } - + /** * @return A read-only version of the unknown attributes collection. */ @@ -564,7 +577,7 @@ public class UiElementNode implements IPropertySource { return null; } - + /** * Returns the {@link UiAttributeNode} matching this attribute descriptor or * null if not found. @@ -719,7 +732,16 @@ public class UiElementNode implements IPropertySource { } mXmlNode = doc.createElement(element_name); - parentXmlNode.appendChild(mXmlNode); + + Node xmlNextSibling = null; + + UiElementNode uiNextSibling = getUiNextSibling(); + if (uiNextSibling != null) { + xmlNextSibling = uiNextSibling.getXmlNode(); + } + + parentXmlNode.insertBefore(mXmlNode, xmlNextSibling); + // Insert a separator after the tag, to make it easier to read Text sep = doc.createTextNode("\n"); parentXmlNode.appendChild(sep); @@ -736,7 +758,7 @@ public class UiElementNode implements IPropertySource { attr.setPrefix(desc.getXmlNsPrefix()); mXmlNode.getAttributes().setNamedItemNS(attr); } else { - UiAttributeNode ui_attr = mUiAttributes.get(attr_desc); + UiAttributeNode ui_attr = getInternalUiAttributes().get(attr_desc); commitAttributeToXml(ui_attr, ui_attr.getCurrentValue()); } } @@ -873,7 +895,7 @@ public class UiElementNode implements IPropertySource { false /* recursive */); if (desc == null) { // Unknown element. Simply ignore it. - EditorsPlugin.log(IStatus.WARNING, + AdtPlugin.log(IStatus.WARNING, "AndroidManifest: Ignoring unknown '%s' XML element", //$NON-NLS-1$ element_name); } else { @@ -953,7 +975,7 @@ public class UiElementNode implements IPropertySource { * @param descriptor The {@link ElementDescriptor} that knows how to create the UI node. * @return The new UI node. */ - private UiElementNode insertNewUiChild(int index, ElementDescriptor descriptor) { + public UiElementNode insertNewUiChild(int index, ElementDescriptor descriptor) { UiElementNode ui_node; ui_node = descriptor.createUiNode(); mUiChildren.add(index, ui_node); @@ -1064,7 +1086,7 @@ public class UiElementNode implements IPropertySource { listener.uiElementNodeUpdated(this, state); } catch (Exception e) { // prevent a crashing listener from crashing the whole invocation chain - EditorsPlugin.log(e, "UIElement Listener failed: %s, state=%s", //$NON-NLS-1$ + AdtPlugin.log(e, "UIElement Listener failed: %s, state=%s", //$NON-NLS-1$ getBreadcrumbTrailDescription(true), state.toString()); } @@ -1112,6 +1134,8 @@ public class UiElementNode implements IPropertySource { * Note that the caller MUST ensure that modifying the underlying XML model is * safe and must take care of marking the model as dirty if necessary. * + * @see AndroidEditor#editXmlModel(Runnable) + * * @param uiAttr The attribute node to commit. Must be a child of this UiElementNode. * @param newValue The new value to set. * @return True if the XML attribute was modified or removed, false if nothing changed. @@ -1119,7 +1143,7 @@ public class UiElementNode implements IPropertySource { public boolean commitAttributeToXml(UiAttributeNode uiAttr, String newValue) { // Get (or create) the underlying XML element node that contains the attributes. Node element = prepareCommit(); - if (element != null) { + if (element != null && uiAttr != null) { String attrLocalName = uiAttr.getDescriptor().getXmlLocalName(); String attrNsUri = uiAttr.getDescriptor().getNamespaceUri(); @@ -1146,6 +1170,36 @@ public class UiElementNode implements IPropertySource { } /** + * Helper method to commit all dirty attributes values to XML. + * <p/> + * This method is useful if {@link #setAttributeValue(String, String, boolean)} has been + * called more than once and all the attributes marked as dirty must be commited to the + * XML. It calls {@link #commitAttributeToXml(UiAttributeNode, String)} on each dirty + * attribute. + * <p/> + * Note that the caller MUST ensure that modifying the underlying XML model is + * safe and must take care of marking the model as dirty if necessary. + * + * @see AndroidEditor#editXmlModel(Runnable) + * + * @return True if one or more values were actually modified or removed, + * false if nothing changed. + */ + public boolean commitDirtyAttributesToXml() { + boolean result = false; + HashMap<AttributeDescriptor, UiAttributeNode> attributeMap = getInternalUiAttributes(); + + for (Entry<AttributeDescriptor, UiAttributeNode> entry : attributeMap.entrySet()) { + UiAttributeNode ui_attr = entry.getValue(); + if (ui_attr.isDirty()) { + result |= commitAttributeToXml(ui_attr, ui_attr.getCurrentValue()); + ui_attr.setDirty(false); + } + } + return result; + } + + /** * Returns the namespace prefix matching the requested namespace URI. * If no such declaration is found, returns the default "android" prefix. * @@ -1226,13 +1280,21 @@ public class UiElementNode implements IPropertySource { * This does not commit to the XML model. It does mark the attribute node as dirty. * This is up to the caller. * + * @see #commitAttributeToXml(UiAttributeNode, String) + * @see #commitDirtyAttributesToXml() + * * @param attrXmlName The XML name of the attribute to modify - * @param value The new value for the attribute. + * @param value The new value for the attribute. If set to null, the attribute is removed. * @param override True if the value must be set even if one already exists. + * @return The {@link UiAttributeNode} that has been modified or null. */ - public void setAttributeValue(String attrXmlName, String value, boolean override) { + public UiAttributeNode setAttributeValue(String attrXmlName, String value, boolean override) { HashMap<AttributeDescriptor, UiAttributeNode> attributeMap = getInternalUiAttributes(); + if (value == null) { + value = ""; //$NON-NLS-1$ -- this removes an attribute + } + for (Entry<AttributeDescriptor, UiAttributeNode> entry : attributeMap.entrySet()) { AttributeDescriptor ui_desc = entry.getKey(); if (ui_desc.getXmlLocalName().equals(attrXmlName)) { @@ -1240,16 +1302,20 @@ public class UiElementNode implements IPropertySource { // Not all attributes are editable, ignore those which are not if (ui_attr instanceof IUiSettableAttributeNode) { String current = ui_attr.getCurrentValue(); - if (override || current == null || current.length() == 0) { + // Only update (and mark as dirty) if the attribute did not have any + // value or if the value was different. + if (override || current == null || !current.equals(value)) { ((IUiSettableAttributeNode) ui_attr).setCurrentValue(value); // mark the attribute as dirty since their internal content // as been modified, but not the underlying XML model ui_attr.setDirty(true); + return ui_attr; } } break; } } + return null; } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiFlagAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiFlagAttributeNode.java index 4e0a8c7..ddcf0a0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiFlagAttributeNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiFlagAttributeNode.java @@ -16,7 +16,8 @@ package com.android.ide.eclipse.editors.uimodel; -import com.android.ide.eclipse.common.resources.FrameworkResourceManager; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; +import com.android.ide.eclipse.editors.AndroidEditor; import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; import com.android.ide.eclipse.editors.descriptors.FlagAttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor; @@ -138,8 +139,13 @@ public class UiFlagAttributeNode extends UiTextAttributeNode { } if (values == null) { - // or from the framework resource manager - values = FrameworkResourceManager.getInstance().getValues(element_name, attr_name); + // or from the AndroidTargetData + UiElementNode uiNode = getUiParent(); + AndroidEditor editor = uiNode.getEditor(); + AndroidTargetData data = editor.getTargetData(); + if (data != null) { + values = data.getAttributeValues(element_name, attr_name); + } } return values; diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiListAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiListAttributeNode.java index 261e146..aaad0ce 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiListAttributeNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiListAttributeNode.java @@ -16,9 +16,10 @@ package com.android.ide.eclipse.editors.uimodel; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.resources.FrameworkResourceManager; -import com.android.ide.eclipse.editors.EditorsPlugin; +import com.android.ide.eclipse.editors.AndroidEditor; import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; import com.android.ide.eclipse.editors.descriptors.ListAttributeDescriptor; @@ -110,7 +111,7 @@ public class UiListAttributeNode extends UiAbstractTextAttributeNode { String[] values = getPossibleValues(); if (values == null) { - EditorsPlugin.log(IStatus.ERROR, + AdtPlugin.log(IStatus.ERROR, "FrameworkResourceManager did not provide values yet for %1$s", getDescriptor().getXmlLocalName()); } else { @@ -150,21 +151,27 @@ public class UiListAttributeNode extends UiAbstractTextAttributeNode { } if (values == null) { - // or from the framework resource manager - // get the great-grand-parent descriptor. - - // the parent should always exist. - UiElementNode grandParentNode = uiParent.getUiParent(); - - String greatGrandParentNodeName = null; - if (grandParentNode != null) { - UiElementNode greatGrandParentNode = grandParentNode.getUiParent(); - if (greatGrandParentNode != null) { - greatGrandParentNodeName = greatGrandParentNode.getDescriptor().getXmlName(); + // or from the AndroidTargetData + UiElementNode uiNode = getUiParent(); + AndroidEditor editor = uiNode.getEditor(); + AndroidTargetData data = editor.getTargetData(); + if (data != null) { + // get the great-grand-parent descriptor. + + // the parent should always exist. + UiElementNode grandParentNode = uiParent.getUiParent(); + + String greatGrandParentNodeName = null; + if (grandParentNode != null) { + UiElementNode greatGrandParentNode = grandParentNode.getUiParent(); + if (greatGrandParentNode != null) { + greatGrandParentNodeName = + greatGrandParentNode.getDescriptor().getXmlName(); + } } + + values = data.getAttributeValues(element_name, attr_name, greatGrandParentNodeName); } - values = FrameworkResourceManager.getInstance().getValues(element_name, attr_name, - greatGrandParentNodeName); } return values; diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java index 132ccc0..1c1e1bd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java @@ -16,7 +16,7 @@ package com.android.ide.eclipse.editors.uimodel; -import com.android.ide.eclipse.common.resources.FrameworkResourceManager; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; import com.android.ide.eclipse.common.resources.IResourceRepository; import com.android.ide.eclipse.common.resources.ResourceType; import com.android.ide.eclipse.editors.AndroidEditor; @@ -28,7 +28,6 @@ import com.android.ide.eclipse.editors.ui.SectionHelper; import com.android.ide.eclipse.editors.wizards.ReferenceChooserDialog; import com.android.ide.eclipse.editors.wizards.ResourceChooser; -import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; @@ -41,8 +40,6 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; -import org.eclipse.ui.IEditorInput; -import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.forms.IManagedForm; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.TableWrapData; @@ -118,22 +115,19 @@ public class UiResourceAttributeNode extends UiTextAttributeNode { * containing the result. */ public String showDialog(Shell shell, String currentValue) { - // we need to get the project of the manifest. + // we need to get the project of the file being edited. UiElementNode uiNode = getUiParent(); AndroidEditor editor = uiNode.getEditor(); - IEditorInput input = editor.getEditorInput(); - if (input instanceof IFileEditorInput) { - // from the file editor we can get the IFile object, and from it, the IProject. - IFile file = ((IFileEditorInput)input).getFile(); - IProject project = file.getProject(); - + IProject project = editor.getProject(); + if (project != null) { // get the resource repository for this project and the system resources. IResourceRepository projectRepository = ResourceManager.getInstance().getProjectResources(project); if (mType != null) { - IResourceRepository systemRepository = - FrameworkResourceManager.getInstance().getSystemResources(); + // get the Target Data to get the system resources + AndroidTargetData data = editor.getTargetData(); + IResourceRepository systemRepository = data.getSystemResources(); // open a resource chooser dialog for specified resource type. ResourceChooser dlg = new ResourceChooser(mType, diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiSeparatorAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiSeparatorAttributeNode.java index 192f752..192f752 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiSeparatorAttributeNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiSeparatorAttributeNode.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiTextAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiTextAttributeNode.java index 4c53f4c..4c53f4c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiTextAttributeNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiTextAttributeNode.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiTextValueNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiTextValueNode.java index 5c1db05..5c1db05 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/uimodel/UiTextValueNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiTextValueNode.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java index 39cfc58..b7dffdd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java @@ -604,6 +604,8 @@ public class ConfigurationSelector extends Composite { onTextChange(); } }); + + new Label(this, SWT.NONE).setText("(3 digit code)"); } private void onTextChange() { @@ -666,6 +668,8 @@ public class ConfigurationSelector extends Composite { onTextChange(); } }); + + new Label(this, SWT.NONE).setText("(1-3 digit code)"); } private void onTextChange() { @@ -731,6 +735,8 @@ public class ConfigurationSelector extends Composite { onLanguageChange(); } }); + + new Label(this, SWT.NONE).setText("(2 letter code)"); } private void onLanguageChange() { @@ -797,6 +803,8 @@ public class ConfigurationSelector extends Composite { onRegionChange(); } }); + + new Label(this, SWT.NONE).setText("(2 letter code)"); } private void onRegionChange() { @@ -1236,11 +1244,8 @@ public class ConfigurationSelector extends Composite { String size1 = mSize1.getText(); String size2 = mSize2.getText(); - // if only one of the strings is empty, do nothing - if ((size1.length() == 0) ^ (size2.length() == 0)) { - return; - } else if (size1.length() == 0 && size2.length() == 0) { - // empty size, means no qualifier. + if (size1.length() == 0 || size2.length() == 0) { + // if one of the strings is empty, reset to no qualifier. // Since the qualifier classes are immutable, and we don't want to // remove the qualifier from the configuration, we create a new default one. mSelectedConfiguration.setScreenDimensionQualifier(new ScreenDimensionQualifier()); @@ -1251,9 +1256,11 @@ public class ConfigurationSelector extends Composite { if (qualifier != null) { mSelectedConfiguration.setScreenDimensionQualifier(qualifier); } else { - // Failure! Looks like the value is wrong. - // we do nothing in this case. - return; + // Failure! Looks like the value is wrong, reset the qualifier + // Since the qualifier classes are immutable, and we don't want to + // remove the qualifier from the configuration, we create a new default one. + mSelectedConfiguration.setScreenDimensionQualifier( + new ScreenDimensionQualifier()); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java index 2f3209b..b27dd4f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java @@ -17,18 +17,19 @@ package com.android.ide.eclipse.editors.wizards; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; +import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.ProjectChooserHelper; import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors; import com.android.ide.eclipse.editors.menu.descriptors.MenuDescriptors; import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration; import com.android.ide.eclipse.editors.resources.configurations.ResourceQualifier; import com.android.ide.eclipse.editors.resources.descriptors.ResourcesDescriptors; import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType; import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.ConfigurationState; -import com.android.ide.eclipse.editors.xml.descriptors.XmlDescriptors; +import com.android.sdklib.IAndroidTarget; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; @@ -184,7 +185,7 @@ class NewXmlFileCreationPage extends WizardPage { "Layout", // UI name "An XML file that describes a screen layout.", // tooltip ResourceFolderType.LAYOUT, // folder type - LayoutDescriptors.getInstance().getDescriptor(), // root seed + AndroidTargetData.DESCRIPTOR_LAYOUT, // root seed "LinearLayout", // default root AndroidConstants.NS_RESOURCES, // xmlns "android:layout_width=\"wrap_content\"\n" + // default attributes @@ -209,7 +210,7 @@ class NewXmlFileCreationPage extends WizardPage { new TypeInfo("Preference", // UI name "An XML file that describes preferences.", // tooltip ResourceFolderType.XML, // folder type - XmlDescriptors.getInstance().getPreferencesDescriptor(), // root seed + AndroidTargetData.DESCRIPTOR_PREFERENCES, // root seed AndroidConstants.CLASS_PREFERENCE_SCREEN, // default root AndroidConstants.NS_RESOURCES, // xmlns null // default attributes @@ -217,7 +218,7 @@ class NewXmlFileCreationPage extends WizardPage { new TypeInfo("Searchable", // UI name "An XML file that describes a searchable [TODO].", // tooltip ResourceFolderType.XML, // folder type - XmlDescriptors.getInstance().getSearchableDescriptor(), // root seed + AndroidTargetData.DESCRIPTOR_SEARCHABLE, // root seed null, // default root AndroidConstants.NS_RESOURCES, // xmlns null // default attributes @@ -431,6 +432,11 @@ class NewXmlFileCreationPage extends WizardPage { mProjectTextField = new Text(parent, SWT.BORDER); mProjectTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mProjectTextField.setToolTipText(tooltip); + mProjectTextField.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + onProjectFieldUpdated(); + } + }); mProjectBrowseButton = new Button(parent, SWT.NONE); mProjectBrowseButton.setText("Browse..."); @@ -655,9 +661,10 @@ class NewXmlFileCreationPage extends WizardPage { */ private void initializeRootValues() { for (TypeInfo type : sTypes) { + // Clear all the roots for this type ArrayList<String> roots = type.getRoots(); if (roots.size() > 0) { - continue; + roots.clear(); } // depending of the type of the seed, initialize the root in different ways @@ -671,13 +678,22 @@ class NewXmlFileCreationPage extends WizardPage { for (String value : (String[]) rootSeed) { roots.add(value); } - } else if (rootSeed instanceof ElementDescriptor) { - // The seed is an element descriptor or a document descriptor. + } else if (rootSeed instanceof Integer && mProject != null) { + // The seed is a descriptor reference defined in AndroidTargetData.DESCRIPTOR_* // In this case add all the children element descriptors defined, recursively, // and avoid infinite recursion by keeping track of what has already been added. + + // Note: if project is null, the root list will be empty since it has been + // cleared above. + + // get the AndroidTargetData from the project + IAndroidTarget target = Sdk.getCurrent().getTarget(mProject); + AndroidTargetData data = Sdk.getCurrent().getTargetData(target); + ElementDescriptor descriptor = data.getDescriptorProvider( + (Integer)rootSeed).getDescriptor(); HashSet<ElementDescriptor> visited = new HashSet<ElementDescriptor>(); - initRootElementDescriptor(roots, (ElementDescriptor) rootSeed, visited); + initRootElementDescriptor(roots, descriptor, visited); // Sort alphabetically. Collections.sort(roots); @@ -708,6 +724,35 @@ class NewXmlFileCreationPage extends WizardPage { } } } + + /** + * Callback called when the user edits the project text field. + */ + private void onProjectFieldUpdated() { + String project = mProjectTextField.getText(); + + // Is this a valid project? + IJavaProject[] projects = mProjectChooserHelper.getAndroidProjects(null /*javaModel*/); + IProject found = null; + for (IJavaProject p : projects) { + if (p.getProject().getName().equals(project)) { + found = p.getProject(); + break; + } + } + + if (found != mProject) { + mProject = found; + + // update the Type with the new descriptors. + initializeRootValues(); + + // update the combo + updateRootCombo(getSelectedType()); + + validatePage(); + } + } /** * Callback called when the user uses the "Browse Projects" button. @@ -717,6 +762,13 @@ class NewXmlFileCreationPage extends WizardPage { if (p != null) { mProject = p.getProject(); mProjectTextField.setText(mProject.getName()); + + // update the Type with the new descriptors. + initializeRootValues(); + + // update the combo + updateRootCombo(getSelectedType()); + validatePage(); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/wizards/NewXmlFileWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileWizard.java index 8318e4b..125102b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/wizards/NewXmlFileWizard.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileWizard.java @@ -18,7 +18,7 @@ package com.android.ide.eclipse.editors.wizards; -import com.android.ide.eclipse.editors.EditorsPlugin; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.IconFactory; import com.android.ide.eclipse.editors.wizards.NewXmlFileCreationPage.TypeInfo; @@ -116,7 +116,7 @@ public class NewXmlFileWizard extends Wizard implements INewWizard { try { IDE.openEditor(page, file); } catch (PartInitException e) { - EditorsPlugin.log(e, "Failed to create %1$s: missing type", //$NON-NLS-1$ + AdtPlugin.log(e, "Failed to create %1$s: missing type", //$NON-NLS-1$ file.getFullPath().toString()); } } @@ -133,7 +133,7 @@ public class NewXmlFileWizard extends Wizard implements INewWizard { boolean need_delete = false; if (file.exists()) { - if (!EditorsPlugin.displayPrompt("New Android XML File", + if (!AdtPlugin.displayPrompt("New Android XML File", String.format("Do you want to overwrite the file %1$s ?", name))) { // abort if user selects cancel. return null; @@ -146,14 +146,14 @@ public class NewXmlFileWizard extends Wizard implements INewWizard { TypeInfo type = mMainPage.getSelectedType(); if (type == null) { // this is not expected to happen - EditorsPlugin.log(IStatus.ERROR, "Failed to create %1$s: missing type", name); //$NON-NLS-1$ + AdtPlugin.log(IStatus.ERROR, "Failed to create %1$s: missing type", name); //$NON-NLS-1$ return null; } String xmlns = type.getXmlns(); String root = mMainPage.getRootElement(); if (root == null) { // this is not expected to happen - EditorsPlugin.log(IStatus.ERROR, "Failed to create %1$s: missing root element", //$NON-NLS-1$ + AdtPlugin.log(IStatus.ERROR, "Failed to create %1$s: missing root element", //$NON-NLS-1$ file.toString()); return null; } @@ -191,7 +191,7 @@ public class NewXmlFileWizard extends Wizard implements INewWizard { } error = String.format("Failed to generate %1$s: %2$s", name, error); - EditorsPlugin.displayError("New Android XML File", error); + AdtPlugin.displayError("New Android XML File", error); return null; } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java index 446cc14..d3ff334 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java @@ -16,6 +16,7 @@ package com.android.ide.eclipse.editors.wizards; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.resources.IResourceRepository; import com.android.ide.eclipse.common.resources.ResourceItem; @@ -187,16 +188,16 @@ public class ReferenceChooserDialog extends SelectionStatusDialog { IStatus status; if (treeSelection != null) { if (treeSelection.getSegmentCount() == 2) { - status = new Status(IStatus.OK, AndroidConstants.EDITORS_PLUGIN_ID, + status = new Status(IStatus.OK, AdtPlugin.PLUGIN_ID, IStatus.OK, "", //$NON-NLS-1$ null); } else { - status = new Status(IStatus.ERROR, AndroidConstants.EDITORS_PLUGIN_ID, + status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, IStatus.ERROR, "You must select a Resource Item", null); } } else { - status = new Status(IStatus.ERROR, AndroidConstants.EDITORS_PLUGIN_ID, + status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, IStatus.ERROR, "", //$NON-NLS-1$ null); } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/wizards/ResourceChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceChooser.java index 60a627b..60a627b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/wizards/ResourceChooser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceChooser.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/wizards/ResourceContentProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceContentProvider.java index 7c6a539..7c6a539 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/wizards/ResourceContentProvider.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceContentProvider.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/wizards/ResourceLabelProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceLabelProvider.java index 024d084..024d084 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/wizards/ResourceLabelProvider.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceLabelProvider.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/xml/XmlContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/XmlContentAssist.java index bb010e3..f28b523 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/xml/XmlContentAssist.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/XmlContentAssist.java @@ -16,8 +16,8 @@ package com.android.ide.eclipse.editors.xml; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; import com.android.ide.eclipse.editors.AndroidContentAssist; -import com.android.ide.eclipse.editors.xml.descriptors.XmlDescriptors; /** * Content Assist Processor for /res/xml XML files @@ -28,6 +28,6 @@ class XmlContentAssist extends AndroidContentAssist { * Constructor for LayoutContentAssist */ public XmlContentAssist() { - super(XmlDescriptors.getInstance().getDescriptor().getChildren()); + super(AndroidTargetData.DESCRIPTOR_XML); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/xml/XmlEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/XmlEditor.java index 40d655a..b1900ae 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/xml/XmlEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/XmlEditor.java @@ -16,16 +16,19 @@ package com.android.ide.eclipse.editors.xml; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.sdk.AndroidTargetData; +import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.editors.AndroidEditor; -import com.android.ide.eclipse.editors.EditorsPlugin; import com.android.ide.eclipse.editors.FirstElementParser; import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.uimodel.UiDocumentNode; -import com.android.ide.eclipse.editors.xml.descriptors.XmlDescriptors; +import com.android.sdklib.IAndroidTarget; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.PartInitException; @@ -37,19 +40,16 @@ import org.w3c.dom.Document; */ public class XmlEditor extends AndroidEditor { - public static final String ID = "com.android.ide.eclipse.editors.xml.XmlEditor"; //$NON-NLS-1$ + public static final String ID = AndroidConstants.EDITORS_NAMESPACE + ".xml.XmlEditor"; //$NON-NLS-1$ /** Root node of the UI element hierarchy */ private UiDocumentNode mUiRootNode; - /** Listener to update the root node if the resource framework changes */ - private Runnable mResourceRefreshListener; /** * Creates the form editor for resources XML files. */ public XmlEditor() { super(); - initUiRootNode(); } /** @@ -72,19 +72,25 @@ public class XmlEditor extends AndroidEditor { * @return True if the {@link XmlEditor} can handle that file. */ public static boolean canHandleFile(IFile file) { - - FirstElementParser.Result result = FirstElementParser.parse( - file.getLocation().toOSString(), - AndroidConstants.NS_RESOURCES); + // we need the target of the file's project to access the descriptors. + IProject project = file.getProject(); + IAndroidTarget target = Sdk.getCurrent().getTarget(project); + if (target != null) { + AndroidTargetData data = Sdk.getCurrent().getTargetData(target); - if (result != null) { - String name = result.getElement(); - if (name != null && result.getXmlnsPrefix() != null) { - DocumentDescriptor desc = XmlDescriptors.getInstance().getDescriptor(); - for (ElementDescriptor elem : desc.getChildren()) { - if (elem.getXmlName().equals(name)) { - // This is an element that this document can handle - return true; + FirstElementParser.Result result = FirstElementParser.parse( + file.getLocation().toOSString(), + AndroidConstants.NS_RESOURCES); + + if (result != null) { + String name = result.getElement(); + if (name != null && result.getXmlnsPrefix() != null) { + DocumentDescriptor desc = data.getXmlDescriptors().getDescriptor(); + for (ElementDescriptor elem : desc.getChildren()) { + if (elem.getXmlName().equals(name)) { + // This is an element that this document can handle + return true; + } } } } @@ -95,15 +101,6 @@ public class XmlEditor extends AndroidEditor { // ---- Base Class Overrides ---- - @Override - public void dispose() { - if (mResourceRefreshListener != null) { - EditorsPlugin.getDefault().removeResourceChangedListener(mResourceRefreshListener); - mResourceRefreshListener = null; - } - super.dispose(); - } - /** * Returns whether the "save as" operation is supported by this editor. * <p/> @@ -125,10 +122,10 @@ public class XmlEditor extends AndroidEditor { try { addPage(new XmlTreePage(this)); } catch (PartInitException e) { - EditorsPlugin.log(e, "Error creating nested page"); //$NON-NLS-1$ + AdtPlugin.log(e, "Error creating nested page"); //$NON-NLS-1$ } - } + } /* (non-java doc) * Change the tab/title name to include the project name. @@ -150,35 +147,55 @@ public class XmlEditor extends AndroidEditor { */ @Override protected void xmlModelChanged(Document xml_doc) { + // init the ui root on demand + initUiRootNode(false /*force*/); + mUiRootNode.loadFromXmlNode(xml_doc); super.xmlModelChanged(xml_doc); } - - // ---- Local Methods ---- - /** * Creates the initial UI Root Node, including the known mandatory elements. + * @param force if true, a new UiRootNode is recreated even if it already exists. */ - private void initUiRootNode() { + @Override + protected void initUiRootNode(boolean force) { // The root UI node is always created, even if there's no corresponding XML node. - if (mUiRootNode == null) { - DocumentDescriptor desc = XmlDescriptors.getInstance().getDescriptor(); + if (mUiRootNode == null || force) { + Document doc = null; + if (mUiRootNode != null) { + doc = mUiRootNode.getXmlDocument(); + } + + // get the target data from the opened file (and its project) + AndroidTargetData data = getTargetData(); + + DocumentDescriptor desc; + if (data == null) { + desc = new DocumentDescriptor("temp", null /*children*/); + } else { + desc = data.getXmlDescriptors().getDescriptor(); + } + mUiRootNode = (UiDocumentNode) desc.createUiNode(); mUiRootNode.setEditor(this); - // Add a listener to refresh the root node if the resource framework changes - // by forcing it to parse its own XML - mResourceRefreshListener = new Runnable() { - public void run() { - commitPages(false /* onSave */); + onDescriptorsChanged(doc); + } + } + + // ---- Local Methods ---- - mUiRootNode.reloadFromXmlNode(mUiRootNode.getXmlNode()); - } - }; - EditorsPlugin.getDefault().addResourceChangedListener(mResourceRefreshListener); - mResourceRefreshListener.run(); + /** + * Reloads the UI manifest node from the XML, and calls the pages to update. + */ + private void onDescriptorsChanged(Document document) { + if (document != null) { + mUiRootNode.loadFromXmlNode(document); + } else { + mUiRootNode.reloadFromXmlNode(mUiRootNode.getXmlNode()); } } + } diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/xml/XmlSourceViewerConfig.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/XmlSourceViewerConfig.java index d25c812..d25c812 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/xml/XmlSourceViewerConfig.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/XmlSourceViewerConfig.java diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/xml/XmlTreePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/XmlTreePage.java index 5bb0f72..91ce6dd 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/xml/XmlTreePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/XmlTreePage.java @@ -16,7 +16,7 @@ package com.android.ide.eclipse.editors.xml; -import com.android.ide.eclipse.editors.EditorsPlugin; +import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.editors.ui.tree.UiTreeBlock; import com.android.ide.eclipse.editors.uimodel.UiElementNode; @@ -49,7 +49,7 @@ public final class XmlTreePage extends FormPage { super.createFormContent(managedForm); ScrolledForm form = managedForm.getForm(); form.setText("Android Xml"); - form.setImage(EditorsPlugin.getAndroidLogo()); + form.setImage(AdtPlugin.getAndroidLogo()); UiElementNode rootNode = mEditor.getUiRootNode(); UiTreeBlock block = new UiTreeBlock(mEditor, rootNode, diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/xml/descriptors/XmlDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/descriptors/XmlDescriptors.java index 14cb9d6..31b4c61 100644 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/xml/descriptors/XmlDescriptors.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/descriptors/XmlDescriptors.java @@ -24,6 +24,7 @@ import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.editors.descriptors.IDescriptorProvider; import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.SeparatorAttributeDescriptor; import com.android.ide.eclipse.editors.layout.descriptors.ViewElementDescriptor; @@ -36,10 +37,11 @@ import java.util.Map; * Description of the /res/xml structure. * Currently supports the <searchable> and <preferences> root nodes. */ -public class XmlDescriptors { +public final class XmlDescriptors implements IDescriptorProvider { - /** Singleton instance */ - private static XmlDescriptors sThis; + // Public attributes names, attributes descriptors and elements descriptors referenced + // elsewhere. + public static final String PREF_KEY_ATTR = "key"; //$NON-NLS-1$ /** The root document descriptor for both searchable and preferences. */ private DocumentDescriptor mDescriptor = new DocumentDescriptor("xml_doc", null /* children */); //$NON-NLS-1$ @@ -50,19 +52,15 @@ public class XmlDescriptors { /** The root document descriptor for preferences. */ private DocumentDescriptor mPrefDescriptor = new DocumentDescriptor("xml_doc", null /* children */); //$NON-NLS-1$ - /** Returns a singleton instance of the {@link XmlDescriptors}. */ - public static synchronized XmlDescriptors getInstance() { - if (sThis == null) { - sThis = new XmlDescriptors(); - } - return sThis; - } - /** @return the root descriptor for both searchable and preferences. */ public DocumentDescriptor getDescriptor() { return mDescriptor; } + public ElementDescriptor[] getRootElementDescriptors() { + return mDescriptor.getChildren(); + } + /** @return the root descriptor for searchable. */ public DocumentDescriptor getSearchableDescriptor() { return mSearchDescriptor; @@ -72,6 +70,30 @@ public class XmlDescriptors { public DocumentDescriptor getPreferencesDescriptor() { return mPrefDescriptor; } + + public IDescriptorProvider getSearchableProvider() { + return new IDescriptorProvider() { + public ElementDescriptor getDescriptor() { + return mSearchDescriptor; + } + + public ElementDescriptor[] getRootElementDescriptors() { + return mSearchDescriptor.getChildren(); + } + }; + } + + public IDescriptorProvider getPreferencesProvider() { + return new IDescriptorProvider() { + public ElementDescriptor getDescriptor() { + return mPrefDescriptor; + } + + public ElementDescriptor[] getRootElementDescriptors() { + return mPrefDescriptor.getChildren(); + } + }; + } /** * Updates the document descriptor. diff --git a/eclipse/plugins/com.android.ide.eclipse.common/.classpath b/eclipse/plugins/com.android.ide.eclipse.common/.classpath deleted file mode 100644 index a7b3dc1..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.common/.classpath +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<classpath> - <classpathentry kind="src" path="src"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> - <classpathentry kind="lib" path="androidprefs.jar"/> - <classpathentry kind="lib" path="sdkstats.jar"/> - <classpathentry kind="output" path="bin"/> -</classpath> diff --git a/eclipse/plugins/com.android.ide.eclipse.common/.project b/eclipse/plugins/com.android.ide.eclipse.common/.project deleted file mode 100644 index 510efb7..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.common/.project +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<projectDescription> - <name>common</name> - <comment></comment> - <projects> - </projects> - <buildSpec> - <buildCommand> - <name>org.eclipse.jdt.core.javabuilder</name> - <arguments> - </arguments> - </buildCommand> - <buildCommand> - <name>org.eclipse.pde.ManifestBuilder</name> - <arguments> - </arguments> - </buildCommand> - <buildCommand> - <name>org.eclipse.pde.SchemaBuilder</name> - <arguments> - </arguments> - </buildCommand> - </buildSpec> - <natures> - <nature>org.eclipse.pde.PluginNature</nature> - <nature>org.eclipse.jdt.core.javanature</nature> - </natures> -</projectDescription> diff --git a/eclipse/plugins/com.android.ide.eclipse.common/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.common/META-INF/MANIFEST.MF deleted file mode 100644 index dad85a6..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.common/META-INF/MANIFEST.MF +++ /dev/null @@ -1,22 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Android Common Plugin -Bundle-SymbolicName: com.android.ide.eclipse.common;singleton:=true -Bundle-Version: 0.8.1.qualifier -Bundle-ClassPath: ., - sdkstats.jar, - androidprefs.jar -Bundle-Vendor: The Android Open Source Project -Eclipse-LazyStart: true -Export-Package: com.android.ide.eclipse.common, - com.android.ide.eclipse.common.project, - com.android.ide.eclipse.common.resources -Require-Bundle: org.eclipse.core.resources, - org.eclipse.core.runtime, - org.eclipse.jdt.core, - org.eclipse.ui.console, - org.eclipse.ui, - org.eclipse.jdt.ui, - org.eclipse.jface.text, - org.eclipse.ui.editors -Bundle-Activator: com.android.ide.eclipse.common.CommonPlugin diff --git a/eclipse/plugins/com.android.ide.eclipse.common/MODULE_LICENSE_EPL b/eclipse/plugins/com.android.ide.eclipse.common/MODULE_LICENSE_EPL deleted file mode 100644 index e69de29..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.common/MODULE_LICENSE_EPL +++ /dev/null diff --git a/eclipse/plugins/com.android.ide.eclipse.common/NOTICE b/eclipse/plugins/com.android.ide.eclipse.common/NOTICE deleted file mode 100644 index 49c101d..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.common/NOTICE +++ /dev/null @@ -1,224 +0,0 @@ -*Eclipse Public License - v 1.0* - -THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE -PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF -THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. - -*1. DEFINITIONS* - -"Contribution" means: - -a) in the case of the initial Contributor, the initial code and -documentation distributed under this Agreement, and -b) in the case of each subsequent Contributor: - -i) changes to the Program, and - -ii) additions to the Program; - -where such changes and/or additions to the Program originate from and -are distributed by that particular Contributor. A Contribution -'originates' from a Contributor if it was added to the Program by such -Contributor itself or anyone acting on such Contributor's behalf. -Contributions do not include additions to the Program which: (i) are -separate modules of software distributed in conjunction with the Program -under their own license agreement, and (ii) are not derivative works of -the Program. - -"Contributor" means any person or entity that distributes the Program. - -"Licensed Patents " mean patent claims licensable by a Contributor which -are necessarily infringed by the use or sale of its Contribution alone -or when combined with the Program. - -"Program" means the Contributions distributed in accordance with this -Agreement. - -"Recipient" means anyone who receives the Program under this Agreement, -including all Contributors. - -*2. GRANT OF RIGHTS* - -a) Subject to the terms of this Agreement, each Contributor hereby -grants Recipient a non-exclusive, worldwide, royalty-free copyright -license to reproduce, prepare derivative works of, publicly display, -publicly perform, distribute and sublicense the Contribution of such -Contributor, if any, and such derivative works, in source code and -object code form. - -b) Subject to the terms of this Agreement, each Contributor hereby -grants Recipient a non-exclusive, worldwide, royalty-free patent license -under Licensed Patents to make, use, sell, offer to sell, import and -otherwise transfer the Contribution of such Contributor, if any, in -source code and object code form. This patent license shall apply to the -combination of the Contribution and the Program if, at the time the -Contribution is added by the Contributor, such addition of the -Contribution causes such combination to be covered by the Licensed -Patents. The patent license shall not apply to any other combinations -which include the Contribution. No hardware per se is licensed hereunder. - -c) Recipient understands that although each Contributor grants the -licenses to its Contributions set forth herein, no assurances are -provided by any Contributor that the Program does not infringe the -patent or other intellectual property rights of any other entity. Each -Contributor disclaims any liability to Recipient for claims brought by -any other entity based on infringement of intellectual property rights -or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to -secure any other intellectual property rights needed, if any. For -example, if a third party patent license is required to allow Recipient -to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program. - -d) Each Contributor represents that to its knowledge it has sufficient -copyright rights in its Contribution, if any, to grant the copyright -license set forth in this Agreement. - -*3. REQUIREMENTS* - -A Contributor may choose to distribute the Program in object code form -under its own license agreement, provided that: - -a) it complies with the terms and conditions of this Agreement; and - -b) its license agreement: - -i) effectively disclaims on behalf of all Contributors all warranties -and conditions, express and implied, including warranties or conditions -of title and non-infringement, and implied warranties or conditions of -merchantability and fitness for a particular purpose; - -ii) effectively excludes on behalf of all Contributors all liability for -damages, including direct, indirect, special, incidental and -consequential damages, such as lost profits; - -iii) states that any provisions which differ from this Agreement are -offered by that Contributor alone and not by any other party; and - -iv) states that source code for the Program is available from such -Contributor, and informs licensees how to obtain it in a reasonable -manner on or through a medium customarily used for software exchange. - -When the Program is made available in source code form: - -a) it must be made available under this Agreement; and - -b) a copy of this Agreement must be included with each copy of the Program. - -Contributors may not remove or alter any copyright notices contained -within the Program. - -Each Contributor must identify itself as the originator of its -Contribution, if any, in a manner that reasonably allows subsequent -Recipients to identify the originator of the Contribution. - -*4. COMMERCIAL DISTRIBUTION* - -Commercial distributors of software may accept certain responsibilities -with respect to end users, business partners and the like. While this -license is intended to facilitate the commercial use of the Program, the -Contributor who includes the Program in a commercial product offering -should do so in a manner which does not create potential liability for -other Contributors. Therefore, if a Contributor includes the Program in -a commercial product offering, such Contributor ("Commercial -Contributor") hereby agrees to defend and indemnify every other -Contributor ("Indemnified Contributor") against any losses, damages and -costs (collectively "Losses") arising from claims, lawsuits and other -legal actions brought by a third party against the Indemnified -Contributor to the extent caused by the acts or omissions of such -Commercial Contributor in connection with its distribution of the -Program in a commercial product offering. The obligations in this -section do not apply to any claims or Losses relating to any actual or -alleged intellectual property infringement. In order to qualify, an -Indemnified Contributor must: a) promptly notify the Commercial -Contributor in writing of such claim, and b) allow the Commercial -Contributor to control, and cooperate with the Commercial Contributor -in, the defense and any related settlement negotiations. The Indemnified -Contributor may participate in any such claim at its own expense. - -For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other -Contributor to pay any damages as a result, the Commercial Contributor -must pay those damages. - -*5. NO WARRANTY* - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED -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. Each Recipient is solely responsible for -determining the appropriateness of using and distributing the Program -and assumes all risks associated with its exercise of rights under this -Agreement , including but not limited to the risks and costs of program -errors, compliance with applicable laws, damage to or loss of data, -programs or equipment, and unavailability or interruption of operations. - -*6. DISCLAIMER OF LIABILITY* - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR -ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -*7. GENERAL* - -If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further action -by the parties hereto, such provision shall be reformed to the minimum -extent necessary to make such provision valid and enforceable. - -If Recipient institutes patent litigation against any entity (including -a cross-claim or counterclaim in a lawsuit) alleging that the Program -itself (excluding combinations of the Program with other software or -hardware) infringes such Recipient's patent(s), then such Recipient's -rights granted under Section 2(b) shall terminate as of the date such -litigation is filed. - -All Recipient's rights under this Agreement shall terminate if it fails -to comply with any of the material terms or conditions of this Agreement -and does not cure such failure in a reasonable period of time after -becoming aware of such noncompliance. If all Recipient's rights under -this Agreement terminate, Recipient agrees to cease use and distribution -of the Program as soon as reasonably practicable. However, Recipient's -obligations under this Agreement and any licenses granted by Recipient -relating to the Program shall continue and survive. - -Everyone is permitted to copy and distribute copies of this Agreement, -but in order to avoid inconsistency the Agreement is copyrighted and may -only be modified in the following manner. The Agreement Steward reserves -the right to publish new versions (including revisions) of this -Agreement from time to time. No one other than the Agreement Steward has -the right to modify this Agreement. The Eclipse Foundation is the -initial Agreement Steward. The Eclipse Foundation may assign the -responsibility to serve as the Agreement Steward to a suitable separate -entity. Each new version of the Agreement will be given a distinguishing -version number. The Program (including Contributions) may always be -distributed subject to the version of the Agreement under which it was -received. In addition, after a new version of the Agreement is -published, Contributor may elect to distribute the Program (including -its Contributions) under the new version. Except as expressly stated in -Sections 2(a) and 2(b) above, Recipient receives no rights or licenses -to the intellectual property of any Contributor under this Agreement, -whether expressly, by implication, estoppel or otherwise. All rights in -the Program not expressly granted under this Agreement are reserved. - -This Agreement is governed by the laws of the State of New York and the -intellectual property laws of the United States of America. No party to -this Agreement will bring a legal action under this Agreement more than -one year after the cause of action arose. Each party waives its rights -to a jury trial in any resulting litigation. - - - diff --git a/eclipse/plugins/com.android.ide.eclipse.common/build.properties b/eclipse/plugins/com.android.ide.eclipse.common/build.properties deleted file mode 100644 index 4c9981d..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.common/build.properties +++ /dev/null @@ -1,7 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - .,\ - sdkstats.jar,\ - plugin.xml,\ - androidprefs.jar diff --git a/eclipse/plugins/com.android.ide.eclipse.common/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.common/plugin.xml deleted file mode 100644 index 115eb97..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.common/plugin.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?eclipse version="3.2"?> -<plugin> - <extension - id="com.android.ide.eclipse.common.xmlProblem" - name="XML Problem" - point="org.eclipse.core.resources.markers"> - <super type="org.eclipse.core.resources.problemmarker"/> - <super type="org.eclipse.core.resources.textmarker"/> - <persistent value="true"/> - </extension> - <extension - id="com.android.ide.eclipse.common.aaptProblem" - name="aapt Problem" - point="org.eclipse.core.resources.markers"> - <super type="org.eclipse.core.resources.problemmarker"/> - <super type="org.eclipse.core.resources.textmarker"/> - <persistent value="true"/> - </extension> - <extension - id="com.android.ide.eclipse.common.aidlProblem" - name="aidl Problem" - point="org.eclipse.core.resources.markers"> - <super type="org.eclipse.core.resources.problemmarker"/> - <super type="org.eclipse.core.resources.textmarker"/> - <persistent value="true"/> - </extension> - <extension - id="com.android.ide.eclipse.common.androidProblem" - name="Android Problem" - point="org.eclipse.core.resources.markers"> - <super type="org.eclipse.core.resources.problemmarker"/> - <super type="org.eclipse.core.resources.textmarker"/> - <persistent value="true"/> - </extension> - <extension - point="org.eclipse.ui.preferencePages"> - <page - category="com.android.ide.eclipse.preferences.main" - class="com.android.ide.eclipse.common.preferences.UsagePreferencePage" - id="com.android.ide.eclipse.common.preferences.UsagePreferencePage" - name="Usage Stats"/> - </extension> -</plugin> diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/CommonPlugin.java b/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/CommonPlugin.java deleted file mode 100644 index cfee50f..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/CommonPlugin.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.common; - -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.IWorkbench; -import org.eclipse.ui.console.ConsolePlugin; -import org.eclipse.ui.console.IConsole; -import org.eclipse.ui.console.MessageConsole; -import org.eclipse.ui.plugin.AbstractUIPlugin; -import org.osgi.framework.BundleContext; - - -/** - * The activator class controls the plug-in life cycle - */ -public class CommonPlugin extends AbstractUIPlugin { - - // The plug-in ID - public static final String PLUGIN_ID = "com.android.ide.eclipse.common"; // $NON-NLS-1$ - - // The shared instance - private static CommonPlugin sPlugin; - - // The global android console - private MessageConsole mAndroidConsole; - - /** - * The constructor - */ - public CommonPlugin() { - // pass - } - - /** - * Returns the shared instance - * - * @return the shared instance - */ - public static CommonPlugin getDefault() { - return sPlugin; - } - - /** Returns the global android console */ - public MessageConsole getAndroidConsole() { - return mAndroidConsole; - } - - /** - * The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code> - * method refreshes the plug-in actions. Subclasses may extend this method, - * but must send super <b>first</b>. - * - * {@inheritDoc} - */ - @Override - public void start(BundleContext context) throws Exception { - super.start(context); - sPlugin = this; - - /* - * WARNING: think before adding any initialization here as plugins are dynamically - * started and since no UI is being displayed by this plugin, it'll only start when - * another plugin accesses some of its code. - */ - - // set the default android console. - mAndroidConsole = new MessageConsole("Android", null); //$NON-NLS-1$ - ConsolePlugin.getDefault().getConsoleManager().addConsoles( - new IConsole[] { mAndroidConsole }); - } - - /** - * The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code> - * method saves this plug-in's preference and dialog stores and shuts down - * its image registry (if they are in use). Subclasses may extend this - * method, but must send super <b>last</b>. A try-finally statement should - * be used where necessary to ensure that <code>super.shutdown()</code> is - * always done. - * - * {@inheritDoc} - */ - @Override - public void stop(BundleContext context) throws Exception { - sPlugin = null; - super.stop(context); - } - - /** - * Logs a message to the default Eclipse log. - * - * @param severity One of IStatus' severity codes: OK, ERROR, INFO, WARNING or CANCEL. - * @param format The format string, like for String.format(). - * @param args The arguments for the format string, like for String.format(). - */ - public static void log(int severity, String format, Object ... args) { - String message = String.format(format, args); - Status status = new Status(severity, PLUGIN_ID, message); - getDefault().getLog().log(status); - } - - /** - * Logs an exception to the default Eclipse log. - * <p/> - * The status severity is always set to ERROR. - * - * @param exception The exception to log. Its call trace will be recorded. - * @param format The format string, like for String.format(). - * @param args The arguments for the format string, like for String.format(). - */ - public static void log(Throwable exception, String format, Object ... args) { - String message = String.format(format, args); - Status status = new Status(IStatus.ERROR, PLUGIN_ID, message, exception); - getDefault().getLog().log(status); - } - - private static Display getDisplay() { - IWorkbench bench = sPlugin.getWorkbench(); - if (bench!=null) { - return bench.getDisplay(); - } - return null; - } - - /** - * Display a yes/no question dialog box. This dialog is opened synchronously in the ui thread, - * therefore this message can be called from any thread. - * @param title The title of the dialog box - * @param message The error message - * @return true if OK was clicked. - */ - public final static boolean displayPrompt(final String title, final String message) { - // get the current Display and Shell - final Display display = getDisplay(); - - // we need to ask the user what he wants to do. - final Boolean[] wrapper = new Boolean[] { new Boolean(false) }; - display.syncExec(new Runnable() { - public void run() { - Shell shell = display.getActiveShell(); - wrapper[0] = new Boolean(MessageDialog.openQuestion(shell, title, message)); - } - }); - return wrapper[0].booleanValue(); - } - -} diff --git a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/FrameworkResourceManager.java b/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/FrameworkResourceManager.java deleted file mode 100644 index 3f8f029..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.common/src/com/android/ide/eclipse/common/resources/FrameworkResourceManager.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.common.resources; - -import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.CommonPlugin; - -import java.util.ArrayList; -import java.util.Hashtable; -import java.util.Map; - -/** - * This is a communication between different plugins that don't know each other. It allows one - * plugin to provide detailed information about the framework resources to another plugin. - */ -public class FrameworkResourceManager { - - private static String[] sBooleanValues = new String[] { - "true", //$NON-NLS-1$ - "false", //$NON-NLS-1$ - }; - - private static FrameworkResourceManager sThis = new FrameworkResourceManager(); - - private ArrayList<Runnable> mResourcesChangeListeners = new ArrayList<Runnable>(); - - private Hashtable<String, String[]> mAttributeValues; - - private IResourceRepository mSystemResourceRepository; - - private ViewClassInfo[] mLayoutViewsInfo; - private ViewClassInfo[] mLayoutGroupsInfo; - private ViewClassInfo[] mPreferencesInfo; - private ViewClassInfo[] mPreferenceGroupsInfo; - - private Map<String, DeclareStyleableInfo> mXmlMenuMap; - private Map<String, DeclareStyleableInfo> mXmlSearchableMap; - private Map<String, DeclareStyleableInfo> mManifestMap; - - /** Flags indicating whether we have some resources */ - private boolean mHasResources = false; - - private String mLayoutLibLocation; - - private String mFrameworkResourcesLocation; - - private Map<String, Map<String, Integer>> mEnumValueMap; - - private String mFrameworkFontsLocation; - - private String mDocBaseUrl; - - /** - * Creates a new Framework Resource Manager. - * - * mAttributeValues is a map { key => list [ values ] }. - * The key for the map is "(element-xml-name,attribute-namespace:attribute-xml-local-name)". - * The attribute namespace prefix must be: - * - "android" for AndroidConstants.NS_RESOURCES - * - "xmlns" for the XMLNS URI. - */ - private FrameworkResourceManager() { - /* TODO Attempt to load those values from android.jar */ - mAttributeValues = new Hashtable<String, String[]>(); - mAttributeValues.put("(manifest,xmlns:android)", new String[] { //$NON-NLS-1$ - AndroidConstants.NS_RESOURCES - }); - mAttributeValues.put("(permission,android:protectionLevel)", new String[] { //$NON-NLS-1$ - "application", //$NON-NLS-1$ - "system" //$NON-NLS-1$ - }); - mAttributeValues.put("(application,android:persistent)", new String[] { //$NON-NLS-1$ - "true", //$NON-NLS-1$ - "false", //$NON-NLS-1$ - }); - mAttributeValues.put("(activity,android:clearOnBackground)", sBooleanValues); //$NON-NLS-1$ - mAttributeValues.put("(activity,android:configChanges)", new String[] { //$NON-NLS-1$ - "fontScale", //$NON-NLS-1$ - "mcc", //$NON-NLS-1$ - "mnc", //$NON-NLS-1$ - "locale", //$NON-NLS-1$ - "touchscreen", //$NON-NLS-1$ - "keyboard", //$NON-NLS-1$ - "keyboardHidden", //$NON-NLS-1$ - "navigation", //$NON-NLS-1$ - "orientation", //$NON-NLS-1$ - }); - mAttributeValues.put("(activity,android:launchMode)", new String[] { //$NON-NLS-1$ - "multiple", //$NON-NLS-1$ - "singleTop", //$NON-NLS-1$ - "singleTask", //$NON-NLS-1$ - "singleInstance" //$NON-NLS-1$ - }); - mAttributeValues.put("(activity,android:stateNotNeeded)", sBooleanValues); //$NON-NLS-1$ - mAttributeValues.put("(provider,android:syncable)", sBooleanValues); //$NON-NLS-1$ - mAttributeValues.put("(provider,android:multiprocess)", sBooleanValues); //$NON-NLS-1$ - mAttributeValues.put("(instrumentation,android:functionalTest)", sBooleanValues); //$NON-NLS-1$ - mAttributeValues.put("(instrumentation,android:handleProfiling)", sBooleanValues); //$NON-NLS-1$ - - } - - /** - * Returns the {@link FrameworkResourceManager} instance. - */ - public static FrameworkResourceManager getInstance() { - return sThis; - } - - /** - * Sets the resources and notifies the listeners - * @param documentationBaseUrl - */ - public synchronized void setResources(IResourceRepository systemResourceRepository, - ViewClassInfo[] layoutViewsInfo, - ViewClassInfo[] layoutGroupsInfo, - ViewClassInfo[] preferencesInfo, - ViewClassInfo[] preferenceGroupsInfo, - Map<String, DeclareStyleableInfo> xmlMenuMap, - Map<String, DeclareStyleableInfo> xmlSearchableMap, - Map<String, DeclareStyleableInfo> manifestMap, - Map<String, Map<String, Integer>> enumValueMap, - String[] permissionValues, - String[] activityIntentActionValues, - String[] broadcastIntentActionValues, - String[] serviceIntentActionValues, - String[] intentCategoryValues, - String documentationBaseUrl) { - mSystemResourceRepository = systemResourceRepository; - - mLayoutViewsInfo = layoutViewsInfo; - mLayoutGroupsInfo = layoutGroupsInfo; - - mPreferencesInfo = preferencesInfo; - mPreferenceGroupsInfo = preferenceGroupsInfo; - - mXmlMenuMap = xmlMenuMap; - mXmlSearchableMap = xmlSearchableMap; - mManifestMap = manifestMap; - mEnumValueMap = enumValueMap; - mDocBaseUrl = documentationBaseUrl; - - setPermissions(permissionValues); - setIntentFilterActionsAndCategories(activityIntentActionValues, broadcastIntentActionValues, - serviceIntentActionValues, intentCategoryValues); - - mHasResources = true; - - notifyFrameworkResourcesChangeListeners(); - } - - public synchronized IResourceRepository getSystemResources() { - return mSystemResourceRepository; - } - - public synchronized String[] getValues(String elementName, String attributeName) { - String key = String.format("(%1$s,%2$s)", elementName, attributeName); //$NON-NLS-1$ - return mAttributeValues.get(key); - } - - public synchronized String[] getValues(String elementName, String attributeName, - String greatGrandParentElementName) { - if (greatGrandParentElementName != null) { - String key = String.format("(%1$s,%2$s,%3$s)", greatGrandParentElementName, //$NON-NLS-1$ - elementName, attributeName); - String[] values = mAttributeValues.get(key); - if (values != null) { - return values; - } - } - - return getValues(elementName, attributeName); - } - - public synchronized String[] getValues(String key) { - return mAttributeValues.get(key); - } - - public synchronized ViewClassInfo[] getLayoutViewsInfo() { - return mLayoutViewsInfo; - } - - public synchronized ViewClassInfo[] getLayoutGroupsInfo() { - return mLayoutGroupsInfo; - } - - public synchronized ViewClassInfo[] getPreferencesInfo() { - return mPreferencesInfo; - } - - public synchronized ViewClassInfo[] getPreferenceGroupsInfo() { - return mPreferenceGroupsInfo; - } - - public synchronized Map<String, DeclareStyleableInfo> getXmlMenuDefinitions() { - return mXmlMenuMap; - } - - public synchronized Map<String, DeclareStyleableInfo> getXmlSearchableDefinitions() { - return mXmlSearchableMap; - } - - public synchronized Map<String, DeclareStyleableInfo> getManifestDefinitions() { - return mManifestMap; - } - - public String getDocumentationBaseUrl() { - return mDocBaseUrl == null ? AndroidConstants.CODESITE_BASE_URL : mDocBaseUrl; - } - - /** - * Sets the permission values - * @param permissionValues the list of permissions - */ - private void setPermissions(String[] permissionValues) { - setValues("(uses-permission,android:name)", permissionValues); //$NON-NLS-1$ - setValues("(application,android:permission)", permissionValues); //$NON-NLS-1$ - setValues("(activity,android:permission)", permissionValues); //$NON-NLS-1$ - setValues("(receiver,android:permission)", permissionValues); //$NON-NLS-1$ - setValues("(service,android:permission)", permissionValues); //$NON-NLS-1$ - setValues("(provider,android:permission)", permissionValues); //$NON-NLS-1$ - } - - private void setIntentFilterActionsAndCategories(String[] activityIntentActions, - String[] broadcastIntentActions, String[] serviceIntentActions, - String[] intentCategoryValues) { - setValues("(activity,action,android:name)", activityIntentActions); //$NON-NLS-1$ - setValues("(receiver,action,android:name)", broadcastIntentActions); //$NON-NLS-1$ - setValues("(service,action,android:name)", serviceIntentActions); //$NON-NLS-1$ - setValues("(category,android:name)", intentCategoryValues); //$NON-NLS-1$ - } - - /** - * Sets a (name, values) pair in the hash map. - * <p/> - * If the name is already present in the map, it is first removed. - * @param name the name associated with the values. - * @param values The values to add. - */ - private void setValues(String name, String[] values) { - mAttributeValues.remove(name); - mAttributeValues.put(name, values); - } - - - /** - * Called by the ADT plugin when the SDK path has changed. - * This stores the path locally and then notifies all attached listeners. - */ - private void notifyFrameworkResourcesChangeListeners() { - for (Runnable listener : mResourcesChangeListeners) { - try { - listener.run(); - } catch (Exception e) { - CommonPlugin.log(e, "IPathChangedListener failed."); //$NON-NLS-1$ - } - } - } - - /** Adds a new listener that listens to framework resources changes. - * <p/>If resources have already been set, then the listener is automatically notified. */ - public synchronized void addFrameworkResourcesChangeListener(Runnable listener) { - if (listener != null && mResourcesChangeListeners.indexOf(listener) == -1) { - mResourcesChangeListeners.add(listener); - - if (mHasResources) { - listener.run(); - } - } - } - - /** Removes a framework resources changes listener. - * <p/>Safe to call with null or with the same value. */ - public synchronized void removeFrameworkResourcesChangeListener(Runnable listener) { - mResourcesChangeListeners.remove(listener); - } - - public void setLayoutLibLocation(String osLocation) { - mLayoutLibLocation = osLocation; - } - - public String getLayoutLibLocation() { - return mLayoutLibLocation; - } - - public void setFrameworkResourcesLocation(String osLocation) { - mFrameworkResourcesLocation = osLocation; - } - - public String getFrameworkResourcesLocation() { - return mFrameworkResourcesLocation; - } - - public Map<String, Map<String, Integer>> getEnumValueMap() { - return mEnumValueMap; - } - - public void setFrameworkFontLocation(String osLocation) { - mFrameworkFontsLocation = osLocation; - } - - public String getFrameworkFontLocation() { - return mFrameworkFontsLocation; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.ddms/META-INF/MANIFEST.MF index 2f315c5..09b8085 100644 --- a/eclipse/plugins/com.android.ide.eclipse.ddms/META-INF/MANIFEST.MF +++ b/eclipse/plugins/com.android.ide.eclipse.ddms/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Dalvik Debug Monitor Service Bundle-SymbolicName: com.android.ide.eclipse.ddms;singleton:=true -Bundle-Version: 0.8.1.qualifier +Bundle-Version: 0.9.0.qualifier Bundle-Activator: com.android.ide.eclipse.ddms.DdmsPlugin Bundle-Vendor: The Android Open Source Project Bundle-Localization: plugin @@ -10,11 +10,13 @@ Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime, org.eclipse.ui.console Eclipse-LazyStart: true -Export-Package: com.android.ide.eclipse.ddms, - com.android.ide.eclipse.ddms.views, - com.android.ddmlib, +Export-Package: com.android.ddmlib, + com.android.ddmlib.log, + com.android.ddmlib.testrunner, com.android.ddmuilib, - com.android.ddmuilib.console + com.android.ddmuilib.console, + com.android.ide.eclipse.ddms, + com.android.ide.eclipse.ddms.views Bundle-ClassPath: libs/jcommon-1.0.12.jar, libs/jfreechart-1.0.9.jar, libs/jfreechart-1.0.9-swt.jar, diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/.classpath b/eclipse/plugins/com.android.ide.eclipse.editors/.classpath deleted file mode 100644 index 66dbeb3..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/.classpath +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<classpath> - <classpathentry kind="src" path="src"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"> - <accessrules> - <accessrule kind="accessible" pattern="org/eclipse/wst/**/internal/**"/> - </accessrules> - </classpathentry> - <classpathentry kind="lib" path="kxml2-2.3.0.jar"/> - <classpathentry kind="lib" path="layoutlib_api.jar"/> - <classpathentry kind="lib" path="ninepatch.jar"/> - <classpathentry kind="lib" path="layoutlib_utils.jar"/> - <classpathentry kind="output" path="bin"/> -</classpath> diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/.project b/eclipse/plugins/com.android.ide.eclipse.editors/.project deleted file mode 100644 index 292c698..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/.project +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<projectDescription> - <name>editors</name> - <comment></comment> - <projects> - </projects> - <buildSpec> - <buildCommand> - <name>org.eclipse.jdt.core.javabuilder</name> - <arguments> - </arguments> - </buildCommand> - <buildCommand> - <name>org.eclipse.pde.ManifestBuilder</name> - <arguments> - </arguments> - </buildCommand> - <buildCommand> - <name>org.eclipse.pde.SchemaBuilder</name> - <arguments> - </arguments> - </buildCommand> - </buildSpec> - <natures> - <nature>org.eclipse.pde.PluginNature</nature> - <nature>org.eclipse.jdt.core.javanature</nature> - </natures> -</projectDescription> diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.editors/META-INF/MANIFEST.MF deleted file mode 100644 index 0bfaff9..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/META-INF/MANIFEST.MF +++ /dev/null @@ -1,56 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Android Editors -Bundle-SymbolicName: com.android.ide.eclipse.editors;singleton:=true -Bundle-Version: 0.8.1.qualifier -Bundle-Activator: com.android.ide.eclipse.editors.EditorsPlugin -Bundle-Vendor: The Android Open Source Project -Require-Bundle: com.android.ide.eclipse.common, - org.eclipse.ui, - org.eclipse.core.runtime, - org.eclipse.core.resources, - org.eclipse.ui.editors, - org.eclipse.jface.text, - org.eclipse.ui.ide, - org.eclipse.wst.sse.ui, - org.eclipse.wst.xml.ui, - org.eclipse.wst.xml.core, - org.eclipse.wst.sse.core, - org.eclipse.ui.forms, - org.eclipse.jdt.core, - org.eclipse.ui.browser, - org.eclipse.jdt.ui, - org.eclipse.gef, - org.eclipse.ui.views, - org.eclipse.ui.console -Eclipse-LazyStart: true -Export-Package: com.android.ide.eclipse.editors;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.descriptors;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.layout;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.layout.descriptors;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.layout.parts;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.layout.uimodel;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.manifest;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.manifest.descriptors;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.manifest.model;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.manifest.pages;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.menu;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.menu.descriptors;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.resources;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.resources.configurations;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.resources.descriptors;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.resources.explorer;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.resources.manager;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.resources.manager.files;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.resources.uimodel;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.ui;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.ui.tree;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.uimodel;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.wizards;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.xml;x-friends:="com.android.ide.eclipse.tests", - com.android.ide.eclipse.editors.xml.descriptors;x-friends:="com.android.ide.eclipse.tests" -Bundle-ClassPath: kxml2-2.3.0.jar, - ., - layoutlib_api.jar, - ninepatch.jar, - layoutlib_utils.jar diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/MODULE_LICENSE_EPL b/eclipse/plugins/com.android.ide.eclipse.editors/MODULE_LICENSE_EPL deleted file mode 100644 index e69de29..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/MODULE_LICENSE_EPL +++ /dev/null diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/NOTICE b/eclipse/plugins/com.android.ide.eclipse.editors/NOTICE deleted file mode 100644 index 49c101d..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/NOTICE +++ /dev/null @@ -1,224 +0,0 @@ -*Eclipse Public License - v 1.0* - -THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE -PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF -THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. - -*1. DEFINITIONS* - -"Contribution" means: - -a) in the case of the initial Contributor, the initial code and -documentation distributed under this Agreement, and -b) in the case of each subsequent Contributor: - -i) changes to the Program, and - -ii) additions to the Program; - -where such changes and/or additions to the Program originate from and -are distributed by that particular Contributor. A Contribution -'originates' from a Contributor if it was added to the Program by such -Contributor itself or anyone acting on such Contributor's behalf. -Contributions do not include additions to the Program which: (i) are -separate modules of software distributed in conjunction with the Program -under their own license agreement, and (ii) are not derivative works of -the Program. - -"Contributor" means any person or entity that distributes the Program. - -"Licensed Patents " mean patent claims licensable by a Contributor which -are necessarily infringed by the use or sale of its Contribution alone -or when combined with the Program. - -"Program" means the Contributions distributed in accordance with this -Agreement. - -"Recipient" means anyone who receives the Program under this Agreement, -including all Contributors. - -*2. GRANT OF RIGHTS* - -a) Subject to the terms of this Agreement, each Contributor hereby -grants Recipient a non-exclusive, worldwide, royalty-free copyright -license to reproduce, prepare derivative works of, publicly display, -publicly perform, distribute and sublicense the Contribution of such -Contributor, if any, and such derivative works, in source code and -object code form. - -b) Subject to the terms of this Agreement, each Contributor hereby -grants Recipient a non-exclusive, worldwide, royalty-free patent license -under Licensed Patents to make, use, sell, offer to sell, import and -otherwise transfer the Contribution of such Contributor, if any, in -source code and object code form. This patent license shall apply to the -combination of the Contribution and the Program if, at the time the -Contribution is added by the Contributor, such addition of the -Contribution causes such combination to be covered by the Licensed -Patents. The patent license shall not apply to any other combinations -which include the Contribution. No hardware per se is licensed hereunder. - -c) Recipient understands that although each Contributor grants the -licenses to its Contributions set forth herein, no assurances are -provided by any Contributor that the Program does not infringe the -patent or other intellectual property rights of any other entity. Each -Contributor disclaims any liability to Recipient for claims brought by -any other entity based on infringement of intellectual property rights -or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to -secure any other intellectual property rights needed, if any. For -example, if a third party patent license is required to allow Recipient -to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program. - -d) Each Contributor represents that to its knowledge it has sufficient -copyright rights in its Contribution, if any, to grant the copyright -license set forth in this Agreement. - -*3. REQUIREMENTS* - -A Contributor may choose to distribute the Program in object code form -under its own license agreement, provided that: - -a) it complies with the terms and conditions of this Agreement; and - -b) its license agreement: - -i) effectively disclaims on behalf of all Contributors all warranties -and conditions, express and implied, including warranties or conditions -of title and non-infringement, and implied warranties or conditions of -merchantability and fitness for a particular purpose; - -ii) effectively excludes on behalf of all Contributors all liability for -damages, including direct, indirect, special, incidental and -consequential damages, such as lost profits; - -iii) states that any provisions which differ from this Agreement are -offered by that Contributor alone and not by any other party; and - -iv) states that source code for the Program is available from such -Contributor, and informs licensees how to obtain it in a reasonable -manner on or through a medium customarily used for software exchange. - -When the Program is made available in source code form: - -a) it must be made available under this Agreement; and - -b) a copy of this Agreement must be included with each copy of the Program. - -Contributors may not remove or alter any copyright notices contained -within the Program. - -Each Contributor must identify itself as the originator of its -Contribution, if any, in a manner that reasonably allows subsequent -Recipients to identify the originator of the Contribution. - -*4. COMMERCIAL DISTRIBUTION* - -Commercial distributors of software may accept certain responsibilities -with respect to end users, business partners and the like. While this -license is intended to facilitate the commercial use of the Program, the -Contributor who includes the Program in a commercial product offering -should do so in a manner which does not create potential liability for -other Contributors. Therefore, if a Contributor includes the Program in -a commercial product offering, such Contributor ("Commercial -Contributor") hereby agrees to defend and indemnify every other -Contributor ("Indemnified Contributor") against any losses, damages and -costs (collectively "Losses") arising from claims, lawsuits and other -legal actions brought by a third party against the Indemnified -Contributor to the extent caused by the acts or omissions of such -Commercial Contributor in connection with its distribution of the -Program in a commercial product offering. The obligations in this -section do not apply to any claims or Losses relating to any actual or -alleged intellectual property infringement. In order to qualify, an -Indemnified Contributor must: a) promptly notify the Commercial -Contributor in writing of such claim, and b) allow the Commercial -Contributor to control, and cooperate with the Commercial Contributor -in, the defense and any related settlement negotiations. The Indemnified -Contributor may participate in any such claim at its own expense. - -For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other -Contributor to pay any damages as a result, the Commercial Contributor -must pay those damages. - -*5. NO WARRANTY* - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED -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. Each Recipient is solely responsible for -determining the appropriateness of using and distributing the Program -and assumes all risks associated with its exercise of rights under this -Agreement , including but not limited to the risks and costs of program -errors, compliance with applicable laws, damage to or loss of data, -programs or equipment, and unavailability or interruption of operations. - -*6. DISCLAIMER OF LIABILITY* - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR -ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -*7. GENERAL* - -If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further action -by the parties hereto, such provision shall be reformed to the minimum -extent necessary to make such provision valid and enforceable. - -If Recipient institutes patent litigation against any entity (including -a cross-claim or counterclaim in a lawsuit) alleging that the Program -itself (excluding combinations of the Program with other software or -hardware) infringes such Recipient's patent(s), then such Recipient's -rights granted under Section 2(b) shall terminate as of the date such -litigation is filed. - -All Recipient's rights under this Agreement shall terminate if it fails -to comply with any of the material terms or conditions of this Agreement -and does not cure such failure in a reasonable period of time after -becoming aware of such noncompliance. If all Recipient's rights under -this Agreement terminate, Recipient agrees to cease use and distribution -of the Program as soon as reasonably practicable. However, Recipient's -obligations under this Agreement and any licenses granted by Recipient -relating to the Program shall continue and survive. - -Everyone is permitted to copy and distribute copies of this Agreement, -but in order to avoid inconsistency the Agreement is copyrighted and may -only be modified in the following manner. The Agreement Steward reserves -the right to publish new versions (including revisions) of this -Agreement from time to time. No one other than the Agreement Steward has -the right to modify this Agreement. The Eclipse Foundation is the -initial Agreement Steward. The Eclipse Foundation may assign the -responsibility to serve as the Agreement Steward to a suitable separate -entity. Each new version of the Agreement will be given a distinguishing -version number. The Program (including Contributions) may always be -distributed subject to the version of the Agreement under which it was -received. In addition, after a new version of the Agreement is -published, Contributor may elect to distribute the Program (including -its Contributions) under the new version. Except as expressly stated in -Sections 2(a) and 2(b) above, Recipient receives no rights or licenses -to the intellectual property of any Contributor under this Agreement, -whether expressly, by implication, estoppel or otherwise. All rights in -the Program not expressly granted under this Agreement are reserved. - -This Agreement is governed by the laws of the State of New York and the -intellectual property laws of the United States of America. No party to -this Agreement will bring a legal action under this Agreement more than -one year after the cause of action arose. Each party waives its rights -to a jury trial in any resulting litigation. - - - diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/build.properties b/eclipse/plugins/com.android.ide.eclipse.editors/build.properties deleted file mode 100644 index 0a7bc7d..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/build.properties +++ /dev/null @@ -1,10 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - .,\ - plugin.xml,\ - icons/,\ - layoutlib_api.jar,\ - kxml2-2.3.0.jar,\ - ninepatch.jar,\ - layoutlib_utils.jar diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/android.png b/eclipse/plugins/com.android.ide.eclipse.editors/icons/android.png Binary files differdeleted file mode 100644 index 3779d4d..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/android.png +++ /dev/null diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/android_large.png b/eclipse/plugins/com.android.ide.eclipse.editors/icons/android_large.png Binary files differdeleted file mode 100644 index 64e3601..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/android_large.png +++ /dev/null diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/region.png b/eclipse/plugins/com.android.ide.eclipse.editors/icons/region.png Binary files differdeleted file mode 100644 index 9cfb53f..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/region.png +++ /dev/null diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/icons/world.png b/eclipse/plugins/com.android.ide.eclipse.editors/icons/world.png Binary files differdeleted file mode 100644 index afdc16c..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/icons/world.png +++ /dev/null diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.editors/plugin.xml deleted file mode 100644 index 2678a5d..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/plugin.xml +++ /dev/null @@ -1,107 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?eclipse version="3.2"?> -<plugin> - <extension - point="org.eclipse.ui.editors"> - <editor - class="com.android.ide.eclipse.editors.manifest.ManifestEditor" - default="true" - filenames="AndroidManifest.xml" - icon="icons/android.png" - id="com.android.ide.eclipse.editors.manifest.ManifestEditor" - name="Android Manifest Editor"> - </editor> - <editor - class="com.android.ide.eclipse.editors.resources.ResourcesEditor" - default="false" - extensions="xml" - icon="icons/android.png" - id="com.android.ide.eclipse.editors.resources.ResourcesEditor" - name="Android Resource Editor"> - </editor> - <editor - class="com.android.ide.eclipse.editors.layout.LayoutEditor" - default="false" - extensions="xml" - icon="icons/android.png" - id="com.android.ide.eclipse.editors.layout.LayoutEditor" - matchingStrategy="com.android.ide.eclipse.editors.layout.MatchingStrategy" - name="Android Layout Editor"> - </editor> - <editor - class="com.android.ide.eclipse.editors.menu.MenuEditor" - default="false" - extensions="xml" - icon="icons/android.png" - id="com.android.ide.eclipse.editors.menu.MenuEditor" - name="Android Menu Editor"> - </editor> - <editor - class="com.android.ide.eclipse.editors.xml.XmlEditor" - default="false" - extensions="xml" - icon="icons/android.png" - id="com.android.ide.eclipse.editors.xml.XmlEditor" - name="Android Xml Resources Editor"> - </editor> - </extension> - <extension - point="org.eclipse.wst.sse.ui.editorConfiguration"> - <sourceViewerConfiguration - class="com.android.ide.eclipse.editors.manifest.ManifestSourceViewerConfig" - target="com.android.ide.eclipse.editors.manifest.ManifestEditor"> - </sourceViewerConfiguration> - <sourceViewerConfiguration - class="com.android.ide.eclipse.editors.resources.ResourcesSourceViewerConfig" - target="com.android.ide.eclipse.editors.resources.ResourcesEditor"> - </sourceViewerConfiguration> - <sourceViewerConfiguration - class="com.android.ide.eclipse.editors.layout.LayoutSourceViewerConfig" - target="com.android.ide.eclipse.editors.layout.LayoutEditor"> - </sourceViewerConfiguration> - <sourceViewerConfiguration - class="com.android.ide.eclipse.editors.menu.MenuSourceViewerConfig" - target="com.android.ide.eclipse.editors.menu.MenuEditor"> - </sourceViewerConfiguration> - <sourceViewerConfiguration - class="com.android.ide.eclipse.editors.xml.XmlSourceViewerConfig" - target="com.android.ide.eclipse.editors.xml.XmlEditor"> - </sourceViewerConfiguration> - </extension> - <extension - point="org.eclipse.ui.views"> - <view - allowMultiple="false" - category="com.android.ide.eclipse.ddms.views.category" - class="com.android.ide.eclipse.editors.resources.explorer.ResourceExplorerView" - icon="icons/android.png" - id="com.android.ide.eclipse.editors.resources.explorer.ResourceExplorerView" - name="Resource Explorer"> - </view> - </extension> - <extension - point="org.eclipse.ui.newWizards"> - <wizard - canFinishEarly="false" - category="com.android.ide.eclipse.wizards.category" - class="com.android.ide.eclipse.editors.wizards.NewXmlFileWizard" - finalPerspective="org.eclipse.jdt.ui.JavaPerspective" - hasPages="true" - icon="icons/android.png" - id="com.android.ide.eclipse.editors.wizards.NewXmlFileWizard" - name="Android XML File" - preferredPerspectives="org.eclipse.jdt.ui.JavaPerspective" - project="false"> - </wizard> - </extension> - <extension - point="org.eclipse.ui.perspectiveExtensions"> - <perspectiveExtension - targetID="org.eclipse.jdt.ui.JavaPerspective"> - <newWizardShortcut - id="com.android.ide.eclipse.editors.wizards.NewXmlFileWizard"> - </newWizardShortcut> - </perspectiveExtension> - </extension> - -</plugin> diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/EditorsPlugin.java b/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/EditorsPlugin.java deleted file mode 100644 index 354276a..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/EditorsPlugin.java +++ /dev/null @@ -1,672 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.editors; - -import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.CommonPlugin; -import com.android.ide.eclipse.common.SdkStatsHelper; -import com.android.ide.eclipse.common.StreamHelper; -import com.android.ide.eclipse.common.resources.FrameworkResourceManager; -import com.android.ide.eclipse.editors.EditorsPlugin.LayoutBridge.LoadStatus; -import com.android.ide.eclipse.editors.layout.LayoutEditor; -import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors; -import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors; -import com.android.ide.eclipse.editors.menu.MenuEditor; -import com.android.ide.eclipse.editors.menu.descriptors.MenuDescriptors; -import com.android.ide.eclipse.editors.resources.ResourcesEditor; -import com.android.ide.eclipse.editors.resources.manager.ProjectResources; -import com.android.ide.eclipse.editors.resources.manager.ResourceFolder; -import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType; -import com.android.ide.eclipse.editors.resources.manager.ResourceManager; -import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor; -import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IFileListener; -import com.android.ide.eclipse.editors.xml.XmlEditor; -import com.android.ide.eclipse.editors.xml.descriptors.XmlDescriptors; -import com.android.layoutlib.api.ILayoutBridge; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IMarkerDelta; -import org.eclipse.core.resources.IResourceDelta; -import org.eclipse.core.resources.IWorkspace; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.QualifiedName; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.SubMonitor; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.IEditorDescriptor; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.IWorkbench; -import org.eclipse.ui.IWorkbenchPage; -import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.console.MessageConsole; -import org.eclipse.ui.console.MessageConsoleStream; -import org.eclipse.ui.ide.IDE; -import org.eclipse.ui.part.FileEditorInput; -import org.eclipse.ui.plugin.AbstractUIPlugin; -import org.osgi.framework.BundleContext; -import org.osgi.framework.Constants; -import org.osgi.framework.Version; - -import java.io.File; -import java.io.OutputStream; -import java.lang.reflect.Constructor; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; - -/** - * The activator class controls the plug-in life cycle - */ -public class EditorsPlugin extends AbstractUIPlugin { - // The shared instance - private static EditorsPlugin sPlugin; - - private static Image sAndroidLogo; - private static ImageDescriptor sAndroidLogoDesc; - - private ResourceMonitor mResourceMonitor; - private SdkPathChangedListener mSdkPathChangedListener; - private ArrayList<Runnable> mResourceRefreshListener = new ArrayList<Runnable>(); - - private MessageConsoleStream mAndroidConsoleStream; - /** Stream to write error messages to the android console */ - private MessageConsoleStream mAndroidConsoleErrorStream; - - public final static class LayoutBridge { - public enum LoadStatus { LOADING, LOADED, FAILED } - - /** Link to the layout bridge */ - public ILayoutBridge bridge; - - public LoadStatus status = LoadStatus.LOADING; - } - - private final LayoutBridge mLayoutBridge = new LayoutBridge(); - - private boolean mLayoutBridgeInit; - - private ClassLoader mBridgeClassLoader; - - private Color mRed; - - - /** - * The constructor - */ - public EditorsPlugin() { - } - - /** - * The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code> - * method refreshes the plug-in actions. Subclasses may extend this method, - * but must send super <b>first</b>. - * {@inheritDoc} - * - * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) - */ - @Override - public void start(BundleContext context) throws Exception { - super.start(context); - sPlugin = this; - sAndroidLogoDesc = imageDescriptorFromPlugin(AndroidConstants.EDITORS_PLUGIN_ID, - "/icons/android.png"); //$NON-NLS-1$ - sAndroidLogo = sAndroidLogoDesc.createImage(); - - // get the stream to write in the android console. - MessageConsole androidConsole = CommonPlugin.getDefault().getAndroidConsole(); - mAndroidConsoleStream = androidConsole.newMessageStream(); - - mAndroidConsoleErrorStream = androidConsole.newMessageStream(); - mRed = new Color(getDisplay(), 0xFF, 0x00, 0x00); - - // because this can be run, in some cases, by a non ui thread, and beccause - // changing the console properties update the ui, we need to make this change - // in the ui thread. - getDisplay().asyncExec(new Runnable() { - public void run() { - mAndroidConsoleErrorStream.setColor(mRed); - } - }); - - // Add a resource listener to handle compiled resources. - IWorkspace ws = ResourcesPlugin.getWorkspace(); - mResourceMonitor = ResourceMonitor.startMonitoring(ws); - - if (mResourceMonitor != null) { - setupDefaultEditor(mResourceMonitor); - ResourceManager.setup(mResourceMonitor); - } - - // Setup the sdk location changed listener and invoke it the first time - mSdkPathChangedListener = new SdkPathChangedListener(); - FrameworkResourceManager.getInstance().addFrameworkResourcesChangeListener( - mSdkPathChangedListener); - - // ping the usage server - pingUsageServer(); - } - - /** - * The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code> - * method saves this plug-in's preference and dialog stores and shuts down - * its image registry (if they are in use). Subclasses may extend this - * method, but must send super <b>last</b>. A try-finally statement should - * be used where necessary to ensure that <code>super.shutdown()</code> is - * always done. - * - * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) - */ - @Override - public void stop(BundleContext context) throws Exception { - sPlugin = null; - sAndroidLogo.dispose(); - - IconFactory.getInstance().Dispose(); - - // Remove the resource listener that handles compiled resources. - IWorkspace ws = ResourcesPlugin.getWorkspace(); - ResourceMonitor.stopMonitoring(ws); - - FrameworkResourceManager.getInstance().removeFrameworkResourcesChangeListener( - mSdkPathChangedListener); - mSdkPathChangedListener = null; - - mRed.dispose(); - - super.stop(context); - } - - /** - * Returns the shared instance - * - * @return the shared instance - */ - public static EditorsPlugin getDefault() { - return sPlugin; - } - - /** - * Returns an Image for the small Android logo. - * - * Callers should not dispose it. - */ - public static Image getAndroidLogo() { - return sAndroidLogo; - } - - /** - * Returns an {@link ImageDescriptor} for the small Android logo. - * - * Callers should not dispose it. - */ - public static ImageDescriptor getAndroidLogoDesc() { - return sAndroidLogoDesc; - } - - /** - * Logs a message to the default Eclipse log. - * - * @param severity One of IStatus' severity codes: OK, ERROR, INFO, WARNING or CANCEL. - * @param format The format string, like for String.format(). - * @param args The arguments for the format string, like for String.format(). - */ - public static void log(int severity, String format, Object ... args) { - String message = String.format(format, args); - Status status = new Status(severity, AndroidConstants.EDITORS_PLUGIN_ID, message); - getDefault().getLog().log(status); - } - - /** - * Logs an exception to the default Eclipse log. - * <p/> - * The status severity is always set to ERROR. - * - * @param exception The exception to log. Its call trace will be recorded. - * @param format The format string, like for String.format(). - * @param args The arguments for the format string, like for String.format(). - */ - public static void log(Throwable exception, String format, Object ... args) { - String message = String.format(format, args); - Status status = new Status(IStatus.ERROR, AndroidConstants.EDITORS_PLUGIN_ID, - message, exception); - getDefault().getLog().log(status); - } - - /** - * Returns the ResourceMonitor object. - */ - public ResourceMonitor getResourceMonitor() { - return mResourceMonitor; - } - - - /** - * Sets up the editor to register default editors for resource files when needed. - * - * This is called by the {@link EditorsPlugin} during initialization. - * - * @param monitor The main Resource Monitor object. - */ - public void setupDefaultEditor(ResourceMonitor monitor) { - monitor.addFileListener(new IFileListener() { - - private static final String UNKNOWN_EDITOR = "unknown-editor"; //$NON-NLS-1$ - - /* (non-Javadoc) - * Sent when a file changed. - * @param file The file that changed. - * @param markerDeltas The marker deltas for the file. - * @param kind The change kind. This is equivalent to - * {@link IResourceDelta#accept(IResourceDeltaVisitor)} - * - * @see IFileListener#fileChanged - */ - public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) { - if (AndroidConstants.EXT_XML.equals(file.getFileExtension())) { - // The resources files must have a file path similar to - // project/res/.../*.xml - // There is no support for sub folders, so the segment count must be 4 - if (file.getFullPath().segmentCount() == 4) { - // check if we are inside the res folder. - String segment = file.getFullPath().segment(1); - if (segment.equalsIgnoreCase(AndroidConstants.FD_RESOURCES)) { - // we are inside a res/ folder, get the actual ResourceFolder - ProjectResources resources = ResourceManager.getInstance(). - getProjectResources(file.getProject()); - - // This happens when importing old Android projects in Eclipse - // that lack the container (probably because resources fail to build - // properly.) - if (resources == null) { - log(IStatus.INFO, - "getProjectResources failed for path %1$s in project %2$s", //$NON-NLS-1$ - file.getFullPath().toOSString(), - file.getProject().getName()); - return; - } - - ResourceFolder resFolder = resources.getResourceFolder( - (IFolder)file.getParent()); - - if (resFolder != null) { - if (kind == IResourceDelta.ADDED) { - resourceAdded(file, resFolder.getType()); - } else if (kind == IResourceDelta.CHANGED) { - resourceChanged(file, resFolder.getType()); - } - } else { - // if the res folder is null, this means the name is invalid, - // in this case we remove whatever android editors that was set - // as the default editor. - IEditorDescriptor desc = IDE.getDefaultEditor(file); - String editorId = desc.getId(); - if (editorId.startsWith(AndroidConstants.EDITORS_PLUGIN_ID)) { - // reset the default editor. - IDE.setDefaultEditor(file, null); - } - } - } - } - } - } - - private void resourceAdded(IFile file, ResourceFolderType type) { - // set the default editor based on the type. - if (type == ResourceFolderType.LAYOUT) { - IDE.setDefaultEditor(file, LayoutEditor.ID); - } else if (type == ResourceFolderType.DRAWABLE - || type == ResourceFolderType.VALUES) { - IDE.setDefaultEditor(file, ResourcesEditor.ID); - } else if (type == ResourceFolderType.MENU) { - IDE.setDefaultEditor(file, MenuEditor.ID); - } else if (type == ResourceFolderType.XML) { - if (XmlEditor.canHandleFile(file)) { - IDE.setDefaultEditor(file, XmlEditor.ID); - } else { - // set a property to determine later if the XML can be handled - QualifiedName qname = new QualifiedName( - AndroidConstants.EDITORS_PLUGIN_ID, - UNKNOWN_EDITOR); - try { - file.setPersistentProperty(qname, "1"); - } catch (CoreException e) { - // pass - } - } - } - } - - private void resourceChanged(IFile file, ResourceFolderType type) { - if (type == ResourceFolderType.XML) { - IEditorDescriptor ed = IDE.getDefaultEditor(file); - if (ed == null || ed.getId() != XmlEditor.ID) { - QualifiedName qname = new QualifiedName( - AndroidConstants.EDITORS_PLUGIN_ID, - UNKNOWN_EDITOR); - String prop = null; - try { - prop = file.getPersistentProperty(qname); - } catch (CoreException e) { - // pass - } - if (prop != null && XmlEditor.canHandleFile(file)) { - try { - // remove the property & set editor - file.setPersistentProperty(qname, null); - IWorkbenchPage page = PlatformUI.getWorkbench(). - getActiveWorkbenchWindow().getActivePage(); - - IEditorPart oldEditor = page.findEditor(new FileEditorInput(file)); - if (oldEditor != null && - CommonPlugin.displayPrompt("Android XML Editor", - String.format("The file you just saved as been recognized as a file that could be better handled using the Android XML Editor. Do you want to edit '%1$s' using the Android XML editor instead?", - file.getFullPath()))) { - IDE.setDefaultEditor(file, XmlEditor.ID); - IEditorPart newEditor = page.openEditor( - new FileEditorInput(file), - XmlEditor.ID, - true, /* activate */ - IWorkbenchPage.MATCH_NONE); - - if (newEditor != null) { - page.closeEditor(oldEditor, true /* save */); - } - } - } catch (CoreException e) { - // setPersistentProperty or page.openEditor may have failed - } - } - } - } - } - - }, IResourceDelta.ADDED | IResourceDelta.CHANGED); - } - - /** - * Respond to notifications from the resource manager than the SDK resources have been updated. - * It gets the new resources from the {@link FrameworkResourceManager} and then try to update - * the layout descriptors from the new layout data. - */ - private class SdkPathChangedListener implements Runnable { - public void run() { - - // Perform the update in a thread (here an Eclipse runtime job) - // since this should never block the caller (especially the start method) - new Job("Editors: Load Framework Resource Parser") { - - @Override - protected IStatus run(IProgressMonitor monitor) { - try { - SubMonitor progress = SubMonitor.convert(monitor, "Update Description", - 60); - - FrameworkResourceManager resourceManager = FrameworkResourceManager.getInstance(); - - AndroidManifestDescriptors.updateDescriptors( - resourceManager.getManifestDefinitions()); - progress.worked(10); - - if (progress.isCanceled()) { - return Status.CANCEL_STATUS; - } - - LayoutDescriptors.getInstance().updateDescriptors( - resourceManager.getLayoutViewsInfo(), - resourceManager.getLayoutGroupsInfo()); - progress.worked(10); - - if (progress.isCanceled()) { - return Status.CANCEL_STATUS; - } - - MenuDescriptors.getInstance().updateDescriptors( - resourceManager.getXmlMenuDefinitions()); - progress.worked(10); - - if (progress.isCanceled()) { - return Status.CANCEL_STATUS; - } - - XmlDescriptors.getInstance().updateDescriptors( - resourceManager.getXmlSearchableDefinitions(), - resourceManager.getPreferencesInfo(), - resourceManager.getPreferenceGroupsInfo()); - progress.worked(10); - - // load the layout lib bridge. - if (System.getenv("ANDROID_DISABLE_LAYOUT") == null) { - loadLayoutBridge(); - FrameworkResourceManager frMgr = FrameworkResourceManager.getInstance(); - ResourceManager rMgr = ResourceManager.getInstance(); - rMgr.loadFrameworkResources(frMgr.getFrameworkResourcesLocation()); - } - progress.worked(10); - - // Notify resource changed listeners - progress.subTask("Refresh UI"); - progress.setWorkRemaining(mResourceRefreshListener.size()); - - // Clone the list before iterating, to avoid Concurrent Modification - // exceptions - @SuppressWarnings("unchecked") - ArrayList<Runnable> listeners = (ArrayList<Runnable>) - mResourceRefreshListener.clone(); - for (Runnable listener : listeners) { - try { - getDisplay().syncExec(listener); - } catch (Exception e) { - log(e, "ResourceRefreshListener Failed"); //$NON-NLS-1$ - } finally { - progress.worked(1); - } - } - } catch (Throwable e) { - EditorsPlugin.log(e, "Load Framework Resource Parser failed"); //$NON-NLS-1$ - EditorsPlugin.printToConsole("Framework Resource Parser", - "Failed to parse"); - } finally { - if (monitor != null) { - monitor.done(); - } - } - return Status.OK_STATUS; - } - }.schedule(); - } - } - - public void addResourceChangedListener(Runnable resourceRefreshListener) { - mResourceRefreshListener.add(resourceRefreshListener); - } - - public void removeResourceChangedListener(Runnable resourceRefreshListener) { - mResourceRefreshListener.remove(resourceRefreshListener); - } - - public static Display getDisplay() { - IWorkbench bench = sPlugin.getWorkbench(); - if (bench != null) { - return bench.getDisplay(); - } - return null; - } - - /** - * Pings the usage start server. - */ - private void pingUsageServer() { - // In order to not block the plugin loading, so we spawn another thread. - new Thread("Ping!") { //$NON-NLS-1$ - @Override - public void run() { - // get the version of the plugin - String versionString = (String) getBundle().getHeaders().get( - Constants.BUNDLE_VERSION); - Version version = new Version(versionString); - - SdkStatsHelper.pingUsageServer("editors", version); //$NON-NLS-1$ - } - }.start(); - - } - - /** - * Prints one or more message to the android console. - * @param tag The tag to be associated with the message. Can be null. - * @param objects the objects to print through their <code>toString</code> method. - */ - public static synchronized void printToConsole(String tag, Object... objects) { - StreamHelper.printToStream(sPlugin.mAndroidConsoleStream, tag, objects); - } - - /** - * Prints one or more error messages to the android console. - * @param tag The tag to be associated with the message. Can be null. - * @param objects the objects to print through their <code>toString</code> method. - */ - public static synchronized void printErrorToConsole(String tag, Object... objects) { - StreamHelper.printToStream(sPlugin.mAndroidConsoleErrorStream, tag, objects); - } - - public static synchronized OutputStream getErrorStream() { - return sPlugin.mAndroidConsoleErrorStream; - } - - /** - * Displays an error dialog box. This dialog box is ran asynchronously in the ui thread, - * therefore this method can be called from any thread. - * @param title The title of the dialog box - * @param message The error message - */ - public final static void displayError(final String title, final String message) { - // get the current Display - final Display display = getDisplay(); - - // dialog box only run in ui thread.. - display.asyncExec(new Runnable() { - public void run() { - Shell shell = display.getActiveShell(); - MessageDialog.openError(shell, title, message); - } - }); - } - - /** - * Display a yes/no question dialog box. This dialog is opened synchronously in the ui thread, - * therefore this message can be called from any thread. - * @param title The title of the dialog box - * @param message The error message - * @return true if OK was clicked. - */ - public final static boolean displayPrompt(final String title, final String message) { - // get the current Display and Shell - final Display display = getDisplay(); - - // we need to ask the user what he wants to do. - final boolean[] wrapper = new boolean[] { false }; - display.syncExec(new Runnable() { - public void run() { - Shell shell = display.getActiveShell(); - wrapper[0] = MessageDialog.openQuestion(shell, title, message); - } - }); - return wrapper[0]; - } - - /** - * Returns a {@link LayoutBridge} object possibly containing a {@link ILayoutBridge} object. - * <p/>If {@link LayoutBridge#bridge} is <code>null</code>, {@link LayoutBridge#status} will - * contain the reason (either {@link LoadStatus#LOADING} or {@link LoadStatus#FAILED}). - * <p/>Valid {@link ILayoutBridge} objects are always initialized before being returned. - */ - public synchronized LayoutBridge getLayoutBridge() { - if (mLayoutBridgeInit == false && mLayoutBridge.bridge != null) { - FrameworkResourceManager manager = FrameworkResourceManager.getInstance(); - mLayoutBridge.bridge.init( - manager.getFrameworkFontLocation() + AndroidConstants.FD_DEFAULT_RES, - manager.getEnumValueMap()); - mLayoutBridgeInit = true; - } - return mLayoutBridge; - } - - /** - * Loads the layout bridge from the dynamically loaded layoutlib.jar - */ - private void loadLayoutBridge() { - try { - // reset to be sure we won't be using an obsolete version if we - // get an exception somewhere. - mLayoutBridge.bridge = null; - mLayoutBridge.status = LayoutBridge.LoadStatus.LOADING; - - // get the URL for the file. - File f = new File( - FrameworkResourceManager.getInstance().getLayoutLibLocation()); - if (f.isFile() == false) { - log(IStatus.ERROR, "layoutlib.jar is missing!"); //$NON-NLS-1$ - } else { - URL url = f.toURL(); - - // create a class loader. Because this jar reference interfaces - // that are in the editors plugin, it's important to provide - // a parent class loader. - mBridgeClassLoader = new URLClassLoader(new URL[] { url }, - this.getClass().getClassLoader()); - - // load the class - Class<?> clazz = mBridgeClassLoader.loadClass(AndroidConstants.CLASS_BRIDGE); - if (clazz != null) { - // instantiate an object of the class. - Constructor<?> constructor = clazz.getConstructor(); - if (constructor != null) { - Object bridge = constructor.newInstance(); - if (bridge instanceof ILayoutBridge) { - mLayoutBridge.bridge = (ILayoutBridge)bridge; - } - } - } - - if (mLayoutBridge.bridge == null) { - mLayoutBridge.status = LayoutBridge.LoadStatus.FAILED; - log(IStatus.ERROR, "Failed to load " + AndroidConstants.CLASS_BRIDGE); //$NON-NLS-1$ - } else { - mLayoutBridge.status = LayoutBridge.LoadStatus.LOADED; - } - } - } catch (Throwable t) { - mLayoutBridge.status = LayoutBridge.LoadStatus.FAILED; - // log the error. - log(t, "Failed to load the LayoutLib"); - } - } - - public ClassLoader getLayoutlibBridgeClassLoader() { - return mBridgeClassLoader; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/LayoutTreePage.java b/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/LayoutTreePage.java deleted file mode 100644 index c936be3..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/LayoutTreePage.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.editors.layout; - -import com.android.ide.eclipse.editors.EditorsPlugin; -import com.android.ide.eclipse.editors.resources.manager.ResourceFolder; -import com.android.ide.eclipse.editors.resources.manager.ResourceManager; -import com.android.ide.eclipse.editors.ui.tree.UiTreeBlock; -import com.android.ide.eclipse.editors.uimodel.UiElementNode; - -import org.eclipse.core.resources.IFile; -import org.eclipse.ui.IEditorInput; -import org.eclipse.ui.forms.IManagedForm; -import org.eclipse.ui.forms.editor.FormPage; -import org.eclipse.ui.forms.widgets.ScrolledForm; -import org.eclipse.ui.part.FileEditorInput; - -/** - * Page for the layout tree-based form editor. - * - * @deprecated This is being phased out. We use the GraphicalLayoutEditor instead. - */ -public final class LayoutTreePage extends FormPage { - /** Page id used for switching tabs programmatically */ - public final static String PAGE_ID = "layout_tree_page"; //$NON-NLS-1$ - - /** Container editor */ - LayoutEditor mEditor; - - public LayoutTreePage(LayoutEditor editor) { - super(editor, PAGE_ID, "Layout"); // tab's label, keep it short - mEditor = editor; - } - - /** - * Creates the content in the form hosted in this page. - * - * @param managedForm the form hosted in this page. - */ - @Override - protected void createFormContent(IManagedForm managedForm) { - super.createFormContent(managedForm); - ScrolledForm form = managedForm.getForm(); - - String configText = null; - IEditorInput input = mEditor.getEditorInput(); - if (input instanceof FileEditorInput) { - FileEditorInput fileInput = (FileEditorInput)input; - IFile iFile = fileInput.getFile(); - - ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(iFile); - if (resFolder != null) { - configText = resFolder.getConfiguration().toDisplayString(); - } - } - - if (configText != null) { - form.setText(String.format("Android Layout (%1$s)", configText)); - } else { - form.setText("Android Layout"); - } - - form.setImage(EditorsPlugin.getAndroidLogo()); - - UiElementNode rootNode = mEditor.getUiRootNode(); - UiTreeBlock block = new UiTreeBlock(mEditor, rootNode, - true /* autoCreateRoot */, - null /* no element filters */, - "Layout Elements", - "List of all layout elements in this XML file."); - block.createContent(managedForm); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/PaletteFactory.java b/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/PaletteFactory.java deleted file mode 100644 index 77b1df4..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/PaletteFactory.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.editors.layout; - -import com.android.ide.eclipse.editors.EditorsPlugin; -import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors; - -import org.eclipse.gef.palette.CombinedTemplateCreationEntry; -import org.eclipse.gef.palette.MarqueeToolEntry; -import org.eclipse.gef.palette.PaletteDrawer; -import org.eclipse.gef.palette.PaletteGroup; -import org.eclipse.gef.palette.PaletteRoot; -import org.eclipse.gef.palette.PanningSelectionToolEntry; -import org.eclipse.gef.requests.CreationFactory; - -import java.util.List; - -/** - * Factory that creates the palette for the {@link GraphicalLayoutEditor}. - */ -public class PaletteFactory { - - private static PaletteRoot sPaletteRoot; - private static Runnable sSdkChangedListener; - - /** Static factory, nothing to instantiate here. */ - private PaletteFactory() { - } - - public static PaletteRoot createPaletteRoot() { - if (sSdkChangedListener == null) { - sSdkChangedListener = new Runnable() { - public void run() { - if (sPaletteRoot != null) { - // The SDK has changed. Remove the drawer groups and rebuild them. - for (int n = sPaletteRoot.getChildren().size() - 1; n >= 0; n--) { - sPaletteRoot.getChildren().remove(n); - } - - addTools(sPaletteRoot); - addViews(sPaletteRoot, "Layouts", - LayoutDescriptors.getInstance().getLayoutDescriptors()); - addViews(sPaletteRoot, "Views", - LayoutDescriptors.getInstance().getViewDescriptors()); - } - } - }; - EditorsPlugin.getDefault().addResourceChangedListener(sSdkChangedListener); - } - - if (sPaletteRoot == null) { - sPaletteRoot = new PaletteRoot(); - sSdkChangedListener.run(); - } - return sPaletteRoot; - } - - private static void addTools(PaletteRoot paletteRoot) { - PaletteGroup group = new PaletteGroup("Tools"); - - // Default tools: selection and marquee selection - PanningSelectionToolEntry entry = new PanningSelectionToolEntry(); - group.add(entry); - paletteRoot.setDefaultEntry(entry); - - group.add(new MarqueeToolEntry()); - - paletteRoot.add(group); - } - - private static void addViews(PaletteRoot paletteRoot, String groupName, - List<ElementDescriptor> descriptors) { - PaletteDrawer group = new PaletteDrawer(groupName); - - for (ElementDescriptor desc : descriptors) { - CombinedTemplateCreationEntry entry = new CombinedTemplateCreationEntry( - desc.getUiName(), // label - desc.getTooltip(), // short description - desc.getClass(), // template - new ElementFactory(desc), // factory - desc.getImageDescriptor(), // small icon - desc.getImageDescriptor() // large icon - ); - group.add(entry); - } - - paletteRoot.add(group); - } - - //------------------------------------------ - - public static class ElementFactory implements CreationFactory { - - private final ElementDescriptor mDescriptor; - - public ElementFactory(ElementDescriptor descriptor) { - mDescriptor = descriptor; - } - - public Object getNewObject() { - return mDescriptor; - } - - public Object getObjectType() { - return mDescriptor; - } - - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/UiElementPullParser.java b/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/UiElementPullParser.java deleted file mode 100644 index 41d3747..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/UiElementPullParser.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.editors.layout; - -import com.android.ide.eclipse.editors.uimodel.UiElementNode; -import com.android.layoutlib.api.IXmlPullParser; - -import org.w3c.dom.Node; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.util.ArrayList; -import java.util.List; - -/** - * XmlPullParser implementation on top of {@link UiElementNode}. - * <p/>It's designed to work on layout files, and will most likely not work on other resource - * files. - */ -public final class UiElementPullParser implements IXmlPullParser { - - private final ArrayList<UiElementNode> mNodeStack = new ArrayList<UiElementNode>(); - private int mParsingState = START_DOCUMENT; - private UiElementNode mRoot; - - public UiElementPullParser(UiElementNode top) { - mRoot = top; - push(mRoot); - } - - private UiElementNode getCurrentNode() { - if (mNodeStack.size() > 0) { - return mNodeStack.get(mNodeStack.size()-1); - } - - return null; - } - - private Node getAttribute(int i) { - if (mParsingState != START_TAG) { - throw new IndexOutOfBoundsException(); - } - - // get the current uiNode - UiElementNode uiNode = getCurrentNode(); - - // get its xml node - Node xmlNode = uiNode.getXmlNode(); - - if (xmlNode != null) { - return xmlNode.getAttributes().item(i); - } - - return null; - } - - private void push(UiElementNode node) { - mNodeStack.add(node); - } - - private UiElementNode pop() { - return mNodeStack.remove(mNodeStack.size()-1); - } - - // ------------- IXmlPullParser -------- - - /** - * {@inheritDoc} - * - * This implementation returns the underlying DOM node. - */ - public Object getViewKey() { - return getCurrentNode(); - } - - // ------------- XmlPullParser -------- - - public void setFeature(String name, boolean state) throws XmlPullParserException { - if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) { - return; - } - if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) { - return; - } - throw new XmlPullParserException("Unsupported feature: " + name); - } - - public boolean getFeature(String name) { - if (FEATURE_PROCESS_NAMESPACES.equals(name)) { - return true; - } - if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) { - return true; - } - return false; - } - - public void setProperty(String name, Object value) throws XmlPullParserException { - throw new XmlPullParserException("setProperty() not supported"); - } - - public Object getProperty(String name) { - return null; - } - - public void setInput(Reader in) throws XmlPullParserException { - throw new XmlPullParserException("setInput() not supported"); - } - - public void setInput(InputStream inputStream, String inputEncoding) - throws XmlPullParserException { - throw new XmlPullParserException("setInput() not supported"); - } - - public void defineEntityReplacementText(String entityName, String replacementText) - throws XmlPullParserException { - throw new XmlPullParserException("defineEntityReplacementText() not supported"); - } - - public String getNamespacePrefix(int pos) throws XmlPullParserException { - throw new XmlPullParserException("getNamespacePrefix() not supported"); - } - - public String getInputEncoding() { - return null; - } - - public String getNamespace(String prefix) { - throw new RuntimeException("getNamespace() not supported"); - } - - public int getNamespaceCount(int depth) throws XmlPullParserException { - throw new XmlPullParserException("getNamespaceCount() not supported"); - } - - public String getPositionDescription() { - return "XML DOM element depth:" + mNodeStack.size(); - } - - public String getNamespaceUri(int pos) throws XmlPullParserException { - throw new XmlPullParserException("getNamespaceUri() not supported"); - } - - public int getColumnNumber() { - return -1; - } - - public int getLineNumber() { - return -1; - } - - public int getAttributeCount() { - UiElementNode node = getCurrentNode(); - if (node != null) { - return node.getUiAttributes().size(); - } - - return 0; - } - - public String getAttributeName(int i) { - Node attribute = getAttribute(i); - if (attribute != null) { - return attribute.getLocalName(); - } - - return null; - } - - public String getAttributeNamespace(int i) { - Node attribute = getAttribute(i); - if (attribute != null) { - return attribute.getNamespaceURI(); - } - return ""; //$NON-NLS-1$ - } - - public String getAttributePrefix(int i) { - Node attribute = getAttribute(i); - if (attribute != null) { - return attribute.getPrefix(); - } - return null; - } - - public String getAttributeType(int arg0) { - return "CDATA"; - } - - public String getAttributeValue(int i) { - Node attribute = getAttribute(i); - if (attribute != null) { - return attribute.getNodeValue(); - } - - return null; - } - - public String getAttributeValue(String namespace, String localName) { - // get the current uiNode - UiElementNode uiNode = getCurrentNode(); - - // get its xml node - Node xmlNode = uiNode.getXmlNode(); - - if (xmlNode != null) { - Node attribute = xmlNode.getAttributes().getNamedItemNS(namespace, localName); - if (attribute != null) { - return attribute.getNodeValue(); - } - } - - return null; - } - - public int getDepth() { - return mNodeStack.size(); - } - - public int getEventType() throws XmlPullParserException { - return mParsingState; - } - - public String getName() { - if (mParsingState == START_TAG || mParsingState == END_TAG) { - return getCurrentNode().getDescriptor().getXmlLocalName(); - } - - return null; - } - - public String getNamespace() { - if (mParsingState == START_TAG || mParsingState == END_TAG) { - return getCurrentNode().getDescriptor().getNamespace(); - } - - return null; - } - - public String getPrefix() { - if (mParsingState == START_TAG || mParsingState == END_TAG) { - // FIXME will NEVER work - if (getCurrentNode().getDescriptor().getXmlLocalName().startsWith("android:")) { //$NON-NLS-1$ - return "android"; //$NON-NLS-1$ - } - } - - return null; - } - - public String getText() { - return null; - } - - public char[] getTextCharacters(int[] arg0) { - return null; - } - - public boolean isAttributeDefault(int arg0) { - return false; - } - - public boolean isEmptyElementTag() throws XmlPullParserException { - if (mParsingState == START_TAG) { - return getCurrentNode().getUiChildren().size() == 0; - } - - throw new XmlPullParserException("Must be on START_TAG"); - } - - public boolean isWhitespace() throws XmlPullParserException { - return false; - } - - public int next() throws XmlPullParserException, IOException { - UiElementNode node; - switch (mParsingState) { - case END_DOCUMENT: - throw new XmlPullParserException("Nothing after the end"); - case START_DOCUMENT: - /* intended fall-through */ - case START_TAG: - // get the current node, and look for text or children (children first) - node = getCurrentNode(); - List<UiElementNode> children = node.getUiChildren(); - if (children.size() > 0) { - // move to the new child, and don't change the state. - push(children.get(0)); - - // in case the current state is CURRENT_DOC, we set the proper state. - mParsingState = START_TAG; - } else { - if (mParsingState == START_DOCUMENT) { - // this handles the case where there's no node. - mParsingState = END_DOCUMENT; - } else { - mParsingState = END_TAG; - } - } - break; - case END_TAG: - // look for a sibling. if no sibling, go back to the parent - node = getCurrentNode(); - node = node.getUiNextSibling(); - if (node != null) { - // to go to the sibling, we need to remove the current node, - pop(); - // and add its sibling. - push(node); - mParsingState = START_TAG; - } else { - // move back to the parent - pop(); - - // we have only one element left (mRoot), then we're done with the document. - if (mNodeStack.size() == 1) { - mParsingState = END_DOCUMENT; - } else { - mParsingState = END_TAG; - } - } - break; - case TEXT: - // not used - break; - case CDSECT: - // not used - break; - case ENTITY_REF: - // not used - break; - case IGNORABLE_WHITESPACE: - // not used - break; - case PROCESSING_INSTRUCTION: - // not used - break; - case COMMENT: - // not used - break; - case DOCDECL: - // not used - break; - } - - return mParsingState; - } - - public int nextTag() throws XmlPullParserException, IOException { - int eventType = next(); - if (eventType != START_TAG && eventType != END_TAG) { - throw new XmlPullParserException("expected start or end tag", this, null); - } - return eventType; - } - - public String nextText() throws XmlPullParserException, IOException { - if (getEventType() != START_TAG) { - throw new XmlPullParserException("parser must be on START_TAG to read next text", this, - null); - } - int eventType = next(); - if (eventType == TEXT) { - String result = getText(); - eventType = next(); - if (eventType != END_TAG) { - throw new XmlPullParserException( - "event TEXT it must be immediately followed by END_TAG", this, null); - } - return result; - } else if (eventType == END_TAG) { - return ""; - } else { - throw new XmlPullParserException("parser must be on START_TAG or TEXT to read text", - this, null); - } - } - - public int nextToken() throws XmlPullParserException, IOException { - return next(); - } - - public void require(int type, String namespace, String name) throws XmlPullParserException, - IOException { - if (type != getEventType() || (namespace != null && !namespace.equals(getNamespace())) - || (name != null && !name.equals(getName()))) - throw new XmlPullParserException("expected " + TYPES[type] + getPositionDescription()); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiElementEditPart.java b/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiElementEditPart.java deleted file mode 100644 index 548a3a2..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/layout/parts/UiElementEditPart.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.editors.layout.parts; - -import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.editors.uimodel.IUiUpdateListener; -import com.android.ide.eclipse.editors.uimodel.UiElementNode; - -import org.eclipse.draw2d.geometry.Rectangle; -import org.eclipse.gef.DragTracker; -import org.eclipse.gef.EditPart; -import org.eclipse.gef.EditPolicy; -import org.eclipse.gef.GraphicalEditPart; -import org.eclipse.gef.Request; -import org.eclipse.gef.editparts.AbstractGraphicalEditPart; -import org.eclipse.gef.editpolicies.SelectionEditPolicy; -import org.eclipse.gef.tools.SelectEditPartTracker; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; - -import java.util.List; - -/** - * An {@link EditPart} for a {@link UiElementNode}. - */ -public abstract class UiElementEditPart extends AbstractGraphicalEditPart - implements IUiUpdateListener { - - public UiElementEditPart(UiElementNode uiElementNode) { - setModel(uiElementNode); - } - - //------------------------- - // Derived classes must define these - - abstract protected void hideSelection(); - abstract protected void showSelection(); - - //------------------------- - // Base class overrides - - @Override - public DragTracker getDragTracker(Request request) { - return new SelectEditPartTracker(this); - } - - @Override - protected void createEditPolicies() { - installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE, new SelectionEditPolicy() { - @Override - protected void hideSelection() { - UiElementEditPart.this.hideSelection(); - } - - @Override - protected void showSelection() { - UiElementEditPart.this.showSelection(); - } - }); - // TODO add editing policies - } - - /* (non-javadoc) - * Returns a List containing the children model objects. - * Must not return null, instead use the super which returns an empty list. - */ - @SuppressWarnings("unchecked") - @Override - protected List getModelChildren() { - return getUiNode().getUiChildren(); - } - - @Override - public void activate() { - super.activate(); - getUiNode().addUpdateListener(this); - } - - @Override - public void deactivate() { - super.deactivate(); - getUiNode().removeUpdateListener(this); - } - - @Override - protected void refreshVisuals() { - if (getFigure().getParent() != null) { - ((GraphicalEditPart) getParent()).setLayoutConstraint(this, getFigure(), getBounds()); - } - - // update the visuals of the children as well - refreshChildrenVisuals(); - } - - protected void refreshChildrenVisuals() { - if (children != null) { - for (Object child : children) { - if (child instanceof UiElementEditPart) { - UiElementEditPart childPart = (UiElementEditPart)child; - childPart.refreshVisuals(); - } - } - } - } - - //------------------------- - // IUiUpdateListener implementation - - public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) { - // TODO: optimize by refreshing only when needed - switch(state) { - case ATTR_UPDATED: - refreshVisuals(); - break; - case CHILDREN_CHANGED: - refreshChildren(); - - // new children list, need to update the layout - refreshVisuals(); - break; - case CREATED: - refreshVisuals(); - break; - case DELETED: - // pass - break; - } - } - - //------------------------- - // Local methods - - /** @return The object model casted to an {@link UiElementNode} */ - protected final UiElementNode getUiNode() { - return (UiElementNode) getModel(); - } - - protected final ElementDescriptor getDescriptor() { - return getUiNode().getDescriptor(); - } - - protected final UiElementEditPart getEditPartParent() { - EditPart parent = getParent(); - if (parent instanceof UiElementEditPart) { - return (UiElementEditPart)parent; - } - return null; - } - - /** - * Returns a given XML attribute. - * @param attrbName The local name of the attribute. - * @return the attribute as a {@link String}, if it exists, or <code>null</code> - */ - protected final String getStringAttr(String attrName) { - UiElementNode uiNode = getUiNode(); - if (uiNode.getXmlNode() != null) { - Node xmlNode = uiNode.getXmlNode(); - if (xmlNode != null) { - NamedNodeMap nodeAttributes = xmlNode.getAttributes(); - if (nodeAttributes != null) { - Node attr = nodeAttributes.getNamedItemNS( - AndroidConstants.NS_RESOURCES, attrName); - if (attr != null) { - return attr.getNodeValue(); - } - } - } - } - return null; - } - - protected final Rectangle getBounds() { - UiElementNode model = (UiElementNode)getModel(); - - Object editData = model.getEditData(); - if (editData instanceof Rectangle) { - return (Rectangle)editData; // return a copy? - } - - // return a dummy rect - return new Rectangle(0, 0, 0, 0); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/CopyCutAction.java b/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/CopyCutAction.java deleted file mode 100644 index 7e38032..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/CopyCutAction.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.editors.ui.tree; - -import com.android.ide.eclipse.editors.AndroidEditor; -import com.android.ide.eclipse.editors.EditorsPlugin; -import com.android.ide.eclipse.editors.uimodel.UiElementNode; - -import org.apache.xml.serialize.Method; -import org.apache.xml.serialize.OutputFormat; -import org.apache.xml.serialize.XMLSerializer; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.swt.dnd.Clipboard; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.dnd.Transfer; -import org.eclipse.ui.ISharedImages; -import org.eclipse.ui.PlatformUI; -import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; -import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; -import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; -import org.eclipse.wst.xml.core.internal.document.NodeContainer; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.io.StringWriter; - - -/** - * Provides Cut and Copy actions for the tree nodes. - */ -public class CopyCutAction extends Action { - private UiElementNode mUiNode; - private boolean mPerformCut; - private final AndroidEditor mEditor; - private final Clipboard mClipboard; - private final ICommitXml mXmlCommit; - - /** - * Creates a new Copy or Cut action. - * - * @param ui_node The UI node to cut or copy. It *must* have a non-null XML node. - * @param perform_cut True if the operation is cut, false if it is copy. - */ - public CopyCutAction(AndroidEditor editor, Clipboard clipboard, ICommitXml xmlCommit, - UiElementNode ui_node, boolean perform_cut) { - super(perform_cut ? "Cut" : "Copy"); - mEditor = editor; - mClipboard = clipboard; - mXmlCommit = xmlCommit; - - ISharedImages images = PlatformUI.getWorkbench().getSharedImages(); - if (perform_cut) { - setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_CUT)); - setHoverImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_CUT)); - setDisabledImageDescriptor( - images.getImageDescriptor(ISharedImages.IMG_TOOL_CUT_DISABLED)); - } else { - setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_COPY)); - setHoverImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_COPY)); - setDisabledImageDescriptor( - images.getImageDescriptor(ISharedImages.IMG_TOOL_COPY_DISABLED)); - } - - mUiNode = ui_node; - mPerformCut = perform_cut; - } - - /** - * Performs the cut or copy action. - * First an XML serializer is used to turn the existing XML node into a valid - * XML fragment, which is added as text to the clipboard. - */ - @Override - public void run() { - super.run(); - try { - String data = null; - - // Get the data directly from the editor. - - // Commit the current pages first, to make sure the XML is in sync. - if (mXmlCommit != null) { - mXmlCommit.commitPendingXmlChanges(); - } - - // Committing may change the XML structure. - Node xml_node = mUiNode.getXmlNode(); - if (xml_node == null) { - return; - } - - IStructuredModel model = mEditor.getModelForRead(); - try { - IStructuredDocument sse_doc = mEditor.getStructuredDocument(); - if (xml_node instanceof NodeContainer) { - // The easy way to get the source of an SSE XML node. - data = ((NodeContainer) xml_node).getSource(); - } else if (xml_node instanceof IndexedRegion && sse_doc != null) { - // Try harder. - IndexedRegion region = (IndexedRegion) xml_node; - int start = region.getStartOffset(); - int end = region.getEndOffset(); - - if (end > start) { - data = sse_doc.get(start, end - start); - } - } - } catch (BadLocationException e) { - // the region offset was invalid. ignore. - } finally { - model.releaseFromRead(); - } - - // In the unlikely event that IStructuredDocument failed to extract the text - // directly from the editor, try to fall back on a direct XML serialization - // of the XML node. This uses the generic Node interface with no SSE tricks. - if (data == null) { - StringWriter sw = new StringWriter(); - XMLSerializer serializer = new XMLSerializer(sw, - new OutputFormat(Method.XML, - OutputFormat.Defaults.Encoding /* utf-8 */, - true /* indent */)); - // Serialize will throw an IOException if it fails. - serializer.serialize((Element) xml_node); - data = sw.toString(); - } - - if (data != null && data.length() > 0) { - mClipboard.setContents( - new Object[] { data }, - new Transfer[] { TextTransfer.getInstance() }); - if (mPerformCut) { - mUiNode.deleteXmlNode(); - } - } - } catch (Exception e) { - EditorsPlugin.log(e, "CopyCutAction failed for UI node %1$s", //$NON-NLS-1$ - mUiNode.getBreadcrumbTrailDescription(true)); - } - } -} - diff --git a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/UiActions.java b/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/UiActions.java deleted file mode 100644 index b3d0755..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.editors/src/com/android/ide/eclipse/editors/ui/tree/UiActions.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package com.android.ide.eclipse.editors.ui.tree; - -import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; -import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; -import com.android.ide.eclipse.editors.uimodel.UiDocumentNode; -import com.android.ide.eclipse.editors.uimodel.UiElementNode; - -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.viewers.ILabelProvider; -import org.eclipse.swt.widgets.Shell; -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -/** - * Performs basic actions on an XML tree: add node, remove node, move up/down. - */ -public abstract class UiActions implements ICommitXml { - - public UiActions() { - } - - //--------------------- - // Actual implementations must override these to provide specific hooks - - /** Returns the UiDocumentNode for the current model. */ - abstract protected UiElementNode getRootNode(); - - /** Commits pending data before the XML model is modified. */ - abstract public void commitPendingXmlChanges(); - - /** - * Utility method to select an outline item based on its model node - * - * @param ui_node The node to select. Can be null (in which case nothing should happen) - */ - abstract protected void selectUiNode(UiElementNode ui_node); - - //--------------------- - - /** - * Called when the "Add..." button next to the tree view is selected. - * <p/> - * This simplified version of doAdd does not support descriptor filters and creates - * a new {@link UiModelTreeLabelProvider} for each call. - */ - public void doAdd(UiElementNode ui_node, Shell shell) { - doAdd(ui_node, null /* descriptorFilters */, shell, new UiModelTreeLabelProvider()); - } - - /** - * Called when the "Add..." button next to the tree view is selected. - * - * Displays a selection dialog that lets the user select which kind of node - * to create, depending on the current selection. - */ - public void doAdd(UiElementNode ui_node, - ElementDescriptor[] descriptorFilters, - Shell shell, ILabelProvider labelProvider) { - // If the root node is a document with already a root, use it as the root node - UiElementNode root_node = getRootNode(); - if (root_node instanceof UiDocumentNode && - root_node.getUiChildren().size() > 0) { - root_node = root_node.getUiChildren().get(0); - } - - NewItemSelectionDialog dlg = new NewItemSelectionDialog( - shell, - labelProvider, - descriptorFilters, - ui_node, root_node); - dlg.open(); - Object[] results = dlg.getResult(); - if (results != null && results.length > 0) { - UiElementNode ui_new = addNewTreeElement(dlg.getChosenRootNode(), - (ElementDescriptor) results[0]); - - selectUiNode(ui_new); - } - } - - /** - * Called when the "Remove" button is selected. - * - * If the tree has a selection, remove it. - * This simply deletes the XML node attached to the UI node: when the XML model fires the - * update event, the tree will get refreshed. - */ - public void doRemove(final UiElementNode ui_node, Shell shell) { - if (MessageDialog.openQuestion(shell, - "Remove element from Android XML", // title - String.format("Do you really want to remove %1$s?", - ui_node.getBreadcrumbTrailDescription(false /* include_root */)))) { - commitPendingXmlChanges(); - getRootNode().getEditor().editXmlModel(new Runnable() { - public void run() { - UiElementNode previous = ui_node.getUiPreviousSibling(); - UiElementNode parent = ui_node.getUiParent(); - - // delete node - ui_node.deleteXmlNode(); - - // try to select the previous sibling or the parent - if (previous != null) { - selectUiNode(previous); - } else if (parent != null) { - selectUiNode(parent); - } - } - }); - } - } - - /** - * Called when the "Up" button is selected. - * <p/> - * If the tree has a selection, move it up, either in the child list or as the last child - * of the previous parent. - */ - public void doUp(final UiElementNode ui_node) { - final Node[] select_xml_node = { null }; - // the node will move either up to its parent or grand-parent - UiElementNode search_root = ui_node.getUiParent(); - if (search_root != null && search_root.getUiParent() != null) { - search_root = search_root.getUiParent(); - } - - commitPendingXmlChanges(); - getRootNode().getEditor().editXmlModel(new Runnable() { - public void run() { - Node xml_node = ui_node.getXmlNode(); - if (xml_node != null) { - Node xml_parent = xml_node.getParentNode(); - if (xml_parent != null) { - UiElementNode ui_prev = ui_node.getUiPreviousSibling(); - if (ui_prev != null && ui_prev.getXmlNode() != null) { - // This node is not the first one of the parent, so it can be - // removed and then inserted before its previous sibling. - // If the previous sibling can have children, though, then it - // is inserted at the end of the children list. - Node xml_prev = ui_prev.getXmlNode(); - if (ui_prev.getDescriptor().hasChildren()) { - xml_prev.appendChild(xml_parent.removeChild(xml_node)); - select_xml_node[0] = xml_node; - } else { - xml_parent.insertBefore( - xml_parent.removeChild(xml_node), - xml_prev); - select_xml_node[0] = xml_node; - } - } else if (!(xml_parent instanceof Document) && - xml_parent.getParentNode() != null && - !(xml_parent.getParentNode() instanceof Document)) { - // If the node is the first one of the child list of its - // parent, move it up in the hierarchy as previous sibling - // to the parent. This is only possible if the parent of the - // parent is not a document. - Node grand_parent = xml_parent.getParentNode(); - grand_parent.insertBefore(xml_parent.removeChild(xml_node), - xml_parent); - select_xml_node[0] = xml_node; - } - } - } - } - }); - - if (select_xml_node[0] == null) { - // The XML node has not been moved, we can just select the same UI node - selectUiNode(ui_node); - } else { - // The XML node has moved. At this point the UI model has been reloaded - // and the XML node has been affected to a new UI node. Find that new UI - // node and select it. - if (search_root == null) { - search_root = ui_node.getUiRoot(); - } - if (search_root != null) { - selectUiNode(search_root.findXmlNode(select_xml_node[0])); - } - } - } - - /** - * Called when the "Down" button is selected. - * - * If the tree has a selection, move it down, either in the same child list or as the - * first child of the next parent. - */ - public void doDown(final UiElementNode ui_node) { - final Node[] select_xml_node = { null }; - // the node will move either down to its parent or grand-parent - UiElementNode search_root = ui_node.getUiParent(); - if (search_root != null && search_root.getUiParent() != null) { - search_root = search_root.getUiParent(); - } - - commitPendingXmlChanges(); - getRootNode().getEditor().editXmlModel(new Runnable() { - public void run() { - Node xml_node = ui_node.getXmlNode(); - if (xml_node != null) { - Node xml_parent = xml_node.getParentNode(); - if (xml_parent != null) { - UiElementNode ui_next = ui_node.getUiNextSibling(); - if (ui_next != null && ui_next.getXmlNode() != null) { - // This node is not the last one of the parent, so it can be - // removed and then inserted after its next sibling. - // If the next sibling is a node that can have children, though, - // then the node is inserted as the first child. - Node xml_next = ui_next.getXmlNode(); - if (ui_next.getDescriptor().hasChildren()) { - // Note: insertBefore works as append if the ref node is - // null, i.e. when the node doesn't have children yet. - xml_next.insertBefore(xml_parent.removeChild(xml_node), - xml_next.getFirstChild()); - select_xml_node[0] = xml_node; - } else { - // Insert "before after next" ;-) - xml_parent.insertBefore(xml_parent.removeChild(xml_node), - xml_next.getNextSibling()); - select_xml_node[0] = xml_node; - } - } else if (!(xml_parent instanceof Document) && - xml_parent.getParentNode() != null && - !(xml_parent.getParentNode() instanceof Document)) { - // This node is the last node of its parent. - // If neither the parent nor the grandparent is a document, - // then the node can be insert right after the parent. - Node grand_parent = xml_parent.getParentNode(); - grand_parent.insertBefore(xml_parent.removeChild(xml_node), - xml_parent.getNextSibling()); - select_xml_node[0] = xml_node; - } - } - } - } - }); - - if (select_xml_node[0] == null) { - // The XML node has not been moved, we can just select the same UI node - selectUiNode(ui_node); - } else { - // The XML node has moved. At this point the UI model has been reloaded - // and the XML node has been affected to a new UI node. Find that new UI - // node and select it. - if (search_root == null) { - search_root = ui_node.getUiRoot(); - } - if (search_root != null) { - selectUiNode(search_root.findXmlNode(select_xml_node[0])); - } - } - } - - //--------------------- - - /** - * Adds a new element of the given descriptor's type to the given UI parent node. - * - * This actually creates the corresponding XML node in the XML model, which in turn - * will refresh the current tree view. - * - * @param ui_parent An existing UI node or null to add to the tree root - * @param elementDescriptor The descriptor of the element to add - * @return The {@link UiElementNode} that has been added to the UI tree. - */ - private UiElementNode addNewTreeElement(UiElementNode ui_parent, - ElementDescriptor elementDescriptor) { - commitPendingXmlChanges(); - final UiElementNode ui_new = ui_parent.appendNewUiChild(elementDescriptor); - UiElementNode root_node = getRootNode(); - - root_node.getEditor().editXmlModel(new Runnable() { - public void run() { - DescriptorsUtils.setDefaultLayoutAttributes(ui_new); - ui_new.createXmlNode(); - } - }); - return ui_new; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.platform/.classpath b/eclipse/plugins/com.android.ide.eclipse.platform/.classpath deleted file mode 100644 index 751c8f2..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.platform/.classpath +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<classpath> - <classpathentry kind="src" path="src"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> - <classpathentry kind="output" path="bin"/> -</classpath> diff --git a/eclipse/plugins/com.android.ide.eclipse.platform/.project b/eclipse/plugins/com.android.ide.eclipse.platform/.project deleted file mode 100644 index 145d97c..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.platform/.project +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<projectDescription> - <name>platform</name> - <comment></comment> - <projects> - </projects> - <buildSpec> - <buildCommand> - <name>org.eclipse.jdt.core.javabuilder</name> - <arguments> - </arguments> - </buildCommand> - <buildCommand> - <name>org.eclipse.pde.ManifestBuilder</name> - <arguments> - </arguments> - </buildCommand> - <buildCommand> - <name>org.eclipse.pde.SchemaBuilder</name> - <arguments> - </arguments> - </buildCommand> - </buildSpec> - <natures> - <nature>org.eclipse.pde.PluginNature</nature> - <nature>org.eclipse.jdt.core.javanature</nature> - </natures> -</projectDescription> diff --git a/eclipse/plugins/com.android.ide.eclipse.platform/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.platform/META-INF/MANIFEST.MF deleted file mode 100644 index 178275a..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.platform/META-INF/MANIFEST.MF +++ /dev/null @@ -1,15 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Android Platform Development Toolkit -Bundle-SymbolicName: com.android.ide.eclipse.platform;singleton:=true -Bundle-Version: 0.8.1.qualifier -Bundle-ClassPath: . -Bundle-Activator: com.android.ide.eclipse.platform.AndroidPlatformPlugin -Bundle-Vendor: The Android Open Source Project -Require-Bundle: org.eclipse.ui, - org.eclipse.core.runtime, - com.android.ide.eclipse.ddms, - com.android.ide.eclipse.common, - org.eclipse.core.resources, - org.eclipse.jdt.core -Eclipse-LazyStart: true diff --git a/eclipse/plugins/com.android.ide.eclipse.platform/MODULE_LICENSE_EPL b/eclipse/plugins/com.android.ide.eclipse.platform/MODULE_LICENSE_EPL deleted file mode 100644 index e69de29..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.platform/MODULE_LICENSE_EPL +++ /dev/null diff --git a/eclipse/plugins/com.android.ide.eclipse.platform/NOTICE b/eclipse/plugins/com.android.ide.eclipse.platform/NOTICE deleted file mode 100644 index 49c101d..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.platform/NOTICE +++ /dev/null @@ -1,224 +0,0 @@ -*Eclipse Public License - v 1.0* - -THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE -PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF -THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. - -*1. DEFINITIONS* - -"Contribution" means: - -a) in the case of the initial Contributor, the initial code and -documentation distributed under this Agreement, and -b) in the case of each subsequent Contributor: - -i) changes to the Program, and - -ii) additions to the Program; - -where such changes and/or additions to the Program originate from and -are distributed by that particular Contributor. A Contribution -'originates' from a Contributor if it was added to the Program by such -Contributor itself or anyone acting on such Contributor's behalf. -Contributions do not include additions to the Program which: (i) are -separate modules of software distributed in conjunction with the Program -under their own license agreement, and (ii) are not derivative works of -the Program. - -"Contributor" means any person or entity that distributes the Program. - -"Licensed Patents " mean patent claims licensable by a Contributor which -are necessarily infringed by the use or sale of its Contribution alone -or when combined with the Program. - -"Program" means the Contributions distributed in accordance with this -Agreement. - -"Recipient" means anyone who receives the Program under this Agreement, -including all Contributors. - -*2. GRANT OF RIGHTS* - -a) Subject to the terms of this Agreement, each Contributor hereby -grants Recipient a non-exclusive, worldwide, royalty-free copyright -license to reproduce, prepare derivative works of, publicly display, -publicly perform, distribute and sublicense the Contribution of such -Contributor, if any, and such derivative works, in source code and -object code form. - -b) Subject to the terms of this Agreement, each Contributor hereby -grants Recipient a non-exclusive, worldwide, royalty-free patent license -under Licensed Patents to make, use, sell, offer to sell, import and -otherwise transfer the Contribution of such Contributor, if any, in -source code and object code form. This patent license shall apply to the -combination of the Contribution and the Program if, at the time the -Contribution is added by the Contributor, such addition of the -Contribution causes such combination to be covered by the Licensed -Patents. The patent license shall not apply to any other combinations -which include the Contribution. No hardware per se is licensed hereunder. - -c) Recipient understands that although each Contributor grants the -licenses to its Contributions set forth herein, no assurances are -provided by any Contributor that the Program does not infringe the -patent or other intellectual property rights of any other entity. Each -Contributor disclaims any liability to Recipient for claims brought by -any other entity based on infringement of intellectual property rights -or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to -secure any other intellectual property rights needed, if any. For -example, if a third party patent license is required to allow Recipient -to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program. - -d) Each Contributor represents that to its knowledge it has sufficient -copyright rights in its Contribution, if any, to grant the copyright -license set forth in this Agreement. - -*3. REQUIREMENTS* - -A Contributor may choose to distribute the Program in object code form -under its own license agreement, provided that: - -a) it complies with the terms and conditions of this Agreement; and - -b) its license agreement: - -i) effectively disclaims on behalf of all Contributors all warranties -and conditions, express and implied, including warranties or conditions -of title and non-infringement, and implied warranties or conditions of -merchantability and fitness for a particular purpose; - -ii) effectively excludes on behalf of all Contributors all liability for -damages, including direct, indirect, special, incidental and -consequential damages, such as lost profits; - -iii) states that any provisions which differ from this Agreement are -offered by that Contributor alone and not by any other party; and - -iv) states that source code for the Program is available from such -Contributor, and informs licensees how to obtain it in a reasonable -manner on or through a medium customarily used for software exchange. - -When the Program is made available in source code form: - -a) it must be made available under this Agreement; and - -b) a copy of this Agreement must be included with each copy of the Program. - -Contributors may not remove or alter any copyright notices contained -within the Program. - -Each Contributor must identify itself as the originator of its -Contribution, if any, in a manner that reasonably allows subsequent -Recipients to identify the originator of the Contribution. - -*4. COMMERCIAL DISTRIBUTION* - -Commercial distributors of software may accept certain responsibilities -with respect to end users, business partners and the like. While this -license is intended to facilitate the commercial use of the Program, the -Contributor who includes the Program in a commercial product offering -should do so in a manner which does not create potential liability for -other Contributors. Therefore, if a Contributor includes the Program in -a commercial product offering, such Contributor ("Commercial -Contributor") hereby agrees to defend and indemnify every other -Contributor ("Indemnified Contributor") against any losses, damages and -costs (collectively "Losses") arising from claims, lawsuits and other -legal actions brought by a third party against the Indemnified -Contributor to the extent caused by the acts or omissions of such -Commercial Contributor in connection with its distribution of the -Program in a commercial product offering. The obligations in this -section do not apply to any claims or Losses relating to any actual or -alleged intellectual property infringement. In order to qualify, an -Indemnified Contributor must: a) promptly notify the Commercial -Contributor in writing of such claim, and b) allow the Commercial -Contributor to control, and cooperate with the Commercial Contributor -in, the defense and any related settlement negotiations. The Indemnified -Contributor may participate in any such claim at its own expense. - -For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other -Contributor to pay any damages as a result, the Commercial Contributor -must pay those damages. - -*5. NO WARRANTY* - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED -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. Each Recipient is solely responsible for -determining the appropriateness of using and distributing the Program -and assumes all risks associated with its exercise of rights under this -Agreement , including but not limited to the risks and costs of program -errors, compliance with applicable laws, damage to or loss of data, -programs or equipment, and unavailability or interruption of operations. - -*6. DISCLAIMER OF LIABILITY* - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR -ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -*7. GENERAL* - -If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further action -by the parties hereto, such provision shall be reformed to the minimum -extent necessary to make such provision valid and enforceable. - -If Recipient institutes patent litigation against any entity (including -a cross-claim or counterclaim in a lawsuit) alleging that the Program -itself (excluding combinations of the Program with other software or -hardware) infringes such Recipient's patent(s), then such Recipient's -rights granted under Section 2(b) shall terminate as of the date such -litigation is filed. - -All Recipient's rights under this Agreement shall terminate if it fails -to comply with any of the material terms or conditions of this Agreement -and does not cure such failure in a reasonable period of time after -becoming aware of such noncompliance. If all Recipient's rights under -this Agreement terminate, Recipient agrees to cease use and distribution -of the Program as soon as reasonably practicable. However, Recipient's -obligations under this Agreement and any licenses granted by Recipient -relating to the Program shall continue and survive. - -Everyone is permitted to copy and distribute copies of this Agreement, -but in order to avoid inconsistency the Agreement is copyrighted and may -only be modified in the following manner. The Agreement Steward reserves -the right to publish new versions (including revisions) of this -Agreement from time to time. No one other than the Agreement Steward has -the right to modify this Agreement. The Eclipse Foundation is the -initial Agreement Steward. The Eclipse Foundation may assign the -responsibility to serve as the Agreement Steward to a suitable separate -entity. Each new version of the Agreement will be given a distinguishing -version number. The Program (including Contributions) may always be -distributed subject to the version of the Agreement under which it was -received. In addition, after a new version of the Agreement is -published, Contributor may elect to distribute the Program (including -its Contributions) under the new version. Except as expressly stated in -Sections 2(a) and 2(b) above, Recipient receives no rights or licenses -to the intellectual property of any Contributor under this Agreement, -whether expressly, by implication, estoppel or otherwise. All rights in -the Program not expressly granted under this Agreement are reserved. - -This Agreement is governed by the laws of the State of New York and the -intellectual property laws of the United States of America. No party to -this Agreement will bring a legal action under this Agreement more than -one year after the cause of action arose. Each party waives its rights -to a jury trial in any resulting litigation. - - - diff --git a/eclipse/plugins/com.android.ide.eclipse.platform/build.properties b/eclipse/plugins/com.android.ide.eclipse.platform/build.properties deleted file mode 100644 index 6c480f3..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.platform/build.properties +++ /dev/null @@ -1,6 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - .,\ - plugin.xml,\ - icons/ diff --git a/eclipse/plugins/com.android.ide.eclipse.platform/icons/android_project.png b/eclipse/plugins/com.android.ide.eclipse.platform/icons/android_project.png Binary files differdeleted file mode 100644 index 6171025..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.platform/icons/android_project.png +++ /dev/null diff --git a/eclipse/plugins/com.android.ide.eclipse.platform/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.platform/plugin.xml deleted file mode 100644 index 1c5d067..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.platform/plugin.xml +++ /dev/null @@ -1,67 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?eclipse version="3.2"?> -<plugin> - <extension - id="PlatformNature" - name="PlatformNature" - point="org.eclipse.core.resources.natures"> - <runtime> - <run class="com.android.ide.eclipse.platform.project.PlatformNature"/> - </runtime> - </extension> - <extension - point="org.eclipse.ui.ide.projectNatureImages"> - <image - icon="icons/android_project.png" - id="com.android.ide.eclipse.platform.PlatformNature.image" - natureId="com.android.ide.eclipse.platform.PlatformNature"> - </image> - </extension> - <extension - point="org.eclipse.ui.preferencePages"> - <page - class="com.android.ide.eclipse.platform.preferences.AndroidPreferencePage" - id="com.android.ide.eclipse.preferences.main" - name="Android"/> - </extension> - <extension - point="org.eclipse.jdt.core.classpathContainerInitializer"> - <classpathContainerInitializer - class="com.android.ide.eclipse.platform.project.PlatformClasspathContainerInitializer" - id="com.android.ide.eclipse.platform.DUMMY_CONTAINER"> - </classpathContainerInitializer> - </extension> - <extension - point="org.eclipse.ui.popupMenus"> - <objectContribution - id="com.android.ide.eclipse.platform.contribution1" - nameFilter="*" - objectClass="org.eclipse.core.resources.IProject" - adaptable="true"> - <menu - id="com.android.ide.eclipse.platform.AndroidTools" - label="Android Tools" - path="additions"> - <separator name="group1"/> - </menu> - <visibility> - <not> - <or> - <objectState - name="projectNature" - value="com.android.ide.eclipse.platform.PlatformNature"/> - <objectState - name="open" - value="false"/> - </or> - </not> - </visibility> - <action - class="com.android.ide.eclipse.platform.project.ConvertToPlatformAction" - enablesFor="1" - id="com.android.ide.eclipse.platform.ConvertToPlatformAction" - label="Convert To Android Project" - menubarPath="com.android.ide.eclipse.platform.AndroidTools/group1"/> - </objectContribution> - </extension> -</plugin> diff --git a/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/AndroidPlatformPlugin.java b/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/AndroidPlatformPlugin.java deleted file mode 100644 index 5fa8a29..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/AndroidPlatformPlugin.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.platform; - -import com.android.ddmuilib.StackTracePanel; -import com.android.ddmuilib.StackTracePanel.ISourceRevealer; -import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.project.BaseProjectHelper; -import com.android.ide.eclipse.ddms.DdmsPlugin; -import com.android.ide.eclipse.platform.project.PlatformNature; - -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IWorkspace; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.Preferences; -import org.eclipse.core.runtime.Preferences.IPropertyChangeListener; -import org.eclipse.core.runtime.Preferences.PropertyChangeEvent; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.preference.IPreferenceStore; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.plugin.AbstractUIPlugin; -import org.osgi.framework.BundleContext; - -import java.io.File; - -/** - * The activator class controls the plug-in life cycle - */ -public class AndroidPlatformPlugin extends AbstractUIPlugin { - - // The plug-in ID - public static final String PLUGIN_ID = "com.android.ide.eclipse.apdt"; //$NON-NLS-1$ - - public final static String PREFS_DEVICE_DIRECTORY = PLUGIN_ID + ".deviceDir"; //$NON-NLS-1$ - - // The shared instance - private static AndroidPlatformPlugin sPlugin; - - private IPreferenceStore mStore; - private String mOsDeviceDirectory; - - /** - * The constructor - */ - public AndroidPlatformPlugin() { - sPlugin = this; - } - - /* - * (non-Javadoc) - * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) - */ - @Override - public void start(BundleContext context) throws Exception { - super.start(context); - - // get the eclipse store - mStore = getPreferenceStore(); - - // set the listener for the preference change - Preferences prefs = getPluginPreferences(); - prefs.addPropertyChangeListener(new IPropertyChangeListener() { - public void propertyChange(PropertyChangeEvent event) { - // get the name of the property that changed. - String property = event.getProperty(); - - // if the SDK changed, we update the cached version - if (PREFS_DEVICE_DIRECTORY.equals(property)) { - // get the new one from the preferences - mOsDeviceDirectory = (String)event.getNewValue(); - - // make sure it does not ends with a separator - if (mOsDeviceDirectory.endsWith(File.separator)) { - mOsDeviceDirectory = mOsDeviceDirectory.substring(0, - mOsDeviceDirectory.length() - 1); - } - - // finally restart adb, in case it's a different version - String adbLocation = getOsAdbLocation(); - if (adbLocation != null) { - DdmsPlugin.setAdb(adbLocation, true /* startAdb */); - } - } - } - }); - - - mOsDeviceDirectory = mStore.getString(PREFS_DEVICE_DIRECTORY); - - if (mOsDeviceDirectory.length() == 0) { - // get the current Display - final Display display = sPlugin.getWorkbench().getDisplay(); - - // dialog box only run in ui thread.. - display.asyncExec(new Runnable() { - public void run() { - Shell shell = display.getActiveShell(); - MessageDialog.openError(shell, "Android Preferences", - "Location of the device directory is missing."); - } - }); - } else { - // give the location of adb to ddms - String adbLocation = getOsAdbLocation(); - if (adbLocation != null) { - DdmsPlugin.setAdb(adbLocation, true); - } - } - - // and give it the debug launcher for android projects - DdmsPlugin.setRunningAppDebugLauncher(new DdmsPlugin.IDebugLauncher() { - public boolean debug(String packageName, int port) { - return false; - } - }); - - StackTracePanel.setSourceRevealer(new ISourceRevealer() { - public void reveal(String applicationName, String className, int line) { - IProject project = getDeviceProject(); - if (project != null) { - BaseProjectHelper.revealSource(project, className, line); - } - } - }); - - } - - /* - * (non-Javadoc) - * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) - */ - @Override - public void stop(BundleContext context) throws Exception { - sPlugin = null; - super.stop(context); - } - - /** - * Returns the shared instance - * - * @return the shared instance - */ - public static AndroidPlatformPlugin getDefault() { - return sPlugin; - } - - /** - * Returns the Android project. This is the first project which has the PlatformNature. - * @return the <code>IProject</code> of the Android project, or <code>null</code> if it was - * not found. - */ - public static IProject getDeviceProject() { - // Get the list of projects for the current workspace. - IWorkspace workspace = ResourcesPlugin.getWorkspace(); - IProject[] projects = workspace.getRoot().getProjects(); - - for (IProject project : projects) { - try { - if (project.hasNature(PlatformNature.ID)) { - return project; - } - } catch (CoreException e) { - // Failed to get the nature for this project. Let's just ignore - // it and move on to the next one. - } - } - - return null; - } - - /** - * Returns the OS path of the adb location. - * @return the location of adb or null if it cannot be computed. - */ - private String getOsAdbLocation() { - if (mOsDeviceDirectory == null || mOsDeviceDirectory.length() == 0) { - return null; - } - - if (AndroidConstants.CURRENT_PLATFORM == AndroidConstants.PLATFORM_LINUX) { - return mOsDeviceDirectory + "/out/host/linux-x86/bin/adb"; //$NON-NLS-1$ - } else if (AndroidConstants.CURRENT_PLATFORM == AndroidConstants.PLATFORM_DARWIN) { - return mOsDeviceDirectory + "/out/host/darwin-x86/bin/adb"; //$NON-NLS-1$ - } - return null; - } - -} diff --git a/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/preferences/AndroidPreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/preferences/AndroidPreferencePage.java deleted file mode 100644 index 8427bad..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/preferences/AndroidPreferencePage.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.platform.preferences; - -import com.android.ide.eclipse.platform.AndroidPlatformPlugin; - -import org.eclipse.jface.preference.DirectoryFieldEditor; -import org.eclipse.jface.preference.FieldEditorPreferencePage; -import org.eclipse.ui.IWorkbench; -import org.eclipse.ui.IWorkbenchPreferencePage; - -/** - * This class represents a preference page that is contributed to the - * Preferences dialog. By subclassing <samp>FieldEditorPreferencePage</samp>, - * we can use the field support built into JFace that allows us to create a page - * that is small and knows how to save, restore and apply itself. - * <p> - * This page is used to modify preferences only. They are stored in the - * preference store that belongs to the main plug-in class. That way, - * preferences can be accessed directly via the preference store. - */ -public class AndroidPreferencePage extends FieldEditorPreferencePage implements - IWorkbenchPreferencePage { - - public AndroidPreferencePage() { - super(GRID); - setPreferenceStore(AndroidPlatformPlugin.getDefault().getPreferenceStore()); - setDescription("Android Preferences"); - } - - /** - * Creates the field editors. Field editors are abstractions of the common - * GUI blocks needed to manipulate various types of preferences. Each field - * editor knows how to save and restore itself. - */ - @Override - public void createFieldEditors() { - addField(new DirectoryFieldEditor(AndroidPlatformPlugin.PREFS_DEVICE_DIRECTORY, - "Location of //device", getFieldEditorParent())); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) - */ - public void init(IWorkbench workbench) { - } - -} diff --git a/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/project/ConvertToPlatformAction.java b/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/project/ConvertToPlatformAction.java deleted file mode 100644 index 58d55e5..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/project/ConvertToPlatformAction.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.platform.project; - -import com.android.ide.eclipse.platform.AndroidPlatformPlugin; - -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IProjectDescription; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IAdaptable; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.jdt.core.IClasspathEntry; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jface.action.IAction; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.ui.IObjectActionDelegate; -import org.eclipse.ui.IWorkbenchPart; - -import java.util.Iterator; - -/** - * Converts a project created with the activity creator into an - * Android project. - */ -public class ConvertToPlatformAction implements IObjectActionDelegate { - - private ISelection mSelection; - - /* - * (non-Javadoc) - * - * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart) - */ - public void setActivePart(IAction action, IWorkbenchPart targetPart) { - // pass - } - - /* - * (non-Javadoc) - * - * @see IActionDelegate#run(IAction) - */ - public void run(IAction action) { - if (mSelection instanceof IStructuredSelection) { - for (Iterator<?> it = ((IStructuredSelection)mSelection).iterator(); it.hasNext();) { - Object element = it.next(); - IProject project = null; - if (element instanceof IProject) { - project = (IProject)element; - } else if (element instanceof IAdaptable) { - project = (IProject)((IAdaptable)element).getAdapter(IProject.class); - } - if (project != null) { - convertProject(project); - } - } - } - } - - /* - * (non-Javadoc) - * - * @see IActionDelegate#selectionChanged(IAction, ISelection) - */ - public void selectionChanged(IAction action, ISelection selection) { - this.mSelection = selection; - } - - /** - * Toggles sample nature on a project - * - * @param project to have sample nature added or removed - */ - private void convertProject(final IProject project) { - new Job("Convert Project") { - @Override - protected IStatus run(IProgressMonitor monitor) { - try { - if (monitor != null) { - monitor.beginTask(String.format( - "Convert %1$s to Android", project.getName()), 5); - } - - IProjectDescription description = project.getDescription(); - String[] natures = description.getNatureIds(); - - // check if the project already has the android nature. - for (int i = 0; i < natures.length; ++i) { - if (PlatformNature.ID.equals(natures[i])) { - // we shouldn't be here as the visibility of the item - // is dependent on the project. - return new Status(Status.WARNING, AndroidPlatformPlugin.PLUGIN_ID, - "Project is already an Android Platform Project"); - } - } - - // add the platform nature - String[] newNatures = new String[natures.length + 1]; - System.arraycopy(natures, 0, newNatures, 1, natures.length); - newNatures[0] = PlatformNature.ID; - - // set the new nature list in the project - description.setNatureIds(newNatures); - project.setDescription(description, null); - - IJavaProject javaProject = JavaCore.create(project); - IClasspathEntry[] entries = javaProject.getRawClasspath(); - - int n = entries.length; - IClasspathEntry[] newEntries = new IClasspathEntry[n + 1]; - System.arraycopy(entries, 0, newEntries, 0, n); - newEntries[n] = PlatformClasspathContainerInitializer.getContainerEntry(); - - javaProject.setRawClasspath(newEntries, monitor); - - return Status.OK_STATUS; - } catch (JavaModelException e) { - return e.getJavaModelStatus(); - } catch (CoreException e) { - return e.getStatus(); - } finally { - if (monitor != null) { - monitor.done(); - } - } - } - }.schedule(); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/project/PlatformClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/project/PlatformClasspathContainerInitializer.java deleted file mode 100644 index a54713c..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/project/PlatformClasspathContainerInitializer.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.platform.project; - -import org.eclipse.core.resources.IProject; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.Path; -import org.eclipse.jdt.core.ClasspathContainerInitializer; -import org.eclipse.jdt.core.IClasspathContainer; -import org.eclipse.jdt.core.IClasspathEntry; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.JavaCore; - -/** - * Classpath container initializer responsible for binding {@link PlatformClasspathContainer} to - * {@link IProject}s. Because any projects with this container force Eclipse to load the - * plugin, this is a hack to make sure the android platform plugin is launched as soon as an - * android project is opened. - */ -public class PlatformClasspathContainerInitializer extends ClasspathContainerInitializer { - - /** The container id for the android framework jar file */ - private final static String CONTAINER_ID = "com.android.ide.eclipse.platform.DUMMY_CONTAINER"; //$NON-NLS-1$ - - public PlatformClasspathContainerInitializer() { - // pass - } - - /** - * Binds a classpath container to a {@link IClasspathContainer} for a given project, - * or silently fails if unable to do so. - * @param containerPath the container path that is the container id. - * @param the project to bind - */ - @Override - public void initialize(IPath containerPath, IJavaProject project) throws CoreException { - // pass - } - - /** - * Creates a new {@link IClasspathEntry} of type {@link IClasspathEntry#CPE_CONTAINER} - * linking to the Android Framework. - */ - public static IClasspathEntry getContainerEntry() { - return JavaCore.newContainerEntry(new Path(CONTAINER_ID)); - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/project/PlatformNature.java b/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/project/PlatformNature.java deleted file mode 100644 index 884dc58..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.platform/src/com/android/ide/eclipse/platform/project/PlatformNature.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php - * - * 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.ide.eclipse.platform.project; - -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IProjectNature; -import org.eclipse.core.runtime.CoreException; - -/** - * Project nature for the Android Projects. - */ -public class PlatformNature implements IProjectNature { - - public final static String ID = "com.android.ide.eclipse.platform.PlatformNature"; //$NON-NLS-1$ - - /** the project this nature object is associated with */ - private IProject mProject; - - /** - * Configures this nature for its project. This is called by the workspace - * when natures are added to the project using - * <code>IProject.setDescription</code> and should not be called directly - * by clients. The nature extension id is added to the list of natures - * before this method is called, and need not be added here. - * - * Exceptions thrown by this method will be propagated back to the caller of - * <code>IProject.setDescription</code>, but the nature will remain in - * the project description. - * - * In this implementation there is nothing to be done, since there's no builder associated - * with this nature. - * - * @see org.eclipse.core.resources.IProjectNature#configure() - * @throws CoreException if configuration fails. - */ - public void configure() throws CoreException { - // pass - } - - /** - * De-configures this nature for its project. This is called by the - * workspace when natures are removed from the project using - * <code>IProject.setDescription</code> and should not be called directly - * by clients. The nature extension id is removed from the list of natures - * before this method is called, and need not be removed here. - * - * Exceptions thrown by this method will be propagated back to the caller of - * <code>IProject.setDescription</code>, but the nature will still be - * removed from the project description. - * - * In this implementation there is nothing to be done, since there's no builder associated - * with this nature. - * - * @see org.eclipse.core.resources.IProjectNature#deconfigure() - * @throws CoreException if configuration fails. - */ - public void deconfigure() throws CoreException { - // pass - } - - /** - * Returns the project to which this project nature applies. - * - * @return the project handle - * @see org.eclipse.core.resources.IProjectNature#getProject() - */ - public IProject getProject() { - return mProject; - } - - /** - * Sets the project to which this nature applies. Used when instantiating - * this project nature runtime. This is called by - * <code>IProject.create()</code> or - * <code>IProject.setDescription()</code> and should not be called - * directly by clients. - * - * @param project the project to which this nature applies - * @see org.eclipse.core.resources.IProjectNature#setProject(org.eclipse.core.resources.IProject) - */ - public void setProject(IProject project) { - mProject = project; - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF index a121266..266008c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF +++ b/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF @@ -2,18 +2,17 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Android Plugin Tests Bundle-SymbolicName: com.android.ide.eclipse.tests -Bundle-Version: 0.8.1.qualifier +Bundle-Version: 0.9.0.qualifier Bundle-Activator: com.android.ide.eclipse.tests.AndroidTestPlugin Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime, org.eclipse.core.resources, com.android.ide.eclipse.adt, org.junit, - com.android.ide.eclipse.common, - com.android.ide.eclipse.editors, org.eclipse.jdt.core, org.eclipse.jdt.launching, - org.eclipse.ui.views + org.eclipse.ui.views, + com.android.ide.eclipse.ddms Eclipse-LazyStart: true Bundle-Vendor: The Android Open Source Project Bundle-ClassPath: kxml2-2.3.0.jar, diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/project/internal/StubSampleProjectCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/wizards/newproject/StubSampleProjectCreationPage.java index 3202c67..42f8df0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/project/internal/StubSampleProjectCreationPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/wizards/newproject/StubSampleProjectCreationPage.java @@ -1,11 +1,11 @@ /* - * Copyright (C) 20078The Android Open Source Project + * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * http://www.eclipse.org/org/documents/epl-v10.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT @@ -13,9 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.android.ide.eclipse.adt.project.internal; - -import com.android.ide.eclipse.adt.project.internal.NewProjectCreationPage; +package com.android.ide.eclipse.adt.wizards.newproject; import java.io.File; diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/project/internal/StubSampleProjectWizard.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/wizards/newproject/StubSampleProjectWizard.java index 49a853d..40cd636 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/project/internal/StubSampleProjectWizard.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/wizards/newproject/StubSampleProjectWizard.java @@ -5,7 +5,7 @@ * may not use this file except in compliance with the License. You may obtain a * copy of the License at * - * http://www.eclipse.org/org/documents/epl-v10.php + * http://www.eclipse.org/org/documents/epl-v10.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT @@ -13,10 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.android.ide.eclipse.adt.project.internal; - -import com.android.ide.eclipse.adt.project.internal.NewProjectCreationPage; -import com.android.ide.eclipse.adt.project.internal.NewProjectWizard; +package com.android.ide.eclipse.adt.wizards.newproject; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jface.operation.IRunnableWithProgress; diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/SampleProjectTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/SampleProjectTest.java index 5315db8..98817c6 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/SampleProjectTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/SampleProjectTest.java @@ -5,7 +5,7 @@ * may not use this file except in compliance with the License. You may obtain a * copy of the License at * - * http://www.eclipse.org/org/documents/epl-v10.php + * http://www.eclipse.org/org/documents/epl-v10.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT @@ -16,10 +16,9 @@ package com.android.ide.eclipse.tests.functests.sampleProjects; import com.android.ide.eclipse.adt.project.ProjectHelper; -import com.android.ide.eclipse.adt.project.internal.StubSampleProjectWizard; +import com.android.ide.eclipse.adt.wizards.newproject.StubSampleProjectWizard; import com.android.ide.eclipse.tests.FuncTestCase; - import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/resources/AndroidJarLoaderTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/AndroidJarLoaderTest.java index 9d89d18..f3d9b79 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/resources/AndroidJarLoaderTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/AndroidJarLoaderTest.java @@ -14,21 +14,19 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.resources; +package com.android.ide.eclipse.adt.sdk; -import com.android.ide.eclipse.adt.resources.LayoutParamsParser.IClass; +import com.android.ide.eclipse.adt.sdk.IAndroidClassLoader.IClassDescriptor; import com.android.ide.eclipse.tests.AdtTestData; -import junit.framework.TestCase; - -import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.net.URL; import java.util.ArrayList; import java.util.HashMap; +import junit.framework.TestCase; + /** * Unit Test for {@link FrameworkClassLoader}. * @@ -51,7 +49,7 @@ public class AndroidJarLoaderTest extends TestCase { /** Preloads classes. They should load just fine. */ public final void testPreLoadClasses() throws Exception { - mFrameworkClassLoader.preLoadClasses("jar.example.", null); //$NON-NLS-1$ + mFrameworkClassLoader.preLoadClasses("jar.example.", null, null); //$NON-NLS-1$ HashMap<String, Class<?>> map = getPrivateClassCache(); assertEquals(0, map.size()); HashMap<String,byte[]> data = getPrivateEntryCache(); @@ -64,7 +62,7 @@ public class AndroidJarLoaderTest extends TestCase { /** Preloads a class not in the JAR. Preloading does nothing in this case. */ public final void testPreLoadClasses_classNotFound() throws Exception { - mFrameworkClassLoader.preLoadClasses("not.a.package.", null); //$NON-NLS-1$ + mFrameworkClassLoader.preLoadClasses("not.a.package.", null, null); //$NON-NLS-1$ HashMap<String, Class<?>> map = getPrivateClassCache(); assertEquals(0, map.size()); HashMap<String,byte[]> data = getPrivateEntryCache(); @@ -108,7 +106,7 @@ public class AndroidJarLoaderTest extends TestCase { } public final void testFindClassesDerivingFrom() throws Exception { - HashMap<String, ArrayList<IClass>> found = + HashMap<String, ArrayList<IClassDescriptor>> found = mFrameworkClassLoader.findClassesDerivingFrom("jar.example.", new String[] { //$NON-NLS-1$ "jar.example.Class1", //$NON-NLS-1$ "jar.example.Class2" }); //$NON-NLS-1$ diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/resources/LayoutParamsParserTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/LayoutParamsParserTest.java index 1a2ff9b..b66fcd6 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/resources/LayoutParamsParserTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/LayoutParamsParserTest.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.resources; +package com.android.ide.eclipse.adt.sdk; -import com.android.ide.eclipse.adt.resources.AndroidJarLoader.ClassWrapper; -import com.android.ide.eclipse.adt.resources.LayoutParamsParser.IClass; +import com.android.ide.eclipse.adt.sdk.AndroidJarLoader.ClassWrapper; +import com.android.ide.eclipse.adt.sdk.IAndroidClassLoader.IClassDescriptor; import com.android.ide.eclipse.common.resources.AttrsXmlParser; import com.android.ide.eclipse.common.resources.ViewClassInfo; import com.android.ide.eclipse.common.resources.ViewClassInfo.LayoutParamsInfo; @@ -32,7 +32,7 @@ import java.util.TreeMap; import junit.framework.TestCase; /** - * Test the inner private methods of FrameworkResourceParser. + * Test the inner private methods of PlatformDataParser. * * Convention: method names that start with an underscore are actually local wrappers * that call private methods from {@link FrameworkResourceParser} using reflection. @@ -47,9 +47,9 @@ public class LayoutParamsParserTest extends TestCase { } @Override - public HashMap<String, ArrayList<IClass>> findClassesDerivingFrom( + public HashMap<String, ArrayList<IClassDescriptor>> findClassesDerivingFrom( String rootPackage, String[] superClasses) throws ClassFormatError { - return new HashMap<String, ArrayList<IClass>>(); + return new HashMap<String, ArrayList<IClassDescriptor>>(); } } @@ -70,8 +70,8 @@ public class LayoutParamsParserTest extends TestCase { mTopGroupClass = new ClassWrapper(mock_android.view.ViewGroup.class); mTopLayoutParamsClass = new ClassWrapper(mock_android.view.ViewGroup.LayoutParams.class); - mViewList = new ArrayList<IClass>(); - mGroupList = new ArrayList<IClass>(); + mViewList = new ArrayList<IClassDescriptor>(); + mGroupList = new ArrayList<IClassDescriptor>(); mViewMap = new TreeMap<String, ExtViewClassInfo>(); mGroupMap = new TreeMap<String, ExtViewClassInfo>(); mLayoutParamsMap = new HashMap<String, LayoutParamsInfo>(); @@ -131,16 +131,16 @@ public class LayoutParamsParserTest extends TestCase { //---- access to private methods /** Calls the private constructor of the parser */ - private FrameworkResourceParser _Constructor(String osJarPath) throws Exception { - Constructor<FrameworkResourceParser> constructor = - FrameworkResourceParser.class.getDeclaredConstructor(String.class); + private AndroidTargetParser _Constructor(String osJarPath) throws Exception { + Constructor<AndroidTargetParser> constructor = + AndroidTargetParser.class.getDeclaredConstructor(String.class); constructor.setAccessible(true); return constructor.newInstance(osJarPath); } /** calls the private getLayoutClasses() of the parser */ private void _getLayoutClasses() throws Exception { - Method method = FrameworkResourceParser.class.getDeclaredMethod("getLayoutClasses"); //$NON-NLS-1$ + Method method = AndroidTargetParser.class.getDeclaredMethod("getLayoutClasses"); //$NON-NLS-1$ method.setAccessible(true); method.invoke(mParser); } @@ -148,7 +148,7 @@ public class LayoutParamsParserTest extends TestCase { /** calls the private addGroup() of the parser */ private ViewClassInfo _addGroup(Class<?> groupClass) throws Exception { Method method = LayoutParamsParser.class.getDeclaredMethod("addGroup", //$NON-NLS-1$ - IClass.class); + IClassDescriptor.class); method.setAccessible(true); return (ViewClassInfo) method.invoke(mParser, new ClassWrapper(groupClass)); } @@ -156,7 +156,7 @@ public class LayoutParamsParserTest extends TestCase { /** calls the private addLayoutParams() of the parser */ private LayoutParamsInfo _addLayoutParams(Class<?> groupClass) throws Exception { Method method = LayoutParamsParser.class.getDeclaredMethod("addLayoutParams", //$NON-NLS-1$ - IClass.class); + IClassDescriptor.class); method.setAccessible(true); return (LayoutParamsInfo) method.invoke(mParser, new ClassWrapper(groupClass)); } @@ -164,17 +164,17 @@ public class LayoutParamsParserTest extends TestCase { /** calls the private getLayoutParamsInfo() of the parser */ private LayoutParamsInfo _getLayoutParamsInfo(Class<?> layoutParamsClass) throws Exception { Method method = LayoutParamsParser.class.getDeclaredMethod("getLayoutParamsInfo", //$NON-NLS-1$ - IClass.class); + IClassDescriptor.class); method.setAccessible(true); return (LayoutParamsInfo) method.invoke(mParser, new ClassWrapper(layoutParamsClass)); } /** calls the private findLayoutParams() of the parser */ - private IClass _findLayoutParams(Class<?> groupClass) throws Exception { + private IClassDescriptor _findLayoutParams(Class<?> groupClass) throws Exception { Method method = LayoutParamsParser.class.getDeclaredMethod("findLayoutParams", //$NON-NLS-1$ - IClass.class); + IClassDescriptor.class); method.setAccessible(true); - return (IClass) method.invoke(mParser, new ClassWrapper(groupClass)); + return (IClassDescriptor) method.invoke(mParser, new ClassWrapper(groupClass)); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/layout/UiElementPullParserTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/layout/UiElementPullParserTest.java index 521bb62..1427eee 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/layout/UiElementPullParserTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/layout/UiElementPullParserTest.java @@ -216,9 +216,6 @@ public class UiElementPullParserTest extends TestCase { } catch (XmlPullParserException e) { e.printStackTrace(); assertTrue(false); - } catch (IOException e) { - e.printStackTrace(); - assertTrue(false); } } diff --git a/eclipse/doBuild.sh b/eclipse/scripts/build_plugins.sh index a0d8606..5f94ca0 100755 --- a/eclipse/doBuild.sh +++ b/eclipse/scripts/build_plugins.sh @@ -2,7 +2,7 @@ # build script for eclipse adt build on linux platform # -# Usage: doBuild <build_version> +# Usage: development/tools/eclipse/scripts/build_plugins <build_version> # # It expects environment variable ECLIPSE_HOME to be defined to point to _your_ # version of Eclipse RCP (must have the WTP & GEF plugins available too.) @@ -59,8 +59,10 @@ function dieWithUsage() { HOST=`uname` [ "$HOST" == "Linux" ] || die "ERROR: This script is currently only supported on Linux platform" -# Check which directory this is invoked from -[ "${PWD: -13}" == "tools/eclipse" ] || dieWithUsage "Please run this script from the device/tools/eclipse directory" +# Make sure this runs from the tools/eclipse plugin. +D=`dirname "$0"` +cd "$D/.." +[ `basename "$PWD"` == "eclipse" ] || dieWithUsage "Please run this script from the device/tools/eclipse directory" # check for number of parameters [ $# -lt 1 ] && dieWithUsage "ERROR: Not enough parameters" @@ -86,6 +88,8 @@ if [ -z "$ECLIPSE_HOME" ]; then [ -f "$PID_FILE" ] && ECLIPSE_PID=`cat "$PID_FILE"` fi +echo "PWD=`pwd`" +echo "ECLIPSE_HOME=$ECLIPSE_HOME" # # -- Site parameters and Build version -- @@ -124,7 +128,7 @@ fi # The "configuration directory" will hold the workspace for this build. # If it contains old data the build may fail so we need to clean it first # and create it if it doesn't exist. -CONFIG_DIR="../../out/eclipse-configuration-$BUILD_VERSION" +CONFIG_DIR="../../../out/eclipse-configuration-$BUILD_VERSION" [ -d "$CONFIG_DIR" ] && rm -rfv "$CONFIG_DIR" mkdir -p "$CONFIG_DIR" @@ -173,8 +177,18 @@ echo " Build File: $BUILDFILE" echo " Build Config: $BUILDCONFIG" echo " Config Dir: $CONFIG_DIR" +# clean input directories to make sure there's nothing left from previous run + +rm -fv *.properties *.xml +find . -name "@*" | xargs rm -rfv + +# Now execute the ant runner + +set +e # don't stop on errors anymore, we want to catch there here + java \ -jar $LAUNCHER \ + -data "$CONFIG_DIR" \ -configuration "$CONFIG_DIR" \ -application org.eclipse.ant.core.antRunner \ -buildfile $BUILDFILE \ @@ -183,6 +197,21 @@ java \ -DforceContextQualifier=$BUILD_VERSION \ -DECLIPSE_HOME=$ECLIPSE_HOME \ $SITE_PARAM +RESULT=$? + +if [ "0" != "$RESULT" ]; then + echo "JAVA died with error code $RESULT" + echo "Dump of build config logs:" + for i in "$CONFIG_DIR"/*.log; do + if [ -f "$i" ]; then + echo "----------------------" + echo "--- $i" + echo "----------------------" + cat "$i" + echo + fi + done +fi # # -- Cleanup diff --git a/eclipse/scripts/build_server.sh b/eclipse/scripts/build_server.sh index e30dbb8..39c8dcd 100755 --- a/eclipse/scripts/build_server.sh +++ b/eclipse/scripts/build_server.sh @@ -46,9 +46,10 @@ function die() { } function check_params() { - # This needs to run from //device - [ `basename "$PWD"` == "device" ] || \ - die "Please execute $0 from the //device directory, not $PWD" + # This needs to run from the top android directory + # Automatically CD to the top android directory, whatever its name + D=`dirname "$0"` + cd "$D/../../../../" && echo "Switched to directory $PWD" # The current Eclipse build has some Linux dependency in its config files [ `uname` == "Linux" ] || die "This must run from a Linux box." @@ -60,14 +61,12 @@ function check_params() { function build_libs() { MAKE_OPT="-j8" - echo "*** Building: make $MAKE_OPT dx ping ddms jarutils androidprefs layoutlib_api" - make $MAKE_OPT dx ping ddms jarutils androidprefs layoutlib_api layoutlib_utils + echo "*** Building: make $MAKE_OPT dx ping ddms jarutils androidprefs layoutlib_api ninepatch sdklib sdkuilib" + make $MAKE_OPT dx ping ddms jarutils androidprefs layoutlib_api layoutlib_utils ninepatch sdklib sdkuilib } function build_plugin { - cd tools/eclipse/scripts - ./create_all_symlinks.sh - cd .. # cd back to tools/eclipse + development/tools/eclipse/scripts/create_all_symlinks.sh # Qualifier is "v" followed by date/time in YYYYMMDDHHSS format and the optional # build number. @@ -90,7 +89,7 @@ function build_plugin { [ -d "$DEST_DIR/$BUILD_PREFIX" ] || rm -rfv "$DEST_DIR/$BUILD_PREFIX" # Perform the Eclipse build and move the result in $DEST_DIR/android-build - ./doBuild.sh $QUALIFIER $INTERNAL_BUILD -d "$DEST_DIR" -a "$BUILD_PREFIX" + development/tools/eclipse/scripts/build_plugins.sh $QUALIFIER $INTERNAL_BUILD -d "$DEST_DIR" -a "$BUILD_PREFIX" # Cleanup [ -d "$QUALIFIER" ] && rm -rfv "$QUALIFIER" diff --git a/eclipse/scripts/create_adt_symlinks.sh b/eclipse/scripts/create_adt_symlinks.sh index 4974432..557c4d9 100755 --- a/eclipse/scripts/create_adt_symlinks.sh +++ b/eclipse/scripts/create_adt_symlinks.sh @@ -1,42 +1,50 @@ #!/bin/bash function die() { - echo "Error: $*" - exit 1 + echo "Error: $*" + exit 1 } set -e # fail early -# This may run either from the //device directory or from the -# eclipse/script directory. Allow for both. -D="device/tools/eclipse/scripts" -[ -d "$D" ] && cd "$D" -[ -d "../$D" ] && cd "../$D" +# CD to the top android directory +D=`dirname "$0"` +cd "$D/../../../../" + +DEST="development/tools/eclipse/plugins/com.android.ide.eclipse.adt" +# computes "../.." from DEST to here (in /android) +BACK=`echo $DEST | sed 's@[^/]*@..@g'` + +LIBS="sdkstats jarutils androidprefs layoutlib_api layoutlib_utils ninepatch sdklib sdkuilib" + +echo "make java libs ..." +make -j3 showcommands $LIBS || die "ADT: Fail to build one of $LIBS." + +echo "Copying java libs to $DEST" -cd ../plugins/com.android.ide.eclipse.adt HOST=`uname` if [ "$HOST" == "Linux" ]; then - ln -svf ../../../../out/host/linux-x86/framework/jarutils.jar . - ln -svf ../../../../out/host/linux-x86/framework/androidprefs.jar . + for LIB in $LIBS; do + ln -svf $BACK/out/host/linux-x86/framework/$LIB.jar "$DEST/" + done + ln -svf $BACK/out/host/linux-x86/framework/kxml2-2.3.0.jar "$DEST/" + elif [ "$HOST" == "Darwin" ]; then - ln -svf ../../../../out/host/darwin-x86/framework/jarutils.jar . - ln -svf ../../../../out/host/darwin-x86/framework/androidprefs.jar . -elif [ "${HOST:0:6}" == "CYGWIN" ]; then + for LIB in $LIBS; do + ln -svf $BACK/out/host/darwin-x86/framework/$LIB.jar "$DEST/" + done + ln -svf $BACK/out/host/darwin-x86/framework/kxml2-2.3.0.jar "$DEST/" - DEVICE_DIR="../../../.." - echo "make java libs ..." - ( cd "$DEVICE_DIR" && - make -j3 showcommands jarutils androidprefs ) || \ - die "Define javac and retry." +elif [ "${HOST:0:6}" == "CYGWIN" ]; then + for LIB in $LIBS; do + cp -vf out/host/windows-x86/framework/$LIB.jar "$DEST/" + done - for DIR in "$PWD" ; do - echo "Copying java libs to $DIR" - for JAR in jarutils.jar androidprefs.jar ; do - cp -vf "$DEVICE_DIR/out/host/windows-x86/framework/$JAR" "$DIR" - done - done + if [ ! -f "$DEST/kxml2-2.3.0.jar" ]; then + cp -v "prebuilt/common/kxml2/kxml2-2.3.0.jar" "$DEST/" + fi - chmod a+rx *.jar + chmod -v a+rx "$DEST"/*.jar else - echo "Unsupported platform ($HOST). Nothing done." + echo "Unsupported platform ($HOST). Nothing done." fi diff --git a/eclipse/scripts/create_all_symlinks.sh b/eclipse/scripts/create_all_symlinks.sh index fc9766f..8508343 100755 --- a/eclipse/scripts/create_all_symlinks.sh +++ b/eclipse/scripts/create_all_symlinks.sh @@ -3,30 +3,25 @@ echo "### $0 executing" function die() { - echo "Error: $*" - exit 1 + echo "Error: $*" + exit 1 } -D="device/tools/eclipse/scripts" -if [ -d "../$D" ]; then - cd "../$D" -else - [ "${PWD: -28}" == "$D" ] || die "Please execute this from the $D directory" -fi +# CD to the top android directory +D=`dirname "$0"` +cd "$D/../../../../" + +DEST="development/tools/eclipse/scripts" set -e # fail early echo ; echo "### ADT ###" ; echo -./create_adt_symlinks.sh "$*" -echo ; echo "### COMMON ###" ; echo -./create_common_symlinks.sh "$*" -echo ; echo "### EDITORS ###" ; echo -./create_editors_symlinks.sh "$*" +$DEST/create_adt_symlinks.sh "$*" echo ; echo "### DDMS ###" ; echo -./create_ddms_symlinks.sh "$*" +$DEST/create_ddms_symlinks.sh "$*" echo ; echo "### TEST ###" ; echo -./create_test_symlinks.sh "$*" +$DEST/create_test_symlinks.sh "$*" echo ; echo "### BRIDGE ###" ; echo -./create_bridge_symlinks.sh "$*" +$DEST/create_bridge_symlinks.sh "$*" echo "### $0 done" diff --git a/eclipse/scripts/create_bridge_symlinks.sh b/eclipse/scripts/create_bridge_symlinks.sh index f01a89e..605ef63 100755 --- a/eclipse/scripts/create_bridge_symlinks.sh +++ b/eclipse/scripts/create_bridge_symlinks.sh @@ -1,47 +1,38 @@ #!/bin/bash function die() { - echo "Error: $*" - exit 1 + echo "Error: $*" + exit 1 } set -e # fail early -# This may run either from the //device directory or from the -# eclipse/script directory. Allow for both. -D="device/tools/eclipse/scripts" -[ -d "$D" ] && cd "$D" -[ -d "../$D" ] && cd "../$D" - -cd ../../layoutlib +# CD to the top android directory +D=`dirname "$0"` +cd "$D/../../../../" HOST=`uname` if [ "$HOST" == "Linux" ]; then - echo # nothing to do + echo # nothing to do + elif [ "$HOST" == "Darwin" ]; then - echo # nothing to do + echo # nothing to do + elif [ "${HOST:0:6}" == "CYGWIN" ]; then - if [ "x$1" == "x" ]; then - echo "Usage: $0 sdk/tools/lib/" - echo "Argument 1 should be the path to the jars you want to copy. " - echo " e.g. android_sdk_windows_NNN/tools/lib/ " - echo "This will be used to update layout.lib after it has been built here." - exit 1 - fi + if [ "x$1" == "x" ] || [ `basename "$1"` != "layoutlib.jar" ]; then + echo "Usage: $0 sdk/platforms/xxx/data/layoutlib.jar" + echo "Argument 1 should be the path to the layoutlib.jar that should be updated." + exit 1 + fi - DEVICE_DIR="../../" - echo "make java libs ..." - ( cd "$DEVICE_DIR" && - make -j3 showcommands layoutlib ninepatch ) || \ - die "Define javac and retry." + LIBS="layoutlib ninepatch" + echo "Make java libs: $LIBS" + make -j3 showcommands $LIBS || die "Bridge: Failed to build one of $LIBS." - for DIR in "$PWD" "$1" ; do - echo "Copying java libs to $DIR" - for JAR in ninepatch.jar layoutlib.jar ; do - cp -vf "$DEVICE_DIR/out/host/windows-x86/framework/$JAR" "$DIR" - done - done + echo "Updating your SDK in $1" + cp -vf "out/host/windows-x86/framework/layoutlib.jar" "$1" + chmod -v a+rx "$1" else - echo "Unsupported platform ($HOST). Nothing done." + echo "Unsupported platform ($HOST). Nothing done." fi diff --git a/eclipse/scripts/create_common_symlinks.sh b/eclipse/scripts/create_common_symlinks.sh deleted file mode 100755 index 7726afc..0000000 --- a/eclipse/scripts/create_common_symlinks.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -function die() { - echo "Error: $*" - exit 1 -} - -set -e # fail early - -# This may run either from the //device directory or from the -# eclipse/script directory. Allow for both. -D="device/tools/eclipse/scripts" -[ -d "$D" ] && cd "$D" -[ -d "../$D" ] && cd "../$D" - -cd ../plugins/com.android.ide.eclipse.common -HOST=`uname` -if [ "$HOST" == "Linux" ]; then - ln -svf ../../../../out/host/linux-x86/framework/sdkstats.jar . - ln -svf ../../../../out/host/linux-x86/framework/androidprefs.jar . -elif [ "$HOST" == "Darwin" ]; then - ln -svf ../../../../out/host/darwin-x86/framework/sdkstats.jar . - ln -svf ../../../../out/host/darwin-x86/framework/androidprefs.jar . -elif [ "${HOST:0:6}" == "CYGWIN" ]; then - - DEVICE_DIR="../../../.." - echo "make java libs ..." - ( cd "$DEVICE_DIR" && - make -j3 sdkstats androidprefs ) || \ - die "Define javac and retry." - - for DIR in "$PWD" ; do - echo "Copying java libs to $DIR" - for JAR in sdkstats.jar androidprefs.jar ; do - cp -vf "$DEVICE_DIR/out/host/windows-x86/framework/$JAR" "$DIR" - chmod a+rx "$DIR/$JAR" - done - done - -else - echo "Unsupported platform ($HOST). Nothing done." -fi - diff --git a/eclipse/scripts/create_ddms_symlinks.sh b/eclipse/scripts/create_ddms_symlinks.sh index 5b2e45b..276cf9b 100755 --- a/eclipse/scripts/create_ddms_symlinks.sh +++ b/eclipse/scripts/create_ddms_symlinks.sh @@ -4,58 +4,76 @@ # Run this from device/tools/eclipse/scripts #----------------------------------------------------------------------------| -CMD="ln -svf" -DIR="ln -svf" +set -e + HOST=`uname` if [ "${HOST:0:6}" == "CYGWIN" ]; then - CMD="cp -rvf" - DIR="rsync -avW --delete-after" + # We can't use symlinks under Cygwin + + function cpfile { # $1=dest $2=source + cp -fv $2 $1/ + } + + function cpdir() { # $1=dest $2=source + rsync -avW --delete-after $2 $1 + } + +else + # For all other systems which support symlinks + + # computes the "reverse" path, e.g. "a/b/c" => "../../.." + function back() { + echo $1 | sed 's@[^/]*@..@g' + } + + function cpfile { # $1=dest $2=source + ln -svf `back $1`/$2 $1/ + } + + function cpdir() { # $1=dest $2=source + ln -svf `back $1`/$2 $1 + } fi -cd ../plugins/com.android.ide.eclipse.ddms -mkdir -p libs -cd libs -$CMD ../../../../../prebuilt/common/jfreechart/jcommon-1.0.12.jar . -$CMD ../../../../../prebuilt/common/jfreechart/jfreechart-1.0.9.jar . -$CMD ../../../../../prebuilt/common/jfreechart/jfreechart-1.0.9-swt.jar . - -cd ../src/com/android -$DIR ../../../../../../ddms/libs/ddmlib/src/com/android/ddmlib . -$DIR ../../../../../../ddms/libs/ddmuilib/src/com/android/ddmuilib . - -# goes back to the icons directory -cd ../../../icons/ -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/debug-attach.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/debug-wait.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/debug-error.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/device.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/emulator.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/heap.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/thread.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/empty.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/warning.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/d.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/e.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/i.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/v.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/w.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/add.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/delete.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/edit.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/save.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/push.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/pull.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/clear.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/up.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/down.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/gc.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/halt.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/load.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/importBug.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/play.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/pause.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/forward.png . -$CMD ../../../../ddms/libs/ddmuilib/src/resources/images/backward.png . +# CD to the top android directory +D=`dirname "$0"` +cd "$D/../../../../" + + +BASE="development/tools/eclipse/plugins/com.android.ide.eclipse.ddms" + +DEST=$BASE/libs +mkdir -p $DEST +for i in prebuilt/common/jfreechart/*.jar; do + cpfile $DEST $i +done +DEST=$BASE/src/com/android +mkdir -p $DEST +for i in development/tools/ddms/libs/ddmlib/src/com/android/ddmlib \ + development/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib ; do + cpdir $DEST $i +done +DEST=$BASE/icons +mkdir -p $DEST +for i in \ + add.png \ + backward.png \ + clear.png \ + d.png debug-attach.png debug-error.png debug-wait.png delete.png device.png down.png \ + e.png edit.png empty.png emulator.png \ + forward.png \ + gc.png \ + heap.png halt.png \ + i.png importBug.png \ + load.png \ + pause.png play.png pull.png push.png \ + save.png \ + thread.png \ + up.png \ + v.png \ + w.png warning.png ; do + cpfile $DEST development/tools/ddms/libs/ddmuilib/src/resources/images/$i +done diff --git a/eclipse/scripts/create_editors_symlinks.sh b/eclipse/scripts/create_editors_symlinks.sh deleted file mode 100755 index 2f26ac4..0000000 --- a/eclipse/scripts/create_editors_symlinks.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -function die() { - echo "Error: $*" - exit 1 -} - -cd ../plugins/com.android.ide.eclipse.editors -HOST=`uname` -if [ "$HOST" == "Linux" ]; then - ln -svf ../../../../out/host/linux-x86/framework/layoutlib_api.jar . - ln -svf ../../../../out/host/linux-x86/framework/layoutlib_utils.jar . - ln -svf ../../../../out/host/linux-x86/framework/kxml2-2.3.0.jar . - ln -svf ../../../../out/host/linux-x86/framework/ninepatch.jar . -elif [ "$HOST" == "Darwin" ]; then - ln -svf ../../../../out/host/darwin-x86/framework/layoutlib_api.jar . - ln -svf ../../../../out/host/darwin-x86/framework/layoutlib_utils.jar . - ln -svf ../../../../out/host/darwin-x86/framework/kxml2-2.3.0.jar . - ln -svf ../../../../out/host/darwin-x86/framework/ninepatch.jar . -elif [ "${HOST:0:6}" == "CYGWIN" ]; then - set -e # fail early - DEVICE_DIR="../../../../" - echo "make java libs ..." - ( cd "$DEVICE_DIR" && - make -j3 showcommands layoutlib_api layoutlib_utils ninepatch ) || \ - die "Define javac and 'make layoutlib_api ninepatch' from device." - - echo "Copying java libs to $PWD" - for JAR in layoutlib_api.jar layoutlib_utils.jar ninepatch.jar ; do - cp -vf "$DEVICE_DIR/out/host/windows-x86/framework/$JAR" . - done - if [ ! -f "./kxml2-2.3.0.jar" ]; then - cp -v $DEVICE_DIR/prebuilt/common/kxml2/kxml2-2.3.0.jar . - chmod -v a+rx *.jar - fi - -else - echo "Unsupported platform ($HOST). Nothing done." -fi - diff --git a/eclipse/scripts/create_test_symlinks.sh b/eclipse/scripts/create_test_symlinks.sh index 110539a..1479e04 100755 --- a/eclipse/scripts/create_test_symlinks.sh +++ b/eclipse/scripts/create_test_symlinks.sh @@ -1,24 +1,48 @@ #!/bin/bash -cd ../plugins/com.android.ide.eclipse.tests + +set -e + +# CD to the top android directory +D=`dirname "$0"` +cd "$D/../../../../" + +# computes relative ".." paths from $1 to here (in /android) +function back() { + echo $1 | sed 's@[^/]*@..@g' +} + +BASE="development/tools/eclipse/plugins/com.android.ide.eclipse.tests" +DEST=$BASE +BACK=`back $DEST` + + HOST=`uname` if [ "$HOST" == "Linux" ]; then - ln -svf ../../../../out/host/linux-x86/framework/kxml2-2.3.0.jar . + DIR="ln -svf" + ln -svf $BACK/out/host/linux-x86/framework/kxml2-2.3.0.jar "$DEST/" + elif [ "$HOST" == "Darwin" ]; then - ln -svf ../../../../out/host/darwin-x86/framework/kxml2-2.3.0.jar . + DIR="ln -svf" + ln -svf $BACK/out/host/darwin-x86/framework/kxml2-2.3.0.jar "$DEST/" + elif [ "${HOST:0:6}" == "CYGWIN" ]; then - if [ "x$1" == "x" ]; then - echo "Usage: $0 <path to jars>" - echo "Argument 1 should be the path to the jars you want to copy. " - echo " e.g. android_sdk_windows_NNN/tools/lib/ " - echo "(since you can't rebuild them under Windows, you need prebuilt ones " - echo " from an SDK drop or a Linux/Mac)" - exit 1 - fi - if [ ! -f "kxml2-2.3.0.jar" ]; then - wget -O "kxml2-2.3.0.jar" "http://internap.dl.sourceforge.net/sourceforge/kxml/kxml2-2.3.0.jar" - chmod a+rx *.jar - fi + DIR="rsync -avW --delete-after" + JAR="kxml2-2.3.0.jar" + if [ ! -f "$DEST/$JAR" ]; then + # Get the jar from ADT if we can, otherwise download it. + if [ -f "$DEST/../com.android.ide.eclipse.adt/$JAR" ]; then + cp "$DEST/../com.android.ide.eclipse.adt/$JAR" "$DEST/$JAR" + else + wget -O "$DEST/$JAR" "http://internap.dl.sourceforge.net/sourceforge/kxml/$JAR" + fi + chmod a+rx "$DEST/$JAR" + fi else - echo "Unsupported platform ($HOST). Nothing done." + echo "Unsupported platform ($HOST). Nothing done." fi +# create link to ddmlib tests +DEST=$BASE/unittests/com/android +BACK=`back $DEST` +$DIR $BACK/development/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib $DEST/ + diff --git a/eclipse/scripts/update_version.sh b/eclipse/scripts/update_version.sh new file mode 100644 index 0000000..a288965 --- /dev/null +++ b/eclipse/scripts/update_version.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +OLD="$1" +NEW="$2" + +# sanity check in input args +if [ -z "$OLD" ] || [ -z "$NEW" ]; then + cat <<EOF +Usage: $0 <old> <new> +Changes the ADT plugin revision number. +Example: + cd tools/eclipse + scripts/update_version.sh 0.1.2 0.2.3 +EOF + exit 1 +fi + +# sanity check on current dir +if [ `basename "$PWD"` != "eclipse" ]; then + echo "Please run this from tools/eclipse." + exit 1 +fi + +# quote dots for regexps +OLD="${OLD//./\.}" +NEW="${NEW//./\.}" + +# Find all the files with the old pattern, except changes.txt and +# p4 edit them. Skip that if there's no p4 in path. +if which g4 1>/dev/null 2>/dev/null ; then + grep -rl "$OLD" * | grep -E "\.xml$|\.MF$" | xargs -n 5 g4 edit +fi + +# Now find the same files but this time use sed to replace in-place with +# the new pattern. Old files get backuped with the .old extension. +grep -rl "$OLD" * | grep -E "\.xml$|\.MF$" | xargs -n 1 sed -i.old "s/$OLD/$NEW/g" + diff --git a/eclipse/sites/external/site.xml b/eclipse/sites/external/site.xml index be123f6..e98a988 100644 --- a/eclipse/sites/external/site.xml +++ b/eclipse/sites/external/site.xml @@ -3,10 +3,7 @@ <description url="https://dl-ssl.google.com/android/eclipse/"> Update Site for Android Development Toolkit </description> - <feature url="features/com.android.ide.eclipse.adt_0.8.1.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.8.1.qualifier"> - <category name="developer"/> - </feature> - <feature url="features/com.android.ide.eclipse.editors_0.8.1.qualifier.jar" id="com.android.ide.eclipse.editors" version="0.8.1.qualifier"> + <feature url="features/com.android.ide.eclipse.adt_0.9.0.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.9.0.qualifier"> <category name="developer"/> </feature> <category-def name="developer" label="Developer Tools"> diff --git a/eclipse/sites/internal/site.xml b/eclipse/sites/internal/site.xml index 3263886..a7ef628 100644 --- a/eclipse/sites/internal/site.xml +++ b/eclipse/sites/internal/site.xml @@ -3,17 +3,10 @@ <description url="https://android.corp.google.com/adt/"> Update Site for Android Development Toolkit </description> - <feature url="features/com.android.ide.eclipse.adt_0.8.1.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.8.1.qualifier"> + <feature url="features/com.android.ide.eclipse.adt_0.9.0.qualifier.jar" id="com.android.ide.eclipse.adt" version="0.9.0.qualifier"> <category name="developer"/> </feature> - <feature url="features/com.android.ide.eclipse.editors_0.8.1.qualifier.jar" id="com.android.ide.eclipse.editors" version="0.8.1.qualifier"> - <category name="developer"/> - <category name="platform"/> - </feature> - <feature url="features/com.android.ide.eclipse.platform_0.8.1.qualifier.jar" id="com.android.ide.eclipse.platform" version="0.8.1.qualifier"> - <category name="platform"/> - </feature> - <feature url="features/com.android.ide.eclipse.tests_0.8.1.qualifier.jar" id="com.android.ide.eclipse.tests" version="0.8.1.qualifier"> + <feature url="features/com.android.ide.eclipse.tests_0.9.0.qualifier.jar" id="com.android.ide.eclipse.tests" version="0.9.0.qualifier"> <category name="test"/> </feature> <category-def name="developer" label="Application Developer Tools"> @@ -21,11 +14,6 @@ Features that add Android support to Eclipse for application developers. </description> </category-def> - <category-def name="platform" label="Platform Developer Tools"> - <description> - Features that add Android support to Eclipse for platform developers. - </description> - </category-def> <category-def name="test" label="Plugin Developer Tests"> <description> Tests for the other Android plugins |