From 62da4d93575023bc3740c8357239ba713d025a4b Mon Sep 17 00:00:00 2001 From: Alexey Zaytsev Date: Wed, 22 Oct 2008 02:03:57 +0400 Subject: [PATCH 01/39] Add a few missing headers Fixes build with gcc 4.3.2 on Debian. Signed-off-by: Alexey Zaytsev --- emulator/qtools/dmtrace.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/emulator/qtools/dmtrace.cpp b/emulator/qtools/dmtrace.cpp index a27193a71..3efc10a53 100644 --- a/emulator/qtools/dmtrace.cpp +++ b/emulator/qtools/dmtrace.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "dmtrace.h" static const short kVersion = 2; From 3c290fd186668ce9cf89b1c100ffa1167072cfb1 Mon Sep 17 00:00:00 2001 From: Andy Stadler Date: Tue, 21 Oct 2008 15:04:50 -0700 Subject: [PATCH 02/39] Updated .classpath to use with Eclipse IDE. --- ide/eclipse/.classpath | 167 ++++++++++++++++------------------------- 1 file changed, 64 insertions(+), 103 deletions(-) diff --git a/ide/eclipse/.classpath b/ide/eclipse/.classpath index e1a4ff877..a6c264712 100644 --- a/ide/eclipse/.classpath +++ b/ide/eclipse/.classpath @@ -1,56 +1,52 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -59,77 +55,42 @@ - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + From 88b4291efb3c1be996228f2c0d6f35f9d2e77c2f Mon Sep 17 00:00:00 2001 From: Evan JIANG Date: Thu, 23 Oct 2008 00:12:40 +0800 Subject: [PATCH 03/39] Evan JIANG: Fix build bug when "make sdk" If you run "make sdk" under the root path, there would be an error: development/build/sdk.atree:122: couldn't locate source file: development/emulator/prebuilt/android-arm/kernel-qemu make: *** [out/host/darwin-x86/sdk/android-sdk_eng.Evan_mac-x86.zip] Error 44 I guess the kernel-qemu path is wrong in the sdk.atree file. With this patch, "make sdk" works ok. --- build/sdk.atree | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/sdk.atree b/build/sdk.atree index 113210ed0..c3a6ec35c 100644 --- a/build/sdk.atree +++ b/build/sdk.atree @@ -119,7 +119,7 @@ system.img tools/lib/images/system.img ramdisk.img tools/lib/images/ramdisk.img userdata.img tools/lib/images/userdata.img userdata.img tools/lib/images/userdata.img -development/emulator/prebuilt/android-arm/kernel-qemu tools/lib/images/kernel-qemu +prebuilt/android-arm/kernel/kernel-qemu tools/lib/images/kernel-qemu # emulator skins development/emulator/skins/HVGA tools/lib/images/skins/HVGA From 69e138ba95a2fe310b94a0f3db03533a3f02ec13 Mon Sep 17 00:00:00 2001 From: Alexey Tarasov Date: Sun, 9 Nov 2008 11:00:59 +1000 Subject: [PATCH 04/39] changed main class name in manifest file to correct one --- tools/traceview/etc/manifest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/traceview/etc/manifest.txt b/tools/traceview/etc/manifest.txt index c3c2aec97..6cdbc7e5e 100644 --- a/tools/traceview/etc/manifest.txt +++ b/tools/traceview/etc/manifest.txt @@ -1,2 +1,2 @@ -Main-Class: com.google.traceview.MainWindow +Main-Class: com.android.traceview.MainWindow Class-Path: swt.jar org.eclipse.equinox.common_3.2.0.v20060603.jar org.eclipse.jface_3.2.0.I20060605-1400.jar org.eclipse.core.commands_3.2.0.I20060605-1400.jar From bc4b026621340d4f817c7553cc947fda819c9a5b Mon Sep 17 00:00:00 2001 From: Muthu Ramadoss Date: Tue, 11 Nov 2008 10:59:24 +0530 Subject: [PATCH 05/39] Changed srcdir="." to srcdir="${srcdir}" in scripts/build.template This change makes sure the srcdir is pointing to only the java source folder and not recursively compile all source folders under the current folder. This is useful in having multiple java source folders for main, tests etc., Only the compile target is being affected by this change. NOTE: Now you can have project/tests and work with tests as a separate android project, only for testing. In any case pointing to source explicitly makes sure no side affects arise by recursive compiling. --- tools/scripts/build.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/scripts/build.template b/tools/scripts/build.template index 0081c336d..74e845314 100644 --- a/tools/scripts/build.template +++ b/tools/scripts/build.template @@ -155,7 +155,7 @@ From 4dd550143e9b047c2c30d3d0ab3876e73ca0c36a Mon Sep 17 00:00:00 2001 From: Muthu Ramadoss Date: Thu, 13 Nov 2008 15:36:09 +0530 Subject: [PATCH 06/39] ENHANCEMENT: activitycreator generates 'tests' project for instrumentation. activitycreator script is enhanced to generate 'tests' project along with the main project. The current behavior of generating Activity is extended by generating ActivityTest in 'tests/src' folder. The 'tests' folder follows the example provided asis in 'ApiDemos'. ApiDemos was used as reference project to mimic the project layout for building tests using Instrumentation. From 'tests' project, type: "adb shell am instrument -w [your.package].tests/android.test.InstrumentationTestRunner" to run all tests using Android InstrumentationTestRunner. NOTE: 'tests' is a separate AndroidProject by all means. It has its own AndroidManifest.xml, build.xml, src, res etc., AMEND: Fixed style issues, javadoc Fixed build.template to generate tests/build.xml Removed build.tests.template since its obsolete now. --- build/sdk.atree | 2 + .../activitycreator/ActivityCreator.java | 116 +++++++++++++++--- tools/scripts/AndroidManifest.tests.template | 21 ++++ tools/scripts/build.template | 9 +- tools/scripts/java_tests_file.template | 21 ++++ 5 files changed, 151 insertions(+), 18 deletions(-) create mode 100644 tools/scripts/AndroidManifest.tests.template create mode 100644 tools/scripts/java_tests_file.template diff --git a/build/sdk.atree b/build/sdk.atree index c3a6ec35c..ab6076e98 100644 --- a/build/sdk.atree +++ b/build/sdk.atree @@ -41,6 +41,7 @@ obj/framework.aidl tools/lib/framework.aidl # sdk scripts development/tools/scripts/AndroidManifest.template tools/lib/AndroidManifest.template development/tools/scripts/AndroidManifest.alias.template tools/lib/AndroidManifest.alias.template +development/tools/scripts/AndroidManifest.tests.template tools/lib/AndroidManifest.tests.template development/tools/scripts/build.template tools/lib/build.template development/tools/scripts/build.alias.template tools/lib/build.alias.template development/tools/scripts/default.properties.template tools/lib/default.properties.template @@ -48,6 +49,7 @@ development/tools/scripts/iml.template tools/lib/iml.template development/tools/scripts/ipr.template tools/lib/ipr.template development/tools/scripts/iws.template tools/lib/iws.template development/tools/scripts/java_file.template tools/lib/java_file.template +development/tools/scripts/java_tests_file.template tools/lib/java_tests_file.template development/tools/scripts/layout.template tools/lib/layout.template development/tools/scripts/strings.template tools/lib/strings.template development/tools/scripts/alias.template tools/lib/alias.template diff --git a/tools/activitycreator/src/com/android/activitycreator/ActivityCreator.java b/tools/activitycreator/src/com/android/activitycreator/ActivityCreator.java index c71ca6e7c..44edf49f3 100644 --- a/tools/activitycreator/src/com/android/activitycreator/ActivityCreator.java +++ b/tools/activitycreator/src/com/android/activitycreator/ActivityCreator.java @@ -266,16 +266,37 @@ public class ActivityCreator { * Installs a destination file that is based on the template file at source. * For each match of each key in keywords will be replaced with its * corresponding value in the destination file. - * + * + * Invokes ActivityCreator#installTemplate(String, String, java.util.Map, boolean, String) with + * the main project output directory (#mOutDir) as the last argument. + * * @param source the path to the source template file * @param dest the path to the destination file * @param keywords in the destination file, the keys will be replaced by their values - * @param force True to force writing the file even if it already exists + * @param force True to force writing the file even if it already exists + * + * @see ActivityCreator#installTemplate(String, String, java.util.Map, boolean, String) */ private void installTemplate(String source, String dest, Map keywords, boolean force) { + installTemplate(source, dest, keywords, force, mOutDir); + } + + /** + * Installs a destination file that is based on the template file at source. + * For each match of each key in keywords will be replaced with its + * corresponding value in the destination file. + * + * @param source the path to the source template file + * @param dest the path to the destination file + * @param keywords in the destination file, the keys will be replaced by their values + * @param force True to force writing the file even if it already exists + * @param outDir the output directory to copy the template file to + */ + private void installTemplate(String source, String dest, + Map keywords, boolean force, String outDir) { final String sourcePath = mLibDir + File.separator + source; - final String destPath = mOutDir + File.separator + dest; + final String destPath = outDir + File.separator + dest; final File destPathFile = new File(destPath); if (!force && destPathFile.exists()) { @@ -283,27 +304,27 @@ public class ActivityCreator { destPathFile.getName()); return; } - + try { BufferedWriter out = new BufferedWriter(new FileWriter(destPathFile)); BufferedReader in = new BufferedReader(new FileReader(sourcePath)); String line; - + while ((line = in.readLine()) != null) { for (String key : keywords.keySet()) { line = line.replace(key, keywords.get(key)); } - + out.write(line); out.newLine(); } - + out.close(); in.close(); } catch (Exception e) { printHelpAndExit("ERROR: Could not access %1$s: %2$s", destPath, e.getMessage()); } - + println("Added file %1$s", destPath); } @@ -329,12 +350,14 @@ public class ActivityCreator { private void setupProject() { String packageName = null; String activityName = null; + String activityTestName = null; try { /* Grab package and Activity names */ int lastPeriod = mPackageFull.lastIndexOf('.'); packageName = mPackageFull.substring(0, lastPeriod); if (lastPeriod < mPackageFull.length() - 1) { activityName = mPackageFull.substring(lastPeriod+1); + activityTestName = activityName + "Test"; } if (packageName.indexOf('.') == -1) { @@ -346,9 +369,15 @@ public class ActivityCreator { println("Package: %1$s", packageName); println("Output directory: %1$s", mOutDir); + String testsOutDir = mOutDir + File.separator + "tests"; + println("Tests directory: %1$s", testsOutDir); + if (activityName != null) { println("Activity name: %1$s", activityName); } + if (activityTestName != null) { + println("ActivityTest name: %1$s", activityTestName); + } final HashMap keywords = createBaseKeywordMap(); keywords.put("PACKAGE", packageName); @@ -370,8 +399,17 @@ public class ActivityCreator { installTemplate("java_file.template", srcDir + File.separator + activityName + ".java", keywords); } + /* Make ActivityTest java file */ + createDirs(srcDir, testsOutDir); + if (isDirEmpty(srcDir, "Java", testsOutDir) && activityTestName != null) { + installTemplate("java_tests_file.template", srcDir + File.separator + + activityTestName + ".java", keywords, false, testsOutDir); + } createDirs("bin"); createDirs("libs"); + createDirs("bin", testsOutDir); + createDirs("libs", testsOutDir); + createDirs("res", testsOutDir); /* Make res files */ final String valuesDir = "res" + File.separator + "values"; @@ -393,7 +431,16 @@ public class ActivityCreator { keywords); installTemplate("build.template", "build.xml", keywords); - installTemplate("default.properties.template", "default.properties", keywords, true /*force*/); + installTemplate("default.properties.template", "default.properties", + keywords, true /*force*/); + + /* Make AndroidManifest.xml and build.xml files for tests*/ + installTemplate("AndroidManifest.tests.template", "AndroidManifest.xml", + keywords, false, testsOutDir); + + installTemplate("build.template", "build.xml", keywords, false, testsOutDir); + installTemplate("default.properties.template", "default.properties", + keywords, true /*force*/, testsOutDir); if (mIde.equals("intellij")) { /* IntelliJ files */ @@ -500,10 +547,27 @@ public class ActivityCreator { /** * Creates the path in the output directory along with any parent paths * that don't exist. + * + * Invokes ActivityCreator#createDirs(String, String) with + * the main project output directory (#mOutDir) as the last argument. + * * @param path the directory out/path that is created. + * + * @see com.android.activitycreator.ActivityCreator#createDirs(String, String) */ public void createDirs(String path) { - final File pathFile = new File(mOutDir + File.separator + path); + createDirs(path, mOutDir); + } + + /** + * Creates the path in the output directory along with any parent paths + * that don't exist. + * + * @param path the directory out/path that is created. + * @param dir the directory in which the path to be created + */ + public void createDirs(String path, String dir) { + final File pathFile = new File(dir + File.separator + path); boolean existedBefore = true; if (!pathFile.exists()) { @@ -529,28 +593,45 @@ public class ActivityCreator { } } } - + /** * Checks whether the path in the output directory is empty - * + * + * Invokes ActivityCreator#isDirEmpty(String, String, String) with + * the main project output directory (#mOutDir) as the last argument. + * * @param path the out/path directory that is checked * @param message the logical name for what this path points to (used in * warning message) * @return whether the directory is empty + * @see com.android.activitycreator.ActivityCreator#isDirEmpty(String, String, String) */ public boolean isDirEmpty(String path, String message) { - File pathFile = new File(mOutDir + File.separator + path); - + return isDirEmpty(path, message, mOutDir); + } + + /** + * Checks whether the path in the output directory is empty + * + * @param path the out/path directory that is checked + * @param message the logical name for what this path points to (used in + * warning message) + * @param outDir the output director to check + * @return whether the directory is empty + */ + public boolean isDirEmpty(String path, String message, String outDir) { + File pathFile = new File(outDir + File.separator + path); + String[] pathListing = pathFile.list(); if ((pathListing != null) && (pathListing.length > 0)) { println("WARNING: There are already some %1$s files present. None will be created!", message); return false; } - + return true; } - + /** * Strips the string of beginning and trailing characters (multiple * characters will be stripped, example stripString("..test...", '.') @@ -588,9 +669,10 @@ public class ActivityCreator { * Returns true if the project is an alias project. *

* Alias projects require both the --url and the --label options. + * @return boolean true if the project requested is an alias project */ private boolean isAliasProject() { - return (isStringEmpty(mAliasData) == false && isStringEmpty(mApplicationLabel) == false); + return (!isStringEmpty(mAliasData) && !isStringEmpty(mApplicationLabel)); } /** diff --git a/tools/scripts/AndroidManifest.tests.template b/tools/scripts/AndroidManifest.tests.template new file mode 100644 index 000000000..1f7d827fe --- /dev/null +++ b/tools/scripts/AndroidManifest.tests.template @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/tools/scripts/build.template b/tools/scripts/build.template index 74e845314..9aeaf9869 100644 --- a/tools/scripts/build.template +++ b/tools/scripts/build.template @@ -20,7 +20,8 @@ - + + @@ -60,6 +61,11 @@ else="${basedir}/${outdir-classes}" > + + + @@ -160,6 +166,7 @@ bootclasspath="${android-jar}"> + diff --git a/tools/scripts/java_tests_file.template b/tools/scripts/java_tests_file.template new file mode 100644 index 000000000..7781a33c3 --- /dev/null +++ b/tools/scripts/java_tests_file.template @@ -0,0 +1,21 @@ +package PACKAGE; + +import android.test.ActivityInstrumentationTestCase; + +/** + * This is a simple framework for a test of an Application. See + * {@link android.test.ApplicationTestCase ApplicationTestCase} for more information on + * how to write and extend Application tests. + *

+ * To run this test, you can type: + * adb shell am instrument -w \ + * -e class PACKAGE.ACTIVITY_NAMETest \ + * PACKAGE.tests/android.test.InstrumentationTestRunner + */ +public class ACTIVITY_NAMETest extends ActivityInstrumentationTestCase { + + public ACTIVITY_NAMETest() { + super("PACKAGE", ACTIVITY_NAME.class); + } + +} \ No newline at end of file From 991c4bdd4def55efb1e0fb922befca3fe7e54656 Mon Sep 17 00:00:00 2001 From: raphael Date: Wed, 3 Dec 2008 21:13:12 -0800 Subject: [PATCH 07/39] Fix eclipse setup scripts. The fix will work for Linux and Mac, however for Windows the DDMS icons will have an invalid path. I can address the Windows DDMS issue in a next CL if necessary by merging in a different CL from upstream. --- tools/eclipse/scripts/create_adt_symlinks.sh | 56 +++++++------- tools/eclipse/scripts/create_all_symlinks.sh | 27 ++++--- .../eclipse/scripts/create_bridge_symlinks.sh | 57 +++++++------- .../eclipse/scripts/create_common_symlinks.sh | 56 +++++++------- tools/eclipse/scripts/create_ddms_symlinks.sh | 77 ++++++++----------- .../scripts/create_editors_symlinks.sh | 63 ++++++++------- 6 files changed, 168 insertions(+), 168 deletions(-) diff --git a/tools/eclipse/scripts/create_adt_symlinks.sh b/tools/eclipse/scripts/create_adt_symlinks.sh index 49744323a..4bf127175 100755 --- a/tools/eclipse/scripts/create_adt_symlinks.sh +++ b/tools/eclipse/scripts/create_adt_symlinks.sh @@ -1,42 +1,44 @@ #!/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="jarutils androidprefs" + +echo "make java libs ..." +make -j3 showcommands $LIBS || die "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 + elif [ "$HOST" == "Darwin" ]; then - ln -svf ../../../../out/host/darwin-x86/framework/jarutils.jar . - ln -svf ../../../../out/host/darwin-x86/framework/androidprefs.jar . + for LIB in $LIBS; do + ln -svf $BACK/out/host/darwin-x86/framework/$LIB.jar "$DEST/" + done + elif [ "${HOST:0:6}" == "CYGWIN" ]; then + for LIB in $LIBS; do + cp -vf $BACK/out/host/windows-x86/framework/$LIB.jar "$DEST/" + done - DEVICE_DIR="../../../.." - echo "make java libs ..." - ( cd "$DEVICE_DIR" && - make -j3 showcommands jarutils androidprefs ) || \ - die "Define javac and retry." - - 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 - - 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/tools/eclipse/scripts/create_all_symlinks.sh b/tools/eclipse/scripts/create_all_symlinks.sh index fc9766f9c..6aec4fa52 100755 --- a/tools/eclipse/scripts/create_all_symlinks.sh +++ b/tools/eclipse/scripts/create_all_symlinks.sh @@ -3,30 +3,29 @@ 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 "$*" +$DEST/create_adt_symlinks.sh "$*" echo ; echo "### COMMON ###" ; echo -./create_common_symlinks.sh "$*" +$DEST/create_common_symlinks.sh "$*" echo ; echo "### EDITORS ###" ; echo -./create_editors_symlinks.sh "$*" +$DEST/create_editors_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/tools/eclipse/scripts/create_bridge_symlinks.sh b/tools/eclipse/scripts/create_bridge_symlinks.sh index f01a89ed3..550e84fd9 100755 --- a/tools/eclipse/scripts/create_bridge_symlinks.sh +++ b/tools/eclipse/scripts/create_bridge_symlinks.sh @@ -1,47 +1,44 @@ #!/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" ]; 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 - DEVICE_DIR="../../" - echo "make java libs ..." - ( cd "$DEVICE_DIR" && - make -j3 showcommands layoutlib ninepatch ) || \ - die "Define javac and retry." + echo "make java libs ..." + LIBS="layoutlib ninepatch" + make -j3 showcommands $LIBS || die "Define javac and retry." - 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 + for DIR in frameworks/base/tools/layoutlib "$1" ; do + echo "Copying java libs to $DIR" + for LIB in $LIBS; do + cp -vf "out/host/windows-x86/framework/$LIB.jar" "$DIR" + done + chmod -v a+rx "$LIB"/*.jar + done else - echo "Unsupported platform ($HOST). Nothing done." + echo "Unsupported platform ($HOST). Nothing done." fi diff --git a/tools/eclipse/scripts/create_common_symlinks.sh b/tools/eclipse/scripts/create_common_symlinks.sh index 7726afc58..a9e8a4348 100755 --- a/tools/eclipse/scripts/create_common_symlinks.sh +++ b/tools/eclipse/scripts/create_common_symlinks.sh @@ -1,42 +1,44 @@ #!/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.common" +# computes "../.." from DEST to here (in /android) +BACK=`echo $DEST | sed 's@[^/]*@..@g'` + +LIBS="sdkstats androidprefs" + +echo "make java libs ..." +make -j3 showcommands $LIBS || die "Fail to build one of $LIBS." + +echo "Copying java libs to $DEST" -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 . + for LIB in $LIBS; do + ln -svf $BACK/out/host/linux-x86/framework/$LIB.jar "$DEST/" + done + elif [ "$HOST" == "Darwin" ]; then - ln -svf ../../../../out/host/darwin-x86/framework/sdkstats.jar . - ln -svf ../../../../out/host/darwin-x86/framework/androidprefs.jar . + for LIB in $LIBS; do + ln -svf $BACK/out/host/darwin-x86/framework/$LIB.jar "$DEST/" + done + elif [ "${HOST:0:6}" == "CYGWIN" ]; then + for LIB in $LIBS; do + cp -vf $BACK/out/host/windows-x86/framework/$LIB.jar "$DEST/" + done - 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 - + chmod -v a+rx "$DEST"/*.jar else - echo "Unsupported platform ($HOST). Nothing done." + echo "Unsupported platform ($HOST). Nothing done." fi diff --git a/tools/eclipse/scripts/create_ddms_symlinks.sh b/tools/eclipse/scripts/create_ddms_symlinks.sh index 5b2e45bf8..6c2421612 100755 --- a/tools/eclipse/scripts/create_ddms_symlinks.sh +++ b/tools/eclipse/scripts/create_ddms_symlinks.sh @@ -8,54 +8,43 @@ CMD="ln -svf" DIR="ln -svf" HOST=`uname` if [ "${HOST:0:6}" == "CYGWIN" ]; then - CMD="cp -rvf" - DIR="rsync -avW --delete-after" + CMD="cp -rvf" + DIR="rsync -avW --delete-after" 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 to the top android directory +D=`dirname "$0"` +cd "$D/../../../../" -cd ../src/com/android -$DIR ../../../../../../ddms/libs/ddmlib/src/com/android/ddmlib . -$DIR ../../../../../../ddms/libs/ddmuilib/src/com/android/ddmuilib . +# computes relative ".." paths from $1 to here (in /android) +function back() { + echo $1 | sed 's@[^/]*@..@g' +} -# 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 . +BASE="development/tools/eclipse/plugins/com.android.ide.eclipse.ddms" +DEST=$BASE/libs +mkdir -p $D +BACK=`back $DEST` +for i in prebuilt/common/jfreechart/*.jar; do + $CMD $BACK/$i $DEST/ +done +DEST=$BASE/src/com/android +BACK=`back $DEST` +for i in development/tools/ddms/libs/ddmlib/src/com/android/ddmlib \ + development/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib ; do + $DIR $BACK/$i $DEST/ +done + +DEST=$BASE/icons +BACK=`back $DEST` + +for i in debug-attach.png debug-wait.png debug-error.png device.png emulator.png \ + heap.png thread.png empty.png warning.png d.png e.png i.png \ + v.png w.png add.png delete.png edit.png save.png push.png pull.png \ + clear.png up.png down.png gc.png halt.png load.png importBug.png \ + play.png pause.png forward.png backward.png ; do + $CMD $BACK/development/tools/ddms/libs/ddmuilib/src/resources/images/$i $DEST/ +done diff --git a/tools/eclipse/scripts/create_editors_symlinks.sh b/tools/eclipse/scripts/create_editors_symlinks.sh index 2f26ac42d..1a534cb47 100755 --- a/tools/eclipse/scripts/create_editors_symlinks.sh +++ b/tools/eclipse/scripts/create_editors_symlinks.sh @@ -1,39 +1,50 @@ #!/bin/bash function die() { - echo "Error: $*" - exit 1 + echo "Error: $*" + exit 1 } -cd ../plugins/com.android.ide.eclipse.editors +set -e # fail early + +# CD to the top android directory +D=`dirname "$0"` +cd "$D/../../../../" + +DEST="development/tools/eclipse/plugins/com.android.ide.eclipse.editors" +# computes "../.." from DEST to here (in /android) +BACK=`echo $DEST | sed 's@[^/]*@..@g'` + +LIBS="layoutlib_api layoutlib_utils ninepatch" + +echo "make java libs ..." +make -j3 showcommands $LIBS || die "Fail to build one of $LIBS." + +echo "Copying java libs to $DEST" + 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 . + 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/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 . + 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/" + 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." + for LIB in $LIBS; do + cp -vf $BACK/out/host/windows-x86/framework/$LIB.jar "$DEST/" + done - 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 + if [ ! -f "$DEST/kxml2-2.3.0.jar" ]; then + cp -v "prebuilt/common/kxml2/kxml2-2.3.0.jar" "$DEST/" + fi + chmod -v a+rx "$DEST"/*.jar else - echo "Unsupported platform ($HOST). Nothing done." + echo "Unsupported platform ($HOST). Nothing done." fi From 70af177cf323a6ed7e0fa98a07e53b908ed3fcce Mon Sep 17 00:00:00 2001 From: ralf Date: Fri, 5 Dec 2008 12:36:27 -0800 Subject: [PATCH 08/39] Fix create_ddms_symlinks to create libs/ correctly. Add git ignore files to avoid all build files from being submitted to git. --- .../com.android.ide.eclipse.adt/.gitignore | 1 + .../com.android.ide.eclipse.common/.gitignore | 1 + .../icons/.gitignore | 31 ++++++++++++++ .../libs/.gitignore | 1 + .../src/com/android/.gitignore | 2 + .../.gitignore | 1 + .../com.android.ide.eclipse.tests/.gitignore | 1 + tools/eclipse/scripts/create_ddms_symlinks.sh | 2 +- tools/eclipse/scripts/create_test_symlinks.sh | 40 +++++++++++-------- 9 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/.gitignore create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.common/.gitignore create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/.gitignore create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.ddms/libs/.gitignore create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/.gitignore create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.editors/.gitignore create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.tests/.gitignore diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/.gitignore b/tools/eclipse/plugins/com.android.ide.eclipse.adt/.gitignore new file mode 100644 index 000000000..d392f0e82 --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/.gitignore @@ -0,0 +1 @@ +*.jar diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.common/.gitignore b/tools/eclipse/plugins/com.android.ide.eclipse.common/.gitignore new file mode 100644 index 000000000..d392f0e82 --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.common/.gitignore @@ -0,0 +1 @@ +*.jar diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/.gitignore b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/.gitignore new file mode 100644 index 000000000..f432e88b5 --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/icons/.gitignore @@ -0,0 +1,31 @@ +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 +halt.png +heap.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 diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.ddms/libs/.gitignore b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/libs/.gitignore new file mode 100644 index 000000000..d392f0e82 --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/libs/.gitignore @@ -0,0 +1 @@ +*.jar diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/.gitignore b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/.gitignore new file mode 100644 index 000000000..76d998138 --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/.gitignore @@ -0,0 +1,2 @@ +ddmlib +ddmuilib diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.editors/.gitignore b/tools/eclipse/plugins/com.android.ide.eclipse.editors/.gitignore new file mode 100644 index 000000000..d392f0e82 --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.editors/.gitignore @@ -0,0 +1 @@ +*.jar diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/.gitignore b/tools/eclipse/plugins/com.android.ide.eclipse.tests/.gitignore new file mode 100644 index 000000000..d392f0e82 --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/.gitignore @@ -0,0 +1 @@ +*.jar diff --git a/tools/eclipse/scripts/create_ddms_symlinks.sh b/tools/eclipse/scripts/create_ddms_symlinks.sh index 6c2421612..795882b55 100755 --- a/tools/eclipse/scripts/create_ddms_symlinks.sh +++ b/tools/eclipse/scripts/create_ddms_symlinks.sh @@ -24,7 +24,7 @@ function back() { BASE="development/tools/eclipse/plugins/com.android.ide.eclipse.ddms" DEST=$BASE/libs -mkdir -p $D +mkdir -p $DEST BACK=`back $DEST` for i in prebuilt/common/jfreechart/*.jar; do $CMD $BACK/$i $DEST/ diff --git a/tools/eclipse/scripts/create_test_symlinks.sh b/tools/eclipse/scripts/create_test_symlinks.sh index 110539a65..685d4e0df 100755 --- a/tools/eclipse/scripts/create_test_symlinks.sh +++ b/tools/eclipse/scripts/create_test_symlinks.sh @@ -1,24 +1,32 @@ #!/bin/bash -cd ../plugins/com.android.ide.eclipse.tests + +# CD to the top android directory +D=`dirname "$0"` +cd "$D/../../../../" + +DEST="development/tools/eclipse/plugins/com.android.ide.eclipse.tests" +# computes "../.." from DEST to here (in /android) +BACK=`echo $DEST | sed 's@[^/]*@..@g'` + HOST=`uname` if [ "$HOST" == "Linux" ]; then - ln -svf ../../../../out/host/linux-x86/framework/kxml2-2.3.0.jar . + 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 . + 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 " - 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 + 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" "$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 From 712bebb0fae9801f3c492501a2432730a8b67ba0 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Fri, 5 Dec 2008 11:36:35 -0800 Subject: [PATCH 09/39] Update the Eclipse plugin README with new instructions on how to compile the plug-ins. --- tools/eclipse/plugins/README.txt | 127 +++++++++---------------------- 1 file changed, 38 insertions(+), 89 deletions(-) diff --git a/tools/eclipse/plugins/README.txt b/tools/eclipse/plugins/README.txt index 184d73155..24fb979d7 100644 --- a/tools/eclipse/plugins/README.txt +++ b/tools/eclipse/plugins/README.txt @@ -11,104 +11,53 @@ The ADT feature is composed of four plugins: - com.android.ide.eclipse.ddms: A plugin version of the tool DDMS -Because the DDMS plugin source code is not yet released, compiling the -ADT/Common/Editors plugins requires to install the DDMS plugin in eclipse. +Each of these live in development/tools/eclipse/plugins/ -Basic requirements: +2 Features are used to distribute the plugins: +ADT, which contains ADT, ddms, common +Editors, which contains Editors, and requires the ADT feature. + +The feature projects are located in development/tools/eclipse/features/ + +Finally 2 site projects are located in development/tools/eclipse/sites/ +internal: is a site containing the features mentioned above as well as a test feature. +external: contains only the ADT and Editors features. + + +Basic requirements to develop on the plugins: - Eclipse 3.3 or 3.4 with JDT and PDE. -- DDMS plugin installed and running. --------------------------- -1- Install the DDMS plugin --------------------------- +---------------------------------- +1- Loading the projects in Eclipse +---------------------------------- -The easiest way to setup the DDMS plugin in your Eclipse environment is to -install the ADT features (see SDK documentation for details) and then remove -the following features and plugins: +The plugins projects depend on jar files located in the Android source tree, +or, in some cases, built by the Android source. -- /features/com.android.ide.eclipse.adt_x.x.x.jar -- /plugins/com.android.ide.eclipse.adt_x.x.x.jar -- /plugins/com.android.ide.eclipse.common_x.x.x.jar -- /plugins/com.android.ide.eclipse.editors_x.x.x.jar +Also, some source code (ddms) is located in a different location and needs to +be linked into the DDMS plugin source. -This will leave you with only the DDMS plugin installed in your Eclipse -distribution. +To automatize all of this, cd into development/tools/eclipse/scripts/ +and run create_all_symlinks.sh + +Once this has been done successfully, use the import project action in Eclipse +and point it to development/tools/eclipse. It will find all the projects in the +sub folder. -------------------------------------- -2- Setting up the ADT/Common project -------------------------------------- +----------------------------------------------- +2- Launching/Debugging the plugins from Eclipse +----------------------------------------------- -- Download the ADT/Common/Editors source. +- Open Debug Dialog. +- Create an "Eclipse Application" configuration. +- in the "Plug-ins" tab, make sure the plugins are selected (you may + want to disable the test plugin if you just want to run ADT) -- From the SDK, copy the following jars: - * androidprefs.jar => com.android.ide.eclipse.adt folder. - * jarutils.jar => com.android.ide.eclipse.adt folder. - * ping.jar => com.android.ide.eclipse.common folder. - * androidprefs.jar => com.android.ide.eclipse.common folder. - -- Create a java project from existing source for both the ADT plugin and the - common plugin. - -- In the Package Explorer, right click the projects and choose - PDE Tools > Convert Projects to Plug-in Project... - -- Select your projects in the dialog box and click OK. - -- In the Package Explorer, for ADT and common, right click the jar files mentioned above - and choose Build Path > Add to Build Path - -At this point the projects will compile. - -To launch the projects, open the Run/Debug Dialog and create an "Eclipse -Application" launch configuration. - -Additionnaly, another feature containing the Android Editors Plugin -(com.android.ide.eclipse.editors) is available. - -- Make sure the common project is present in your workspace as the Editors - plugin depends on this plugin. Alternatively, you can have the offical ADT - feature installed in your Eclipse distribution. -- Create a java project from existing source for the Editors project. -- In the Package Explorer, right click the project and choose - PDE Tools > Convert Projects to Plug-in Project... -- Select your project in the dialog box and click OK. - -Create an "Eclipse Application" launch configuration to test the plugin. - -------------------------------------- -3- Setting up the Editors project -------------------------------------- - -The "editors" plugin is optional. You can use ADT to develop Android -applications without the XML editor support. When this plugin is present, it -offers several customized form-based XML editors and one graphical layout -editor. - -At the time of this release (Android 0.9 SDK), some of the supporting libraries -still need some cleanup and are currently only provided as JAR files. - -- Download the ADT/Common/Editors source. - -- From the source archives, copy the following jars: - * ninepatch.jar => com.android.ide.eclipse.editors folder. - * layoutlib_utils.jar => com.android.ide.eclipse.editors folder. - * layoutlib_api.jar => com.android.ide.eclipse.editors folder. - -- From http://kxml.sourceforge.net/ download: - * kXML2-2.3.0.jar => com.android.ide.eclipse.editors folder. - -- Create a java project from existing source for both the editors plugin. - -- In the Package Explorer, right click the project and choose - PDE Tools > Convert Projects to Plug-in Project... - -- Select your project in the dialog box and click OK. - -- In the Package Explorer for editors, right click the jar files mentioned - above and choose Build Path > Add to Build Path - -To launch the projects, reuse the "Eclipse Application" launch configuration -created for ADT. +----------------------------- +3- Building a new update site +----------------------------- +- From Eclipse, open the site.xml of the site project you want to build and click + "Build All" from the "Site Map" tab. From a1a69d0936cd9c3120c52de7ea80548a0486b749 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Thu, 12 Feb 2009 17:22:57 +0000 Subject: [PATCH 10/39] Improving comments in make_windows_sdk.sh. This commit adds notes about some cygwin packages which I found were required, and a few other things which cropped up. It has no functional changes. List of packages amended as per request by Raphael Moll (ralf@android.com). --- build/tools/make_windows_sdk.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/build/tools/make_windows_sdk.sh b/build/tools/make_windows_sdk.sh index bf52679ac..24a5ad820 100755 --- a/build/tools/make_windows_sdk.sh +++ b/build/tools/make_windows_sdk.sh @@ -1,11 +1,17 @@ #!/bin/bash # Quick semi-auto file to build Windows SDK tools. # -# Limitations: +# Limitations and requirements: # - Expects the emulator has been built first, will pick it up from prebuilt. # - Run in Cygwin -# - Needs Cygwin package zip # - Expects to have one of the existing SDK (Darwin or Linux) to build the Windows one +# - Needs Cygwin packages: autoconf, bison, curl, flex, gcc, g++, git, +# gnupg, make, mingw-zlib, python, zip, unzip +# - Must NOT have cygwin package readline (its GPL license might taint the SDK if +# it gets compiled in) +# - Does not need a Java Development Kit or any other tools outside of cygwin. +# - If you think you may have Windows versions of tools (e.g. make) installed, it may +# reduce confusion levels to 'export PATH=/usr/bin' set -e # Fail this script as soon as a command fails -- fail early, fail fast @@ -37,6 +43,10 @@ function check() { # SDK number if you want, but not after, e.g these are valid: # android_sdk_4242_platform.zip or blah_42_.zip # + # Note that the root directory name in the zip must match the zip + # name, too, so there's no point just changing the zip name to match + # the above format. + # # SDK_NUMBER will be empty if nothing matched. filename=`basename "$SDK_ZIP"` SDK_NUMBER=`echo $filename | sed -n 's/^.*_\([^_./]\+\)_[^_.]*\..*$/\1/p'` From b0bdccc71a1fc97e88a07864597773637dcdcab2 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Thu, 19 Mar 2009 23:08:36 -0700 Subject: [PATCH 11/39] auto import from //branches/cupcake_rel/...@141571 --- apps/CustomLocale/NOTICE | 190 ++++ apps/Development/NOTICE | 190 ++++ apps/SdkSetup/NOTICE | 190 ++++ apps/SpareParts/NOTICE | 190 ++++ apps/launchperf/NOTICE | 190 ++++ .../META-INF/MANIFEST.MF | 4 +- .../com.android.ide.eclipse.adt/plugin.xml | 52 +- .../android/ide/eclipse/adt/AdtPlugin.java | 2 +- .../ide/eclipse/adt/build/ApkBuilder.java | 2 +- .../eclipse/adt/build/PreCompilerBuilder.java | 2 +- .../adt/build/ResourceManagerBuilder.java | 2 +- .../launch/AndroidLaunchConfiguration.java | 31 +- .../adt/launch/AndroidLaunchController.java | 105 ++- .../eclipse/adt/launch/EmulatorConfigTab.java | 14 +- .../adt/launch/LaunchConfigDelegate.java | 42 +- .../adt/launch/MainLaunchConfigTab.java | 2 +- .../junit/AndroidJUnitLaunchAction.java | 6 +- .../AndroidJUnitLaunchConfigDelegate.java | 60 +- .../AndroidJUnitLaunchConfigurationTab.java | 8 + .../junit/AndroidJUnitLaunchShortcut.java | 30 +- ...stRunner.java => RemoteAdtTestRunner.java} | 4 +- .../eclipse/adt/project/ProjectHelper.java | 71 ++ .../extractstring/ExtractStringAction.java | 161 ++++ .../ExtractStringContribution.java | 53 ++ .../ExtractStringDescriptor.java | 71 ++ .../extractstring/ExtractStringInputPage.java | 177 ++++ .../ExtractStringRefactoring.java | 890 ++++++++++++++++++ .../extractstring/ExtractStringWizard.java | 42 + .../ide/eclipse/common/AndroidConstants.java | 3 + .../common/project/AndroidManifestParser.java | 68 +- .../editors/layout/GraphicalLayoutEditor.java | 154 ++- .../ide/eclipse/tests/AdtTestData.java | 29 +- .../project/AndroidManifestParserTest.java | 75 +- .../data/AndroidManifest-instrumentation.xml | 18 + .../data/AndroidManifest-testapp.xml | 17 + .../com/android/sdklib/avd/AvdManager.java | 20 +- 36 files changed, 2967 insertions(+), 198 deletions(-) create mode 100644 apps/CustomLocale/NOTICE create mode 100644 apps/Development/NOTICE create mode 100644 apps/SdkSetup/NOTICE create mode 100644 apps/SpareParts/NOTICE create mode 100644 apps/launchperf/NOTICE rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/{RemoteADTTestRunner.java => RemoteAdtTestRunner.java} (98%) create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringContribution.java create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringDescriptor.java create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-instrumentation.xml create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-testapp.xml diff --git a/apps/CustomLocale/NOTICE b/apps/CustomLocale/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/apps/CustomLocale/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/apps/Development/NOTICE b/apps/Development/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/apps/Development/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/apps/SdkSetup/NOTICE b/apps/SdkSetup/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/apps/SdkSetup/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/apps/SpareParts/NOTICE b/apps/SpareParts/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/apps/SpareParts/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/apps/launchperf/NOTICE b/apps/launchperf/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/apps/launchperf/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF index 55c18bb26..c0dfcefd5 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF @@ -41,7 +41,9 @@ Require-Bundle: com.android.ide.eclipse.ddms, org.eclipse.wst.xml.core, org.eclipse.wst.xml.ui, org.eclipse.jdt.junit, - org.eclipse.jdt.junit.runtime + org.eclipse.jdt.junit.runtime, + org.eclipse.ltk.core.refactoring, + org.eclipse.ltk.ui.refactoring Eclipse-LazyStart: true Export-Package: com.android.ide.eclipse.adt, com.android.ide.eclipse.adt.build;x-friends:="com.android.ide.eclipse.tests", diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml index c18c72f05..2bf633d32 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml @@ -470,7 +470,7 @@ point="org.eclipse.ui.actionSets"> - - - - + + + + @@ -506,7 +515,7 @@ delegateDescription="Removes the Android JAR from the Bootstrap Classpath" id="com.android.ide.eclipse.adt.launch.JUnitLaunchConfigDelegate.launchAndroidJunit" modes="run,debug" - name="Android JUnit" + name="Android JUnit Test" type="org.eclipse.jdt.junit.launchconfig"> @@ -516,7 +525,7 @@ delegate="com.android.ide.eclipse.adt.launch.junit.AndroidJUnitLaunchConfigDelegate" id="com.android.ide.eclipse.adt.junit.launchConfigurationType" modes="run,debug" - name="Android Instrumentation" + name="Android JUnit Test" public="true" sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector" sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer"> @@ -534,7 +543,7 @@ point="org.eclipse.debug.ui.launchConfigurationTabGroups"> @@ -544,7 +553,7 @@ class="com.android.ide.eclipse.adt.launch.junit.AndroidJUnitLaunchShortcut" icon="icons/android.png" id="com.android.ide.eclipse.adt.junit.launchShortcut" - label="Android Instrumentation" + label="Android JUnit Test" modes="run,debug"> @@ -565,4 +574,25 @@ + + + + + + + + + + diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java index e0708f32b..42db64a61 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java @@ -1282,7 +1282,7 @@ public class AdtPlugin extends AbstractUIPlugin { AdtPlugin.PLUGIN_ID, UNKNOWN_EDITOR); try { - file.setPersistentProperty(qname, "1"); + file.setPersistentProperty(qname, "1"); //$NON-NLS-1$ } catch (CoreException e) { // pass } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java index f8a969e94..bc5b01c5f 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java @@ -196,7 +196,7 @@ public class ApkBuilder extends BaseBuilder { } // build() returns a list of project from which this project depends for future compilation. - @SuppressWarnings("unchecked") //$NON-NLS-1$ + @SuppressWarnings("unchecked") @Override protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java index 8aa1abad8..6f9c2f16a 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java @@ -197,7 +197,7 @@ public class PreCompilerBuilder extends BaseBuilder { } // build() returns a list of project from which this project depends for future compilation. - @SuppressWarnings("unchecked") //$NON-NLS-1$ + @SuppressWarnings("unchecked") @Override protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java index 035aa5b73..b1f9ec1d4 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java @@ -55,7 +55,7 @@ public class ResourceManagerBuilder extends BaseBuilder { } // build() returns a list of project from which this project depends for future compilation. - @SuppressWarnings("unchecked") //$NON-NLS-1$ + @SuppressWarnings("unchecked") @Override protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java index 448cda6b5..3e610db08 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java @@ -32,8 +32,30 @@ public class AndroidLaunchConfiguration { */ public int mLaunchAction = LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION; - public static final boolean AUTO_TARGET_MODE = true; + public enum TargetMode { + AUTO(true), MANUAL(false); + + private boolean mValue; + TargetMode(boolean value) { + mValue = value; + } + + public boolean getValue() { + return mValue; + } + + public static TargetMode getMode(boolean value) { + for (TargetMode mode : values()) { + if (mode.mValue == value) { + return mode; + } + } + + return null; + } + } + /** * Target selection mode. *

    @@ -41,7 +63,7 @@ public class AndroidLaunchConfiguration { *
  • false: manual mode
  • *
*/ - public boolean mTargetMode = LaunchConfigDelegate.DEFAULT_TARGET_MODE; + public TargetMode mTargetMode = LaunchConfigDelegate.DEFAULT_TARGET_MODE; /** * Indicates whether the emulator should be called with -wipe-data @@ -81,8 +103,9 @@ public class AndroidLaunchConfiguration { } try { - mTargetMode = config.getAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE, - mTargetMode); + boolean value = config.getAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE, + mTargetMode.getValue()); + mTargetMode = TargetMode.getMode(value); } catch (CoreException e) { // nothing to be done here, we'll use the default value } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java index 499cca704..3aa0b91f6 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java @@ -29,11 +29,15 @@ import com.android.ddmlib.MultiLineReceiver; import com.android.ddmlib.SyncService; import com.android.ddmlib.SyncService.SyncResult; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration.TargetMode; import com.android.ide.eclipse.adt.launch.DelayedLaunchInfo.InstallRetryMode; import com.android.ide.eclipse.adt.launch.DeviceChooserDialog.DeviceChooserResponse; 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.ide.eclipse.common.project.AndroidManifestParser; +import com.android.prefs.AndroidLocation.AndroidLocationException; +import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkManager; import com.android.sdklib.avd.AvdManager; @@ -52,6 +56,9 @@ import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.jdt.core.IJavaModel; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jdt.launching.IVMConnector; import org.eclipse.jdt.launching.JavaRuntime; @@ -64,6 +71,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -236,7 +244,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener // set default target mode wc.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE, - LaunchConfigDelegate.DEFAULT_TARGET_MODE); + LaunchConfigDelegate.DEFAULT_TARGET_MODE.getValue()); // default AVD: None wc.setAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, (String) null); @@ -332,6 +340,16 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener Sdk currentSdk = Sdk.getCurrent(); AvdManager avdManager = currentSdk.getAvdManager(); + // reload the AVDs to make sure we are up to date + try { + avdManager.reloadAvds(); + } catch (AndroidLocationException e1) { + // this happens if the AVD Manager failed to find the folder in which the AVDs are + // stored. This is unlikely to happen, but if it does, we should force to go manual + // to allow using physical devices. + config.mTargetMode = TargetMode.MANUAL; + } + // get the project target final IAndroidTarget projectTarget = currentSdk.getTarget(project); @@ -356,7 +374,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * If == 1, launch the application on this AVD/device. */ - if (config.mTargetMode == AndroidLaunchConfiguration.AUTO_TARGET_MODE) { + if (config.mTargetMode == TargetMode.AUTO) { // if we are in automatic target mode, we need to find the current devices IDevice[] devices = AndroidDebugBridge.getBridge().getDevices(); @@ -795,6 +813,14 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener return false; } + // The app is now installed, now try the dependent projects + for (DelayedLaunchInfo dependentLaunchInfo : getDependenciesLaunchInfo(launchInfo)) { + String msg = String.format("Project dependency found, syncing: %s", + dependentLaunchInfo.getProject().getName()); + AdtPlugin.printToConsole(launchInfo.getProject(), msg); + syncApp(dependentLaunchInfo, device); + } + return installResult; } @@ -806,6 +832,81 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener return false; } + /** + * For the current launchInfo, create additional DelayedLaunchInfo that should be used to + * sync APKs that we are dependent on to the device. + * + * @param launchInfo the original launch info that we want to find the + * @return a list of DelayedLaunchInfo (may be empty if no dependencies were found or error) + */ + public List getDependenciesLaunchInfo(DelayedLaunchInfo launchInfo) { + List dependencies = new ArrayList(); + + // Convert to equivalent JavaProject + IJavaProject javaProject; + try { + //assuming this is an Android (and Java) project since it is attached to the launchInfo. + javaProject = BaseProjectHelper.getJavaProject(launchInfo.getProject()); + } catch (CoreException e) { + // return empty dependencies + AdtPlugin.printErrorToConsole(launchInfo.getProject(), e); + return dependencies; + } + + // Get all projects that this depends on + List androidProjectList; + try { + androidProjectList = ProjectHelper.getAndroidProjectDependencies(javaProject); + } catch (JavaModelException e) { + // return empty dependencies + AdtPlugin.printErrorToConsole(launchInfo.getProject(), e); + return dependencies; + } + + // for each project, parse manifest and create launch information + for (IJavaProject androidProject : androidProjectList) { + // Parse the Manifest to get various required information + // copied from LaunchConfigDelegate + AndroidManifestParser manifestParser; + try { + manifestParser = AndroidManifestParser.parse( + androidProject, null /* errorListener */, + true /* gatherData */, false /* markErrors */); + } catch (CoreException e) { + AdtPlugin.printErrorToConsole( + launchInfo.getProject(), + String.format("Error parsing manifest of %s", + androidProject.getElementName())); + continue; + } + + // Get the APK location (can return null) + IFile apk = ProjectHelper.getApplicationPackage(androidProject.getProject()); + if (apk == null) { + // getApplicationPackage will have logged an error message + continue; + } + + // Create new launchInfo as an hybrid between parent and dependency information + DelayedLaunchInfo delayedLaunchInfo = new DelayedLaunchInfo( + androidProject.getProject(), + manifestParser.getPackage(), + launchInfo.getLaunchAction(), + apk, + manifestParser.getDebuggable(), + manifestParser.getApiLevelRequirement(), + launchInfo.getLaunch(), + launchInfo.getMonitor()); + + // Add to the list + dependencies.add(delayedLaunchInfo); + } + + return dependencies; + } + + + /** * Installs the application package that was pushed to a temporary location on the device. * @param launchInfo The launch information diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java index b898f63c5..3789153e4 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.launch; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration.TargetMode; import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.ide.eclipse.ddms.DdmsPlugin; @@ -292,14 +293,15 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab { public void initializeFrom(ILaunchConfiguration configuration) { AvdManager avdManager = Sdk.getCurrent().getAvdManager(); - boolean value = LaunchConfigDelegate.DEFAULT_TARGET_MODE; // true == automatic + TargetMode mode = LaunchConfigDelegate.DEFAULT_TARGET_MODE; // true == automatic try { - value = configuration.getAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE, value); + mode = TargetMode.getMode(configuration.getAttribute( + LaunchConfigDelegate.ATTR_TARGET_MODE, mode.getValue())); } catch (CoreException e) { // let's not do anything here, we'll use the default value } - mAutoTargetButton.setSelection(value); - mManualTargetButton.setSelection(!value); + mAutoTargetButton.setSelection(mode.getValue()); + mManualTargetButton.setSelection(!mode.getValue()); // look for the project name to get its target. String stringValue = ""; @@ -354,7 +356,7 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab { mPreferredAvdSelector.setSelection(null); } - value = LaunchConfigDelegate.DEFAULT_WIPE_DATA; + boolean value = LaunchConfigDelegate.DEFAULT_WIPE_DATA; try { value = configuration.getAttribute(LaunchConfigDelegate.ATTR_WIPE_DATA, value); } catch (CoreException e) { @@ -440,7 +442,7 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab { */ public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { configuration.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE, - LaunchConfigDelegate.DEFAULT_TARGET_MODE); + LaunchConfigDelegate.DEFAULT_TARGET_MODE.getValue()); configuration.setAttribute(LaunchConfigDelegate.ATTR_SPEED, LaunchConfigDelegate.DEFAULT_SPEED); configuration.setAttribute(LaunchConfigDelegate.ATTR_DELAY, diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java index 80f62eaa8..d057ac709 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java @@ -18,15 +18,14 @@ package com.android.ide.eclipse.adt.launch; import com.android.ddmlib.AndroidDebugBridge; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration.TargetMode; import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.ide.eclipse.common.project.BaseProjectHelper; import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; @@ -51,7 +50,7 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate { /** Target mode parameters: true is automatic, false is manual */ public static final String ATTR_TARGET_MODE = AdtPlugin.PLUGIN_ID + ".target"; //$NON-NLS-1$ - public static final boolean DEFAULT_TARGET_MODE = true; //automatic mode + public static final TargetMode DEFAULT_TARGET_MODE = TargetMode.AUTO; /** * Launch action: @@ -152,7 +151,7 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate { AdtPlugin.printToConsole(project, "Android Launch!"); // check if the project is using the proper sdk. - // if that throws an exception, we simply let it propage to the caller. + // if that throws an exception, we simply let it propagate to the caller. if (checkAndroidProject(project) == false) { AdtPlugin.printErrorToConsole(project, "Project is not an Android Project. Aborting!"); androidLaunch.stopLaunch(); @@ -215,7 +214,7 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate { AndroidLaunchController controller = AndroidLaunchController.getInstance(); // get the application package - IFile applicationPackage = getApplicationPackage(project); + IFile applicationPackage = ProjectHelper.getApplicationPackage(project); if (applicationPackage == null) { androidLaunch.stopLaunch(); return; @@ -387,39 +386,6 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate { } - /** - * Returns the android package file as an IFile object for the specified - * project. - * @param project The project - * @return The android package as an IFile object or null if not found. - */ - private IFile getApplicationPackage(IProject project) { - // get the output folder - IFolder outputLocation = BaseProjectHelper.getOutputFolder(project); - - if (outputLocation == null) { - AdtPlugin.printErrorToConsole(project, - "Failed to get the output location of the project. Check build path properties" - ); - return null; - } - - - // get the package path - String packageName = project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE; - IResource r = outputLocation.findMember(packageName); - - // check the package is present - if (r instanceof IFile && r.exists()) { - return (IFile)r; - } - - String msg = String.format("Could not find %1$s!", packageName); - AdtPlugin.printErrorToConsole(project, msg); - - return null; - } - /** * Returns the name of the activity. */ diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java index 30b072389..91bd21cb7 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java @@ -58,7 +58,7 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab { /** * */ - public static final String LAUNCH_TAB_IMAGE = "mainLaunchTab.png"; + public static final String LAUNCH_TAB_IMAGE = "mainLaunchTab.png"; //$NON-NLS-1$ protected static final String EMPTY_STRING = ""; //$NON-NLS-1$ diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java index 4dfe37d1d..b88026329 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java @@ -20,7 +20,7 @@ import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.launch.DelayedLaunchInfo; import com.android.ide.eclipse.adt.launch.IAndroidLaunchAction; import com.android.ide.eclipse.adt.launch.junit.runtime.AndroidJUnitLaunchInfo; -import com.android.ide.eclipse.adt.launch.junit.runtime.RemoteADTTestRunner; +import com.android.ide.eclipse.adt.launch.junit.runtime.RemoteAdtTestRunner; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; @@ -166,7 +166,7 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { private final VMRunnerConfiguration mRunConfig; private final ILaunch mLaunch; private final AndroidJUnitLaunchInfo mJUnitInfo; - private RemoteADTTestRunner mTestRunner = null; + private RemoteAdtTestRunner mTestRunner = null; private boolean mIsTerminated = false; TestRunnerProcess(VMRunnerConfiguration runConfig, ILaunch launch, @@ -256,7 +256,7 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { */ @Override public void run() { - mTestRunner = new RemoteADTTestRunner(); + mTestRunner = new RemoteAdtTestRunner(); mTestRunner.runTests(mRunConfig.getProgramArguments(), mJUnitInfo); } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java index 05cc6ae73..a624b0001 100755 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java @@ -18,10 +18,11 @@ package com.android.ide.eclipse.adt.launch.junit; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.launch.AndroidLaunch; +import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration; import com.android.ide.eclipse.adt.launch.AndroidLaunchController; import com.android.ide.eclipse.adt.launch.IAndroidLaunchAction; import com.android.ide.eclipse.adt.launch.LaunchConfigDelegate; -import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration; +import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.ide.eclipse.common.project.BaseProjectHelper; @@ -31,6 +32,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants; import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry; @@ -46,6 +48,7 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { /** Launch config attribute that stores instrumentation runner */ static final String ATTR_INSTR_NAME = AdtPlugin.PLUGIN_ID + ".instrumentation"; //$NON-NLS-1$ + static final String INSTRUMENTATION_OK = null; private static final String EMPTY_STRING = ""; //$NON-NLS-1$ @Override @@ -87,7 +90,8 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { * Helper method to return the set of instrumentations for the Android project * * @param project the {@link IProject} to get instrumentations for - * @return null if no error occurred parsing instrumentations + * @return null if error occurred parsing instrumentations, otherwise returns array of + * instrumentation class names */ static String[] getInstrumentationsForProject(IProject project) { if (project != null) { @@ -117,4 +121,56 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_RUNNER_KIND, TestKindRegistry.JUNIT3_TEST_KIND_ID); } + + /** + * Helper method to determine if specified instrumentation can be used as a test runner + * + * @param project the {@link IJavaProject} to validate + * @param instrumentation the instrumentation class name to validate + * @return INSTRUMENTATION_OK if valid, otherwise returns error message + */ + static String validateInstrumentationRunner(IJavaProject project, String instrumentation) { + AndroidManifestParser manifestParser; + try { + manifestParser = AndroidManifestParser.parse( + project, null /* errorListener */, + true /* gatherData */, false /* markErrors */); + // check if this instrumentation is the standard test runner + if (!instrumentation.equals(AndroidConstants.CLASS_INSTRUMENTATION_RUNNER)) { + // check if it extends the standard test runner + String result = BaseProjectHelper.testClassForManifest(project, + instrumentation, AndroidConstants.CLASS_INSTRUMENTATION_RUNNER, true); + if (result != BaseProjectHelper.TEST_CLASS_OK) { + return String.format("The instrumentation runner must be of type %s", + AndroidConstants.CLASS_INSTRUMENTATION_RUNNER); + } + } + if (!hasTestRunnerLibrary(manifestParser)) { + return String.format("%s does not not use the %s library", + project.getProject().getName(), AndroidConstants.LIBRARY_TEST_RUNNER); + } + } catch (CoreException e) { + String err = String.format("Error parsing AndroidManifest for %s", + project.getProject().getName()); + AdtPlugin.log(e, err); + return err; + } + return INSTRUMENTATION_OK; + } + + /** + * Helper method to determine if given manifest has a AndroidConstants.LIBRARY_TEST_RUNNER + * library reference + * + * @param manifestParser the {@link AndroidManifestParser} to search + * @return true if test runner library found, false otherwise + */ + static boolean hasTestRunnerLibrary(AndroidManifestParser manifestParser) { + for (String lib : manifestParser.getUsesLibraries()) { + if (lib.equals(AndroidConstants.LIBRARY_TEST_RUNNER)) { + return true; + } + } + return false; + } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java index 5fbda983d..aa59a5157 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java @@ -691,10 +691,18 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat private void validateInstrumentation(IJavaProject javaProject) { if (mInstrumentations == null || mInstrumentations.length < 1) { setErrorMessage("Specified project has no defined instrumentations"); + return; } String instrumentation = getSelectedInstrumentation(); if (instrumentation == null) { setErrorMessage("Instrumentation not specified"); + return; + } + String result = AndroidJUnitLaunchConfigDelegate.validateInstrumentationRunner( + javaProject, instrumentation); + if (result != AndroidJUnitLaunchConfigDelegate.INSTRUMENTATION_OK) { + setErrorMessage(result); + return; } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java index e03f2822b..30649e2e8 100755 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java @@ -16,6 +16,9 @@ package com.android.ide.eclipse.adt.launch.junit; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.common.AndroidConstants; + import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; @@ -34,7 +37,7 @@ public class AndroidJUnitLaunchShortcut extends JUnitLaunchShortcut { /** * Creates a default Android JUnit launch configuration. Sets the instrumentation runner to the - * first instrumentation found in the AndroidManifest. + * first instrumentation found in the AndroidManifest. */ @Override protected ILaunchConfigurationWorkingCopy createLaunchConfiguration(IJavaElement element) @@ -43,10 +46,27 @@ public class AndroidJUnitLaunchShortcut extends JUnitLaunchShortcut { IProject project = element.getResource().getProject(); String[] instrumentations = AndroidJUnitLaunchConfigDelegate.getInstrumentationsForProject(project); - if (instrumentations != null && instrumentations.length > 0) { - // just pick the first runner - config.setAttribute(AndroidJUnitLaunchConfigDelegate.ATTR_INSTR_NAME, - instrumentations[0]); + boolean runnerFound = false; + if (instrumentations != null) { + // just pick the first valid runner + for (String instr : instrumentations) { + if (AndroidJUnitLaunchConfigDelegate.validateInstrumentationRunner( + element.getJavaProject(), instr) == + AndroidJUnitLaunchConfigDelegate.INSTRUMENTATION_OK) { + + config.setAttribute(AndroidJUnitLaunchConfigDelegate.ATTR_INSTR_NAME, + instr); + runnerFound = true; + break; + } + } + } + if (!runnerFound) { + // TODO: put this in a string properties + String msg = String.format("ERROR: Application does not specify a %s instrumentation or does not declare uses-library %s", + AndroidConstants.CLASS_INSTRUMENTATION_RUNNER, + AndroidConstants.LIBRARY_TEST_RUNNER); + AdtPlugin.printErrorToConsole(project, msg); } AndroidJUnitLaunchConfigDelegate.setJUnitDefaults(config); diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteADTTestRunner.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java similarity index 98% rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteADTTestRunner.java rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java index 6834c089a..0a6a3daee 100755 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteADTTestRunner.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java @@ -34,7 +34,7 @@ import org.eclipse.jdt.internal.junit.runner.TestReferenceFailure; * @see org.eclipse.jdt.internal.junit.runner.RemoteTestRunner for more details on the protocol */ @SuppressWarnings("restriction") -public class RemoteADTTestRunner extends RemoteTestRunner { +public class RemoteAdtTestRunner extends RemoteTestRunner { private AndroidJUnitLaunchInfo mLaunchInfo; private TestExecution mExecution; @@ -97,6 +97,8 @@ public class RemoteADTTestRunner extends RemoteTestRunner { // error occurred during test collection. reportError(collector.getErrorMessage()); // abort here + notifyTestRunEnded(0); + return; } notifyTestRunStarted(collector.getTestCaseCount()); collector.sendTrees(this); diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java index c650b9846..e091b1385 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java @@ -20,8 +20,10 @@ import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerInitializer; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestParser; +import com.android.ide.eclipse.common.project.BaseProjectHelper; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; @@ -34,12 +36,14 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaModel; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.launching.JavaRuntime; import java.util.ArrayList; +import java.util.List; /** * Utility class to manipulate Project parameters/properties. @@ -679,4 +683,71 @@ public final class ProjectHelper { return project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE; } + + /** + * Find the list of projects on which this JavaProject is dependent on at the compilation level. + * + * @param javaProject Java project that we are looking for the dependencies. + * @return A list of Java projects for which javaProject depend on. + * @throws JavaModelException + */ + public static List getAndroidProjectDependencies(IJavaProject javaProject) + throws JavaModelException { + String[] requiredProjectNames = javaProject.getRequiredProjectNames(); + + // Go from java project name to JavaProject name + IJavaModel javaModel = javaProject.getJavaModel(); + + // loop through all dependent projects and keep only those that are Android projects + List projectList = new ArrayList(requiredProjectNames.length); + for (String javaProjectName : requiredProjectNames) { + IJavaProject androidJavaProject = javaModel.getJavaProject(javaProjectName); + + //Verify that the project has also the Android Nature + try { + if (!androidJavaProject.getProject().hasNature(AndroidConstants.NATURE)) { + continue; + } + } catch (CoreException e) { + continue; + } + + projectList.add(androidJavaProject); + } + + return projectList; + } + + /** + * Returns the android package file as an IFile object for the specified + * project. + * @param project The project + * @return The android package as an IFile object or null if not found. + */ + public static IFile getApplicationPackage(IProject project) { + // get the output folder + IFolder outputLocation = BaseProjectHelper.getOutputFolder(project); + + if (outputLocation == null) { + AdtPlugin.printErrorToConsole(project, + "Failed to get the output location of the project. Check build path properties" + ); + return null; + } + + + // get the package path + String packageName = project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE; + IResource r = outputLocation.findMember(packageName); + + // check the package is present + if (r instanceof IFile && r.exists()) { + return (IFile)r; + } + + String msg = String.format("Could not find %1$s!", packageName); + AdtPlugin.printErrorToConsole(project, msg); + + return null; + } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java new file mode 100644 index 000000000..528701573 --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2009 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.refactorings.extractstring; + +import org.eclipse.core.resources.IFile; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.ui.JavaUI; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ltk.ui.refactoring.RefactoringWizard; +import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.IWorkbenchWindowActionDelegate; +import org.eclipse.ui.PlatformUI; + +/* + * Quick Reference Link: + * http://www.eclipse.org/articles/article.php?file=Article-Unleashing-the-Power-of-Refactoring/index.html + * and + * http://www.ibm.com/developerworks/opensource/library/os-ecjdt/ + */ + +/** + * Action executed when the "Extract String" menu item is invoked. + *

+ * The intent of the action is to start a refactoring that extracts a source string and + * replaces it by an Android string resource ID. + *

+ * Workflow: + *

    + *
  • The action is currently located in the Refactoring menu in the main menu. + *
  • TODO: extend the popup refactoring menu in a Java or Android XML file. + *
  • The action is only enabled if the selection is 1 character or more. That is at least part + * of the string must be selected, it's not enough to just move the insertion point. This is + * a limitation due to {@link #selectionChanged(IAction, ISelection)} not being called when + * the insertion point is merely moved. TODO: address this limitation. + *
      The action gets the current {@link ISelection}. It also knows the current + * {@link IWorkbenchWindow}. However for the refactoring we are also interested in having the + * actual resource file. By looking at the Active Window > Active Page > Active Editor we + * can get the {@link IEditorInput} and find the {@link ICompilationUnit} (aka Java file) + * that is being edited. + *
        TODO: change this to find the {@link IFile} being manipulated. The {@link ICompilationUnit} + * can be inferred using {@link JavaCore#createCompilationUnitFrom(IFile)}. This will allow + * us to be able to work with a selection from an Android XML file later. + *
      • The action creates a new {@link ExtractStringRefactoring} and make it run on in a new + * {@link ExtractStringWizard}. + *
          + */ +public class ExtractStringAction implements IWorkbenchWindowActionDelegate { + + /** Keep track of the current workbench window. */ + private IWorkbenchWindow mWindow; + private ITextSelection mSelection; + private ICompilationUnit mUnit; + + /** + * Keep track of the current workbench window. + */ + public void init(IWorkbenchWindow window) { + mWindow = window; + } + + public void dispose() { + // Nothing to do + } + + /** + * Examine the selection to determine if the action should be enabled or not. + *

          + * Keep a link to the relevant selection structure (i.e. a part of the Java AST). + */ + public void selectionChanged(IAction action, ISelection selection) { + + // Note, two kinds of selections are returned here: + // ITextSelection on a Java source window + // IStructuredSelection in the outline or navigator + // This simply deals with the refactoring based on a non-empty selection. + // At that point, just enable the action and later decide if it's valid when it actually + // runs since we don't have access to the AST yet. + + mSelection = null; + mUnit = null; + + if (selection instanceof ITextSelection) { + mSelection = (ITextSelection) selection; + if (mSelection.getLength() > 0) { + mUnit = getCompilationUnit(); + } + + // Keep for debugging purposes + //System.out.println(String.format("-- Selection: %d + %d = %s", + // mSelection.getOffset(), + // mSelection.getLength(), + // mSelection.getText())); + } + + action.setEnabled(mSelection != null && mUnit != null); + } + + /** + * Create a new instance of our refactoring and a wizard to configure it. + */ + public void run(IAction action) { + if (mSelection != null && mUnit != null) { + ExtractStringRefactoring ref = new ExtractStringRefactoring(mUnit, mSelection); + RefactoringWizard wizard = new ExtractStringWizard(ref, "Extract Android String"); + RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); + try { + op.run(mWindow.getShell(), wizard.getDefaultPageTitle()); + } catch (InterruptedException e) { + // Interrupted. Pass. + } + } + } + + /** + * Returns the active {@link ICompilationUnit} or null. + */ + private ICompilationUnit getCompilationUnit() { + IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (wwin != null) { + IWorkbenchPage page = wwin.getActivePage(); + if (page != null) { + IEditorPart editor = page.getActiveEditor(); + if (editor != null) { + IEditorInput input = editor.getEditorInput(); + if (input != null) { + ITypeRoot typeRoot = JavaUI.getEditorInputTypeRoot(input); + // The type root can be either a .class or a .java (aka compilation unit). + // We want the compilation unit kind. + if (typeRoot instanceof ICompilationUnit) { + return (ICompilationUnit) typeRoot; + } + } + } + } + } + + return null; + } +} diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringContribution.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringContribution.java new file mode 100644 index 000000000..465e1a36c --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringContribution.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2009 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.refactorings.extractstring; + +import org.eclipse.ltk.core.refactoring.RefactoringContribution; +import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; + +import java.util.Map; + +/** + * @see ExtractStringDescriptor + */ +public class ExtractStringContribution extends RefactoringContribution { + + /* (non-Javadoc) + * @see org.eclipse.ltk.core.refactoring.RefactoringContribution#createDescriptor(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.util.Map, int) + */ + @SuppressWarnings("unchecked") + @Override + public RefactoringDescriptor createDescriptor( + String id, + String project, + String description, + String comment, + Map arguments, + int flags) + throws IllegalArgumentException { + return new ExtractStringDescriptor(project, description, comment, arguments); + } + + @SuppressWarnings("unchecked") + @Override + public Map retrieveArgumentMap(RefactoringDescriptor descriptor) { + if (descriptor instanceof ExtractStringDescriptor) { + return ((ExtractStringDescriptor) descriptor).getArguments(); + } + return super.retrieveArgumentMap(descriptor); + } +} diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringDescriptor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringDescriptor.java new file mode 100644 index 000000000..6e999e942 --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringDescriptor.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2009 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.refactorings.extractstring; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.ltk.core.refactoring.Refactoring; +import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; + +import java.util.Map; + +/** + * A descriptor that allows an {@link ExtractStringRefactoring} to be created from + * a previous instance of itself. + */ +public class ExtractStringDescriptor extends RefactoringDescriptor { + + public static final String ID = + "com.android.ide.eclipse.adt.refactoring.extract.string"; //$NON-NLS-1$ + + private final Map mArguments; + + public ExtractStringDescriptor(String project, String description, String comment, + Map arguments) { + super(ID, project, description, comment, + RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE //flags + ); + mArguments = arguments; + } + + public Map getArguments() { + return mArguments; + } + + /** + * Creates a new refactoring instance for this refactoring descriptor based on + * an argument map. The argument map is created by the refactoring itself in + * {@link ExtractStringRefactoring#createChange(org.eclipse.core.runtime.IProgressMonitor)} + *

          + * This is apparently used to replay a refactoring. + * + * {@inheritDoc} + * + * @throws CoreException + */ + @Override + public Refactoring createRefactoring(RefactoringStatus status) throws CoreException { + try { + ExtractStringRefactoring ref = new ExtractStringRefactoring(mArguments); + return ref; + } catch (NullPointerException e) { + status.addFatalError("Failed to recreate ExtractStringRefactoring from descriptor"); + return null; + } + } + +} diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java new file mode 100644 index 000000000..cb449f02e --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2009 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.refactorings.extractstring; + +import org.eclipse.jface.wizard.IWizardPage; +import org.eclipse.ltk.ui.refactoring.UserInputWizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +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; + +/** + * @see ExtractStringRefactoring + */ +class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage { + + public ExtractStringInputPage() { + super("ExtractStringInputPage"); //$NON-NLS-1$ + } + + private Label mStringLabel; + private Text mNewIdTextField; + private Label mFileLabel; + + /** + * Create the UI for the refactoring wizard. + *

          + * Note that at that point the initial conditions have been checked in + * {@link ExtractStringRefactoring}. + */ + public void createControl(Composite parent) { + + final ExtractStringRefactoring ref = getOurRefactoring(); + + Composite content = new Composite(parent, SWT.NONE); + + GridLayout layout = new GridLayout(); + layout.numColumns = 2; + content.setLayout(layout); + + // line 1: String found in selection + + Label label = new Label(content, SWT.NONE); + label.setText("String:"); + + String selectedString = ref.getTokenString(); + + mStringLabel = new Label(content, SWT.NONE); + mStringLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + mStringLabel.setText(selectedString != null ? selectedString : ""); + + // TODO provide an option to replace all occurences of this string instead of + // just the one. + + // line 2 : Textfield for new ID + + label = new Label(content, SWT.NONE); + label.setText("Replace by R.string."); + + mNewIdTextField = new Text(content, SWT.SINGLE | SWT.LEFT | SWT.BORDER); + mNewIdTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + mNewIdTextField.setText(guessId(selectedString)); + + ref.setReplacementStringId(mNewIdTextField.getText().trim()); + + mNewIdTextField.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + if (validatePage(ref)) { + ref.setReplacementStringId(mNewIdTextField.getText().trim()); + } + } + }); + + // line 3: selection of the output file + // TODO add a file field/chooser combo to let the user select the file to edit. + + label = new Label(content, SWT.NONE); + label.setText("Resource file:"); + + mFileLabel = new Label(content, SWT.NONE); + mFileLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + mFileLabel.setText("/res/values/strings.xml"); + ref.setTargetFile(mFileLabel.getText()); + + // line 4: selection of the res config + // TODO add the Configuration Selector to decide with strings.xml to change + + label = new Label(content, SWT.NONE); + label.setText("Configuration:"); + + label = new Label(content, SWT.NONE); + label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + label.setText("default"); + + validatePage(ref); + setControl(content); + } + + private String guessId(String text) { + // make lower case + text = text.toLowerCase(); + + // everything not alphanumeric becomes an underscore + text = text.replaceAll("[^a-zA-Z0-9]+", "_"); //$NON-NLS-1$ //$NON-NLS-2$ + + // the id must be a proper Java identifier, so it can't start with a number + if (text.length() > 0 && !Character.isJavaIdentifierStart(text.charAt(0))) { + text = "_" + text; //$NON-NLS-1$ + } + return text; + } + + private ExtractStringRefactoring getOurRefactoring() { + return (ExtractStringRefactoring) getRefactoring(); + } + + private boolean validatePage(ExtractStringRefactoring ref) { + String text = mNewIdTextField.getText().trim(); + boolean success = true; + + // Analyze fatal errors. + + if (text == null || text.length() < 1) { + setErrorMessage("Please provide a resource ID to replace with."); + success = false; + } else { + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + boolean ok = i == 0 ? + Character.isJavaIdentifierStart(c) : + Character.isJavaIdentifierPart(c); + if (!ok) { + setErrorMessage(String.format( + "The resource ID must be a valid Java identifier. The character %1$c at position %2$d is not acceptable.", + c, i+1)); + success = false; + break; + } + } + } + + // Analyze info & warnings. + + if (success) { + if (ref.isResIdDuplicate(mFileLabel.getText(), text)) { + setErrorMessage(null); + setMessage( + String.format("Warning: There's already a string item called '%1$s' in %2$s.", + text, mFileLabel.getText())); + } else { + setMessage(null); + setErrorMessage(null); + } + } + + setPageComplete(success); + return success; + } +} diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java new file mode 100644 index 000000000..715503c1f --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java @@ -0,0 +1,890 @@ +/* + * Copyright (C) 2009 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.refactorings.extractstring; + +import com.android.ide.eclipse.common.AndroidConstants; +import com.android.ide.eclipse.common.project.AndroidManifestParser; +import com.android.ide.eclipse.common.project.AndroidXPathFactory; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourceAttributes; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.jdt.core.IBuffer; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.ToolFactory; +import org.eclipse.jdt.core.compiler.IScanner; +import org.eclipse.jdt.core.compiler.ITerminalSymbols; +import org.eclipse.jdt.core.compiler.InvalidInputException; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.Name; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.StringLiteral; +import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; +import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.ChangeDescriptor; +import org.eclipse.ltk.core.refactoring.CompositeChange; +import org.eclipse.ltk.core.refactoring.Refactoring; +import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.ltk.core.refactoring.TextFileChange; +import org.eclipse.text.edits.InsertEdit; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.ReplaceEdit; +import org.eclipse.text.edits.TextEdit; +import org.eclipse.text.edits.TextEditGroup; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; + +/** + * This refactoring extracts a string from a file and replaces it by an Android resource ID + * such as R.string.foo. + *

          + * There are a number of scenarios, which are not all supported yet. The workflow works as + * such: + *

            + *
          • User selects a string in a Java (TODO: or XML file) and invokes + * the {@link ExtractStringAction}. + *
          • The action finds the {@link ICompilationUnit} being edited as well as the current + * {@link ITextSelection}. The action creates a new instance of this refactoring as + * well as an {@link ExtractStringWizard} and runs the operation. + *
          • TODO: to support refactoring from an XML file, the action should give the {@link IFile} + * and then here we would have to determine whether it's a suitable Android XML file or a + * suitable Java file. + * TODO: enumerate the exact valid contexts in Android XML files, e.g. attributes in layout + * files or text elements (e.g. foo) for values, etc. + *
          • Step 1 of the refactoring is to check the preliminary conditions. Right now we check + * that the java source is not read-only and is in sync. We also try to find a string under + * the selection. If this fails, the refactoring is aborted. + *
          • TODO: Find the string in an XML file based on selection. + *
          • On success, the wizard is shown, which let the user input the new ID to use. + *
          • The wizard sets the user input values into this refactoring instance, e.g. the new string + * ID, the XML file to update, etc. The wizard does use the utility method + * {@link #isResIdDuplicate(String, String)} to check whether the new ID is already defined + * in the target XML file. + *
          • Once Preview or Finish is selected in the wizard, the + * {@link #checkFinalConditions(IProgressMonitor)} is called to double-check the user input + * and compute the actual changes. + *
          • When all changes are computed, {@link #createChange(IProgressMonitor)} is invoked. + *
          + * + * The list of changes are: + *
            + *
          • If the target XML does not exist, create it with the new string ID. + *
          • If the target XML exists, find the node and add the new string ID right after. + * If the node is , it needs to be opened. + *
          • Create an AST rewriter to edit the source Java file and replace all occurences by the + * new computed R.string.foo. Also need to rewrite imports to import R as needed. + * If there's already a conflicting R included, we need to insert the FQCN instead. + *
          • TODO: If the source is an XML file, determine if we need to change an attribute or a + * a text element. + *
          • TODO: Have a pref in the wizard: [x] Change other XML Files + *
          • TODO: Have a pref in the wizard: [x] Change other Java Files + *
          + */ +class ExtractStringRefactoring extends Refactoring { + + /** The compilation unit, a.k.a. the Java file model. */ + private final ICompilationUnit mUnit; + private final int mSelectionStart; + private final int mSelectionEnd; + /** The actual string selected, after UTF characters have been escaped, good for display. */ + private String mTokenString; + /** Start position of the string token in the source buffer. */ + private int mTokenStart; + /** End position of the string token in the source buffer. */ + private int mTokenEnd; + private String mXmlStringId; + private String mTargetXmlFileWsPath; + private HashMap> mResIdCache; + private XPath mXPath; + private ArrayList mChanges; + + public ExtractStringRefactoring(Map arguments) + throws NullPointerException { + mUnit = (ICompilationUnit) JavaCore.create(arguments.get("CU")); //$NON-NLS-1$ + mSelectionStart = Integer.parseInt(arguments.get("sel-start")); //$NON-NLS-1$ + mSelectionEnd = Integer.parseInt(arguments.get("sel-end")); //$NON-NLS-1$ + mTokenStart = Integer.parseInt(arguments.get("tok-start")); //$NON-NLS-1$ + mTokenEnd = Integer.parseInt(arguments.get("tok-end")); //$NON-NLS-1$ + mTokenString = arguments.get("tok-esc"); //$NON-NLS-1$ + } + + private Map createArgumentMap() { + HashMap args = new HashMap(); + args.put("CU", mUnit.getHandleIdentifier()); //$NON-NLS-1$ + args.put("sel-start", Integer.toString(mSelectionStart)); //$NON-NLS-1$ + args.put("sel-end", Integer.toString(mSelectionEnd)); //$NON-NLS-1$ + args.put("tok-start", Integer.toString(mTokenStart)); //$NON-NLS-1$ + args.put("tok-end", Integer.toString(mTokenEnd)); //$NON-NLS-1$ + args.put("tok-esc", mTokenString); //$NON-NLS-1$ + return args; + } + + public ExtractStringRefactoring(ICompilationUnit unit, ITextSelection selection) { + mUnit = unit; + + mSelectionStart = selection.getOffset(); + mSelectionEnd = mSelectionStart + selection.getLength(); + } + + /** + * @see org.eclipse.ltk.core.refactoring.Refactoring#getName() + */ + @Override + public String getName() { + return "Extract Android String"; + } + + /** + * Gets the actual string selected, after UTF characters have been escaped, + * good for display. + */ + public String getTokenString() { + return mTokenString; + } + + /** + * Step 1 of 3 of the refactoring: + * Checks that the current selection meets the initial condition before the ExtractString + * wizard is shown. The check is supposed to be lightweight and quick. Note that at that + * point the wizard has not been created yet. + *

          + * Here we scan the source buffer to find the token matching the selection. + * The check is successful is a Java string literal is selected, the source is in sync + * and is not read-only. + *

          + * This is also used to extract the string to be modified, so that we can display it in + * the refactoring wizard. + * + * @see org.eclipse.ltk.core.refactoring.Refactoring#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor) + * + * @throws CoreException + */ + @Override + public RefactoringStatus checkInitialConditions(IProgressMonitor monitor) + throws CoreException, OperationCanceledException { + + mTokenString = null; + mTokenStart = -1; + mTokenEnd = -1; + + RefactoringStatus status = new RefactoringStatus(); + + try { + monitor.beginTask("Checking preconditions...", 3); + + if (!extraChecks(monitor, status)) { + return status; + } + + try { + IBuffer buffer = mUnit.getBuffer(); + + IScanner scanner = ToolFactory.createScanner( + false, //tokenizeComments + false, //tokenizeWhiteSpace + false, //assertMode + false //recordLineSeparator + ); + scanner.setSource(buffer.getCharacters()); + monitor.worked(1); + + for(int token = scanner.getNextToken(); + token != ITerminalSymbols.TokenNameEOF; + token = scanner.getNextToken()) { + if (scanner.getCurrentTokenStartPosition() <= mSelectionStart && + scanner.getCurrentTokenEndPosition() >= mSelectionEnd) { + // found the token, but only keep of the right type + if (token == ITerminalSymbols.TokenNameStringLiteral) { + mTokenString = new String(scanner.getCurrentTokenSource()); + mTokenStart = scanner.getCurrentTokenStartPosition(); + mTokenEnd = scanner.getCurrentTokenEndPosition(); + } + break; + } else if (scanner.getCurrentTokenStartPosition() > mSelectionEnd) { + // scanner is past the selection, abort. + break; + } + } + } catch (JavaModelException e1) { + // Error in mUnit.getBuffer. Ignore. + } catch (InvalidInputException e2) { + // Error in scanner.getNextToken. Ignore. + } finally { + monitor.worked(1); + } + + if (mTokenString != null) { + // As a literal string, the token should have surrounding quotes. Remove them. + int len = mTokenString.length(); + if (len > 0 && + mTokenString.charAt(0) == '"' && + mTokenString.charAt(len - 1) == '"') { + mTokenString = mTokenString.substring(1, len - 1); + } + // We need a non-empty string literal + if (mTokenString.length() == 0) { + mTokenString = null; + } + } + + if (mTokenString == null) { + status.addFatalError("Please select a Java string literal."); + } + + monitor.worked(1); + } finally { + monitor.done(); + } + + return status; + } + + /** + * Tests from org.eclipse.jdt.internal.corext.refactoringChecks#validateEdit() + * Might not be useful. + * + * @return False if caller should abort, true if caller should continue. + */ + private boolean extraChecks(IProgressMonitor monitor, RefactoringStatus status) { + // + IResource res = mUnit.getPrimary().getResource(); + if (res == null || res.getType() != IResource.FILE) { + status.addFatalError("Cannot access resource; only regular files can be used."); + return false; + } + monitor.worked(1); + + // check whether the source file is in sync + if (!res.isSynchronized(IResource.DEPTH_ZERO)) { + status.addFatalError("The file is not synchronized. Please save it first."); + return false; + } + monitor.worked(1); + + // make sure we can write to it. + ResourceAttributes resAttr = res.getResourceAttributes(); + if (mUnit.isReadOnly() || resAttr == null || resAttr.isReadOnly()) { + status.addFatalError("The file is read-only, please make it writeable first."); + return false; + } + monitor.worked(1); + + return true; + } + + /** + * Step 2 of 3 of the refactoring: + * Check the conditions once the user filled values in the refactoring wizard, + * then prepare the changes to be applied. + *

          + * In this case, most of the sanity checks are done by the wizard so essentially this + * should only be called if the wizard positively validated the user input. + * + * Here we do check that the target resource XML file either does not exists or + * is not read-only. + * + * @see org.eclipse.ltk.core.refactoring.Refactoring#checkFinalConditions(IProgressMonitor) + * + * @throws CoreException + */ + @Override + public RefactoringStatus checkFinalConditions(IProgressMonitor monitor) + throws CoreException, OperationCanceledException { + RefactoringStatus status = new RefactoringStatus(); + + try { + monitor.beginTask("Checking post-conditions...", 3); + + if (mXmlStringId == null || mXmlStringId.length() <= 0) { + // this is not supposed to happen + status.addFatalError("Missing replacement string ID"); + } else if (mTargetXmlFileWsPath == null || mTargetXmlFileWsPath.length() <= 0) { + // this is not supposed to happen + status.addFatalError("Missing target xml file path"); + } + monitor.worked(1); + + // Either that resource must not exist or it must be a writeable file. + IResource targetXml = getTargetXmlResource(mTargetXmlFileWsPath); + if (targetXml != null) { + if (targetXml.getType() != IResource.FILE) { + status.addFatalError( + String.format("XML file '%1$s' is not a file.", mTargetXmlFileWsPath)); + } else { + ResourceAttributes attr = targetXml.getResourceAttributes(); + if (attr != null && attr.isReadOnly()) { + status.addFatalError( + String.format("XML file '%1$s' is read-only.", + mTargetXmlFileWsPath)); + } + } + } + monitor.worked(1); + + if (status.hasError()) { + return status; + } + + mChanges = new ArrayList(); + + + // Prepare the change for the XML file. + + if (!isResIdDuplicate(mTargetXmlFileWsPath, mXmlStringId)) { + // We actually change it only if the ID doesn't exist yet + TextFileChange xmlChange = new TextFileChange(getName(), (IFile) targetXml); + xmlChange.setTextType("xml"); //$NON-NLS-1$ + TextEdit edit = createXmlEdit((IFile) targetXml, mXmlStringId, mTokenString); + if (edit == null) { + status.addFatalError(String.format("Failed to modify file %1$s", + mTargetXmlFileWsPath)); + } + xmlChange.setEdit(edit); + mChanges.add(xmlChange); + } + monitor.worked(1); + + if (status.hasError()) { + return status; + } + + // Prepare the change to the Java compilation unit + List changes = computeJavaChanges(mUnit, mXmlStringId, mTokenString, status, + SubMonitor.convert(monitor, 1)); + if (changes != null) { + mChanges.addAll(changes); + } + + monitor.worked(1); + } finally { + monitor.done(); + } + + return status; + } + + /** + * Internal helper that actually prepares the {@link TextEdit} that adds the given + * ID to the given XML File. + *

          + * This does not actually modify the file. + * + * @param xmlFile The file resource to modify. + * @param replacementStringId The new ID to insert. + * @param oldString The old string, which will be the value in the XML string. + * @return A new {@link TextEdit} that describes how to change the file. + */ + private TextEdit createXmlEdit(IFile xmlFile, String replacementStringId, String oldString) { + + if (!xmlFile.exists()) { + // The XML file does not exist. Simply create it. + StringBuilder content = new StringBuilder(); + content.append("\n"); //$NON-NLS-1$ + content.append("\n"); //$NON-NLS-1$ + content.append(" "). //$NON-NLS-1$ + append(oldString). + append("\n"); //$NON-NLS-1$ + content.append("\n"); //$NON-NLS-1$ + + return new InsertEdit(0, content.toString()); + } + + // The file exist. Attempt to parse it as a valid XML document. + try { + int[] indices = new int[2]; + if (findXmlOpeningTagPos(xmlFile.getContents(), "resources", indices)) { //$NON-NLS-1$ + // Indices[1] indicates whether we found > or />. It can only be 1 or 2. + // Indices[0] is the position of the first character of either > or />. + // + // Note: we don't even try to adapt our formatting to the existing structure (we + // could by capturing whatever whitespace is after the closing bracket and + // applying it here before our tag, unless we were dealing with an empty + // resource tag.) + + int offset = indices[0]; + int len = indices[1]; + StringBuilder content = new StringBuilder(); + content.append(">\n"); //$NON-NLS-1$ + content.append(" "). //$NON-NLS-1$ + append(oldString). + append(""); //$NON-NLS-1$ + if (len == 2) { + content.append("\n"); //$NON-NLS-1$ + } + + return new ReplaceEdit(offset, len, content.toString()); + } + + } catch (CoreException e) { + // Failed to read file. Ignore. Will return null below. + } + + return null; + } + + /** + * Parse an XML input stream, looking for an opening tag. + *

          + * If found, returns the character offet in the buffer of the closing bracket of that + * tag, e.g. the position of > in "". The first character is at offset 0. + *

          + * The implementation here relies on a simple character-based parser. No DOM nor SAX + * parsing is used, due to the simplified nature of the task: we just want the first + * opening tag, which in our case should be the document root. We deal however with + * with the tag being commented out, so comments are skipped. We assume the XML doc + * is sane, e.g. we don't expect the tag to appear in the middle of a string. But + * again since in fact we want the root element, that's unlikely to happen. + *

          + * We need to deal with the case where the element is written as , in + * which case the caller will want to replace /> by ">...". To do that we return + * two values: the first offset of the closing tag (e.g. / or >) and the length, which + * can only be 1 or 2. If it's 2, the caller have to deal with /> instead of just >. + * + * @param contents An existing buffer to parse. + * @param tag The tag to look for. + * @param indices The return values: [0] is the offset of the closing bracket and [1] is + * the length which can be only 1 for > and 2 for /> + * @return True if we found the tag, in which case indices can be used. + */ + private boolean findXmlOpeningTagPos(InputStream contents, String tag, int[] indices) { + + BufferedReader br = new BufferedReader(new InputStreamReader(contents)); + StringBuilder sb = new StringBuilder(); // scratch area + + tag = "<" + tag; + int tagLen = tag.length(); + int maxLen = tagLen < 3 ? 3 : tagLen; + + try { + int offset = 0; + int i = 0; + char searching = '<'; // we want opening tags + boolean capture = false; + boolean inComment = false; + boolean inTag = false; + while ((i = br.read()) != -1) { + char c = (char) i; + if (c == searching) { + capture = true; + } + if (capture) { + sb.append(c); + int len = sb.length(); + if (inComment && c == '>') { + // is the comment being closed? + if (len >= 3 && sb.substring(len-3).equals("-->")) { //$NON-NLS-1$ + // yes, comment is closing, stop capturing + capture = false; + inComment = false; + sb.setLength(0); + } + } else if (inTag && c == '>') { + // we're capturing in our tag, waiting for the closing >, we just got it + // so we're totally done here. Simply detect whether it's /> or >. + indices[0] = offset; + indices[1] = 1; + if (sb.charAt(len - 2) == '/') { + indices[0]--; + indices[1]++; + } + return true; + + } else if (!inComment && !inTag) { + // not a comment and not our tag yet, so we're capturing because a + // tag is being opened but we don't know which one yet. + + // look for either the opening or a comment or + // the opening of our tag. + if (len == 3 && sb.equals("<--")) { //$NON-NLS-1$ + inComment = true; + } else if (len == tagLen && sb.toString().equals(tag)) { + inTag = true; + } + + // if we're not interested in this tag yet, deal with when to stop + // capturing: the opening tag ends with either any kind of whitespace + // or with a > or maybe there's a PI that starts with ' || c == '?' || c == ' ' || c == '\n' || c == '\r') { + // stop capturing + capture = false; + sb.setLength(0); + } + } + } + + if (capture && len > maxLen) { + // in any case we don't need to capture more than the size of our tag + // or the comment opening tag + sb.deleteCharAt(0); + } + } + offset++; + } + } catch (IOException e) { + // Ignore. + } finally { + try { + br.close(); + } catch (IOException e) { + // oh come on... + } + } + + return false; + } + + private List computeJavaChanges(ICompilationUnit unit, + String xmlStringId, + String tokenString, + RefactoringStatus status, + SubMonitor subMonitor) { + + // Get the Android package name from the Android Manifest. We need it to create + // the FQCN of the R class. + String packageName = null; + String error = null; + IProject proj = unit.getJavaProject().getProject(); + IResource manifestFile = proj.findMember(AndroidConstants.FN_ANDROID_MANIFEST); + if (manifestFile == null || manifestFile.getType() != IResource.FILE) { + error = "File not found"; + } else { + try { + AndroidManifestParser manifest = AndroidManifestParser.parseForData( + (IFile) manifestFile); + if (manifest == null) { + error = "Invalid content"; + } else { + packageName = manifest.getPackage(); + if (packageName == null) { + error = "Missing package definition"; + } + } + } catch (CoreException e) { + error = e.getLocalizedMessage(); + } + } + + if (error != null) { + status.addFatalError( + String.format("Failed to parse file %1$s: %2$s.", + manifestFile.getFullPath(), error)); + return null; + } + + // TODO in a future version we might want to collect various Java files that + // need to be updated in the same project and process them all together. + // To do that we need to use an ASTRequestor and parser.createASTs, kind of + // like this: + // + // ASTRequestor requestor = new ASTRequestor() { + // @Override + // public void acceptAST(ICompilationUnit sourceUnit, CompilationUnit astNode) { + // super.acceptAST(sourceUnit, astNode); + // // TODO process astNode + // } + // }; + // ... + // parser.createASTs(compilationUnits, bindingKeys, requestor, monitor) + // + // and then add multiple TextFileChange to the changes arraylist. + + // Right now the changes array will contain one TextFileChange at most. + ArrayList changes = new ArrayList(); + + // This is the unit that will be modified. + TextFileChange change = new TextFileChange(getName(), (IFile) unit.getResource()); + change.setTextType("java"); //$NON-NLS-1$ + + // Create an AST for this compilation unit + ASTParser parser = ASTParser.newParser(AST.JLS3); + parser.setProject(unit.getJavaProject()); + parser.setSource(unit); + parser.setResolveBindings(true); + ASTNode node = parser.createAST(subMonitor.newChild(1)); + + // The ASTNode must be a CompilationUnit, by design + if (!(node instanceof CompilationUnit)) { + status.addFatalError(String.format("Internal error: ASTNode class %s", //$NON-NLS-1$ + node.getClass())); + return null; + } + + // ImportRewrite will allow us to add the new type to the imports and will resolve + // what the Java source must reference, e.g. the FQCN or just the simple name. + ImportRewrite ir = ImportRewrite.create((CompilationUnit) node, true); + String Rqualifier = packageName + ".R"; //$NON-NLS-1$ + Rqualifier = ir.addImport(Rqualifier); + + // Rewrite the AST itself via an ASTVisitor + AST ast = node.getAST(); + ASTRewrite ar = ASTRewrite.create(ast); + ReplaceStringsVisitor visitor = new ReplaceStringsVisitor(ast, ar, + tokenString, Rqualifier, xmlStringId); + node.accept(visitor); + + // Finally prepare the change set + try { + MultiTextEdit edit = new MultiTextEdit(); + + // Create the edit to change the imports, only if anything changed + TextEdit subEdit = ir.rewriteImports(subMonitor.newChild(1)); + if (subEdit.hasChildren()) { + edit.addChild(subEdit); + } + + // Create the edit to change the Java source, only if anything changed + subEdit = ar.rewriteAST(); + if (subEdit.hasChildren()) { + edit.addChild(subEdit); + } + + // Only create a change set if any edit was collected + if (edit.hasChildren()) { + change.setEdit(edit); + changes.add(change); + } + + // TODO to modify another Java source, loop back to the creation of the + // TextFileChange and accumulate in changes. Right now only one source is + // modified. + + if (changes.size() > 0) { + return changes; + } + + } catch (CoreException e) { + // ImportRewrite.rewriteImports failed. + status.addFatalError(e.getMessage()); + } + return null; + } + + public class ReplaceStringsVisitor extends ASTVisitor { + + private final AST mAst; + private final ASTRewrite mRewriter; + private final String mOldString; + private final String mRQualifier; + private final String mXmlId; + + public ReplaceStringsVisitor(AST ast, + ASTRewrite astRewrite, + String oldString, + String rQualifier, + String xmlId) { + mAst = ast; + mRewriter = astRewrite; + mOldString = oldString; + mRQualifier = rQualifier; + mXmlId = xmlId; + } + + @Override + public boolean visit(StringLiteral node) { + if (node.getLiteralValue().equals(mOldString)) { + + Name qualifierName = mAst.newName(mRQualifier + ".string"); //$NON-NLS-1$ + SimpleName idName = mAst.newSimpleName(mXmlId); + QualifiedName newNode = mAst.newQualifiedName(qualifierName, idName); + + TextEditGroup editGroup = new TextEditGroup(getName()); + mRewriter.replace(node, newNode, editGroup); + } + return super.visit(node); + } + } + + /** + * Step 3 of 3 of the refactoring: returns the {@link Change} that will be able to do the + * work and creates a descriptor that can be used to replay that refactoring later. + * + * @see org.eclipse.ltk.core.refactoring.Refactoring#createChange(org.eclipse.core.runtime.IProgressMonitor) + * + * @throws CoreException + */ + @Override + public Change createChange(IProgressMonitor monitor) + throws CoreException, OperationCanceledException { + + try { + monitor.beginTask("Applying changes...", 1); + + CompositeChange change = new CompositeChange( + getName(), + mChanges.toArray(new Change[mChanges.size()])) { + @Override + public ChangeDescriptor getDescriptor() { + + String comment = String.format( + "Extracts string '%1$s' into R.string.%2$s", + mTokenString, + mXmlStringId); + + ExtractStringDescriptor desc = new ExtractStringDescriptor( + mUnit.getJavaProject().getElementName(), //project + comment, //description + comment, //comment + createArgumentMap()); + + return new RefactoringChangeDescriptor(desc); + } + }; + + monitor.worked(1); + + return change; + + } finally { + monitor.done(); + } + + } + + /** + * Utility method used by the wizard to check whether the given string ID is already + * defined in the XML file which path is given. + * + * @param xmlFileWsPath The project path of the XML file, e.g. "/res/values/strings.xml". + * The given file may or may not exist. + * @param stringId The string ID to find. + * @return True if such a string ID is already defined. + */ + public boolean isResIdDuplicate(String xmlFileWsPath, String stringId) { + // This is going to be called many times on the same file. + // Build a cache of the existing IDs for a given file. + if (mResIdCache == null) { + mResIdCache = new HashMap>(); + } + HashSet cache = mResIdCache.get(xmlFileWsPath); + if (cache == null) { + cache = getResIdsForFile(xmlFileWsPath); + mResIdCache.put(xmlFileWsPath, cache); + } + + return cache.contains(stringId); + } + + /** + * Extract all the defined string IDs from a given file using XPath. + * + * @param xmlFileWsPath The project path of the file to parse. It may not exist. + * @return The set of all string IDs defined in the file. The returned set is always non + * null. It is empty if the file does not exist. + */ + private HashSet getResIdsForFile(String xmlFileWsPath) { + HashSet ids = new HashSet(); + + if (mXPath == null) { + mXPath = AndroidXPathFactory.newXPath(); + } + + // Access the project that contains the resource that contains the compilation unit + IResource resource = getTargetXmlResource(xmlFileWsPath); + + if (resource != null && resource.exists() && resource.getType() == IResource.FILE) { + InputSource source; + try { + source = new InputSource(((IFile) resource).getContents()); + + // We want all the IDs in an XML structure like this: + // + // something + // + + String xpathExpr = "/resources/string/@name"; //$NON-NLS-1$ + + Object result = mXPath.evaluate(xpathExpr, source, XPathConstants.NODESET); + if (result instanceof NodeList) { + NodeList list = (NodeList) result; + for (int n = list.getLength() - 1; n >= 0; n--) { + String id = list.item(n).getNodeValue(); + ids.add(id); + } + } + + } catch (CoreException e1) { + // IFile.getContents failed. Ignore. + } catch (XPathExpressionException e) { + // mXPath.evaluate failed. Ignore. + } + } + + return ids; + } + + /** + * Given a file project path, returns its resource in the same project than the + * compilation unit. The resource may not exist. + */ + private IResource getTargetXmlResource(String xmlFileWsPath) { + IProject proj = mUnit.getPrimary().getResource().getProject(); + Path path = new Path(xmlFileWsPath); + IResource resource = proj.findMember(path); + return resource; + } + + /** + * Sets the replacement string ID. Used by the wizard to set the user input. + */ + public void setReplacementStringId(String replacementStringId) { + mXmlStringId = replacementStringId; + } + + /** + * Sets the target file. This is a project path, e.g. "/res/values/strings.xml". + * Used by the wizard to set the user input. + */ + public void setTargetFile(String targetXmlFileWsPath) { + mTargetXmlFileWsPath = targetXmlFileWsPath; + } + +} diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java new file mode 100644 index 000000000..2083a6e5d --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2009 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.refactorings.extractstring; + +import org.eclipse.ltk.ui.refactoring.RefactoringWizard; + +/** + * A wizard for ExtractString based on a simple dialog with one page. + * + * @see ExtractStringInputPage + * @see ExtractStringRefactoring + */ +class ExtractStringWizard extends RefactoringWizard { + + /** + * Create a wizard for ExtractString based on a simple dialog with one page. + */ + public ExtractStringWizard(ExtractStringRefactoring ref, String title) { + super(ref, DIALOG_BASED_USER_INTERFACE | PREVIEW_EXPAND_FIRST_NODE); + setDefaultPageTitle(title); + } + + @Override + protected void addUserInputPages() { + addPage(new ExtractStringInputPage()); + } + +} diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java index 5abfd811d..1da753c7b 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java @@ -180,6 +180,8 @@ public class AndroidConstants { public final static String CLASS_BROADCASTRECEIVER = "android.content.BroadcastReceiver"; //$NON-NLS-1$ public final static String CLASS_CONTENTPROVIDER = "android.content.ContentProvider"; //$NON-NLS-1$ public final static String CLASS_INSTRUMENTATION = "android.app.Instrumentation"; //$NON-NLS-1$ + public final static String CLASS_INSTRUMENTATION_RUNNER = + "android.test.InstrumentationTestRunner"; //$NON-NLS-1$ public final static String CLASS_BUNDLE = "android.os.Bundle"; //$NON-NLS-1$ public final static String CLASS_R = "android.R"; //$NON-NLS-1$ public final static String CLASS_MANIFEST_PERMISSION = "android.Manifest$permission"; //$NON-NLS-1$ @@ -215,4 +217,5 @@ public class AndroidConstants { /** The base URL where to find the Android class & manifest documentation */ public static final String CODESITE_BASE_URL = "http://code.google.com/android"; //$NON-NLS-1$ + public static final String LIBRARY_TEST_RUNNER = "android.test.runner"; // $NON-NLS-1$ } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java index 42c881b15..fa7e9b9f4 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java @@ -16,6 +16,7 @@ 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.project.XmlErrorHandler.XmlErrorListener; import com.android.sdklib.SdkConstants; @@ -255,12 +256,8 @@ public class AndroidManifestParser { } catch (NumberFormatException e) { handleError(e, -1 /* lineNumber */); } - } else if (NODE_INSTRUMENTATION.equals(localName)) { - value = getAttributeValue(attributes, ATTRIBUTE_NAME, - true /* hasNamespace */); - if (value != null) { - mInstrumentations.add(value); - } + } else if (NODE_INSTRUMENTATION.equals(localName)) { + processInstrumentationNode(attributes); } break; case LEVEL_ACTIVITY: @@ -449,6 +446,25 @@ public class AndroidManifestParser { addProcessName(processName); } } + + /** + * Processes the instrumentation nodes. + * @param attributes the attributes for the activity node. + * node is representing + */ + private void processInstrumentationNode(Attributes attributes) { + // lets get the class name, and check it if required. + String instrumentationName = getAttributeValue(attributes, ATTRIBUTE_NAME, + true /* hasNamespace */); + if (instrumentationName != null) { + String instrClassName = combinePackageAndClassName(mPackage, instrumentationName); + mInstrumentations.add(instrClassName); + if (mMarkErrors) { + checkClass(instrClassName, AndroidConstants.CLASS_INSTRUMENTATION, + true /* testVisibility */); + } + } + } /** * Checks that a class is valid and can be used in the Android Manifest. @@ -484,8 +500,7 @@ public class AndroidManifestParser { } catch (CoreException e) { } } - } - + } } /** @@ -565,7 +580,6 @@ public class AndroidManifestParser { ManifestHandler manifestHandler = new ManifestHandler(manifestFile, errorListener, gatherData, javaProject, markErrors); - parser.parse(new InputSource(manifestFile.getContents()), manifestHandler); // get the result from the handler @@ -576,14 +590,19 @@ public class AndroidManifestParser { manifestHandler.getApiLevelRequirement(), manifestHandler.getInstrumentations(), manifestHandler.getUsesLibraries()); } catch (ParserConfigurationException e) { + AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), + "Bad parser configuration for %s", manifestFile.getFullPath()); } catch (SAXException e) { + AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), + "Parser exception for %s", manifestFile.getFullPath()); } catch (IOException e) { - } finally { - } + AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), + "I/O error for %s", manifestFile.getFullPath()); + } return null; } - + /** * Parses the Android Manifest, and returns an object containing the result of the parsing. *

          @@ -619,11 +638,15 @@ public class AndroidManifestParser { manifestHandler.getApiLevelRequirement(), manifestHandler.getInstrumentations(), manifestHandler.getUsesLibraries()); } catch (ParserConfigurationException e) { + AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), + "Bad parser configuration for %s", manifestFile.getAbsolutePath()); } catch (SAXException e) { + AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), + "Parser exception for %s", manifestFile.getAbsolutePath()); } catch (IOException e) { - } finally { - } - + AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), + "I/O error for %s", manifestFile.getAbsolutePath()); + } return null; } @@ -646,10 +669,12 @@ public class AndroidManifestParser { boolean gatherData, boolean markErrors) throws CoreException { + + IFile manifestFile = getManifest(javaProject.getProject()); + try { SAXParser parser = sParserFactory.newSAXParser(); - - IFile manifestFile = getManifest(javaProject.getProject()); + if (manifestFile != null) { ManifestHandler manifestHandler = new ManifestHandler(manifestFile, errorListener, gatherData, javaProject, markErrors); @@ -664,10 +689,15 @@ public class AndroidManifestParser { manifestHandler.getInstrumentations(), manifestHandler.getUsesLibraries()); } } catch (ParserConfigurationException e) { + AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), + "Bad parser configuration for %s", manifestFile.getFullPath()); } catch (SAXException e) { + AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), + "Parser exception for %s", manifestFile.getFullPath()); } catch (IOException e) { - } finally { - } + AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), + "I/O error for %s", manifestFile.getFullPath()); + } return null; } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java index 9c529e5da..12d49fe27 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java @@ -61,8 +61,10 @@ import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.LanguageReg import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.MobileCodeVerifier; import com.android.layoutlib.api.ILayoutLog; import com.android.layoutlib.api.ILayoutResult; +import com.android.layoutlib.api.IProjectCallback; import com.android.layoutlib.api.IResourceValue; import com.android.layoutlib.api.IStyleResourceValue; +import com.android.layoutlib.api.IXmlPullParser; import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo; import com.android.sdklib.IAndroidTarget; @@ -222,7 +224,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor // updateUiFromFramework will reset language/region combo, so we must call // setConfiguration after, or the settext on language/region will be lost. if (mEditedConfig != null) { - setConfiguration(mEditedConfig); + setConfiguration(mEditedConfig, false /*force*/); } // make sure we remove the custom view loader, since its parent class loader is the @@ -867,7 +869,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor @Override void editNewFile(FolderConfiguration configuration) { // update the configuration UI - setConfiguration(configuration); + setConfiguration(configuration, true /*force*/); // enable the create button if the current and edited config are not equals mCreateButton.setEnabled(mEditedConfig.equals(mCurrentConfig) == false); @@ -975,18 +977,14 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor 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, + ILayoutResult result = computeLayout(bridge, parser, null /* projectKey */, - 300 /* width */, 300 /* height */, theme, + 300 /* width */, 300 /* height */, 160 /*density*/, + 160.f /*xdpi*/, 160.f /*ydpi*/, theme, + themeIndex >= mPlatformThemeCount /*isProjectTheme*/, configuredProjectResources, frameworkResources, projectCallback, null /* logger */); @@ -1073,11 +1071,14 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor /** * Update the UI controls state with a given {@link FolderConfiguration}. - *

          If a qualifier is not present in the {@link FolderConfiguration} object, the UI control - * is not modified. However if the value in the control is not the default value, a warning - * icon is showed. + *

          If force is set to true the UI will be changed to exactly reflect + * config, otherwise, if a qualifier is not present in config, + * the UI control is not modified. However if the value in the control is not the default value, + * a warning icon is shown. + * @param config The {@link FolderConfiguration} to set. + * @param force Whether the UI should be changed to exactly match the received configuration. */ - void setConfiguration(FolderConfiguration config) { + void setConfiguration(FolderConfiguration config, boolean force) { mDisableUpdates = true; // we do not want to trigger onXXXChange when setting new values in the widgets. mEditedConfig = config; @@ -1088,6 +1089,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor if (countryQualifier != null) { mCountry.setText(String.format("%1$d", countryQualifier.getCode())); mCurrentConfig.setCountryCodeQualifier(countryQualifier); + } else if (force) { + mCountry.setText(""); //$NON-NLS-1$ + mCurrentConfig.setCountryCodeQualifier(null); } else if (mCountry.getText().length() > 0) { mCountryIcon.setImage(mWarningImage); } @@ -1097,6 +1101,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor if (networkQualifier != null) { mNetwork.setText(String.format("%1$d", networkQualifier.getCode())); mCurrentConfig.setNetworkCodeQualifier(networkQualifier); + } else if (force) { + mNetwork.setText(""); //$NON-NLS-1$ + mCurrentConfig.setNetworkCodeQualifier(null); } else if (mNetwork.getText().length() > 0) { mNetworkIcon.setImage(mWarningImage); } @@ -1106,6 +1113,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor if (languageQualifier != null) { mLanguage.setText(languageQualifier.getValue()); mCurrentConfig.setLanguageQualifier(languageQualifier); + } else if (force) { + mLanguage.setText(""); //$NON-NLS-1$ + mCurrentConfig.setLanguageQualifier(null); } else if (mLanguage.getText().length() > 0) { mLanguageIcon.setImage(mWarningImage); } @@ -1115,6 +1125,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor if (regionQualifier != null) { mRegion.setText(regionQualifier.getValue()); mCurrentConfig.setRegionQualifier(regionQualifier); + } else if (force) { + mRegion.setText(""); //$NON-NLS-1$ + mCurrentConfig.setRegionQualifier(null); } else if (mRegion.getText().length() > 0) { mRegionIcon.setImage(mWarningImage); } @@ -1125,6 +1138,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor mOrientation.select( ScreenOrientation.getIndex(orientationQualifier.getValue()) + 1); mCurrentConfig.setScreenOrientationQualifier(orientationQualifier); + } else if (force) { + mOrientation.select(0); + mCurrentConfig.setScreenOrientationQualifier(null); } else if (mOrientation.getSelectionIndex() != 0) { mOrientationIcon.setImage(mWarningImage); } @@ -1134,6 +1150,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor if (densityQualifier != null) { mDensity.setText(String.format("%1$d", densityQualifier.getValue())); mCurrentConfig.setPixelDensityQualifier(densityQualifier); + } else if (force) { + mDensity.setText(""); //$NON-NLS-1$ + mCurrentConfig.setPixelDensityQualifier(null); } else if (mDensity.getText().length() > 0) { mDensityIcon.setImage(mWarningImage); } @@ -1143,6 +1162,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor if (touchQualifier != null) { mTouch.select(TouchScreenType.getIndex(touchQualifier.getValue()) + 1); mCurrentConfig.setTouchTypeQualifier(touchQualifier); + } else if (force) { + mTouch.select(0); + mCurrentConfig.setTouchTypeQualifier(null); } else if (mTouch.getSelectionIndex() != 0) { mTouchIcon.setImage(mWarningImage); } @@ -1152,6 +1174,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor if (keyboardQualifier != null) { mKeyboard.select(KeyboardState.getIndex(keyboardQualifier.getValue()) + 1); mCurrentConfig.setKeyboardStateQualifier(keyboardQualifier); + } else if (force) { + mKeyboard.select(0); + mCurrentConfig.setKeyboardStateQualifier(null); } else if (mKeyboard.getSelectionIndex() != 0) { mKeyboardIcon.setImage(mWarningImage); } @@ -1161,6 +1186,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor if (inputQualifier != null) { mTextInput.select(TextInputMethod.getIndex(inputQualifier.getValue()) + 1); mCurrentConfig.setTextInputMethodQualifier(inputQualifier); + } else if (force) { + mTextInput.select(0); + mCurrentConfig.setTextInputMethodQualifier(null); } else if (mTextInput.getSelectionIndex() != 0) { mTextInputIcon.setImage(mWarningImage); } @@ -1171,6 +1199,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor mNavigation.select( NavigationMethod.getIndex(navigationQualifiter.getValue()) + 1); mCurrentConfig.setNavigationMethodQualifier(navigationQualifiter); + } else if (force) { + mNavigation.select(0); + mCurrentConfig.setNavigationMethodQualifier(null); } else if (mNavigation.getSelectionIndex() != 0) { mNavigationIcon.setImage(mWarningImage); } @@ -1181,6 +1212,10 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor mSize1.setText(String.format("%1$d", sizeQualifier.getValue1())); mSize2.setText(String.format("%1$d", sizeQualifier.getValue2())); mCurrentConfig.setScreenDimensionQualifier(sizeQualifier); + } else if (force) { + mSize1.setText(""); //$NON-NLS-1$ + mSize2.setText(""); //$NON-NLS-1$ + mCurrentConfig.setScreenDimensionQualifier(null); } else if (mSize1.getText().length() > 0 && mSize2.getText().length() > 0) { mSizeIcon.setImage(mWarningImage); } @@ -1607,7 +1642,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor // at this point, we have not opened a new file. // update the configuration icons with the new edited config. - setConfiguration(mEditedConfig); + setConfiguration(mEditedConfig, false /*force*/); // enable the create button if the current and edited config are not equals mCreateButton.setEnabled(mEditedConfig.equals(mCurrentConfig) == false); @@ -1794,45 +1829,16 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor // Compute the layout UiElementPullParser parser = new UiElementPullParser(getModel()); Rectangle rect = getBounds(); - ILayoutResult result = null; - if (bridge.apiLevel >= 3) { - // call the new api with proper theme differentiator and - // density/dpi support. - boolean isProjectTheme = themeIndex >= mPlatformThemeCount; + boolean isProjectTheme = themeIndex >= mPlatformThemeCount; - // FIXME pass the density/dpi from somewhere (resource config or skin). - result = bridge.bridge.computeLayout(parser, - iProject /* projectKey */, - rect.width, rect.height, 160, 160.f, 160.f, - theme, isProjectTheme, - mConfiguredProjectRes, frameworkResources, mProjectCallback, - mLogger); - } else if (bridge.apiLevel == 2) { - // api with boolean for separation of project/framework theme - boolean isProjectTheme = themeIndex >= mPlatformThemeCount; + // FIXME pass the density/dpi from somewhere (resource config or skin). + ILayoutResult result = computeLayout(bridge, parser, + iProject /* projectKey */, + rect.width, rect.height, 160, 160.f, 160.f, + theme, isProjectTheme, + mConfiguredProjectRes, frameworkResources, mProjectCallback, + mLogger); - result = bridge.bridge.computeLayout(parser, - iProject /* projectKey */, - rect.width, rect.height, theme, isProjectTheme, - mConfiguredProjectRes, frameworkResources, mProjectCallback, - mLogger); - } else { - // oldest api with no density/dpi, and project theme boolean mixed - // into the theme name. - - // change the string if it's a custom theme to make sure we can - // differentiate them - if (themeIndex >= mPlatformThemeCount) { - theme = "*" + theme; //$NON-NLS-1$ - } - - 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()); @@ -2383,4 +2389,48 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor return null; } + + /** + * Computes a layout by calling the correct computeLayout method of ILayoutBridge based on + * the implementation API level. + */ + @SuppressWarnings("deprecation") + private ILayoutResult computeLayout(LayoutBridge bridge, + IXmlPullParser layoutDescription, Object projectKey, + int screenWidth, int screenHeight, int density, float xdpi, float ydpi, + String themeName, boolean isProjectTheme, + Map> projectResources, + Map> frameworkResources, + IProjectCallback projectCallback, ILayoutLog logger) { + + if (bridge.apiLevel >= 3) { + // newer api with boolean for separation of project/framework theme, + // and density support. + return bridge.bridge.computeLayout(layoutDescription, + projectKey, screenWidth, screenHeight, density, xdpi, ydpi, + themeName, isProjectTheme, + projectResources, frameworkResources, projectCallback, + logger); + } else if (bridge.apiLevel == 2) { + // api with boolean for separation of project/framework theme + return bridge.bridge.computeLayout(layoutDescription, + projectKey, screenWidth, screenHeight, themeName, isProjectTheme, + mConfiguredProjectRes, frameworkResources, mProjectCallback, + mLogger); + } else { + // oldest api with no density/dpi, and project theme boolean mixed + // into the theme name. + + // change the string if it's a custom theme to make sure we can + // differentiate them + if (isProjectTheme) { + themeName = "*" + themeName; //$NON-NLS-1$ + } + + return bridge.bridge.computeLayout(layoutDescription, + projectKey, screenWidth, screenHeight, themeName, + mConfiguredProjectRes, frameworkResources, mProjectCallback, + mLogger); + } + } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java index 262ef65a1..d86d585a3 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java @@ -15,7 +15,13 @@ */ package com.android.ide.eclipse.tests; +import com.android.ide.eclipse.common.AndroidConstants; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Platform; + import java.io.File; +import java.io.IOException; import java.net.URL; import java.util.logging.Logger; @@ -45,12 +51,29 @@ public class AdtTestData { // accessed normally mOsRootDataPath = System.getProperty("test_data"); if (mOsRootDataPath == null) { - sLogger.info("Cannot find test_data directory, init to class loader"); + sLogger.info("Cannot find test_data environment variable, init to class loader"); URL url = this.getClass().getClassLoader().getResource("data"); //$NON-NLS-1$ - mOsRootDataPath = url.getFile(); + + if (Platform.isRunning()) { + sLogger.info("Running as an Eclipse Plug-in JUnit test, using FileLocator"); + try { + mOsRootDataPath = FileLocator.resolve(url).getFile(); + } catch (IOException e) { + sLogger.warning("IOException while using FileLocator, reverting to url"); + mOsRootDataPath = url.getFile(); + } + } else { + sLogger.info("Running as an plain JUnit test, using url as-is"); + mOsRootDataPath = url.getFile(); + } } + + if (mOsRootDataPath.equals(AndroidConstants.WS_SEP + "data")) { + sLogger.warning("Resource data not found using class loader!, Defaulting to no path"); + } + if (!mOsRootDataPath.endsWith(File.separator)) { - sLogger.info("Fixing test_data env variable does not end with path separator"); + sLogger.info("Fixing test_data env variable (does not end with path separator)"); mOsRootDataPath = mOsRootDataPath.concat(File.separator); } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java index 516e448e6..7e8b0af10 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java @@ -16,17 +16,20 @@ package com.android.ide.eclipse.common.project; -import junit.framework.TestCase; +import com.android.ide.eclipse.tests.AdtTestData; -import com.android.ide.eclipse.mock.FileMock; +import junit.framework.TestCase; /** * Tests for {@link AndroidManifestParser} */ public class AndroidManifestParserTest extends TestCase { - private AndroidManifestParser mManifest; + private AndroidManifestParser mManifestTestApp; + private AndroidManifestParser mManifestInstrumentation; - private static final String PACKAGE_NAME = "com.android.testapp"; //$NON-NLS-1$ + private static final String INSTRUMENTATION_XML = "AndroidManifest-instrumentation.xml"; //$NON-NLS-1$ + private static final String TESTAPP_XML = "AndroidManifest-testapp.xml"; //$NON-NLS-1$ + private static final String PACKAGE_NAME = "com.android.testapp"; //$NON-NLS-1$ private static final String ACTIVITY_NAME = "com.android.testapp.MainActivity"; //$NON-NLS-1$ private static final String LIBRARY_NAME = "android.test.runner"; //$NON-NLS-1$ private static final String INSTRUMENTATION_NAME = "android.test.InstrumentationTestRunner"; //$NON-NLS-1$ @@ -35,60 +38,46 @@ public class AndroidManifestParserTest extends TestCase { protected void setUp() throws Exception { super.setUp(); - // create the test data - StringBuilder sb = new StringBuilder(); - sb.append("\n"); //$NON-NLS-1$ - sb.append("\n"); //$NON-NLS-1$ - sb.append(" \n"); //$NON-NLS-1$ - sb.append(" \n"); //$NON-NLS-1$ - sb.append(" \n"); //$NON-NLS-1$ - sb.append(" \n"); //$NON-NLS-1$ - sb.append(" \"\n"); //$NON-NLS-1$ - sb.append(" \n"); //$NON-NLS-1$ - sb.append(" \n"); //$NON-NLS-1$ - sb.append(" \n"); //$NON-NLS-1$ - sb.append(" \n"); //$NON-NLS-1$ - sb.append(" "); //$NON-NLS-1$ - sb.append(" \n"); - sb.append("\n"); //$NON-NLS-1$ - - FileMock mockFile = new FileMock("AndroidManifest.xml", sb.toString().getBytes()); + String testFilePath = AdtTestData.getInstance().getTestFilePath( + TESTAPP_XML); + mManifestTestApp = AndroidManifestParser.parseForData(testFilePath); + assertNotNull(mManifestTestApp); - mManifest = AndroidManifestParser.parseForData(mockFile); - assertNotNull(mManifest); + testFilePath = AdtTestData.getInstance().getTestFilePath( + INSTRUMENTATION_XML); + mManifestInstrumentation = AndroidManifestParser.parseForData(testFilePath); + assertNotNull(mManifestInstrumentation); } + public void testGetInstrumentationInformation() { + assertEquals(1, mManifestInstrumentation.getInstrumentations().length); + assertEquals(INSTRUMENTATION_NAME, mManifestTestApp.getInstrumentations()[0]); + } + public void testGetPackage() { - assertEquals("com.android.testapp", mManifest.getPackage()); + assertEquals(PACKAGE_NAME, mManifestTestApp.getPackage()); } public void testGetActivities() { - assertEquals(1, mManifest.getActivities().length); - assertEquals(ACTIVITY_NAME, mManifest.getActivities()[0]); + assertEquals(1, mManifestTestApp.getActivities().length); + assertEquals(ACTIVITY_NAME, mManifestTestApp.getActivities()[0]); } public void testGetLauncherActivity() { - assertEquals(ACTIVITY_NAME, mManifest.getLauncherActivity()); + assertEquals(ACTIVITY_NAME, mManifestTestApp.getLauncherActivity()); } public void testGetUsesLibraries() { - assertEquals(1, mManifest.getUsesLibraries().length); - assertEquals(LIBRARY_NAME, mManifest.getUsesLibraries()[0]); + assertEquals(1, mManifestTestApp.getUsesLibraries().length); + assertEquals(LIBRARY_NAME, mManifestTestApp.getUsesLibraries()[0]); } public void testGetInstrumentations() { - assertEquals(1, mManifest.getInstrumentations().length); - assertEquals(INSTRUMENTATION_NAME, mManifest.getInstrumentations()[0]); + assertEquals(1, mManifestTestApp.getInstrumentations().length); + assertEquals(INSTRUMENTATION_NAME, mManifestTestApp.getInstrumentations()[0]); + } + + public void testGetPackageName() { + assertEquals(PACKAGE_NAME, mManifestTestApp.getPackage()); } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-instrumentation.xml b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-instrumentation.xml new file mode 100644 index 000000000..b380f967e --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-instrumentation.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-testapp.xml b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-testapp.xml new file mode 100644 index 000000000..8ae70121c --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-testapp.xml @@ -0,0 +1,17 @@ + + + + + + + " + + + + + " + + \ No newline at end of file diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java index 65cbbe356..93577e42b 100644 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java @@ -177,7 +177,7 @@ public final class AvdManager { public AvdManager(SdkManager sdk, ISdkLog sdkLog) throws AndroidLocationException { mSdk = sdk; mSdkLog = sdkLog; - buildAvdList(); + buildAvdList(mAvdList); } /** @@ -201,6 +201,20 @@ public final class AvdManager { return null; } + + /** + * Reloads the AVD list. + * @throws AndroidLocationException if there was an error finding the location of the + * AVD folder. + */ + public void reloadAvds() throws AndroidLocationException { + // build the list in a temp list first, in case the method throws an exception. + // It's better than deleting the whole list before reading the new one. + ArrayList list = new ArrayList(); + buildAvdList(list); + mAvdList.clear(); + mAvdList.addAll(list); + } /** * Creates a new AVD. It is expected that there is no existing AVD with this name already. @@ -620,7 +634,7 @@ public final class AvdManager { } } - private void buildAvdList() throws AndroidLocationException { + private void buildAvdList(ArrayList list) throws AndroidLocationException { // get the Android prefs location. String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD; @@ -664,7 +678,7 @@ public final class AvdManager { for (File avd : avds) { AvdInfo info = parseAvdInfo(avd); if (info != null) { - mAvdList.add(info); + list.add(info); if (avdListDebug) { mSdkLog.printf("[AVD LIST DEBUG] Added AVD '%s'\n", info.getPath()); } From 0c4ee7741c94376599256f923c08dac18d090e97 Mon Sep 17 00:00:00 2001 From: Scott Tsai Date: Sat, 21 Mar 2009 07:41:30 +0800 Subject: [PATCH 12/39] Correctly expect the return value of rindex(const char*) to be of type 'const char*' to make the code build on gcc-4.4. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The C++ spec overloads string fucntions like strtsr and rindex so that rindex(char *) returns 'char*' and rindex(const char*) returns 'const char*'. Without this patch you get an "invalid conversion from ‘const char*’ to ‘char*’" error on gcc-4.4 --- emulator/qtools/trace_reader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emulator/qtools/trace_reader.cpp b/emulator/qtools/trace_reader.cpp index b38c0b418..0bf64ddba 100644 --- a/emulator/qtools/trace_reader.cpp +++ b/emulator/qtools/trace_reader.cpp @@ -1009,10 +1009,10 @@ void TraceReaderBase::ParseDexList(char *filename) // be freed by the caller after it is no longer needed. static char *ExtractDexPathFromMmap(const char *mmap_path) { - char *end = rindex(mmap_path, '@'); + const char *end = rindex(mmap_path, '@'); if (end == NULL) return NULL; - char *start = rindex(mmap_path, '/'); + const char *start = rindex(mmap_path, '/'); if (start == NULL) return NULL; int len = end - start; From eda65f5f60c01a6eb4f2e9e7ff79c59fe755c2f7 Mon Sep 17 00:00:00 2001 From: Scott Tsai Date: Sat, 21 Mar 2009 08:17:41 +0800 Subject: [PATCH 13/39] q2dm: correctly expect the return type of strchr(const char*) to be 'const char*' in C++ so that the code builds on gcc-4.4 ISO C++ overloads strchr() so that strchr(const char*) return 'const char*' and strchr(char *) return 'char *'. Since DmTrace::parseAndAddFunction really wants to write to its 'const char *name' argument I just casted a pointer pointing inside of 'name' to 'char*' --- emulator/qtools/dmtrace.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emulator/qtools/dmtrace.cpp b/emulator/qtools/dmtrace.cpp index 66e12a85e..c486c5fe9 100644 --- a/emulator/qtools/dmtrace.cpp +++ b/emulator/qtools/dmtrace.cpp @@ -164,7 +164,7 @@ void DmTrace::parseAndAddFunction(int functionId, const char *name) // sig = "()I" // Find the first parenthesis, the start of the signature. - char *paren = strchr(name, '('); + char *paren = (char*)strchr(name, '('); // If not found, then add the original name. if (paren == NULL) { @@ -181,7 +181,7 @@ void DmTrace::parseAndAddFunction(int functionId, const char *name) *paren = 0; // Search for the last period, the start of the method name - char *dot = strrchr(name, '.'); + char *dot = (char*)strrchr(name, '.'); // If not found, then add the original name. if (dot == NULL || dot == name) { From 41d58c4459c278d5a05841c379a2b92d0e8089f6 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet <> Date: Tue, 24 Mar 2009 17:27:48 -0700 Subject: [PATCH 14/39] Automated import from //branches/cupcake/...@141556,141556 --- .../com/android/ide/eclipse/adt/build/ApkBuilder.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java index bc5b01c5f..1edcf79fd 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java @@ -979,7 +979,10 @@ public class ApkBuilder extends BaseBuilder { writeStandardProjectResources(jarBuilder, javaProject, wsRoot, list); for (IJavaProject referencedJavaProject : referencedJavaProjects) { - if (referencedJavaProject.getProject().hasNature(AndroidConstants.NATURE)) { + // only include output from non android referenced project + // (This is to handle the case of reference Android projects in the context of + // instrumentation projects that need to reference the projects to be tested). + if (referencedJavaProject.getProject().hasNature(AndroidConstants.NATURE) == false) { writeStandardProjectResources(jarBuilder, referencedJavaProject, wsRoot, list); } } @@ -1084,7 +1087,10 @@ public class ApkBuilder extends BaseBuilder { IWorkspaceRoot wsRoot = ws.getRoot(); for (IJavaProject javaProject : referencedJavaProjects) { - if (javaProject.getProject().hasNature(AndroidConstants.NATURE)) { + // only include output from non android referenced project + // (This is to handle the case of reference Android projects in the context of + // instrumentation projects that need to reference the projects to be tested). + if (javaProject.getProject().hasNature(AndroidConstants.NATURE) == false) { // get the output folder IPath path = null; try { From 538e3100be6a2c605979422c54aede390b53ad46 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet <> Date: Tue, 24 Mar 2009 17:57:45 -0700 Subject: [PATCH 15/39] Automated import from //branches/cupcake/...@141592,141592 --- .../src/com/android/ant/AaptExecLoopTask.java | 11 +++++++---- tools/scripts/android_rules.xml | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java b/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java index d2c71624d..6444e4d01 100644 --- a/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java +++ b/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java @@ -181,11 +181,14 @@ public final class AaptExecLoopTask extends Task { task.createArg().setValue("-M"); task.createArg().setValue(mManifest); - // resources location - task.createArg().setValue("-S"); - task.createArg().setValue(mResources); + // resources location. This may not exists, and aapt doesn't like it, so we check first. + File res = new File(mResources); + if (res.isDirectory()) { + task.createArg().setValue("-S"); + task.createArg().setValue(mResources); + } - // assets location. this may not exists, and aapt doesn't like it, so we check first. + // assets location. This may not exists, and aapt doesn't like it, so we check first. File assets = new File(mAssets); if (assets.isDirectory()) { task.createArg().setValue("-A"); diff --git a/tools/scripts/android_rules.xml b/tools/scripts/android_rules.xml index aad9dbd6e..003021c35 100644 --- a/tools/scripts/android_rules.xml +++ b/tools/scripts/android_rules.xml @@ -72,6 +72,7 @@ Creating output directories if needed... + From b10c75c8956b1132325aaab4f30539ffce7a1024 Mon Sep 17 00:00:00 2001 From: Jeff Hamilton <> Date: Tue, 24 Mar 2009 18:16:44 -0700 Subject: [PATCH 16/39] Automated import from //branches/cupcake/...@141732,141732 --- .../android/apis/app/VoiceRecognition.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java b/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java index a784e1587..1a4b5e4a0 100644 --- a/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java +++ b/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java @@ -16,17 +16,18 @@ package com.example.android.apis.app; +import com.example.android.apis.R; + import android.app.Activity; import android.content.Intent; import android.os.Bundle; +import android.speech.RecognizerIntent; import android.view.View; import android.view.View.OnClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; -import com.example.android.apis.R; - import java.util.ArrayList; /** @@ -71,9 +72,10 @@ public class VoiceRecognition extends Activity implements OnClickListener { */ private void startVoiceRecognitionActivity() { //TODO Get these values from constants - Intent intent = new Intent("android.speech.action.RECOGNIZE_SPEECH"); - intent.putExtra("language_model", "free_form"); - intent.putExtra("prompt", "Speech recognition demo"); + Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); + intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, + RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); + intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Speech recognition demo"); startActivityForResult(intent, VOICE_RECOGNITION_REQUEST_CODE); } @@ -83,8 +85,8 @@ public class VoiceRecognition extends Activity implements OnClickListener { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == VOICE_RECOGNITION_REQUEST_CODE && resultCode == RESULT_OK) { - //TODO get the value from a constant - ArrayListmatches = data.getStringArrayListExtra("results"); + ArrayList matches = data.getStringArrayListExtra( + RecognizerIntent.EXTRA_RESULTS); mList.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, matches)); } From e6a13284e8e60fdae459860e7764e00c2f1f4ca6 Mon Sep 17 00:00:00 2001 From: Brett Chabot <> Date: Tue, 24 Mar 2009 18:21:57 -0700 Subject: [PATCH 17/39] Automated import from //branches/cupcake/...@141778,141778 --- .../AndroidJUnitLaunchConfigDelegate.java | 138 ++++++----------- .../AndroidJUnitLaunchConfigurationTab.java | 42 ++--- .../junit/AndroidJUnitLaunchShortcut.java | 39 ++--- .../junit/InstrumentationRunnerValidator.java | 145 ++++++++++++++++++ 4 files changed, 225 insertions(+), 139 deletions(-) create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java index a624b0001..fa8e4b01a 100755 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java @@ -32,23 +32,21 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; -import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants; import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry; /** - * Run configuration that can execute JUnit tests on an Android platform + * Run configuration that can execute JUnit tests on an Android platform. *

          - * Will deploy apps on target Android platform by reusing functionality from ADT - * LaunchConfigDelegate, and then run JUnits tests by reusing functionality from JDT + * Will deploy apps on target Android platform by reusing functionality from ADT + * LaunchConfigDelegate, and then run JUnits tests by reusing functionality from JDT * JUnitLaunchConfigDelegate. */ -@SuppressWarnings("restriction") //$NON-NLS-1$ +@SuppressWarnings("restriction") public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { - /** Launch config attribute that stores instrumentation runner */ + /** Launch config attribute that stores instrumentation runner. */ static final String ATTR_INSTR_NAME = AdtPlugin.PLUGIN_ID + ".instrumentation"; //$NON-NLS-1$ - static final String INSTRUMENTATION_OK = null; private static final String EMPTY_STRING = ""; //$NON-NLS-1$ @Override @@ -58,7 +56,7 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { IFile applicationPackage, AndroidManifestParser manifestParser) { String testPackage = manifestParser.getPackage(); - String runner = getRunnerFromConfig(configuration); + String runner = getRunner(project, configuration, manifestParser); if (runner == null) { AdtPlugin.displayError("Android Launch", "An instrumention test runner is not specified!"); @@ -72,45 +70,57 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { manifestParser.getDebuggable(), manifestParser.getApiLevelRequirement(), junitLaunch, config, androidLaunch, monitor); } - - private String getRunnerFromConfig(ILaunchConfiguration configuration) { - String runner = EMPTY_STRING; + + /** + * Gets a instrumentation runner for the launch. + *

          + * If a runner is stored in the given configuration, will return that. + * Otherwise, will try to find the first valid runner for the project. + * If a runner can still not be found, will return null. + * + * @param project the {@link IProject} for the app + * @param configuration the {@link ILaunchConfiguration} for the launch + * @param manifestParser the {@link AndroidManifestParser} for the project + * + * @return null if no instrumentation runner can be found, otherwise return + * the fully qualified runner name. + */ + private String getRunner(IProject project, ILaunchConfiguration configuration, + AndroidManifestParser manifestParser) { try { - runner = configuration.getAttribute(ATTR_INSTR_NAME, EMPTY_STRING); + String runner = getRunnerFromConfig(configuration); + if (runner != null) { + return runner; + } + final InstrumentationRunnerValidator instrFinder = new InstrumentationRunnerValidator( + BaseProjectHelper.getJavaProject(project), manifestParser); + runner = instrFinder.getValidInstrumentationTestRunner(); + if (runner != null) { + AdtPlugin.printErrorToConsole(project, + String.format("Warning: No instrumentation runner found for the launch, " + + "using %1$s", runner)); + return runner; + } + AdtPlugin.printErrorToConsole(project, + String.format("ERROR: Application does not specify a %1$s instrumentation or does not declare uses-library %2$s", + AndroidConstants.CLASS_INSTRUMENTATION_RUNNER, + AndroidConstants.LIBRARY_TEST_RUNNER)); + return null; } catch (CoreException e) { - AdtPlugin.log(e, "Error when retrieving instrumentation info from launch config"); //$NON-NLS-1$ + AdtPlugin.log(e, "Error when retrieving instrumentation info"); //$NON-NLS-1$ } + return null; + + } + + private String getRunnerFromConfig(ILaunchConfiguration configuration) throws CoreException { + String runner = configuration.getAttribute(ATTR_INSTR_NAME, EMPTY_STRING); if (runner.length() < 1) { return null; } return runner; } - /** - * Helper method to return the set of instrumentations for the Android project - * - * @param project the {@link IProject} to get instrumentations for - * @return null if error occurred parsing instrumentations, otherwise returns array of - * instrumentation class names - */ - static String[] getInstrumentationsForProject(IProject project) { - if (project != null) { - try { - // parse the manifest for the list of instrumentations - AndroidManifestParser manifestParser = AndroidManifestParser.parse( - BaseProjectHelper.getJavaProject(project), null /* errorListener */, - true /* gatherData */, false /* markErrors */); - if (manifestParser != null) { - return manifestParser.getInstrumentations(); - } - } catch (CoreException e) { - AdtPlugin.log(e, "%s: Error parsing AndroidManifest.xml", //$NON-NLS-1$ - project.getName()); - } - } - return null; - } - /** * Helper method to set JUnit-related attributes expected by JDT JUnit runner * @@ -121,56 +131,4 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate { config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_RUNNER_KIND, TestKindRegistry.JUNIT3_TEST_KIND_ID); } - - /** - * Helper method to determine if specified instrumentation can be used as a test runner - * - * @param project the {@link IJavaProject} to validate - * @param instrumentation the instrumentation class name to validate - * @return INSTRUMENTATION_OK if valid, otherwise returns error message - */ - static String validateInstrumentationRunner(IJavaProject project, String instrumentation) { - AndroidManifestParser manifestParser; - try { - manifestParser = AndroidManifestParser.parse( - project, null /* errorListener */, - true /* gatherData */, false /* markErrors */); - // check if this instrumentation is the standard test runner - if (!instrumentation.equals(AndroidConstants.CLASS_INSTRUMENTATION_RUNNER)) { - // check if it extends the standard test runner - String result = BaseProjectHelper.testClassForManifest(project, - instrumentation, AndroidConstants.CLASS_INSTRUMENTATION_RUNNER, true); - if (result != BaseProjectHelper.TEST_CLASS_OK) { - return String.format("The instrumentation runner must be of type %s", - AndroidConstants.CLASS_INSTRUMENTATION_RUNNER); - } - } - if (!hasTestRunnerLibrary(manifestParser)) { - return String.format("%s does not not use the %s library", - project.getProject().getName(), AndroidConstants.LIBRARY_TEST_RUNNER); - } - } catch (CoreException e) { - String err = String.format("Error parsing AndroidManifest for %s", - project.getProject().getName()); - AdtPlugin.log(e, err); - return err; - } - return INSTRUMENTATION_OK; - } - - /** - * Helper method to determine if given manifest has a AndroidConstants.LIBRARY_TEST_RUNNER - * library reference - * - * @param manifestParser the {@link AndroidManifestParser} to search - * @return true if test runner library found, false otherwise - */ - static boolean hasTestRunnerLibrary(AndroidManifestParser manifestParser) { - for (String lib : manifestParser.getUsesLibraries()) { - if (lib.equals(AndroidConstants.LIBRARY_TEST_RUNNER)) { - return true; - } - } - return false; - } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java index aa59a5157..eb5748269 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java @@ -118,7 +118,9 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat private Image mTabIcon = null; private Combo mInstrumentationCombo; private static final String EMPTY_STRING = ""; //$NON-NLS-1$ + private static final String TAG = "AndroidJUnitLaunchConfigurationTab"; //$NON-NLS-1$ private String[] mInstrumentations = null; + private InstrumentationRunnerValidator mInstrValidator = null; private ProjectChooserHelper mProjectChooserHelper; /* (non-Javadoc) @@ -349,7 +351,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat break; } } - } + } } catch (CoreException ce) { // ignore } @@ -454,7 +456,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat mapResources(config); } catch (CoreException e) { // TODO: does the real error need to be extracted out of CoreException - AdtPlugin.log(e, "Error occurred saving configuration"); + AdtPlugin.log(e, "Error occurred saving configuration"); //$NON-NLS-1$ } AndroidJUnitLaunchConfigDelegate.setJUnitDefaults(config); @@ -486,7 +488,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat public Image getImage() { // reuse icon from the Android App Launch config tab if (mTabIcon == null) { - mTabIcon= AdtPlugin.getImageLoader().loadImage(MainLaunchConfigTab.LAUNCH_TAB_IMAGE, + mTabIcon = AdtPlugin.getImageLoader().loadImage(MainLaunchConfigTab.LAUNCH_TAB_IMAGE, null); } return mTabIcon; @@ -514,7 +516,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat setErrorMessage(e.getMessage()); return; } catch (InvocationTargetException e) { - AdtPlugin.log(e.getTargetException(), "Error finding test types"); + AdtPlugin.log(e.getTargetException(), "Error finding test types"); //$NON-NLS-1$ return; } finally { mTestRadioButton.setSelection(radioSetting[0]); @@ -675,10 +677,10 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat return; } } catch (CoreException e) { - AdtPlugin.log(e, "validatePage failed"); + AdtPlugin.log(e, "validatePage failed"); //$NON-NLS-1$ } - validateInstrumentation(javaProject); + validateInstrumentation(); } private void validateJavaProject(IJavaProject javaProject) { @@ -688,19 +690,14 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat } } - private void validateInstrumentation(IJavaProject javaProject) { - if (mInstrumentations == null || mInstrumentations.length < 1) { - setErrorMessage("Specified project has no defined instrumentations"); - return; - } + private void validateInstrumentation() { String instrumentation = getSelectedInstrumentation(); if (instrumentation == null) { - setErrorMessage("Instrumentation not specified"); + setErrorMessage("Instrumentation runner not specified"); return; } - String result = AndroidJUnitLaunchConfigDelegate.validateInstrumentationRunner( - javaProject, instrumentation); - if (result != AndroidJUnitLaunchConfigDelegate.INSTRUMENTATION_OK) { + String result = mInstrValidator.validateInstrumentationRunner(instrumentation); + if (result != InstrumentationRunnerValidator.INSTRUMENTATION_OK) { setErrorMessage(result); return; } @@ -949,14 +946,15 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat /** * Loads the UI with the instrumentations of the specified project, and stores the - * activities in mActivities. - *

          - * First activity is selected by default if present. + * instrumentations in mInstrumentations. * * @param project the {@link IProject} to load the instrumentations from. */ private void loadInstrumentations(IProject project) { - mInstrumentations = AndroidJUnitLaunchConfigDelegate.getInstrumentationsForProject(project); + try { + mInstrValidator = new InstrumentationRunnerValidator(project); + mInstrumentations = (mInstrValidator == null ? null : + mInstrValidator.getInstrumentations()); if (mInstrumentations != null) { mInstrumentationCombo.removeAll(); for (String instrumentation : mInstrumentations) { @@ -966,9 +964,13 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat // config object. return; } - + } catch (CoreException e) { + AdtPlugin.logAndPrintError(e, TAG, "ERROR: Failed to get instrumentations for %1$s", + project.getName()); + } // if we reach this point, either project is null, or we got an exception during // the parsing. In either case, we empty the instrumentation list. + mInstrValidator = null; mInstrumentations = null; mInstrumentationCombo.removeAll(); } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java index 30649e2e8..f06f7eb4c 100755 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java @@ -16,10 +16,6 @@ package com.android.ide.eclipse.adt.launch.junit; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.common.AndroidConstants; - -import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.jdt.core.IJavaElement; @@ -43,33 +39,18 @@ public class AndroidJUnitLaunchShortcut extends JUnitLaunchShortcut { protected ILaunchConfigurationWorkingCopy createLaunchConfiguration(IJavaElement element) throws CoreException { ILaunchConfigurationWorkingCopy config = super.createLaunchConfiguration(element); - IProject project = element.getResource().getProject(); - String[] instrumentations = - AndroidJUnitLaunchConfigDelegate.getInstrumentationsForProject(project); - boolean runnerFound = false; - if (instrumentations != null) { - // just pick the first valid runner - for (String instr : instrumentations) { - if (AndroidJUnitLaunchConfigDelegate.validateInstrumentationRunner( - element.getJavaProject(), instr) == - AndroidJUnitLaunchConfigDelegate.INSTRUMENTATION_OK) { + // just get first valid instrumentation runner + String instrumentation = new InstrumentationRunnerValidator(element.getJavaProject()). + getValidInstrumentationTestRunner(); + if (instrumentation != null) { + config.setAttribute(AndroidJUnitLaunchConfigDelegate.ATTR_INSTR_NAME, + instrumentation); + } + // if a valid runner is not found, rely on launch delegate to log error. + // This method is called without explicit user action to launch Android JUnit, so avoid + // logging an error here. - config.setAttribute(AndroidJUnitLaunchConfigDelegate.ATTR_INSTR_NAME, - instr); - runnerFound = true; - break; - } - } - } - if (!runnerFound) { - // TODO: put this in a string properties - String msg = String.format("ERROR: Application does not specify a %s instrumentation or does not declare uses-library %s", - AndroidConstants.CLASS_INSTRUMENTATION_RUNNER, - AndroidConstants.LIBRARY_TEST_RUNNER); - AdtPlugin.printErrorToConsole(project, msg); - } AndroidJUnitLaunchConfigDelegate.setJUnitDefaults(config); - return config; } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java new file mode 100644 index 000000000..f22fc7cda --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2009 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.launch.junit; + +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.common.AndroidConstants; +import com.android.ide.eclipse.common.project.AndroidManifestParser; +import com.android.ide.eclipse.common.project.BaseProjectHelper; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.IJavaProject; + +/** + * Provides validation for Android instrumentation test runner + */ +class InstrumentationRunnerValidator { + private final IJavaProject mJavaProject; + private String[] mInstrumentations = null; + private boolean mHasRunnerLibrary = false; + + static final String INSTRUMENTATION_OK = null; + + /** + * Initializes the InstrumentationRunnerValidator. + * + * @param javaProject the {@link IJavaProject} for the Android project to validate + */ + InstrumentationRunnerValidator(IJavaProject javaProject) { + mJavaProject = javaProject; + try { + AndroidManifestParser manifestParser = AndroidManifestParser.parse(javaProject, + null /* errorListener */, true /* gatherData */, false /* markErrors */); + init(manifestParser); + } catch (CoreException e) { + AdtPlugin.printErrorToConsole(javaProject.getProject(), "ERROR: Failed to parse %1$s", + AndroidConstants.FN_ANDROID_MANIFEST); + } + } + + /** + * Initializes the InstrumentationRunnerValidator. + * + * @param project the {@link IProject} for the Android project to validate + * @throws CoreException if a fatal error occurred in initialization + */ + InstrumentationRunnerValidator(IProject project) throws CoreException { + this(BaseProjectHelper.getJavaProject(project)); + } + + /** + * Initializes the InstrumentationRunnerValidator with an existing {@link AndroidManifestParser} + * + * @param javaProject the {@link IJavaProject} for the Android project to validate + * @param manifestParser the {@link AndroidManifestParser} for the Android project + */ + InstrumentationRunnerValidator(IJavaProject javaProject, AndroidManifestParser manifestParser) { + mJavaProject = javaProject; + init(manifestParser); + } + + private void init(AndroidManifestParser manifestParser) { + mInstrumentations = manifestParser.getInstrumentations(); + mHasRunnerLibrary = hasTestRunnerLibrary(manifestParser); + } + + /** + * Helper method to determine if given manifest has a AndroidConstants.LIBRARY_TEST_RUNNER + * library reference + * + * @param manifestParser the {@link AndroidManifestParser} to search + * @return true if test runner library found, false otherwise + */ + private boolean hasTestRunnerLibrary(AndroidManifestParser manifestParser) { + for (String lib : manifestParser.getUsesLibraries()) { + if (lib.equals(AndroidConstants.LIBRARY_TEST_RUNNER)) { + return true; + } + } + return false; + } + + /** + * Return the set of instrumentations for the Android project. + * + * @return nullnull if no valid + * instrumentation can be found. + */ + String getValidInstrumentationTestRunner() { + for (String instrumentation : getInstrumentations()) { + if (validateInstrumentationRunner(instrumentation) == INSTRUMENTATION_OK) { + return instrumentation; + } + } + return null; + } + + /** + * Helper method to determine if specified instrumentation can be used as a test runner + * + * @param instrumentation the instrumentation class name to validate. Assumes this + * instrumentation is one of {@link #getInstrumentations()} + * @return INSTRUMENTATION_OK if valid, otherwise returns error message + */ + String validateInstrumentationRunner(String instrumentation) { + if (!mHasRunnerLibrary) { + return String.format("The application does not declare uses-library %1$s", + AndroidConstants.LIBRARY_TEST_RUNNER); + } + // check if this instrumentation is the standard test runner + if (!instrumentation.equals(AndroidConstants.CLASS_INSTRUMENTATION_RUNNER)) { + // check if it extends the standard test runner + String result = BaseProjectHelper.testClassForManifest(mJavaProject, + instrumentation, AndroidConstants.CLASS_INSTRUMENTATION_RUNNER, true); + if (result != BaseProjectHelper.TEST_CLASS_OK) { + return String.format("The instrumentation runner must be of type %s", + AndroidConstants.CLASS_INSTRUMENTATION_RUNNER); + } + } + return INSTRUMENTATION_OK; + } +} From 8860250a5310a0dfde9050ca9cc6c33f17b18c56 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet <> Date: Tue, 24 Mar 2009 18:24:44 -0700 Subject: [PATCH 18/39] Automated import from //branches/cupcake/...@141808,141808 --- .../icons/androidjunit.png | Bin 0 -> 393 bytes .../com.android.ide.eclipse.adt/plugin.xml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/androidjunit.png diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/androidjunit.png b/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/androidjunit.png new file mode 100644 index 0000000000000000000000000000000000000000..25826dce773d24bec4b87f86e814bdefef638505 GIT binary patch literal 393 zcmV;40e1e0P)(bu;eV6wl%QIx`oy#!OC4~{B895d}hT;SoORN4fvJW{^t=8i4FvY;3Rr4r|-~R~}vtcPci8xWt%1QFq|h zDTemT``~JBynFy=yU6RplP+8mjlq From 3cf4c91ef144990b6a467ba2ab2262c05a21017b Mon Sep 17 00:00:00 2001 From: Brett Chabot <> Date: Tue, 24 Mar 2009 18:25:24 -0700 Subject: [PATCH 19/39] Automated import from //branches/cupcake/...@141814,141814 --- testrunner/coverage_targets.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/testrunner/coverage_targets.xml b/testrunner/coverage_targets.xml index d7700f63a..32a485659 100644 --- a/testrunner/coverage_targets.xml +++ b/testrunner/coverage_targets.xml @@ -108,4 +108,8 @@ + + + From 9abc811e66364163c2f9806ba1ce8b943929bc94 Mon Sep 17 00:00:00 2001 From: Raphael Moll <> Date: Tue, 24 Mar 2009 18:27:05 -0700 Subject: [PATCH 20/39] Automated import from //branches/cupcake/...@141822,141822 --- .../extractstring/ExtractStringAction.java | 51 +++-- .../ExtractStringRefactoring.java | 204 +++++++++++------- 2 files changed, 153 insertions(+), 102 deletions(-) diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java index 528701573..dceb1441d 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java @@ -16,7 +16,11 @@ package com.android.ide.eclipse.adt.refactorings.extractstring; +import com.android.ide.eclipse.common.AndroidConstants; + import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaCore; @@ -32,6 +36,7 @@ import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.IWorkbenchWindowActionDelegate; import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.part.FileEditorInput; /* * Quick Reference Link: @@ -71,7 +76,7 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate { /** Keep track of the current workbench window. */ private IWorkbenchWindow mWindow; private ITextSelection mSelection; - private ICompilationUnit mUnit; + private IFile mFile; /** * Keep track of the current workbench window. @@ -99,30 +104,24 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate { // runs since we don't have access to the AST yet. mSelection = null; - mUnit = null; + mFile = null; if (selection instanceof ITextSelection) { mSelection = (ITextSelection) selection; if (mSelection.getLength() > 0) { - mUnit = getCompilationUnit(); + mFile = getSelectedFile(); } - - // Keep for debugging purposes - //System.out.println(String.format("-- Selection: %d + %d = %s", - // mSelection.getOffset(), - // mSelection.getLength(), - // mSelection.getText())); } - action.setEnabled(mSelection != null && mUnit != null); + action.setEnabled(mSelection != null && mFile != null); } /** * Create a new instance of our refactoring and a wizard to configure it. */ public void run(IAction action) { - if (mSelection != null && mUnit != null) { - ExtractStringRefactoring ref = new ExtractStringRefactoring(mUnit, mSelection); + if (mSelection != null && mFile != null) { + ExtractStringRefactoring ref = new ExtractStringRefactoring(mFile, mSelection); RefactoringWizard wizard = new ExtractStringWizard(ref, "Extract Android String"); RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); try { @@ -134,9 +133,14 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate { } /** - * Returns the active {@link ICompilationUnit} or null. + * Returns the active {@link IFile} (hopefully matching our selection) or null. + * The file is only returned if it's a file from a project with an Android nature. + *

          + * At that point we do not try to analyze if the selection nor the file is suitable + * for the refactoring. This check is performed when the refactoring is invoked since + * it can then produce meaningful error messages as needed. */ - private ICompilationUnit getCompilationUnit() { + private IFile getSelectedFile() { IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); if (wwin != null) { IWorkbenchPage page = wwin.getActivePage(); @@ -144,12 +148,19 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate { IEditorPart editor = page.getActiveEditor(); if (editor != null) { IEditorInput input = editor.getEditorInput(); - if (input != null) { - ITypeRoot typeRoot = JavaUI.getEditorInputTypeRoot(input); - // The type root can be either a .class or a .java (aka compilation unit). - // We want the compilation unit kind. - if (typeRoot instanceof ICompilationUnit) { - return (ICompilationUnit) typeRoot; + + if (input instanceof FileEditorInput) { + FileEditorInput fi = (FileEditorInput) input; + IFile file = fi.getFile(); + if (file.exists()) { + IProject proj = file.getProject(); + try { + if (proj != null && proj.hasNature(AndroidConstants.NATURE)) { + return file; + } + } catch (CoreException e) { + // ignore + } } } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java index 715503c1f..99711bf20 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java @@ -24,7 +24,9 @@ import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourceAttributes; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; @@ -126,46 +128,54 @@ import javax.xml.xpath.XPathExpressionException; */ class ExtractStringRefactoring extends Refactoring { - /** The compilation unit, a.k.a. the Java file model. */ - private final ICompilationUnit mUnit; + /** The file model being manipulated. */ + private final IFile mFile; + /** The start of the selection in {@link #mFile}. */ private final int mSelectionStart; + /** The end of the selection in {@link #mFile}. */ private final int mSelectionEnd; + + /** The compilation unit, only defined if {@link #mFile} points to a usable Java source file. */ + private ICompilationUnit mUnit; /** The actual string selected, after UTF characters have been escaped, good for display. */ private String mTokenString; - /** Start position of the string token in the source buffer. */ - private int mTokenStart; - /** End position of the string token in the source buffer. */ - private int mTokenEnd; + + /** The XML string ID selected by the user in the wizard. */ private String mXmlStringId; + /** The path of the XML file that will define {@link #mXmlStringId}, selected by the user + * in the wizard. */ private String mTargetXmlFileWsPath; + + /** A temporary cache of R.string IDs defined by a given xml file. The key is the + * project path of the file, the data is a set of known string Ids for that file. */ private HashMap> mResIdCache; + /** An instance of XPath, created lazily on demand. */ private XPath mXPath; + /** The list of changes computed by {@link #checkFinalConditions(IProgressMonitor)} and + * used by {@link #createChange(IProgressMonitor)}. */ private ArrayList mChanges; public ExtractStringRefactoring(Map arguments) throws NullPointerException { - mUnit = (ICompilationUnit) JavaCore.create(arguments.get("CU")); //$NON-NLS-1$ + + IPath path = Path.fromPortableString(arguments.get("file")); //$NON-NLS-1$ + mFile = (IFile) ResourcesPlugin.getWorkspace().getRoot().findMember(path); mSelectionStart = Integer.parseInt(arguments.get("sel-start")); //$NON-NLS-1$ mSelectionEnd = Integer.parseInt(arguments.get("sel-end")); //$NON-NLS-1$ - mTokenStart = Integer.parseInt(arguments.get("tok-start")); //$NON-NLS-1$ - mTokenEnd = Integer.parseInt(arguments.get("tok-end")); //$NON-NLS-1$ mTokenString = arguments.get("tok-esc"); //$NON-NLS-1$ } private Map createArgumentMap() { HashMap args = new HashMap(); - args.put("CU", mUnit.getHandleIdentifier()); //$NON-NLS-1$ + args.put("file", mFile.getFullPath().toPortableString()); //$NON-NLS-1$ args.put("sel-start", Integer.toString(mSelectionStart)); //$NON-NLS-1$ args.put("sel-end", Integer.toString(mSelectionEnd)); //$NON-NLS-1$ - args.put("tok-start", Integer.toString(mTokenStart)); //$NON-NLS-1$ - args.put("tok-end", Integer.toString(mTokenEnd)); //$NON-NLS-1$ args.put("tok-esc", mTokenString); //$NON-NLS-1$ return args; } - public ExtractStringRefactoring(ICompilationUnit unit, ITextSelection selection) { - mUnit = unit; - + public ExtractStringRefactoring(IFile file, ITextSelection selection) { + mFile = file; mSelectionStart = selection.getOffset(); mSelectionEnd = mSelectionStart + selection.getLength(); } @@ -207,75 +217,42 @@ class ExtractStringRefactoring extends Refactoring { public RefactoringStatus checkInitialConditions(IProgressMonitor monitor) throws CoreException, OperationCanceledException { + mUnit = null; mTokenString = null; - mTokenStart = -1; - mTokenEnd = -1; RefactoringStatus status = new RefactoringStatus(); try { - monitor.beginTask("Checking preconditions...", 3); - - if (!extraChecks(monitor, status)) { + monitor.beginTask("Checking preconditions...", 5); + + if (!checkSourceFile(mFile, status, monitor)) { return status; } - + + // Try to get a compilation unit from this file. If it fails, mUnit is null. try { - IBuffer buffer = mUnit.getBuffer(); + mUnit = JavaCore.createCompilationUnitFrom(mFile); - IScanner scanner = ToolFactory.createScanner( - false, //tokenizeComments - false, //tokenizeWhiteSpace - false, //assertMode - false //recordLineSeparator - ); - scanner.setSource(buffer.getCharacters()); - monitor.worked(1); - - for(int token = scanner.getNextToken(); - token != ITerminalSymbols.TokenNameEOF; - token = scanner.getNextToken()) { - if (scanner.getCurrentTokenStartPosition() <= mSelectionStart && - scanner.getCurrentTokenEndPosition() >= mSelectionEnd) { - // found the token, but only keep of the right type - if (token == ITerminalSymbols.TokenNameStringLiteral) { - mTokenString = new String(scanner.getCurrentTokenSource()); - mTokenStart = scanner.getCurrentTokenStartPosition(); - mTokenEnd = scanner.getCurrentTokenEndPosition(); - } - break; - } else if (scanner.getCurrentTokenStartPosition() > mSelectionEnd) { - // scanner is past the selection, abort. - break; - } + // Make sure the unit is not read-only, e.g. it's not a class file or inside a Jar + if (mUnit.isReadOnly()) { + status.addFatalError("The file is read-only, please make it writeable first."); + return status; } - } catch (JavaModelException e1) { - // Error in mUnit.getBuffer. Ignore. - } catch (InvalidInputException e2) { - // Error in scanner.getNextToken. Ignore. - } finally { - monitor.worked(1); - } - - if (mTokenString != null) { - // As a literal string, the token should have surrounding quotes. Remove them. - int len = mTokenString.length(); - if (len > 0 && - mTokenString.charAt(0) == '"' && - mTokenString.charAt(len - 1) == '"') { - mTokenString = mTokenString.substring(1, len - 1); - } - // We need a non-empty string literal - if (mTokenString.length() == 0) { - mTokenString = null; + + // This is a Java file. Check if it contains the selection we want. + if (!findSelectionInJavaUnit(mUnit, status, monitor)) { + return status; } + + } catch (Exception e) { + // That was not a Java file. Ignore. } - if (mTokenString == null) { - status.addFatalError("Please select a Java string literal."); + if (mUnit == null) { + // Check this an XML file and get the selection and its context. + // TODO + status.addFatalError("Selection must be inside a Java source file."); } - - monitor.worked(1); } finally { monitor.done(); } @@ -283,31 +260,94 @@ class ExtractStringRefactoring extends Refactoring { return status; } + /** + * Try to find the selected Java element in the compilation unit. + * + * If selection matches a string literal, capture it, otherwise add a fatal error + * to the status. + * + * On success, advance the monitor by 3. + */ + private boolean findSelectionInJavaUnit(ICompilationUnit unit, + RefactoringStatus status, IProgressMonitor monitor) { + try { + IBuffer buffer = unit.getBuffer(); + + IScanner scanner = ToolFactory.createScanner( + false, //tokenizeComments + false, //tokenizeWhiteSpace + false, //assertMode + false //recordLineSeparator + ); + scanner.setSource(buffer.getCharacters()); + monitor.worked(1); + + for(int token = scanner.getNextToken(); + token != ITerminalSymbols.TokenNameEOF; + token = scanner.getNextToken()) { + if (scanner.getCurrentTokenStartPosition() <= mSelectionStart && + scanner.getCurrentTokenEndPosition() >= mSelectionEnd) { + // found the token, but only keep of the right type + if (token == ITerminalSymbols.TokenNameStringLiteral) { + mTokenString = new String(scanner.getCurrentTokenSource()); + } + break; + } else if (scanner.getCurrentTokenStartPosition() > mSelectionEnd) { + // scanner is past the selection, abort. + break; + } + } + } catch (JavaModelException e1) { + // Error in unit.getBuffer. Ignore. + } catch (InvalidInputException e2) { + // Error in scanner.getNextToken. Ignore. + } finally { + monitor.worked(1); + } + + if (mTokenString != null) { + // As a literal string, the token should have surrounding quotes. Remove them. + int len = mTokenString.length(); + if (len > 0 && + mTokenString.charAt(0) == '"' && + mTokenString.charAt(len - 1) == '"') { + mTokenString = mTokenString.substring(1, len - 1); + } + // We need a non-empty string literal + if (mTokenString.length() == 0) { + mTokenString = null; + } + } + + if (mTokenString == null) { + status.addFatalError("Please select a Java string literal."); + } + + monitor.worked(1); + return status.isOK(); + } + /** * Tests from org.eclipse.jdt.internal.corext.refactoringChecks#validateEdit() * Might not be useful. * + * On success, advance the monitor by 2. + * * @return False if caller should abort, true if caller should continue. */ - private boolean extraChecks(IProgressMonitor monitor, RefactoringStatus status) { - // - IResource res = mUnit.getPrimary().getResource(); - if (res == null || res.getType() != IResource.FILE) { - status.addFatalError("Cannot access resource; only regular files can be used."); - return false; - } - monitor.worked(1); - + private boolean checkSourceFile(IFile file, + RefactoringStatus status, + IProgressMonitor monitor) { // check whether the source file is in sync - if (!res.isSynchronized(IResource.DEPTH_ZERO)) { + if (!file.isSynchronized(IResource.DEPTH_ZERO)) { status.addFatalError("The file is not synchronized. Please save it first."); return false; } monitor.worked(1); // make sure we can write to it. - ResourceAttributes resAttr = res.getResourceAttributes(); - if (mUnit.isReadOnly() || resAttr == null || resAttr.isReadOnly()) { + ResourceAttributes resAttr = file.getResourceAttributes(); + if (resAttr == null || resAttr.isReadOnly()) { status.addFatalError("The file is read-only, please make it writeable first."); return false; } From 076609b9c96a13f5fb8f37cb91444f7fcbdfe3c5 Mon Sep 17 00:00:00 2001 From: Raphael Moll <> Date: Tue, 24 Mar 2009 19:14:49 -0700 Subject: [PATCH 21/39] Automated import from //branches/cupcake/...@142190,142190 --- .../resources/DeclareStyleableInfo.java | 12 ++ .../descriptors/ElementDescriptor.java | 13 +++ .../layout/descriptors/LayoutDescriptors.java | 110 ++++++++++++++++-- 3 files changed, 128 insertions(+), 7 deletions(-) diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java index efa5981b5..7aad7c84a 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java @@ -70,6 +70,18 @@ public class DeclareStyleableInfo { mFormats = formats; } + /** + * @param name The XML Name of the attribute + * @param formats The formats of the attribute. Cannot be null. + * Should have at least one format. + * @param javadoc Short javadoc (i.e. the first sentence). + */ + public AttributeInfo(String name, Format[] formats, String javadoc) { + mName = name; + mFormats = formats; + mJavaDoc = javadoc; + } + public AttributeInfo(AttributeInfo info) { mName = info.mName; mFormats = info.mFormats; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/ElementDescriptor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/ElementDescriptor.java index 555015537..6a9b7db29 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/ElementDescriptor.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/ElementDescriptor.java @@ -24,6 +24,7 @@ import com.android.sdklib.SdkConstants; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.graphics.Image; +import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -221,6 +222,18 @@ public class ElementDescriptor { mChildren = newChildren; } + /** Sets the list of allowed children. + *

          + * This is just a convenience method that converts a Collection into an array and + * calls {@link #setChildren(ElementDescriptor[])}. + *

          + * This means a copy of the collection is made. The collection is not + * stored by the recipient and can thus be altered by the caller. + */ + public void setChildren(Collection newChildren) { + setChildren(newChildren.toArray(new ElementDescriptor[newChildren.size()])); + } + /** * Returns an optional tooltip. Will be null if not present. *

          diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java index 5726d7899..2c0f984db 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java @@ -43,7 +43,7 @@ public final class LayoutDescriptors implements IDescriptorProvider { public static final String ID_ATTR = "id"; //$NON-NLS-1$ /** The document descriptor. Contains all layouts and views linked together. */ - private DocumentDescriptor mDescriptor = + private DocumentDescriptor mRootDescriptor = new DocumentDescriptor("layout_doc", null); //$NON-NLS-1$ /** The list of all known ViewLayout descriptors. */ @@ -60,7 +60,7 @@ public final class LayoutDescriptors implements IDescriptorProvider { /** @return the document descriptor. Contains all layouts and views linked together. */ public DocumentDescriptor getDescriptor() { - return mDescriptor; + return mRootDescriptor; } /** @return The read-only list of all known ViewLayout descriptors. */ @@ -74,7 +74,7 @@ public final class LayoutDescriptors implements IDescriptorProvider { } public ElementDescriptor[] getRootElementDescriptors() { - return mDescriptor.getChildren(); + return mRootDescriptor.getChildren(); } /** @@ -98,6 +98,10 @@ public final class LayoutDescriptors implements IDescriptorProvider { } } + // Create as a synthetic regular view. + // Note: ViewStub is already described by attrs.xml + insertInclude(newViews); + ArrayList newLayouts = new ArrayList(); if (layouts != null) { for (ViewClassInfo info : layouts) { @@ -109,17 +113,22 @@ public final class LayoutDescriptors implements IDescriptorProvider { ArrayList newDescriptors = new ArrayList(); newDescriptors.addAll(newLayouts); newDescriptors.addAll(newViews); - ElementDescriptor[] newArray = newDescriptors.toArray( - new ElementDescriptor[newDescriptors.size()]); // Link all layouts to everything else here.. recursively for (ElementDescriptor layoutDesc : newLayouts) { - layoutDesc.setChildren(newArray); + layoutDesc.setChildren(newDescriptors); } + // The tag can only be a root tag, so it is added at the end. + // It gets everything else as children but it is not made a child itself. + ElementDescriptor mergeTag = createMerge(); + mergeTag.setChildren(newDescriptors); // mergeTag makes a copy of the list + newDescriptors.add(mergeTag); + newLayouts.add(mergeTag); + mViewDescriptors = newViews; mLayoutDescriptors = newLayouts; - mDescriptor.setChildren(newArray); + mRootDescriptor.setChildren(newDescriptors); mROLayoutDescriptors = Collections.unmodifiableList(mLayoutDescriptors); mROViewDescriptors = Collections.unmodifiableList(mViewDescriptors); @@ -217,4 +226,91 @@ public final class LayoutDescriptors implements IDescriptorProvider { false /* mandatory */); } + /** + * Creates a new descriptor and adds it to the list of view descriptors. + * + * @param newViews A list of view descriptors being populated. Also used to find the + * View description and extract its layout attributes. + */ + private void insertInclude(ArrayList newViews) { + String xml_name = "include"; //$NON-NLS-1$ + + // Create the include custom attributes + ArrayList attributes = new ArrayList(); + + // Note that the "layout" attribute does NOT have the Android namespace + DescriptorsUtils.appendAttribute(attributes, + null, //elementXmlName + null, //nsUri + new AttributeInfo( + "layout", //$NON-NLS-1$ + new AttributeInfo.Format[] { AttributeInfo.Format.REFERENCE } + ), + true, //required + null); //overrides + + DescriptorsUtils.appendAttribute(attributes, + null, //elementXmlName + SdkConstants.NS_RESOURCES, //nsUri + new AttributeInfo( + "id", //$NON-NLS-1$ + new AttributeInfo.Format[] { AttributeInfo.Format.REFERENCE } + ), + true, //required + null); //overrides + + // Find View and inherit all its layout attributes + AttributeDescriptor[] viewLayoutAttribs = findViewLayoutAttributes(newViews); + + // Create the include descriptor + ViewElementDescriptor desc = new ViewElementDescriptor(xml_name, // xml_name + xml_name, // ui_name + null, // canonical class name, we don't have one + "Lets you statically include XML layouts inside other XML layouts.", // tooltip + null, // sdk_url + attributes.toArray(new AttributeDescriptor[attributes.size()]), + viewLayoutAttribs, // layout attributes + null, // children + false /* mandatory */); + + newViews.add(desc); + } + + /** + * Finds the View descriptor and retrieves all its layout attributes. + */ + private AttributeDescriptor[] findViewLayoutAttributes( + ArrayList newViews) { + + for (ElementDescriptor desc : newViews) { + if (desc instanceof ViewElementDescriptor) { + ViewElementDescriptor viewDesc = (ViewElementDescriptor) desc; + if (AndroidConstants.CLASS_VIEW.equals(viewDesc.getCanonicalClassName())) { + return viewDesc.getLayoutAttributes(); + } + } + } + + return null; + } + + /** + * Creates and return a new descriptor. + */ + private ElementDescriptor createMerge() { + String xml_name = "merge"; //$NON-NLS-1$ + + // Create the include descriptor + ViewElementDescriptor desc = new ViewElementDescriptor(xml_name, // xml_name + xml_name, // ui_name + null, // canonical class name, we don't have one + "A root tag useful for XML layouts inflated using a ViewStub.", // tooltip + null, // sdk_url + null, // attributes + null, // layout attributes + null, // children + false /* mandatory */); + + return desc; + } } From 4595b7960df0a6df606292b2b6de256543afe674 Mon Sep 17 00:00:00 2001 From: Raphael Moll <> Date: Tue, 24 Mar 2009 19:15:35 -0700 Subject: [PATCH 22/39] Automated import from //branches/cupcake/...@142191,142191 --- .../com.android.ide.eclipse.adt/plugin.xml | 2 +- .../extractstring/ExtractStringAction.java | 4 +- .../extractstring/ExtractStringInputPage.java | 392 ++++++++++++++++-- .../ExtractStringRefactoring.java | 7 +- .../extractstring/ExtractStringWizard.java | 14 +- .../wizards/NewXmlFileCreationPage.java | 26 +- 6 files changed, 375 insertions(+), 70 deletions(-) diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml index 81bb60634..2c1394cd2 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml @@ -501,7 +501,7 @@ class="com.android.ide.eclipse.adt.refactorings.extractstring.ExtractStringAction" definitionId="com.android.ide.eclipse.adt.refactoring.extract.string" id="com.android.ide.eclipse.adt.actions.ExtractString" - label="Extract Android String" + label="Extract Android String..." menubarPath="org.eclipse.jdt.ui.refactoring.menu/codingGroup" style="push" tooltip="Extracts a string into Android resource string"> diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java index dceb1441d..4ef1268de 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java @@ -22,9 +22,7 @@ import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.ui.JavaUI; import org.eclipse.jface.action.IAction; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.viewers.ISelection; @@ -122,7 +120,7 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate { public void run(IAction action) { if (mSelection != null && mFile != null) { ExtractStringRefactoring ref = new ExtractStringRefactoring(mFile, mSelection); - RefactoringWizard wizard = new ExtractStringWizard(ref, "Extract Android String"); + RefactoringWizard wizard = new ExtractStringWizard(ref, mFile.getProject()); RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); try { op.run(mWindow.getShell(), wizard.getDefaultPageTitle()); diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java index cb449f02e..5ffeeb05f 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java @@ -16,29 +16,74 @@ package com.android.ide.eclipse.adt.refactorings.extractstring; + +import com.android.ide.eclipse.common.AndroidConstants; +import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration; +import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType; +import com.android.ide.eclipse.editors.wizards.ConfigurationSelector; +import com.android.sdklib.SdkConstants; + +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.wizard.IWizardPage; +import org.eclipse.jface.wizard.WizardPage; import org.eclipse.ltk.ui.refactoring.UserInputWizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; +import java.util.HashMap; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * @see ExtractStringRefactoring */ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage { - public ExtractStringInputPage() { - super("ExtractStringInputPage"); //$NON-NLS-1$ - } + /** Last res file path used, shared across the session instances but specific to the + * current project. The default for unknown projects is {@link #DEFAULT_RES_FILE_PATH}. */ + private static HashMap sLastResFilePath = new HashMap(); + /** The project where the user selection happened. */ + private final IProject mProject; + + /** Field displaying the user-selected string to be replaced. */ private Label mStringLabel; + /** Test field where the user enters the new ID to be generated or replaced with. */ private Text mNewIdTextField; - private Label mFileLabel; + /** The configuration selector, to select the resource path of the XML file. */ + private ConfigurationSelector mConfigSelector; + /** The combo to display the existing XML files or enter a new one. */ + private Combo mResFileCombo; + + /** Regex pattern to read a valid res XML file path. It checks that the are 2 folders and + * a leaf file name ending with .xml */ + private static final Pattern RES_XML_FILE_REGEX = Pattern.compile( + "/res/[a-z][a-zA-Z0-9_-]+/[^.]+\\.xml"); //$NON-NLS-1$ + /** Absolute destination folder root, e.g. "/res/" */ + private static final String RES_FOLDER_ABS = + AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP; + /** Relative destination folder root, e.g. "res/" */ + private static final String RES_FOLDER_REL = + SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP; + + private static final String DEFAULT_RES_FILE_PATH = "/res/values/strings.xml"; + + public ExtractStringInputPage(IProject project) { + super("ExtractStringInputPage"); //$NON-NLS-1$ + mProject = project; + } /** * Create the UI for the refactoring wizard. @@ -48,34 +93,57 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage */ public void createControl(Composite parent) { - final ExtractStringRefactoring ref = getOurRefactoring(); - Composite content = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); - layout.numColumns = 2; + layout.numColumns = 1; content.setLayout(layout); - - // line 1: String found in selection - Label label = new Label(content, SWT.NONE); + createStringReplacementGroup(content); + createResFileGroup(content); + + validatePage(); + setControl(content); + } + + /** + * Creates the top group with the field to replace which string and by what + * and by which options. + * + * @param content A composite with a 1-column grid layout + */ + private void createStringReplacementGroup(Composite content) { + + final ExtractStringRefactoring ref = getOurRefactoring(); + + Group group = new Group(content, SWT.NONE); + group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + group.setText("String Replacement"); + + GridLayout layout = new GridLayout(); + layout.numColumns = 2; + group.setLayout(layout); + + // line: String found in selection + + Label label = new Label(group, SWT.NONE); label.setText("String:"); String selectedString = ref.getTokenString(); - mStringLabel = new Label(content, SWT.NONE); + mStringLabel = new Label(group, SWT.NONE); mStringLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mStringLabel.setText(selectedString != null ? selectedString : ""); // TODO provide an option to replace all occurences of this string instead of // just the one. - // line 2 : Textfield for new ID + // line : Textfield for new ID - label = new Label(content, SWT.NONE); + label = new Label(group, SWT.NONE); label.setText("Replace by R.string."); - mNewIdTextField = new Text(content, SWT.SINGLE | SWT.LEFT | SWT.BORDER); + mNewIdTextField = new Text(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER); mNewIdTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mNewIdTextField.setText(guessId(selectedString)); @@ -83,37 +151,65 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage mNewIdTextField.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { - if (validatePage(ref)) { + if (validatePage()) { ref.setReplacementStringId(mNewIdTextField.getText().trim()); } } }); - - // line 3: selection of the output file - // TODO add a file field/chooser combo to let the user select the file to edit. - - label = new Label(content, SWT.NONE); - label.setText("Resource file:"); - - mFileLabel = new Label(content, SWT.NONE); - mFileLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - mFileLabel.setText("/res/values/strings.xml"); - ref.setTargetFile(mFileLabel.getText()); - - // line 4: selection of the res config - // TODO add the Configuration Selector to decide with strings.xml to change - - label = new Label(content, SWT.NONE); - label.setText("Configuration:"); - - label = new Label(content, SWT.NONE); - label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - label.setText("default"); - - validatePage(ref); - setControl(content); } + /** + * Creates the lower group with the fields to choose the resource confirmation and + * the target XML file. + * + * @param content A composite with a 1-column grid layout + */ + private void createResFileGroup(Composite content) { + + Group group = new Group(content, SWT.NONE); + group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + group.setText("XML resource to edit"); + + GridLayout layout = new GridLayout(); + layout.numColumns = 2; + group.setLayout(layout); + + // line: selection of the res config + + Label label; + label = new Label(group, SWT.NONE); + label.setText("Configuration:"); + + mConfigSelector = new ConfigurationSelector(group); + GridData gd = new GridData(2, GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL); + gd.widthHint = ConfigurationSelector.WIDTH_HINT; + gd.heightHint = ConfigurationSelector.HEIGHT_HINT; + mConfigSelector.setLayoutData(gd); + OnConfigSelectorUpdated onConfigSelectorUpdated = new OnConfigSelectorUpdated(); + mConfigSelector.setOnChangeListener(onConfigSelectorUpdated); + + // line: selection of the output file + + label = new Label(group, SWT.NONE); + label.setText("Resource file:"); + + mResFileCombo = new Combo(group, SWT.DROP_DOWN); + mResFileCombo.select(0); + mResFileCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + mResFileCombo.addModifyListener(onConfigSelectorUpdated); + + // set output file name to the last one used + + String projPath = mProject.getFullPath().toPortableString(); + String filePath = sLastResFilePath.get(projPath); + + mResFileCombo.setText(filePath != null ? filePath : DEFAULT_RES_FILE_PATH); + onConfigSelectorUpdated.run(); + } + + /** + * Utility method to guess a suitable new XML ID based on the selected string. + */ private String guessId(String text) { // make lower case text = text.toLowerCase(); @@ -128,16 +224,25 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage return text; } + /** + * Returns the {@link ExtractStringRefactoring} instance used by this wizard page. + */ private ExtractStringRefactoring getOurRefactoring() { return (ExtractStringRefactoring) getRefactoring(); } - private boolean validatePage(ExtractStringRefactoring ref) { - String text = mNewIdTextField.getText().trim(); + /** + * Validates fields of the wizard input page. Displays errors as appropriate and + * enable the "Next" button (or not) by calling {@link #setPageComplete(boolean)}. + * + * @return True if the page has been positively validated. It may still have warnings. + */ + private boolean validatePage() { boolean success = true; // Analyze fatal errors. + String text = mNewIdTextField.getText().trim(); if (text == null || text.length() < 1) { setErrorMessage("Please provide a resource ID to replace with."); success = false; @@ -157,21 +262,216 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage } } + String resFile = mResFileCombo.getText(); + if (success) { + if (resFile == null || resFile.length() == 0) { + setErrorMessage("A resource file name is required."); + success = false; + } else if (!RES_XML_FILE_REGEX.matcher(resFile).matches()) { + setErrorMessage("The XML file name is not valid."); + success = false; + } + } + // Analyze info & warnings. if (success) { - if (ref.isResIdDuplicate(mFileLabel.getText(), text)) { - setErrorMessage(null); + setErrorMessage(null); + + ExtractStringRefactoring ref = getOurRefactoring(); + + ref.setTargetFile(resFile); + sLastResFilePath.put(mProject.getFullPath().toPortableString(), resFile); + + if (ref.isResIdDuplicate(resFile, text)) { setMessage( - String.format("Warning: There's already a string item called '%1$s' in %2$s.", - text, mFileLabel.getText())); + String.format("There's already a string item called '%1$s' in %2$s.", + text, resFile), + WizardPage.WARNING); + } else if (mProject.findMember(resFile) == null) { + setMessage( + String.format("File %2$s does not exist and will be created.", + text, resFile), + WizardPage.INFORMATION); } else { setMessage(null); - setErrorMessage(null); } } setPageComplete(success); return success; } + + public class OnConfigSelectorUpdated implements Runnable, ModifyListener { + + /** Regex pattern to parse a valid res path: it reads (/res/folder-name/)+(filename). */ + private final Pattern mPathRegex = Pattern.compile( + "(/res/[a-z][a-zA-Z0-9_-]+/)(.+)"); //$NON-NLS-1$ + + /** Temporary config object used to retrieve the Config Selector value. */ + private FolderConfiguration mTempConfig = new FolderConfiguration(); + + private HashMap> mFolderCache = + new HashMap>(); + private String mLastFolderUsedInCombo = null; + private boolean mInternalConfigChange; + private boolean mInternalFileComboChange; + + /** + * Callback invoked when the {@link ConfigurationSelector} has been changed. + *

          + * The callback does the following: + *

            + *
          • Examine the current file name to retrieve the XML filename, if any. + *
          • Recompute the path based on the configuration selector (e.g. /res/values-fr/). + *
          • Examine the path to retrieve all the files in it. Keep those in a local cache. + *
          • If the XML filename from step 1 is not in the file list, it's a custom file name. + * Insert it and sort it. + *
          • Re-populate the file combo with all the choices. + *
          • Select the original XML file. + */ + public void run() { + if (mInternalConfigChange) { + return; + } + + // get current leafname, if any + String leafName = ""; + String currPath = mResFileCombo.getText(); + Matcher m = mPathRegex.matcher(currPath); + if (m.matches()) { + // Note: groups 1 and 2 cannot be null. + leafName = m.group(2); + currPath = m.group(1); + } else { + // There was a path but it was invalid. Ignore it. + currPath = ""; + } + + // recreate the res path from the current configuration + mConfigSelector.getConfiguration(mTempConfig); + StringBuffer sb = new StringBuffer(RES_FOLDER_ABS); + sb.append(mTempConfig.getFolderName(ResourceFolderType.VALUES)); + sb.append('/'); + + String newPath = sb.toString(); + if (newPath.equals(currPath) && newPath.equals(mLastFolderUsedInCombo)) { + // Path has not changed. No need to reload. + return; + } + + // Get all the files at the new path + + TreeSet filePaths = mFolderCache.get(newPath); + + if (filePaths == null) { + filePaths = new TreeSet(); + + IFolder folder = mProject.getFolder(newPath); + if (folder != null && folder.exists()) { + try { + for (IResource res : folder.members()) { + String name = res.getName(); + if (res.getType() == IResource.FILE && name.endsWith(".xml")) { + filePaths.add(newPath + name); + } + } + } catch (CoreException e) { + // Ignore. + } + } + + mFolderCache.put(newPath, filePaths); + } + + currPath = newPath + leafName; + if (leafName.length() > 0 && !filePaths.contains(currPath)) { + filePaths.add(currPath); + } + + // Fill the combo + try { + mInternalFileComboChange = true; + + mResFileCombo.removeAll(); + + for (String filePath : filePaths) { + mResFileCombo.add(filePath); + } + + int index = -1; + if (leafName.length() > 0) { + index = mResFileCombo.indexOf(currPath); + if (index >= 0) { + mResFileCombo.select(index); + } + } + + if (index == -1) { + mResFileCombo.setText(currPath); + } + + mLastFolderUsedInCombo = newPath; + + } finally { + mInternalFileComboChange = false; + } + + // finally validate the whole page + validatePage(); + } + + /** + * Callback invoked when {@link ExtractStringInputPage#mResFileCombo} has been + * modified. + */ + public void modifyText(ModifyEvent e) { + if (mInternalFileComboChange) { + return; + } + + String wsFolderPath = mResFileCombo.getText(); + + // This is a custom path, we need to sanitize it. + // First it should start with "/res/". Then we need to make sure there are no + // relative paths, things like "../" or "./" or even "//". + wsFolderPath = wsFolderPath.replaceAll("/+\\.\\./+|/+\\./+|//+|\\\\+|^/+", "/"); //$NON-NLS-1$ //$NON-NLS-2$ + wsFolderPath = wsFolderPath.replaceAll("^\\.\\./+|^\\./+", ""); //$NON-NLS-1$ //$NON-NLS-2$ + wsFolderPath = wsFolderPath.replaceAll("/+\\.\\.$|/+\\.$|/+$", ""); //$NON-NLS-1$ //$NON-NLS-2$ + + // We get "res/foo" from selections relative to the project when we want a "/res/foo" path. + if (wsFolderPath.startsWith(RES_FOLDER_REL)) { + wsFolderPath = RES_FOLDER_ABS + wsFolderPath.substring(RES_FOLDER_REL.length()); + + mInternalFileComboChange = true; + mResFileCombo.setText(wsFolderPath); + mInternalFileComboChange = false; + } + + if (wsFolderPath.startsWith(RES_FOLDER_ABS)) { + wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length()); + + int pos = wsFolderPath.indexOf(AndroidConstants.WS_SEP_CHAR); + if (pos >= 0) { + wsFolderPath = wsFolderPath.substring(0, pos); + } + + String[] folderSegments = wsFolderPath.split(FolderConfiguration.QUALIFIER_SEP); + + if (folderSegments.length > 0) { + String folderName = folderSegments[0]; + + if (folderName != null && !folderName.equals(wsFolderPath)) { + // update config selector + mInternalConfigChange = true; + mConfigSelector.setConfiguration(folderSegments); + mInternalConfigChange = false; + } + } + } + + validatePage(); + } + } + } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java index 99711bf20..6c88a38c1 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java @@ -177,7 +177,7 @@ class ExtractStringRefactoring extends Refactoring { public ExtractStringRefactoring(IFile file, ITextSelection selection) { mFile = file; mSelectionStart = selection.getOffset(); - mSelectionEnd = mSelectionStart + selection.getLength(); + mSelectionEnd = mSelectionStart + Math.max(0, selection.getLength() - 1); } /** @@ -906,9 +906,8 @@ class ExtractStringRefactoring extends Refactoring { * compilation unit. The resource may not exist. */ private IResource getTargetXmlResource(String xmlFileWsPath) { - IProject proj = mUnit.getPrimary().getResource().getProject(); - Path path = new Path(xmlFileWsPath); - IResource resource = proj.findMember(path); + IProject proj = mFile.getProject(); + IResource resource = proj.getFile(xmlFileWsPath); return resource; } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java index 2083a6e5d..c5b0c7d11 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java @@ -16,6 +16,7 @@ package com.android.ide.eclipse.adt.refactorings.extractstring; +import org.eclipse.core.resources.IProject; import org.eclipse.ltk.ui.refactoring.RefactoringWizard; /** @@ -26,17 +27,24 @@ import org.eclipse.ltk.ui.refactoring.RefactoringWizard; */ class ExtractStringWizard extends RefactoringWizard { + private final IProject mProject; + /** * Create a wizard for ExtractString based on a simple dialog with one page. + * + * @param ref The instance of {@link ExtractStringRefactoring} to associate to the wizard. + * @param project The project where the wizard was invoked from (e.g. where the user selection + * happened, so that we can retrieve project resources.) */ - public ExtractStringWizard(ExtractStringRefactoring ref, String title) { + public ExtractStringWizard(ExtractStringRefactoring ref, IProject project) { super(ref, DIALOG_BASED_USER_INTERFACE | PREVIEW_EXPAND_FIRST_NODE); - setDefaultPageTitle(title); + mProject = project; + setDefaultPageTitle(ref.getName()); } @Override protected void addUserInputPages() { - addPage(new ExtractStringInputPage()); + addPage(new ExtractStringInputPage(mProject)); } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java index e84c05123..80cc0f5aa 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java @@ -271,9 +271,9 @@ class NewXmlFileCreationPage extends WizardPage { final static int NUM_COL = 4; /** Absolute destination folder root, e.g. "/res/" */ - private static String sResFolderAbs = AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP; + private static final String RES_FOLDER_ABS = AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP; /** Relative destination folder root, e.g. "res/" */ - private static String sResFolderRel = SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP; + private static final String RES_FOLDER_REL = SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP; private IProject mProject; private Text mProjectTextField; @@ -856,16 +856,16 @@ class NewXmlFileCreationPage extends WizardPage { ArrayList matches = new ArrayList(); // We get "res/foo" from selections relative to the project when we want a "/res/foo" path. - if (wsFolderPath.startsWith(sResFolderRel)) { - wsFolderPath = sResFolderAbs + wsFolderPath.substring(sResFolderRel.length()); + if (wsFolderPath.startsWith(RES_FOLDER_REL)) { + wsFolderPath = RES_FOLDER_ABS + wsFolderPath.substring(RES_FOLDER_REL.length()); mInternalWsFolderPathUpdate = true; mWsFolderPathTextField.setText(wsFolderPath); mInternalWsFolderPathUpdate = false; } - if (wsFolderPath.startsWith(sResFolderAbs)) { - wsFolderPath = wsFolderPath.substring(sResFolderAbs.length()); + if (wsFolderPath.startsWith(RES_FOLDER_ABS)) { + wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length()); int pos = wsFolderPath.indexOf(AndroidConstants.WS_SEP_CHAR); if (pos >= 0) { @@ -952,16 +952,16 @@ class NewXmlFileCreationPage extends WizardPage { // The configuration is valid. Reformat the folder path using the canonical // value from the configuration. - newPath = sResFolderAbs + mTempConfig.getFolderName(type.getResFolderType()); + newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType()); } else { // The configuration is invalid. We still update the path but this time // do it manually on the string. - if (wsFolderPath.startsWith(sResFolderAbs)) { + if (wsFolderPath.startsWith(RES_FOLDER_ABS)) { wsFolderPath.replaceFirst( - "^(" + sResFolderAbs +")[^-]*(.*)", //$NON-NLS-1$ //$NON-NLS-2$ + "^(" + RES_FOLDER_ABS +")[^-]*(.*)", //$NON-NLS-1$ //$NON-NLS-2$ "\\1" + type.getResFolderName() + "\\2"); //$NON-NLS-1$ //$NON-NLS-2$ } else { - newPath = sResFolderAbs + mTempConfig.getFolderName(type.getResFolderType()); + newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType()); } } @@ -1018,7 +1018,7 @@ class NewXmlFileCreationPage extends WizardPage { if (type != null) { mConfigSelector.getConfiguration(mTempConfig); - StringBuffer sb = new StringBuffer(sResFolderAbs); + StringBuffer sb = new StringBuffer(RES_FOLDER_ABS); sb.append(mTempConfig.getFolderName(type.getResFolderType())); mInternalWsFolderPathUpdate = true; @@ -1131,8 +1131,8 @@ class NewXmlFileCreationPage extends WizardPage { // -- validate generated path if (error == null) { String wsFolderPath = getWsFolderPath(); - if (!wsFolderPath.startsWith(sResFolderAbs)) { - error = String.format("Target folder must start with %1$s.", sResFolderAbs); + if (!wsFolderPath.startsWith(RES_FOLDER_ABS)) { + error = String.format("Target folder must start with %1$s.", RES_FOLDER_ABS); } } From bea7f769d87f03b22d071abae527bc49102b7c8a Mon Sep 17 00:00:00 2001 From: Jack Palevich <> Date: Tue, 24 Mar 2009 20:22:52 -0700 Subject: [PATCH 23/39] Automated import from //branches/cupcake/...@142485,142485 --- .../graphics/TranslucentGLSurfaceViewActivity.java | 4 ++++ .../android/apis/graphics/TriangleActivity.java | 1 + .../android/apis/graphics/TriangleRenderer.java | 10 ---------- .../android/apis/graphics/kube/KubeRenderer.java | 9 --------- .../com/example/android/compass/CompassActivity.java | 10 ---------- 5 files changed, 5 insertions(+), 29 deletions(-) diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/TranslucentGLSurfaceViewActivity.java b/samples/ApiDemos/src/com/example/android/apis/graphics/TranslucentGLSurfaceViewActivity.java index 750a47ba7..a0bad4bc9 100644 --- a/samples/ApiDemos/src/com/example/android/apis/graphics/TranslucentGLSurfaceViewActivity.java +++ b/samples/ApiDemos/src/com/example/android/apis/graphics/TranslucentGLSurfaceViewActivity.java @@ -34,6 +34,10 @@ public class TranslucentGLSurfaceViewActivity extends Activity { // Create our Preview view and set it as the content of our // Activity mGLSurfaceView = new GLSurfaceView(this); + // We want an 8888 pixel format because that's required for + // a translucent window. + // And we want a depth buffer. + mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Tell the cube renderer that we want to render a translucent version // of the cube: mGLSurfaceView.setRenderer(new CubeRenderer(true)); diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java b/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java index 59f3c6cf8..e5b06f426 100644 --- a/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java +++ b/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java @@ -26,6 +26,7 @@ public class TriangleActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mGLView = new GLSurfaceView(this); + mGLView.setEGLConfigChooser(false); mGLView.setRenderer(new TriangleRenderer(this)); setContentView(mGLView); } diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleRenderer.java b/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleRenderer.java index 451b927a9..e5299b332 100644 --- a/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleRenderer.java +++ b/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleRenderer.java @@ -44,16 +44,6 @@ public class TriangleRenderer implements GLSurfaceView.Renderer{ mTriangle = new Triangle(); } - public int[] getConfigSpec() { - // We don't need a depth buffer, and don't care about our - // color depth. - int[] configSpec = { - EGL10.EGL_DEPTH_SIZE, 0, - EGL10.EGL_NONE - }; - return configSpec; - } - public void onSurfaceCreated(GL10 gl, EGLConfig config) { /* * By default, OpenGL enables features that improve quality diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/kube/KubeRenderer.java b/samples/ApiDemos/src/com/example/android/apis/graphics/kube/KubeRenderer.java index 252f566f9..9977041a2 100644 --- a/samples/ApiDemos/src/com/example/android/apis/graphics/kube/KubeRenderer.java +++ b/samples/ApiDemos/src/com/example/android/apis/graphics/kube/KubeRenderer.java @@ -74,15 +74,6 @@ class KubeRenderer implements GLSurfaceView.Renderer { mWorld.draw(gl); } - public int[] getConfigSpec() { - // Need a depth buffer, don't care about color depth. - int[] configSpec = { - EGL10.EGL_DEPTH_SIZE, 16, - EGL10.EGL_NONE - }; - return configSpec; - } - public void onSurfaceChanged(GL10 gl, int width, int height) { gl.glViewport(0, 0, width, height); diff --git a/samples/Compass/src/com/example/android/compass/CompassActivity.java b/samples/Compass/src/com/example/android/compass/CompassActivity.java index c4b956675..74f3bc71d 100644 --- a/samples/Compass/src/com/example/android/compass/CompassActivity.java +++ b/samples/Compass/src/com/example/android/compass/CompassActivity.java @@ -89,16 +89,6 @@ public class CompassActivity extends Activity implements Renderer, SensorEventLi mSensorManager.unregisterListener(this); } - public int[] getConfigSpec() { - // We want a depth buffer, don't care about the - // details of the color buffer. - int[] configSpec = { - EGL10.EGL_DEPTH_SIZE, 16, - EGL10.EGL_NONE - }; - return configSpec; - } - public void onDrawFrame(GL10 gl) { /* * Usually, the first thing one might want to do is to clear From 1e163c86ed46a25ddec38e1d27bce9631f316582 Mon Sep 17 00:00:00 2001 From: Raphael Moll <> Date: Wed, 25 Mar 2009 15:02:20 -0700 Subject: [PATCH 24/39] Automated import from //branches/cupcake/...@142584,142584 --- .../common/project/AndroidManifestParser.java | 56 ++++++++++++++----- .../editors/uimodel/UiElementNode.java | 18 +++--- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java index fa7e9b9f4..85ba96839 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java @@ -34,6 +34,7 @@ import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; @@ -585,19 +586,31 @@ public class AndroidManifestParser { // get the result from the handler return new AndroidManifestParser(manifestHandler.getPackage(), - manifestHandler.getActivities(), manifestHandler.getLauncherActivity(), - manifestHandler.getProcesses(), manifestHandler.getDebuggable(), - manifestHandler.getApiLevelRequirement(), manifestHandler.getInstrumentations(), + manifestHandler.getActivities(), + manifestHandler.getLauncherActivity(), + manifestHandler.getProcesses(), + manifestHandler.getDebuggable(), + manifestHandler.getApiLevelRequirement(), + manifestHandler.getInstrumentations(), manifestHandler.getUsesLibraries()); } catch (ParserConfigurationException e) { AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), - "Bad parser configuration for %s", manifestFile.getFullPath()); + "Bad parser configuration for %s: %s", + manifestFile.getFullPath(), + e.getMessage()); } catch (SAXException e) { AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), - "Parser exception for %s", manifestFile.getFullPath()); + "Parser exception for %s: %s", + manifestFile.getFullPath(), + e.getMessage()); } catch (IOException e) { - AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), - "I/O error for %s", manifestFile.getFullPath()); + // Don't log a console error when failing to read a non-existing file + if (!(e instanceof FileNotFoundException)) { + AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), + "I/O error for %s: %s", + manifestFile.getFullPath(), + e.getMessage()); + } } return null; @@ -633,20 +646,33 @@ public class AndroidManifestParser { // get the result from the handler return new AndroidManifestParser(manifestHandler.getPackage(), - manifestHandler.getActivities(), manifestHandler.getLauncherActivity(), - manifestHandler.getProcesses(), manifestHandler.getDebuggable(), - manifestHandler.getApiLevelRequirement(), manifestHandler.getInstrumentations(), + manifestHandler.getActivities(), + manifestHandler.getLauncherActivity(), + manifestHandler.getProcesses(), + manifestHandler.getDebuggable(), + manifestHandler.getApiLevelRequirement(), + manifestHandler.getInstrumentations(), manifestHandler.getUsesLibraries()); } catch (ParserConfigurationException e) { AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), - "Bad parser configuration for %s", manifestFile.getAbsolutePath()); + "Bad parser configuration for %s: %s", + manifestFile.getAbsolutePath(), + e.getMessage()); } catch (SAXException e) { AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), - "Parser exception for %s", manifestFile.getAbsolutePath()); + "Parser exception for %s: %s", + manifestFile.getAbsolutePath(), + e.getMessage()); } catch (IOException e) { - AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), - "I/O error for %s", manifestFile.getAbsolutePath()); - } + // Don't log a console error when failing to read a non-existing file + if (!(e instanceof FileNotFoundException)) { + AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(), + "I/O error for %s: %s", + manifestFile.getAbsolutePath(), + e.getMessage()); + } + } + return null; } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiElementNode.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiElementNode.java index 3728886b5..517284c83 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiElementNode.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiElementNode.java @@ -1233,8 +1233,9 @@ public class UiElementNode implements IPropertySource { Node attr = attrs.item(n); if ("xmlns".equals(attr.getPrefix())) { //$NON-NLS-1$ String uri = attr.getNodeValue(); - String nsPrefix = attr.getLocalName(); - if (SdkConstants.NS_RESOURCES.equals(uri)) { + String nsPrefix = attr.getLocalName(); + // Is this the URI we are looking for? If yes, we found its prefix. + if (nsUri.equals(uri)) { return nsPrefix; } visited.add(nsPrefix); @@ -1244,7 +1245,8 @@ public class UiElementNode implements IPropertySource { // Use a sensible default prefix if we can't find one. // We need to make sure the prefix is not one that was declared in the scope - // visited above. + // visited above. Use a default namespace prefix "android" for the Android resource + // NS and use "ns" for all other custom namespaces. String prefix = SdkConstants.NS_RESOURCES.equals(nsUri) ? "android" : "ns"; //$NON-NLS-1$ //$NON-NLS-2$ String base = prefix; for (int i = 1; visited.contains(prefix); i++) { @@ -1475,18 +1477,18 @@ public class UiElementNode implements IPropertySource { } } } - + if (attribute != null) { - final UiAttributeNode fAttribute = attribute; // get the current value and compare it to the new value - String oldValue = fAttribute.getCurrentValue(); + String oldValue = attribute.getCurrentValue(); final String newValue = (String)value; if (oldValue.equals(newValue)) { return; } - + + final UiAttributeNode fAttribute = attribute; AndroidEditor editor = getEditor(); editor.editXmlModel(new Runnable() { public void run() { @@ -1495,6 +1497,4 @@ public class UiElementNode implements IPropertySource { }); } } - - } From 660c0c97ee43e438032b2dff03f0d2997f397931 Mon Sep 17 00:00:00 2001 From: Raphael Moll <> Date: Wed, 25 Mar 2009 15:03:39 -0700 Subject: [PATCH 25/39] Automated import from //branches/cupcake/...@142585,142585 --- .../ExtractStringRefactoring.java | 162 +++++++++++------- 1 file changed, 99 insertions(+), 63 deletions(-) diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java index 6c88a38c1..430ff1819 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java @@ -57,6 +57,7 @@ import org.eclipse.ltk.core.refactoring.CompositeChange; import org.eclipse.ltk.core.refactoring.Refactoring; import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor; import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.ltk.core.refactoring.TextEditChangeGroup; import org.eclipse.ltk.core.refactoring.TextFileChange; import org.eclipse.text.edits.InsertEdit; import org.eclipse.text.edits.MultiTextEdit; @@ -413,28 +414,23 @@ class ExtractStringRefactoring extends Refactoring { // Prepare the change for the XML file. - + if (!isResIdDuplicate(mTargetXmlFileWsPath, mXmlStringId)) { // We actually change it only if the ID doesn't exist yet - TextFileChange xmlChange = new TextFileChange(getName(), (IFile) targetXml); - xmlChange.setTextType("xml"); //$NON-NLS-1$ - TextEdit edit = createXmlEdit((IFile) targetXml, mXmlStringId, mTokenString); - if (edit == null) { - status.addFatalError(String.format("Failed to modify file %1$s", - mTargetXmlFileWsPath)); + Change change = createXmlChange((IFile) targetXml, mXmlStringId, mTokenString, + status, SubMonitor.convert(monitor, 1)); + if (change != null) { + mChanges.add(change); } - xmlChange.setEdit(edit); - mChanges.add(xmlChange); } - monitor.worked(1); - + if (status.hasError()) { return status; } // Prepare the change to the Java compilation unit - List changes = computeJavaChanges(mUnit, mXmlStringId, mTokenString, status, - SubMonitor.convert(monitor, 1)); + List changes = computeJavaChanges(mUnit, mXmlStringId, mTokenString, + status, SubMonitor.convert(monitor, 1)); if (changes != null) { mChanges.addAll(changes); } @@ -448,72 +444,94 @@ class ExtractStringRefactoring extends Refactoring { } /** - * Internal helper that actually prepares the {@link TextEdit} that adds the given + * Internal helper that actually prepares the {@link Change} that adds the given * ID to the given XML File. *

            * This does not actually modify the file. * - * @param xmlFile The file resource to modify. - * @param replacementStringId The new ID to insert. - * @param oldString The old string, which will be the value in the XML string. + * @param targetXml The file resource to modify. + * @param xmlStringId The new ID to insert. + * @param tokenString The old string, which will be the value in the XML string. * @return A new {@link TextEdit} that describes how to change the file. */ - private TextEdit createXmlEdit(IFile xmlFile, String replacementStringId, String oldString) { + private Change createXmlChange(IFile targetXml, + String xmlStringId, + String tokenString, + RefactoringStatus status, + SubMonitor subMonitor) { - if (!xmlFile.exists()) { + TextFileChange xmlChange = new TextFileChange(getName(), targetXml); + xmlChange.setTextType("xml"); //$NON-NLS-1$ + + TextEdit edit = null; + TextEditGroup editGroup = null; + + if (!targetXml.exists()) { // The XML file does not exist. Simply create it. StringBuilder content = new StringBuilder(); content.append("\n"); //$NON-NLS-1$ content.append("\n"); //$NON-NLS-1$ content.append(" "). //$NON-NLS-1$ - append(oldString). + append(tokenString). append("\n"); //$NON-NLS-1$ content.append("\n"); //$NON-NLS-1$ - return new InsertEdit(0, content.toString()); - } + edit = new InsertEdit(0, content.toString()); + editGroup = new TextEditGroup("Create ID in new XML file", edit); + } else { + // The file exist. Attempt to parse it as a valid XML document. + try { + int[] indices = new int[2]; + if (findXmlOpeningTagPos(targetXml.getContents(), "resources", indices)) { //$NON-NLS-1$ + // Indices[1] indicates whether we found > or />. It can only be 1 or 2. + // Indices[0] is the position of the first character of either > or />. + // + // Note: we don't even try to adapt our formatting to the existing structure (we + // could by capturing whatever whitespace is after the closing bracket and + // applying it here before our tag, unless we were dealing with an empty + // resource tag.) + + int offset = indices[0]; + int len = indices[1]; + StringBuilder content = new StringBuilder(); + content.append(">\n"); //$NON-NLS-1$ + content.append(" "). //$NON-NLS-1$ + append(tokenString). + append(""); //$NON-NLS-1$ + if (len == 2) { + content.append("\n"); //$NON-NLS-1$ + } - // The file exist. Attempt to parse it as a valid XML document. - try { - int[] indices = new int[2]; - if (findXmlOpeningTagPos(xmlFile.getContents(), "resources", indices)) { //$NON-NLS-1$ - // Indices[1] indicates whether we found > or />. It can only be 1 or 2. - // Indices[0] is the position of the first character of either > or />. - // - // Note: we don't even try to adapt our formatting to the existing structure (we - // could by capturing whatever whitespace is after the closing bracket and - // applying it here before our tag, unless we were dealing with an empty - // resource tag.) - - int offset = indices[0]; - int len = indices[1]; - StringBuilder content = new StringBuilder(); - content.append(">\n"); //$NON-NLS-1$ - content.append(" "). //$NON-NLS-1$ - append(oldString). - append(""); //$NON-NLS-1$ - if (len == 2) { - content.append("\n"); //$NON-NLS-1$ + edit = new ReplaceEdit(offset, len, content.toString()); + editGroup = new TextEditGroup("Insert ID in XML file", edit); } - - return new ReplaceEdit(offset, len, content.toString()); + } catch (CoreException e) { + // Failed to read file. Ignore. Will return null below. } - - } catch (CoreException e) { - // Failed to read file. Ignore. Will return null below. } - - return null; + + if (edit == null) { + status.addFatalError(String.format("Failed to modify file %1$s", + mTargetXmlFileWsPath)); + return null; + } + + xmlChange.setEdit(edit); + // The TextEditChangeGroup let the user toggle this change on and off later. + xmlChange.addTextEditChangeGroup(new TextEditChangeGroup(xmlChange, editGroup)); + + subMonitor.worked(1); + return xmlChange; } /** * Parse an XML input stream, looking for an opening tag. *

            - * If found, returns the character offet in the buffer of the closing bracket of that + * If found, returns the character offest in the buffer of the closing bracket of that * tag, e.g. the position of > in "". The first character is at offset 0. *

            * The implementation here relies on a simple character-based parser. No DOM nor SAX @@ -622,6 +640,9 @@ class ExtractStringRefactoring extends Refactoring { return false; } + /** + * Computes the changes to be made to Java file(s) and returns a list of {@link Change}. + */ private List computeJavaChanges(ICompilationUnit unit, String xmlStringId, String tokenString, @@ -700,29 +721,31 @@ class ExtractStringRefactoring extends Refactoring { // ImportRewrite will allow us to add the new type to the imports and will resolve // what the Java source must reference, e.g. the FQCN or just the simple name. - ImportRewrite ir = ImportRewrite.create((CompilationUnit) node, true); + ImportRewrite importRewrite = ImportRewrite.create((CompilationUnit) node, true); String Rqualifier = packageName + ".R"; //$NON-NLS-1$ - Rqualifier = ir.addImport(Rqualifier); + Rqualifier = importRewrite.addImport(Rqualifier); // Rewrite the AST itself via an ASTVisitor AST ast = node.getAST(); - ASTRewrite ar = ASTRewrite.create(ast); - ReplaceStringsVisitor visitor = new ReplaceStringsVisitor(ast, ar, + ASTRewrite astRewrite = ASTRewrite.create(ast); + ArrayList astEditGroups = new ArrayList(); + ReplaceStringsVisitor visitor = new ReplaceStringsVisitor( + ast, astRewrite, astEditGroups, tokenString, Rqualifier, xmlStringId); node.accept(visitor); - + // Finally prepare the change set try { MultiTextEdit edit = new MultiTextEdit(); // Create the edit to change the imports, only if anything changed - TextEdit subEdit = ir.rewriteImports(subMonitor.newChild(1)); + TextEdit subEdit = importRewrite.rewriteImports(subMonitor.newChild(1)); if (subEdit.hasChildren()) { edit.addChild(subEdit); } // Create the edit to change the Java source, only if anything changed - subEdit = ar.rewriteAST(); + subEdit = astRewrite.rewriteAST(); if (subEdit.hasChildren()) { edit.addChild(subEdit); } @@ -730,6 +753,13 @@ class ExtractStringRefactoring extends Refactoring { // Only create a change set if any edit was collected if (edit.hasChildren()) { change.setEdit(edit); + + // Create TextEditChangeGroups which let the user turn changes on or off + // individually. This must be done after the change.setEdit() call above. + for (TextEditGroup editGroup : astEditGroups) { + change.addTextEditChangeGroup(new TextEditChangeGroup(change, editGroup)); + } + changes.add(change); } @@ -740,7 +770,9 @@ class ExtractStringRefactoring extends Refactoring { if (changes.size() > 0) { return changes; } - + + subMonitor.worked(1); + } catch (CoreException e) { // ImportRewrite.rewriteImports failed. status.addFatalError(e.getMessage()); @@ -755,14 +787,17 @@ class ExtractStringRefactoring extends Refactoring { private final String mOldString; private final String mRQualifier; private final String mXmlId; + private final ArrayList mEditGroups; public ReplaceStringsVisitor(AST ast, ASTRewrite astRewrite, + ArrayList editGroups, String oldString, String rQualifier, String xmlId) { mAst = ast; mRewriter = astRewrite; + mEditGroups = editGroups; mOldString = oldString; mRQualifier = rQualifier; mXmlId = xmlId; @@ -776,7 +811,8 @@ class ExtractStringRefactoring extends Refactoring { SimpleName idName = mAst.newSimpleName(mXmlId); QualifiedName newNode = mAst.newQualifiedName(qualifierName, idName); - TextEditGroup editGroup = new TextEditGroup(getName()); + TextEditGroup editGroup = new TextEditGroup("Replace string by ID"); + mEditGroups.add(editGroup); mRewriter.replace(node, newNode, editGroup); } return super.visit(node); From fbeb67eb39c2b5f36a29f34e1d4ce28077ba0328 Mon Sep 17 00:00:00 2001 From: Raphael Moll <> Date: Wed, 25 Mar 2009 15:04:55 -0700 Subject: [PATCH 26/39] Automated import from //branches/cupcake/...@142586,142586 --- .../wizards/NewXmlFileCreationPage.java | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java index 80cc0f5aa..7a9115050 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.editors.wizards; +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; @@ -39,6 +40,7 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jface.viewers.IStructuredSelection; @@ -745,8 +747,35 @@ class NewXmlFileCreationPage extends WizardPage { // cleared above. // get the AndroidTargetData from the project - IAndroidTarget target = Sdk.getCurrent().getTarget(mProject); - AndroidTargetData data = Sdk.getCurrent().getTargetData(target); + IAndroidTarget target = null; + AndroidTargetData data = null; + + target = Sdk.getCurrent().getTarget(mProject); + if (target == null) { + // A project should have a target. The target can be missing if the project + // is an old project for which a target hasn't been affected or if the + // target no longer exists in this SDK. Simply log the error and dismiss. + + AdtPlugin.log(IStatus.INFO, + "NewXmlFile wizard: no platform target for project %s", //$NON-NLS-1$ + mProject.getName()); + continue; + } else { + data = Sdk.getCurrent().getTargetData(target); + + if (data == null) { + // We should have both a target and its data. + // However if the wizard is invoked whilst the platform is still being + // loaded we can end up in a weird case where we have a target but it + // doesn't have any data yet. + // Lets log a warning and silently ignore this root. + + AdtPlugin.log(IStatus.INFO, + "NewXmlFile wizard: no data for target %s, project %s", //$NON-NLS-1$ + target.getName(), mProject.getName()); + continue; + } + } IDescriptorProvider provider = data.getDescriptorProvider((Integer)rootSeed); ElementDescriptor descriptor = provider.getDescriptor(); From c22f78e2382fa3f5181e25968cb651b5d164a3c1 Mon Sep 17 00:00:00 2001 From: Raphael Moll <> Date: Wed, 25 Mar 2009 21:11:04 -0700 Subject: [PATCH 27/39] Automated import from //branches/cupcake/...@142851,142851 --- .../eclipse/adt/sdk/LayoutParamsParser.java | 7 +-- .../ide/eclipse/common/AndroidConstants.java | 12 ++-- .../layout/descriptors/LayoutDescriptors.java | 62 +++++++++++-------- .../editors/layout/parts/DropFeedback.java | 2 + .../layout/uimodel/UiViewElementNode.java | 2 +- 5 files changed, 48 insertions(+), 37 deletions(-) diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/LayoutParamsParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/LayoutParamsParser.java index dc600d7f7..19ef16cfa 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/LayoutParamsParser.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/LayoutParamsParser.java @@ -157,7 +157,7 @@ public class LayoutParamsParser { superClasses[2] = paramsClassName; } HashMap> found = - mClassLoader.findClassesDerivingFrom("android.", superClasses); + mClassLoader.findClassesDerivingFrom("android.", superClasses); //$NON-NLS-1$ mTopViewClass = mClassLoader.getClass(rootClassName); mTopGroupClass = mClassLoader.getClass(groupClassName); if (paramsClassName != null) { @@ -179,8 +179,7 @@ public class LayoutParamsParser { addView(mTopViewClass); // ViewGroup derives from View - mGroupMap.get(groupClassName).setSuperClass( - mViewMap.get(rootClassName)); + mGroupMap.get(groupClassName).setSuperClass(mViewMap.get(rootClassName)); progress.setWorkRemaining(mGroupList.size() + mViewList.size()); @@ -346,7 +345,7 @@ public class LayoutParamsParser { private IClassDescriptor findLayoutParams(IClassDescriptor groupClass) { IClassDescriptor[] innerClasses = groupClass.getDeclaredClasses(); for (IClassDescriptor innerClass : innerClasses) { - if (innerClass.getSimpleName().equals(AndroidConstants.CLASS_LAYOUTPARAMS)) { + if (innerClass.getSimpleName().equals(AndroidConstants.CLASS_NAME_LAYOUTPARAMS)) { return innerClass; } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java index 1da753c7b..226357f7a 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java @@ -189,14 +189,16 @@ public class AndroidConstants { public final static String CLASS_CONTEXT = "android.content.Context"; //$NON-NLS-1$ public final static String CLASS_VIEW = "android.view.View"; //$NON-NLS-1$ public final static String CLASS_VIEWGROUP = "android.view.ViewGroup"; //$NON-NLS-1$ - public final static String CLASS_LAYOUTPARAMS = "LayoutParams"; //$NON-NLS-1$ + public final static String CLASS_NAME_LAYOUTPARAMS = "LayoutParams"; //$NON-NLS-1$ public final static String CLASS_VIEWGROUP_LAYOUTPARAMS = - CLASS_VIEWGROUP + "$" + CLASS_LAYOUTPARAMS; //$NON-NLS-1$ - public final static String CLASS_FRAMELAYOUT = "FrameLayout"; //$NON-NLS-1$ + CLASS_VIEWGROUP + "$" + CLASS_NAME_LAYOUTPARAMS; //$NON-NLS-1$ + public final static String CLASS_NAME_FRAMELAYOUT = "FrameLayout"; //$NON-NLS-1$ + public final static String CLASS_FRAMELAYOUT = + "android.widget." + CLASS_NAME_FRAMELAYOUT; //$NON-NLS-1$ public final static String CLASS_PREFERENCE = "android.preference.Preference"; //$NON-NLS-1$ - public final static String CLASS_PREFERENCE_SCREEN = "PreferenceScreen"; //$NON-NLS-1$ + public final static String CLASS_NAME_PREFERENCE_SCREEN = "PreferenceScreen"; //$NON-NLS-1$ public final static String CLASS_PREFERENCES = - "android.preference." + CLASS_PREFERENCE_SCREEN; //$NON-NLS-1$ + "android.preference." + CLASS_NAME_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$ diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java index 2c0f984db..a59ad6f19 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java @@ -121,7 +121,7 @@ public final class LayoutDescriptors implements IDescriptorProvider { // The tag can only be a root tag, so it is added at the end. // It gets everything else as children but it is not made a child itself. - ElementDescriptor mergeTag = createMerge(); + ElementDescriptor mergeTag = createMerge(newLayouts); mergeTag.setChildren(newDescriptors); // mergeTag makes a copy of the list newDescriptors.add(mergeTag); newLayouts.add(mergeTag); @@ -195,7 +195,7 @@ public final class LayoutDescriptors implements IDescriptorProvider { if (need_separator) { String title; if (layoutParams.getShortClassName().equals( - AndroidConstants.CLASS_LAYOUTPARAMS)) { + AndroidConstants.CLASS_NAME_LAYOUTPARAMS)) { title = String.format("Layout Attributes from %1$s", layoutParams.getViewLayoutClass().getShortClassName()); } else { @@ -229,10 +229,10 @@ public final class LayoutDescriptors implements IDescriptorProvider { /** * Creates a new descriptor and adds it to the list of view descriptors. * - * @param newViews A list of view descriptors being populated. Also used to find the - * View description and extract its layout attributes. + * @param knownViews A list of view descriptors being populated. Also used to find the + * View descriptor and extract its layout attributes. */ - private void insertInclude(ArrayList newViews) { + private void insertInclude(ArrayList knownViews) { String xml_name = "include"; //$NON-NLS-1$ // Create the include custom attributes @@ -260,7 +260,8 @@ public final class LayoutDescriptors implements IDescriptorProvider { null); //overrides // Find View and inherit all its layout attributes - AttributeDescriptor[] viewLayoutAttribs = findViewLayoutAttributes(newViews); + AttributeDescriptor[] viewLayoutAttribs = findViewLayoutAttributes( + AndroidConstants.CLASS_VIEW, knownViews); // Create the include descriptor ViewElementDescriptor desc = new ViewElementDescriptor(xml_name, // xml_name @@ -273,33 +274,21 @@ public final class LayoutDescriptors implements IDescriptorProvider { null, // children false /* mandatory */); - newViews.add(desc); - } - - /** - * Finds the View descriptor and retrieves all its layout attributes. - */ - private AttributeDescriptor[] findViewLayoutAttributes( - ArrayList newViews) { - - for (ElementDescriptor desc : newViews) { - if (desc instanceof ViewElementDescriptor) { - ViewElementDescriptor viewDesc = (ViewElementDescriptor) desc; - if (AndroidConstants.CLASS_VIEW.equals(viewDesc.getCanonicalClassName())) { - return viewDesc.getLayoutAttributes(); - } - } - } - - return null; + knownViews.add(desc); } /** * Creates and return a new descriptor. + * @param knownLayouts A list of all known layout view descriptors, used to find the + * FrameLayout descriptor and extract its layout attributes. */ - private ElementDescriptor createMerge() { + private ElementDescriptor createMerge(ArrayList knownLayouts) { String xml_name = "merge"; //$NON-NLS-1$ + // Find View and inherit all its layout attributes + AttributeDescriptor[] viewLayoutAttribs = findViewLayoutAttributes( + AndroidConstants.CLASS_FRAMELAYOUT, knownLayouts); + // Create the include descriptor ViewElementDescriptor desc = new ViewElementDescriptor(xml_name, // xml_name xml_name, // ui_name @@ -307,10 +296,29 @@ public final class LayoutDescriptors implements IDescriptorProvider { "A root tag useful for XML layouts inflated using a ViewStub.", // tooltip null, // sdk_url null, // attributes - null, // layout attributes + viewLayoutAttribs, // layout attributes null, // children false /* mandatory */); return desc; } + + /** + * Finds the descriptor and retrieves all its layout attributes. + */ + private AttributeDescriptor[] findViewLayoutAttributes( + String viewFqcn, + ArrayList knownViews) { + + for (ElementDescriptor desc : knownViews) { + if (desc instanceof ViewElementDescriptor) { + ViewElementDescriptor viewDesc = (ViewElementDescriptor) desc; + if (viewFqcn.equals(viewDesc.getCanonicalClassName())) { + return viewDesc.getLayoutAttributes(); + } + } + } + + return null; + } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/DropFeedback.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/DropFeedback.java index 6e79d64d4..b7d69af06 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/DropFeedback.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/DropFeedback.java @@ -96,6 +96,8 @@ class DropFeedback { RelativeInfo info = null; UiElementEditPart sibling = null; + // TODO consider merge like a vertical layout + // TODO consider TableLayout like a linear if (LayoutConstants.LINEAR_LAYOUT.equals(layoutXmlName)) { sibling = findLinearTarget(parentPart, where)[1]; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/uimodel/UiViewElementNode.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/uimodel/UiViewElementNode.java index 1bf5d5abf..738591a29 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/uimodel/UiViewElementNode.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/uimodel/UiViewElementNode.java @@ -81,7 +81,7 @@ public class UiViewElementNode extends UiElementNode { if (layoutDescriptors != null) { for (ElementDescriptor desc : layoutDescriptors) { if (desc instanceof ViewElementDescriptor && - desc.getXmlName().equals(AndroidConstants.CLASS_FRAMELAYOUT)) { + desc.getXmlName().equals(AndroidConstants.CLASS_NAME_FRAMELAYOUT)) { layout_attrs = ((ViewElementDescriptor) desc).getLayoutAttributes(); need_xmlns = true; break; From 4ec2d6f6030670e112e06212f43a99b9ad37784a Mon Sep 17 00:00:00 2001 From: Raphael Moll <> Date: Wed, 25 Mar 2009 21:11:59 -0700 Subject: [PATCH 28/39] Automated import from //branches/cupcake/...@142852,142852 --- .../newproject/NewProjectCreationPage.java | 51 +++++++++++++++---- .../wizards/NewXmlFileCreationPage.java | 46 ++++++++++++++++- 2 files changed, 85 insertions(+), 12 deletions(-) diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java index 7d3cad3a0..e26b31cc3 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java @@ -22,7 +22,9 @@ 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.adt.sdk.Sdk.ITargetChangeListener; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.sdklib.IAndroidTarget; @@ -122,6 +124,7 @@ public class NewProjectCreationPage extends WizardPage { private Button mCreateActivityCheck; private Text mMinSdkVersionField; private SdkTargetSelector mSdkTargetSelector; + private ITargetChangeListener mSdkTargetChangeListener; private boolean mInternalLocationPathUpdate; protected boolean mInternalProjectNameUpdate; @@ -263,6 +266,17 @@ public class NewProjectCreationPage extends WizardPage { // Validate. This will complain about the first empty field. setPageComplete(validatePage()); } + + @Override + public void dispose() { + + if (mSdkTargetChangeListener != null) { + AdtPlugin.getDefault().removeTargetListener(mSdkTargetChangeListener); + mSdkTargetChangeListener = null; + } + + super.dispose(); + } /** * Creates the group for the project name: @@ -389,18 +403,35 @@ public class NewProjectCreationPage extends WizardPage { group.setFont(parent.getFont()); group.setText("Target"); - // get the targets from the sdk - IAndroidTarget[] targets = null; - if (Sdk.getCurrent() != null) { - targets = Sdk.getCurrent().getTargets(); - } + // The selector is created without targets. They are added below in the change listener. + mSdkTargetSelector = new SdkTargetSelector(group, null, false /*multi-selection*/); - mSdkTargetSelector = new SdkTargetSelector(group, targets, false /*multi-selection*/); + mSdkTargetChangeListener = new ITargetChangeListener() { + public void onProjectTargetChange(IProject changedProject) { + // Ignore + } - // If there's only one target, select it - if (targets != null && targets.length == 1) { - mSdkTargetSelector.setSelection(targets[0]); - } + public void onTargetsLoaded() { + // Update the sdk target selector with the new targets + + // get the targets from the sdk + IAndroidTarget[] targets = null; + if (Sdk.getCurrent() != null) { + targets = Sdk.getCurrent().getTargets(); + } + mSdkTargetSelector.setTargets(targets); + + // If there's only one target, select it + if (targets != null && targets.length == 1) { + mSdkTargetSelector.setSelection(targets[0]); + } + } + }; + + AdtPlugin.getDefault().addTargetListener(mSdkTargetChangeListener); + + // Invoke it once to initialize the targets + mSdkTargetChangeListener.onTargetsLoaded(); mSdkTargetSelector.setSelectionListener(new SelectionAdapter() { @Override diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java index 7a9115050..83ab59be1 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java @@ -20,6 +20,7 @@ package com.android.ide.eclipse.editors.wizards; 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.adt.sdk.Sdk.ITargetChangeListener; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.ProjectChooserHelper; import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor; @@ -237,7 +238,7 @@ class NewXmlFileCreationPage extends WizardPage { "An XML file that describes preferences.", // tooltip ResourceFolderType.XML, // folder type AndroidTargetData.DESCRIPTOR_PREFERENCES, // root seed - AndroidConstants.CLASS_PREFERENCE_SCREEN, // default root + AndroidConstants.CLASS_NAME_PREFERENCE_SCREEN, // default root SdkConstants.NS_RESOURCES, // xmlns null, // default attributes 1 // target API level @@ -290,7 +291,9 @@ class NewXmlFileCreationPage extends WizardPage { private boolean mInternalTypeUpdate; private boolean mInternalConfigSelectorUpdate; private ProjectChooserHelper mProjectChooserHelper; + private ITargetChangeListener mSdkTargetChangeListener; + private TypeInfo mCurrentTypeInfo; // --- UI creation --- @@ -337,8 +340,43 @@ class NewXmlFileCreationPage extends WizardPage { initializeFromSelection(mInitialSelection); initializeRootValues(); enableTypesBasedOnApi(); + if (mCurrentTypeInfo != null) { + updateRootCombo(mCurrentTypeInfo); + } + installTargetChangeListener(); validatePage(); } + + private void installTargetChangeListener() { + mSdkTargetChangeListener = new ITargetChangeListener() { + public void onProjectTargetChange(IProject changedProject) { + // If this is the current project, force it to reload its data + if (changedProject != null && changedProject == mProject) { + changeProject(mProject); + } + } + + public void onTargetsLoaded() { + // Reload the current project, if any, in case its target has changed. + if (mProject != null) { + changeProject(mProject); + } + } + }; + + AdtPlugin.getDefault().addTargetListener(mSdkTargetChangeListener); + } + + @Override + public void dispose() { + + if (mSdkTargetChangeListener != null) { + AdtPlugin.getDefault().removeTargetListener(mSdkTargetChangeListener); + mSdkTargetChangeListener = null; + } + + super.dispose(); + } /** * Returns the target project or null. @@ -652,7 +690,6 @@ class NewXmlFileCreationPage extends WizardPage { return; } - // Find the best match in the element list. In case there are multiple selected elements // select the one that provides the most information and assign them a score, // e.g. project=1 + folder=2 + file=4. @@ -848,6 +885,10 @@ class NewXmlFileCreationPage extends WizardPage { /** * Changes mProject to the given new project and update the UI accordingly. + *

            + * Note that this does not check if the new project is the same as the current one + * on purpose, which allows a project to be updated when its target has changed or + * when targets are loaded in the background. */ private void changeProject(IProject newProject) { mProject = newProject; @@ -1067,6 +1108,7 @@ class NewXmlFileCreationPage extends WizardPage { private void selectType(TypeInfo type) { if (type == null || !type.getWidget().getSelection()) { mInternalTypeUpdate = true; + mCurrentTypeInfo = type; for (TypeInfo type2 : sTypes) { type2.getWidget().setSelection(type2 == type); } From 4d88b72e476429b72a9c5aca76f04dccc6fe3301 Mon Sep 17 00:00:00 2001 From: Evan Millar <> Date: Thu, 26 Mar 2009 11:36:18 -0700 Subject: [PATCH 29/39] Automated import from //branches/cupcake/...@142951,142951 --- .../com/android/commands/monkey/Monkey.java | 14 ++--- .../android/commands/monkey/MonkeyEvent.java | 1 + .../commands/monkey/MonkeySourceRandom.java | 14 ++++- .../commands/monkey/MonkeyThrottleEvent.java | 52 +++++++++++++++++++ 4 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 cmds/monkey/src/com/android/commands/monkey/MonkeyThrottleEvent.java diff --git a/cmds/monkey/src/com/android/commands/monkey/Monkey.java b/cmds/monkey/src/com/android/commands/monkey/Monkey.java index f6ab19d73..00fb40cb9 100644 --- a/cmds/monkey/src/com/android/commands/monkey/Monkey.java +++ b/cmds/monkey/src/com/android/commands/monkey/Monkey.java @@ -369,7 +369,7 @@ public class Monkey { if (mVerbose >= 2) { // check seeding performance System.out.println("// Seeded: " + mSeed); } - mEventSource = new MonkeySourceRandom(mSeed, mMainApps); + mEventSource = new MonkeySourceRandom(mSeed, mMainApps, mThrottle); mEventSource.setVerbose(mVerbose); //set any of the factors that has been set for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) { @@ -709,13 +709,6 @@ public class Monkey { } } - try { - Thread.sleep(mThrottle); - } catch (InterruptedException e1) { - System.out.println("** Monkey interrupted in sleep."); - return i; - } - // In this debugging mode, we never send any events. This is primarily // here so you can manually test the package or category limits, while manually // exercising the system. @@ -730,7 +723,10 @@ public class Monkey { MonkeyEvent ev = mEventSource.getNextEvent(); if (ev != null) { - i++; + // We don't want to count throttling as an event. + if (!(ev instanceof MonkeyThrottleEvent)) { + i++; + } int injectCode = ev.injectEvent(mWm, mAm, mVerbose); if (injectCode == MonkeyEvent.INJECT_FAIL) { if (ev instanceof MonkeyKeyEvent) { diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeyEvent.java b/cmds/monkey/src/com/android/commands/monkey/MonkeyEvent.java index ff99f5f06..7176073b9 100644 --- a/cmds/monkey/src/com/android/commands/monkey/MonkeyEvent.java +++ b/cmds/monkey/src/com/android/commands/monkey/MonkeyEvent.java @@ -29,6 +29,7 @@ public abstract class MonkeyEvent { public static final int EVENT_TYPE_TRACKBALL = 2; public static final int EVENT_TYPE_ACTIVITY = 3; public static final int EVENT_TYPE_FLIP = 4; // Keyboard flip + public static final int EVENT_TYPE_THROTTLE = 5; public static final int INJECT_SUCCESS = 1; public static final int INJECT_FAIL = 0; diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java index 3dbb575aa..902d8e84c 100644 --- a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java +++ b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java @@ -171,6 +171,7 @@ public class MonkeySourceRandom implements MonkeyEventSource{ private LinkedList mQ = new LinkedList(); private Random mRandom; private int mVerbose = 0; + private long mThrottle = 0; private boolean mKeyboardOpen = false; @@ -185,7 +186,7 @@ public class MonkeySourceRandom implements MonkeyEventSource{ return KEY_NAMES[keycode]; } - public MonkeySourceRandom(long seed, ArrayList MainApps) { + public MonkeySourceRandom(long seed, ArrayList MainApps, long throttle) { // default values for random distributions // note, these are straight percentages, to match user input (cmd line args) // but they will be converted to 0..1 values before the main loop runs. @@ -202,6 +203,7 @@ public class MonkeySourceRandom implements MonkeyEventSource{ mRandom = new SecureRandom(); mRandom.setSeed((seed == 0) ? -1 : seed); mMainApps = MainApps; + mThrottle = throttle; } /** @@ -334,6 +336,7 @@ public class MonkeySourceRandom implements MonkeyEventSource{ downAt, MotionEvent.ACTION_UP, x, y, 0); e.setIntermediateNote(false); mQ.addLast(e); + addThrottle(); } /** @@ -384,6 +387,7 @@ public class MonkeySourceRandom implements MonkeyEventSource{ e.setIntermediateNote(false); mQ.addLast(e); } + addThrottle(); } /** @@ -416,11 +420,13 @@ public class MonkeySourceRandom implements MonkeyEventSource{ MonkeyActivityEvent e = new MonkeyActivityEvent(mMainApps.get( mRandom.nextInt(mMainApps.size()))); mQ.addLast(e); + addThrottle(); return; } else if (cls < mFactors[FACTOR_FLIP]) { MonkeyFlipEvent e = new MonkeyFlipEvent(mKeyboardOpen); mKeyboardOpen = !mKeyboardOpen; mQ.addLast(e); + addThrottle(); return; } else { lastKey = 1 + mRandom.nextInt(KeyEvent.getMaxKeyCode() - 1); @@ -431,6 +437,8 @@ public class MonkeySourceRandom implements MonkeyEventSource{ e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, lastKey); mQ.addLast(e); + + addThrottle(); } public boolean validate() { @@ -464,4 +472,8 @@ public class MonkeySourceRandom implements MonkeyEventSource{ mQ.removeFirst(); return e; } + + private void addThrottle() { + mQ.addLast(new MonkeyThrottleEvent(MonkeyEvent.EVENT_TYPE_THROTTLE, mThrottle)); + } } diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeyThrottleEvent.java b/cmds/monkey/src/com/android/commands/monkey/MonkeyThrottleEvent.java new file mode 100644 index 000000000..3d7d48aa5 --- /dev/null +++ b/cmds/monkey/src/com/android/commands/monkey/MonkeyThrottleEvent.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.commands.monkey; + +import android.app.IActivityManager; +import android.os.RemoteException; +import android.os.SystemClock; +import android.view.IWindowManager; +import android.view.MotionEvent; + + +/** + * monkey throttle event + */ +public class MonkeyThrottleEvent extends MonkeyEvent { + private long mThrottle; + + public MonkeyThrottleEvent(int type, long throttle) { + super(type); + mThrottle = throttle; + } + + @Override + public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) { + + if (verbose > 1) { + System.out.println("Sleeping for " + mThrottle + " milliseconds"); + } + try { + Thread.sleep(mThrottle); + } catch (InterruptedException e1) { + System.out.println("** Monkey interrupted in sleep."); + return MonkeyEvent.INJECT_FAIL; + } + + return MonkeyEvent.INJECT_SUCCESS; + } +} From e71a4a39a457e5fc8feb504279c0ea78a7f0122b Mon Sep 17 00:00:00 2001 From: Raphael Moll <> Date: Thu, 26 Mar 2009 13:53:00 -0700 Subject: [PATCH 30/39] Automated import from //branches/cupcake/...@142978,142978 --- .../launch/AndroidLaunchConfiguration.java | 13 +++++++---- .../adt/launch/AndroidLaunchController.java | 17 +++++++------- .../junit/AndroidJUnitLaunchAction.java | 23 +++++++++++++++---- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java index 3e610db08..b5ea7275c 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java @@ -32,8 +32,14 @@ public class AndroidLaunchConfiguration { */ public int mLaunchAction = LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION; + /** + * Target selection mode for the configuration: either {@link #AUTO} or {@link #MANUAL}. + */ public enum TargetMode { - AUTO(true), MANUAL(false); + /** Automatic target selection mode. */ + AUTO(true), + /** Manual target selection mode. */ + MANUAL(false); private boolean mValue; @@ -58,10 +64,7 @@ public class AndroidLaunchConfiguration { /** * Target selection mode. - *

              - *
            • true: automatic mode, see {@link #AUTO_TARGET_MODE}
            • - *
            • false: manual mode
            • - *
            + * @see TargetMode */ public TargetMode mTargetMode = LaunchConfigDelegate.DEFAULT_TARGET_MODE; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java index 3aa0b91f6..fafc4020b 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java @@ -17,9 +17,6 @@ package com.android.ide.eclipse.adt.launch; import com.android.ddmlib.AndroidDebugBridge; -import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; -import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener; -import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; import com.android.ddmlib.Client; import com.android.ddmlib.ClientData; import com.android.ddmlib.Device; @@ -27,6 +24,9 @@ import com.android.ddmlib.IDevice; import com.android.ddmlib.Log; import com.android.ddmlib.MultiLineReceiver; import com.android.ddmlib.SyncService; +import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; +import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener; +import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; import com.android.ddmlib.SyncService.SyncResult; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration.TargetMode; @@ -34,10 +34,9 @@ import com.android.ide.eclipse.adt.launch.DelayedLaunchInfo.InstallRetryMode; import com.android.ide.eclipse.adt.launch.DeviceChooserDialog.DeviceChooserResponse; 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.ide.eclipse.common.project.AndroidManifestParser; -import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.ide.eclipse.common.project.BaseProjectHelper; +import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkManager; import com.android.sdklib.avd.AvdManager; @@ -56,7 +55,6 @@ import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.ui.DebugUITools; -import org.eclipse.jdt.core.IJavaModel; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; @@ -102,8 +100,9 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener /** * List of {@link DelayedLaunchInfo} waiting for an emulator to connect. - *

            Once an emulator has connected, {@link DelayedLaunchInfo#mDevice} is set and the - * DelayedLaunchInfo object is moved to {@link AndroidLaunchController#mWaitingForReadyEmulatorList}. + *

            Once an emulator has connected, {@link DelayedLaunchInfo#getDevice()} is set and the + * DelayedLaunchInfo object is moved to + * {@link AndroidLaunchController#mWaitingForReadyEmulatorList}. * ALL ACCESS MUST BE INSIDE A synchronized (sListLock) block! */ private final ArrayList mWaitingForEmulatorLaunches = @@ -487,7 +486,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener // FIXME: ask the user if he wants to create a AVD. // we found no compatible AVD. AdtPlugin.printErrorToConsole(project, String.format( - "Failed to find a AVD compatible with target '%1$s'. Launch aborted.", + "Failed to find an AVD compatible with target '%1$s'. Launch aborted.", projectTarget.getName())); stopLaunch(launchInfo); return; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java index b88026329..747fcfe5c 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java @@ -57,7 +57,7 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { * Launch a instrumentation test run on given Android device. * Reuses JDT JUnit launch delegate so results can be communicated back to JDT JUnit UI. * - * @see com.android.ide.eclipse.adt.launch.IAndroidLaunchAction#doLaunchAction(com.android.ide.eclipse.adt.launch.AndroidLaunchController.DelayedLaunchInfo, com.android.ddmlib.Device) + * @see IAndroidLaunchAction#doLaunchAction(DelayedLaunchInfo, IDevice) */ public boolean doLaunchAction(DelayedLaunchInfo info, IDevice device) { String msg = String.format("Launching instrumentation %s on device %s", mRunner, @@ -108,7 +108,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { super.launch(configuration, mode, launch, monitor); } - /* (non-Javadoc) + /** + * {@inheritDoc} + * @throws CoreException * @see org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate#verifyMainTypeName(org.eclipse.debug.core.ILaunchConfiguration) */ @Override @@ -119,6 +121,7 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { /** * Overrides parent to return a VM Runner implementation which launches a thread, rather * than a separate VM process + * @throws CoreException */ @Override public IVMRunner getVMRunner(ILaunchConfiguration configuration, String mode) @@ -127,7 +130,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { mTestPackage, mRunner, mLaunchInfo.isDebugMode(), mDevice)); } - /* (non-Javadoc) + /** + * {@inheritDoc} + * @throws CoreException * @see org.eclipse.debug.core.model.LaunchConfigurationDelegate#getLaunch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String) */ @Override @@ -148,6 +153,10 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { mJUnitInfo = info; } + /** + * {@inheritDoc} + * @throws CoreException + */ public void run(final VMRunnerConfiguration config, ILaunch launch, IProgressMonitor monitor) throws CoreException { @@ -183,7 +192,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { return null; } - /* (non-Javadoc) + /** + * {@inheritDoc} + * @throws DebugException * @see org.eclipse.debug.core.model.IProcess#getExitValue() */ public int getExitValue() throws DebugException { @@ -241,7 +252,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction { return mIsTerminated; } - /* (non-Javadoc) + /** + * {@inheritDoc} + * @throws DebugException * @see org.eclipse.debug.core.model.ITerminate#terminate() */ public void terminate() throws DebugException { From d7f1b57f484fdc2fce8f121c95f1b2e811f6526c Mon Sep 17 00:00:00 2001 From: Mike Lockwood <> Date: Thu, 26 Mar 2009 14:44:25 -0700 Subject: [PATCH 31/39] Automated import from //branches/cupcake/...@142988,142988 --- emulator/qemud/Android.mk | 1 + emulator/sensors/Android.mk | 1 + 2 files changed, 2 insertions(+) diff --git a/emulator/qemud/Android.mk b/emulator/qemud/Android.mk index 454f32dda..a186c73de 100644 --- a/emulator/qemud/Android.mk +++ b/emulator/qemud/Android.mk @@ -11,5 +11,6 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ LOCAL_MODULE:= qemud +LOCAL_MODULE_TAGS := debug include $(BUILD_EXECUTABLE) diff --git a/emulator/sensors/Android.mk b/emulator/sensors/Android.mk index 402da823f..74e02adb6 100644 --- a/emulator/sensors/Android.mk +++ b/emulator/sensors/Android.mk @@ -24,5 +24,6 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw LOCAL_SHARED_LIBRARIES := liblog libcutils LOCAL_SRC_FILES := sensors_qemu.c LOCAL_MODULE := sensors.goldfish +LOCAL_MODULE_TAGS := debug include $(BUILD_SHARED_LIBRARY) endif From 2d2cf2889c1a0b251ddddbf6f867209dda9362f1 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet <> Date: Thu, 26 Mar 2009 15:29:07 -0700 Subject: [PATCH 32/39] Automated import from //branches/cupcake/...@142994,142994 --- samples/ApiDemos/AndroidManifest.xml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml index 0cbba1449..7079e9ecf 100644 --- a/samples/ApiDemos/AndroidManifest.xml +++ b/samples/ApiDemos/AndroidManifest.xml @@ -33,8 +33,6 @@ android:label="@string/activity_sample_code" android:icon="@drawable/app_sample_code" > - - @@ -1305,20 +1303,6 @@ - - - - - - - - - - - - - - From bc4a6cc7bb3fd0fad7445697a647078e8118d921 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn <> Date: Thu, 26 Mar 2009 17:27:02 -0700 Subject: [PATCH 33/39] Automated import from //branches/cupcake/...@143022,143022 --- apps/SpareParts/res/values/strings.xml | 4 ++++ apps/SpareParts/res/xml/spare_parts.xml | 6 ++++++ .../src/com/android/spare_parts/SpareParts.java | 10 ++++++++++ 3 files changed, 20 insertions(+) diff --git a/apps/SpareParts/res/values/strings.xml b/apps/SpareParts/res/values/strings.xml index 6aecca826..7bd13e54a 100644 --- a/apps/SpareParts/res/values/strings.xml +++ b/apps/SpareParts/res/values/strings.xml @@ -45,6 +45,10 @@ Use fancier animations for input method windows Use normal animations for input method windows + Fancy rotation animations + Use fancier animations for screen rotation + Use normal animations for screen rotation + Haptic feedback Use haptic feedback with user interaction Use haptic feedback with user interaction diff --git a/apps/SpareParts/res/xml/spare_parts.xml b/apps/SpareParts/res/xml/spare_parts.xml index 3e06397a6..3ef1b7961 100644 --- a/apps/SpareParts/res/xml/spare_parts.xml +++ b/apps/SpareParts/res/xml/spare_parts.xml @@ -72,6 +72,12 @@ android:summaryOn="@string/summary_on_fancy_ime_animations" android:summaryOff="@string/summary_off_fancy_ime_animations"/> + + Date: Thu, 26 Mar 2009 18:38:21 -0700 Subject: [PATCH 34/39] Automated import from //branches/cupcake/...@143034,143034 --- .../app/src/com/android/sdkmanager/Main.java | 51 +++++++++++++++++-- .../sdklib/project/ProjectCreator.java | 33 ++++++++++-- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java index 154788ee1..adf37ed0b 100644 --- a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java +++ b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Pattern; /** * Main class for the 'android' application. @@ -50,6 +51,12 @@ class Main { private final static String[] BOOLEAN_YES_REPLIES = new String[] { "yes", "y" }; private final static String[] BOOLEAN_NO_REPLIES = new String[] { "no", "n" }; + /** Regex used to validate characters that compose an AVD name. */ + private final static Pattern RE_AVD_NAME = Pattern.compile("[a-zA-Z0-9._-]+"); + /** List of valid characters for an AVD name. Used for display purposes. */ + private final static String CHARS_AVD_NAME = "a-z A-Z 0-9 . _ -"; + + /** Path to the SDK folder. This is the parent of {@link #TOOLSDIR}. */ private String mSdkFolder; /** Logger object. Use this to print normal output, warnings or errors. */ @@ -239,11 +246,41 @@ class Main { mSdkLog); String projectDir = getProjectLocation(mSdkCommandLine.getParamLocationPath()); + + String projectName = mSdkCommandLine.getParamName(); + String packageName = mSdkCommandLine.getParamProjectPackage(); + String activityName = mSdkCommandLine.getParamProjectActivity(); + if (projectName != null && + !ProjectCreator.RE_PROJECT_NAME.matcher(projectName).matches()) { + errorAndExit( + "Project name '%1$s' contains invalid characters.\nAllowed characters are: %2$s", + projectName, ProjectCreator.CHARS_PROJECT_NAME); + return; + } + + if (activityName != null && + !ProjectCreator.RE_ACTIVITY_NAME.matcher(activityName).matches()) { + errorAndExit( + "Activity name '%1$s' contains invalid characters.\nAllowed characters are: %2$s", + activityName, ProjectCreator.CHARS_ACTIVITY_NAME); + return; + } + + if (packageName != null && + !ProjectCreator.RE_PACKAGE_NAME.matcher(packageName).matches()) { + errorAndExit( + "Package name '%1$s' contains invalid characters.\n" + + "A package name must be constitued of two Java identifiers.\n" + + "Each identifier allowed characters are: %2$s", + packageName, ProjectCreator.CHARS_PACKAGE_NAME); + return; + } + creator.createProject(projectDir, - mSdkCommandLine.getParamName(), - mSdkCommandLine.getParamProjectPackage(), - mSdkCommandLine.getParamProjectActivity(), + projectName, + activityName, + packageName, target, false /* isTestProject*/); } @@ -447,6 +484,14 @@ class Main { AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog); String avdName = mSdkCommandLine.getParamName(); + + if (!RE_AVD_NAME.matcher(avdName).matches()) { + errorAndExit( + "AVD name '%1$s' contains invalid characters.\nAllowed characters are: %2$s", + avdName, CHARS_AVD_NAME); + return; + } + AvdInfo info = avdManager.getAvd(avdName); if (info != null) { if (mSdkCommandLine.getFlagForce()) { diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java index 7489b65d6..b84be18da 100644 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java @@ -62,6 +62,27 @@ public class ProjectCreator { private final static String FOLDER_TESTS = "tests"; + /** Pattern for characters accepted in a project name. Since this will be used as a + * directory name, we're being a bit conservative on purpose: dot and space cannot be used. */ + public static final Pattern RE_PROJECT_NAME = Pattern.compile("[a-zA-Z0-9_]+"); + /** List of valid characters for a project name. Used for display purposes. */ + public final static String CHARS_PROJECT_NAME = "a-z A-Z 0-9 _"; + + /** Pattern for characters accepted in a package name. A package is list of Java identifier + * separated by a dot. We need to have at least one dot (e.g. a two-level package name). + * A Java identifier cannot start by a digit. */ + public static final Pattern RE_PACKAGE_NAME = + Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)+"); + /** List of valid characters for a project name. Used for display purposes. */ + public final static String CHARS_PACKAGE_NAME = "a-z A-Z 0-9 _"; + + /** Pattern for characters accepted in an activity name, which is a Java identifier. */ + public static final Pattern RE_ACTIVITY_NAME = + Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*"); + /** List of valid characters for a project name. Used for display purposes. */ + public final static String CHARS_ACTIVITY_NAME = "a-z A-Z 0-9 _"; + + public enum OutputLevel { /** Silent mode. Project creation will only display errors. */ SILENT, @@ -106,11 +127,17 @@ public class ProjectCreator { /** * Creates a new project. + *

            + * The caller should have already checked and sanitized the parameters. * * @param folderPath the folder of the project to create. - * @param projectName the name of the project. - * @param packageName the package of the project. - * @param activityName the activity of the project as it will appear in the manifest. + * @param projectName the name of the project. The name must match the + * {@link #RE_PROJECT_NAME} regex. + * @param packageName the package of the project. The name must match the + * {@link #RE_PACKAGE_NAME} regex. + * @param activityName the activity of the project as it will appear in the manifest. Can be + * null if no activity should be created. The name must match the + * {@link #RE_ACTIVITY_NAME} regex. * @param target the project target. * @param isTestProject whether the project to create is a test project. */ From ad30a85cfce580bcde7287221db3b1aa56230897 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet <> Date: Fri, 27 Mar 2009 14:31:26 -0700 Subject: [PATCH 35/39] AI 143143: Update ADT changes.txt with JUnit features, and properly restrict ADT package access. BUG=1743054 Automated import of CL 143143 --- tools/eclipse/changes.txt | 1 + .../com.android.ide.eclipse.adt/META-INF/MANIFEST.MF | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/eclipse/changes.txt b/tools/eclipse/changes.txt index 8cd843e20..02d907585 100644 --- a/tools/eclipse/changes.txt +++ b/tools/eclipse/changes.txt @@ -5,6 +5,7 @@ * Project properties (right click project in Package Explorer, then "Properties"), lets you edit project target. * New Launch configuration option to choose debug deployment target. - Ability to export multiple apk from one project, using resource filters. See the 'android' property for Android projects. +- Support for running JUnit tests on a device/emulator from a new "Android JUnit tests" launch configuration. 0.8.1: diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF index c0dfcefd5..0ec97aa38 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF @@ -45,16 +45,16 @@ Require-Bundle: com.android.ide.eclipse.ddms, org.eclipse.ltk.core.refactoring, org.eclipse.ltk.ui.refactoring Eclipse-LazyStart: true -Export-Package: com.android.ide.eclipse.adt, +Export-Package: com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.build;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.launch;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.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.common;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.common.project;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.common.resources;x-friends:="com.android.ide.eclipse.tests", 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", From fe8bec70caf01fa91ae3dd05bc441ba7d7b66929 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet <> Date: Fri, 27 Mar 2009 15:05:58 -0700 Subject: [PATCH 36/39] AI 143149: Make ADT look for javadoc in docs/reference for the optional libraries (to match the base docs). BUG=1743022 Automated import of CL 143149 --- .../com/android/ide/eclipse/common/AndroidConstants.java | 5 +++-- .../libs/sdklib/src/com/android/sdklib/AddOnTarget.java | 3 ++- .../libs/sdklib/src/com/android/sdklib/SdkConstants.java | 2 ++ .../sdkuilib/src/com/android/sdkuilib/AvdSelector.java | 8 ++++---- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java index 226357f7a..d0d8ae3af 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java @@ -90,7 +90,7 @@ public class AndroidConstants { /** Name of the android sources directory */ public static final String FD_ANDROID_SOURCES = "sources"; //$NON-NLS-1$ - + /** Resource java class filename, i.e. "R.java" */ public final static String FN_RESOURCE_CLASS = "R.java"; //$NON-NLS-1$ /** Resource class file filename, i.e. "R.class" */ @@ -128,7 +128,8 @@ public class AndroidConstants { public final static String WS_ASSETS = WS_SEP + SdkConstants.FD_ASSETS; /** Leaf of the javaDoc folder. Does not start with a separator. */ - public final static String WS_JAVADOC_FOLDER_LEAF = SdkConstants.FD_DOCS + "/reference"; //$NON-NLS-1$ + public final static String WS_JAVADOC_FOLDER_LEAF = SdkConstants.FD_DOCS + "/" + + 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. diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java index 0a5910734..d1ae343d5 100644 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java @@ -153,7 +153,8 @@ final class AddOnTarget implements IAndroidTarget { case SKINS: return mLocation + SdkConstants.OS_SKINS_FOLDER; case DOCS: - return mLocation + SdkConstants.FD_DOCS + File.separator; + return mLocation + SdkConstants.FD_DOCS + File.separator + + SdkConstants.FD_DOCS_REFERENCE; default : return mBasePlatform.getPath(pathId); } diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java index 00594d12f..9eb6ade6b 100644 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java @@ -143,6 +143,8 @@ public final class SdkConstants { public final static String FD_LIB = "lib"; /** Name of the SDK docs folder. */ public final static String FD_DOCS = "docs"; + /** Name of the doc folder containing API reference doc (javadoc) */ + public static final String FD_DOCS_REFERENCE = "reference"; /** Name of the SDK images folder. */ public final static String FD_IMAGES = "images"; /** Name of the SDK skins folder. */ diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java index 67c70a676..d62c231c1 100644 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java @@ -89,9 +89,9 @@ public final class AvdSelector { final TableColumn column1 = new TableColumn(mTable, SWT.NONE); column1.setText("Target Name"); final TableColumn column2 = new TableColumn(mTable, SWT.NONE); - column2.setText("API Level"); + column2.setText("SDK"); final TableColumn column3 = new TableColumn(mTable, SWT.NONE); - column3.setText("SDK"); + column3.setText("API Level"); adjustColumnsWidth(mTable, column0, column1, column2, column3); setupSelectionListener(mTable); @@ -235,8 +235,8 @@ public final class AvdSelector { Rectangle r = table.getClientArea(); column0.setWidth(r.width * 30 / 100); // 30% column1.setWidth(r.width * 45 / 100); // 45% - column2.setWidth(r.width * 15 / 100); // 15% - column3.setWidth(r.width * 10 / 100); // 10% + column2.setWidth(r.width * 10 / 100); // 10% + column3.setWidth(r.width * 15 / 100); // 15% } }); } From 729ff799f77fff783ba1fa46d9adb57e500e4150 Mon Sep 17 00:00:00 2001 From: Jack Palevich <> Date: Fri, 27 Mar 2009 15:49:45 -0700 Subject: [PATCH 37/39] AI 143256: Make the Term emulator work with the most recent keyboard IME. + Makes the "Enter" key work again. + Makes the "Delete" key delete just one character each time you press it instead of two. BUG=1615131 Automated import of CL 143256 --- apps/Term/src/com/android/term/Term.java | 36 +++++++++--------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/apps/Term/src/com/android/term/Term.java b/apps/Term/src/com/android/term/Term.java index d74159b90..1f43843a3 100644 --- a/apps/Term/src/com/android/term/Term.java +++ b/apps/Term/src/com/android/term/Term.java @@ -548,16 +548,6 @@ public class Term extends Activity { + controlKey + " 6 ==> Control-^"). show(); } - - private void print(String msg) { - char[] chars = msg.toCharArray(); - int len = chars.length; - byte[] bytes = new byte[len]; - for (int i = 0; i < len; i++) { - bytes[i] = (byte) chars[i]; - } - mEmulatorView.append(bytes, 0, len); - } } @@ -2707,8 +2697,13 @@ class EmulatorView extends View implements GestureDetector.OnGestureListener { return null; } - public boolean hideStatusIcon() { - return true; + public boolean performEditorAction(int actionCode) { + if(actionCode == EditorInfo.IME_ACTION_UNSPECIFIED) { + // The "return" key has been pressed on the IME. + sendText("\n"); + return true; + } + return false; } public boolean performContextMenuAction(int id) { @@ -2720,13 +2715,12 @@ class EmulatorView extends View implements GestureDetector.OnGestureListener { } public boolean sendKeyEvent(KeyEvent event) { - switch(event.getKeyCode()) { - case KeyEvent.KEYCODE_ENTER: - sendChar('\r'); - break; - case KeyEvent.KEYCODE_DEL: - sendChar(127); - break; + if (event.getAction() == KeyEvent.ACTION_DOWN) { + switch(event.getKeyCode()) { + case KeyEvent.KEYCODE_DEL: + sendChar(127); + break; + } } return true; } @@ -2739,10 +2733,6 @@ class EmulatorView extends View implements GestureDetector.OnGestureListener { return true; } - public boolean showStatusIcon(String packageName, int resId) { - return true; - } - private void sendChar(int c) { try { mTermOut.write(c); From f739e79bf4387a6accbd066291f7ee082f9c2259 Mon Sep 17 00:00:00 2001 From: Raphael Moll <> Date: Fri, 27 Mar 2009 16:06:03 -0700 Subject: [PATCH 38/39] AI 143259: ADT #1743364: Refactor all wizard classes & their actions together. BUG=1743364 Automated import of CL 143259 --- .../ide/eclipse/adt/wizards/actions/NewXmlFileAction.java | 2 +- .../{project => wizards/actions}/NewXmlFileWizardAction.java | 4 ++-- .../wizards/newxmlfile}/NewXmlFileCreationPage.java | 3 ++- .../wizards => adt/wizards/newxmlfile}/NewXmlFileWizard.java | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/{project => wizards/actions}/NewXmlFileWizardAction.java (94%) rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/{editors/wizards => adt/wizards/newxmlfile}/NewXmlFileCreationPage.java (99%) rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/{editors/wizards => adt/wizards/newxmlfile}/NewXmlFileWizard.java (98%) diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java index 8c4a115b4..d1530d4ea 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java @@ -16,7 +16,7 @@ package com.android.ide.eclipse.adt.wizards.actions; -import com.android.ide.eclipse.editors.wizards.NewXmlFileWizard; +import com.android.ide.eclipse.adt.wizards.newxmlfile.NewXmlFileWizard; import org.eclipse.jface.action.IAction; import org.eclipse.ui.IWorkbenchWizard; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/NewXmlFileWizardAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileWizardAction.java similarity index 94% rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/NewXmlFileWizardAction.java rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileWizardAction.java index c117b4e57..20cfc8275 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/NewXmlFileWizardAction.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileWizardAction.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.project; +package com.android.ide.eclipse.adt.wizards.actions; -import com.android.ide.eclipse.editors.wizards.NewXmlFileWizard; +import com.android.ide.eclipse.adt.wizards.newxmlfile.NewXmlFileWizard; import org.eclipse.jface.action.IAction; import org.eclipse.jface.viewers.ISelection; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java similarity index 99% rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java index 83ab59be1..f3cbfa221 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java @@ -15,7 +15,7 @@ */ -package com.android.ide.eclipse.editors.wizards; +package com.android.ide.eclipse.adt.wizards.newxmlfile; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.sdk.AndroidTargetData; @@ -31,6 +31,7 @@ import com.android.ide.eclipse.editors.resources.configurations.FolderConfigurat 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; import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.ConfigurationState; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkConstants; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileWizard.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileWizard.java similarity index 98% rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileWizard.java rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileWizard.java index 125102b96..d7e43cfd7 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileWizard.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileWizard.java @@ -16,11 +16,11 @@ -package com.android.ide.eclipse.editors.wizards; +package com.android.ide.eclipse.adt.wizards.newxmlfile; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.wizards.newxmlfile.NewXmlFileCreationPage.TypeInfo; import com.android.ide.eclipse.editors.IconFactory; -import com.android.ide.eclipse.editors.wizards.NewXmlFileCreationPage.TypeInfo; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; From 3141ffd322e03b0d7d71f4e3a3052aec2418a54e Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet <> Date: Fri, 27 Mar 2009 18:28:38 -0700 Subject: [PATCH 39/39] AI 143407: Prevent reinstalling APKs during launch if they have not been recompiled since the previous launch. BUG=1743026 Automated import of CL 143407 --- .../ide/eclipse/adt/build/ApkBuilder.java | 4 + .../adt/launch/AndroidLaunchController.java | 46 +++- .../adt/project/ApkInstallManager.java | 207 ++++++++++++++++++ 3 files changed, 250 insertions(+), 7 deletions(-) create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ApkInstallManager.java diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java index 1edcf79fd..47ea3e731 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java @@ -18,6 +18,7 @@ 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.ApkInstallManager; import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.adt.sdk.AndroidTargetData; import com.android.ide.eclipse.adt.sdk.Sdk; @@ -551,6 +552,9 @@ public class ApkBuilder extends BaseBuilder { // and store it saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage); + // reset the installation manager to force new installs of this project + ApkInstallManager.getInstance().resetInstallationFor(project); + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(), "Build Success!"); } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java index fafc4020b..b6c7640c7 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java @@ -32,6 +32,7 @@ import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration.TargetMode; import com.android.ide.eclipse.adt.launch.DelayedLaunchInfo.InstallRetryMode; import com.android.ide.eclipse.adt.launch.DeviceChooserDialog.DeviceChooserResponse; +import com.android.ide.eclipse.adt.project.ApkInstallManager; import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.project.AndroidManifestParser; @@ -762,13 +763,46 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener /** - * Syncs the application on the device/emulator. + * If needed, syncs the application and all its dependencies on the device/emulator. * * @param launchInfo The Launch information object. * @param device the device on which to sync the application * @return true if the install succeeded. */ private boolean syncApp(DelayedLaunchInfo launchInfo, IDevice device) { + boolean alreadyInstalled = ApkInstallManager.getInstance().isApplicationInstalled( + launchInfo.getProject(), device); + + if (alreadyInstalled) { + AdtPlugin.printToConsole(launchInfo.getProject(), + "Application already deployed. No need to reinstall."); + } else { + if (doSyncApp(launchInfo, device) == false) { + return false; + } + } + + // The app is now installed, now try the dependent projects + for (DelayedLaunchInfo dependentLaunchInfo : getDependenciesLaunchInfo(launchInfo)) { + String msg = String.format("Project dependency found, installing: %s", + dependentLaunchInfo.getProject().getName()); + AdtPlugin.printToConsole(launchInfo.getProject(), msg); + if (syncApp(dependentLaunchInfo, device) == false) { + return false; + } + } + + return true; + } + + /** + * Syncs the application on the device/emulator. + * + * @param launchInfo The Launch information object. + * @param device the device on which to sync the application + * @return true if the install succeeded. + */ + private boolean doSyncApp(DelayedLaunchInfo launchInfo, IDevice device) { SyncService sync = device.getSyncService(); if (sync != null) { IPath path = launchInfo.getPackageFile().getLocation(); @@ -812,12 +846,10 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener return false; } - // The app is now installed, now try the dependent projects - for (DelayedLaunchInfo dependentLaunchInfo : getDependenciesLaunchInfo(launchInfo)) { - String msg = String.format("Project dependency found, syncing: %s", - dependentLaunchInfo.getProject().getName()); - AdtPlugin.printToConsole(launchInfo.getProject(), msg); - syncApp(dependentLaunchInfo, device); + // if the installation succeeded, we register it. + if (installResult) { + ApkInstallManager.getInstance().registerInstallation( + launchInfo.getProject(), device); } return installResult; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ApkInstallManager.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ApkInstallManager.java new file mode 100644 index 000000000..172c555f4 --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ApkInstallManager.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2009 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.ddmlib.AndroidDebugBridge; +import com.android.ddmlib.Device; +import com.android.ddmlib.IDevice; +import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener; +import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; +import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor; +import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener; + +import org.eclipse.core.resources.IProject; + +import java.util.ArrayList; + +/** + * Registers which apk was installed on which device. + *

            + * The goal of this class is to remember the installation of APKs on devices, and provide + * information about whether a new APK should be installed on a device prior to running the + * application from a launch configuration. + *

            + * The manager uses {@link IProject} and {@link IDevice} to identify the target device and the + * (project generating the) APK. This ensures that disconnected and reconnected devices will + * always receive new APKs (since the APK could be uninstalled manually). + *

            + * Manually uninstalling an APK from a connected device will still be a problem, but this should + * be a limited use case. + *

            + * This is a singleton. To get the instance, use {@link #getInstance()} + */ +public class ApkInstallManager implements IDeviceChangeListener, IDebugBridgeChangeListener, + IProjectListener { + + private final static ApkInstallManager sThis = new ApkInstallManager(); + + /** + * Internal struct to associate a project and a device. + */ + private static class ApkInstall { + public ApkInstall(IProject project, IDevice device) { + this.project = project; + this.device = device; + } + IProject project; + IDevice device; + } + + private final ArrayList mInstallList = new ArrayList(); + + public static ApkInstallManager getInstance() { + return sThis; + } + + /** + * Registers an installation of project onto device + * @param project The project that was installed. + * @param device The device that received the installation. + */ + public void registerInstallation(IProject project, IDevice device) { + synchronized (mInstallList) { + mInstallList.add(new ApkInstall(project, device)); + } + } + + /** + * Returns whether a project was installed on the device. + * @param project the project that may have been installed. + * @param device the device that may have received the installation. + * @return + */ + public boolean isApplicationInstalled(IProject project, IDevice device) { + synchronized (mInstallList) { + for (ApkInstall install : mInstallList) { + if (project == install.project && device == install.device) { + return true; + } + } + } + return false; + } + + /** + * Resets registered installations for a specific {@link IProject}. + *

            This ensures that {@link #isApplicationInstalled(IProject, IDevice)} will always return + * null for this specified project, for any device. + * @param project the project for which to reset all installations. + */ + public void resetInstallationFor(IProject project) { + synchronized (mInstallList) { + for (int i = 0 ; i < mInstallList.size() ;) { + ApkInstall install = mInstallList.get(i); + if (install.project == project) { + mInstallList.remove(i); + } else { + i++; + } + } + } + } + + private ApkInstallManager() { + AndroidDebugBridge.addDeviceChangeListener(this); + AndroidDebugBridge.addDebugBridgeChangeListener(this); + ResourceMonitor.getMonitor().addProjectListener(this); + } + + /* + * Responds to a bridge change by clearing the full installation list. + * (non-Javadoc) + * @see com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener#bridgeChanged(com.android.ddmlib.AndroidDebugBridge) + */ + public void bridgeChanged(AndroidDebugBridge bridge) { + // the bridge changed, there is no way to know which IDevice will be which. + // We reset everything + synchronized (mInstallList) { + mInstallList.clear(); + } + } + + /* + * Responds to a device being disconnected by removing all installations related to this device. + * (non-Javadoc) + * @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceDisconnected(com.android.ddmlib.Device) + */ + public void deviceDisconnected(Device device) { + synchronized (mInstallList) { + for (int i = 0 ; i < mInstallList.size() ;) { + ApkInstall install = mInstallList.get(i); + if (install.device == device) { + mInstallList.remove(i); + } else { + i++; + } + } + } + } + + /* + * Responds to a close project by resetting all its installation. + * (non-Javadoc) + * @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectClosed(org.eclipse.core.resources.IProject) + */ + public void projectClosed(IProject project) { + resetInstallationFor(project); + } + + /* + * Responds to a close project by resetting all its installation. + * (non-Javadoc) + * @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectDeleted(org.eclipse.core.resources.IProject) + */ + public void projectDeleted(IProject project) { + resetInstallationFor(project); + } + + /* + * Does nothing + * (non-Javadoc) + * @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceChanged(com.android.ddmlib.Device, int) + */ + public void deviceChanged(Device device, int changeMask) { + // nothing to do. + } + + /* + * Does nothing + * (non-Javadoc) + * @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceConnected(com.android.ddmlib.Device) + */ + public void deviceConnected(Device device) { + // nothing to do. + } + + /* + * Does nothing + * (non-Javadoc) + * @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectOpened(org.eclipse.core.resources.IProject) + */ + public void projectOpened(IProject project) { + // nothing to do. + } + + /* + * Does nothing + * (non-Javadoc) + * @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectOpenedWithWorkspace(org.eclipse.core.resources.IProject) + */ + public void projectOpenedWithWorkspace(IProject project) { + // nothing to do. + } +}