Merge "First version of the PDT."
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java
index 04c912b..e9b8bb5 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java
@@ -24,6 +24,8 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Parses the 'raw output mode' results of an instrumentation test run from shell and informs a
@@ -254,7 +256,7 @@
             mTestRunFinished = true;
             // just ignore the remaining data on this line
         } else if (line.startsWith(Prefixes.TIME_REPORT)) {
-            parseTime(line, Prefixes.TIME_REPORT.length());
+            parseTime(line);
         } else {
             if (mCurrentValue != null) {
                 // this is a value that has wrapped to next line.
@@ -475,13 +477,20 @@
     /**
      * Parses out and store the elapsed time.
      */
-    private void parseTime(String line, int startPos) {
-        String timeString = line.substring(startPos);
-        try {
-            float timeSeconds = Float.parseFloat(timeString);
-            mTestTime = (long) (timeSeconds * 1000);
-        } catch (NumberFormatException e) {
-            Log.e(LOG_TAG, "Unexpected time format " + timeString);
+    private void parseTime(String line) {
+        final Pattern timePattern = Pattern.compile(String.format("%s\\s*([\\d\\.]+)",
+                Prefixes.TIME_REPORT));
+        Matcher timeMatcher = timePattern.matcher(line);
+        if (timeMatcher.find()) {
+            String timeString = timeMatcher.group(1);
+            try {
+                float timeSeconds = Float.parseFloat(timeString);
+                mTestTime = (long) (timeSeconds * 1000);
+            } catch (NumberFormatException e) {
+                Log.w(LOG_TAG, String.format("Unexpected time format %s", line));
+            }
+        } else {
+            Log.w(LOG_TAG, String.format("Unexpected time format %s", line));
         }
     }
 
diff --git a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
index c971a71..fe80427 100644
--- a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
+++ b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
@@ -126,6 +126,16 @@
     }
 
     /**
+     * Test parsing and conversion of time output that contains extra chars.
+     */
+    public void testTimeParsing_bracket() {
+        StringBuilder output = createSuccessTest();
+        output.append("Time: 0.001)");
+        injectTestString(output.toString());
+        assertEquals(1, mTestResult.mTestTime);
+    }
+
+    /**
      * Test basic parsing of a test run failure.
      */
     public void testRunFailed() {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/annotations/Nullable.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/annotations/Nullable.java
new file mode 100755
index 0000000..6ea5b36
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/annotations/Nullable.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 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.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes a parameter or field can be null.
+ * <p/>
+ * When decorating a method call parameter, this denotes the parameter can
+ * legitimately be null and the method will gracefully deal with it. Typically used
+ * on optional parameters.
+ * <p/>
+ * When decorating a method, this denotes the method might legitimately return null.
+ * <p/>
+ * This is a marker annotation and it has no specific attributes.
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface Nullable {
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IClientRulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IClientRulesEngine.java
index a9ff834..44372ef 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IClientRulesEngine.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IClientRulesEngine.java
@@ -17,6 +17,10 @@
 
 package com.android.ide.eclipse.adt.editors.layout.gscripts;
 
+import com.android.ide.eclipse.adt.annotations.Nullable;
+
+import groovy.lang.Closure;
+
 
 
 /**
@@ -51,5 +55,24 @@
      *   is fast and will return the same rule instance.
      */
     IViewRule loadRule(String fqcn);
+
+    /**
+     * Displays the given message string in an alert dialog with an "OK" button.
+     */
+    void displayAlert(String message);
+
+    /**
+     * Display a simple input alert dialog with an OK and Cancel buttons.
+     *
+     * @param message The message to display in the alert dialog.
+     * @param value The initial value to display in the input field. Can be null.
+     * @param filter An optional closure acting as a filter. It receives the current
+     *   string as input. It must return an error string (possibly empty) or false if the
+     *   validation fails. Otherwise it should return true or null if validation succeeds.
+     * @return Null if canceled by the user. Otherwise the possibly-empty input string.
+     * @null Return value is null if dialog was canceled by the user.
+     */
+    @Nullable
+    String displayInput(String message, @Nullable String value, @Nullable Closure filter);
 }
 
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/MenuAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/MenuAction.java
index 3674b02..63454a4 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/MenuAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/MenuAction.java
@@ -16,6 +16,8 @@
 
 package com.android.ide.eclipse.adt.editors.layout.gscripts;
 
+import com.android.ide.eclipse.adt.annotations.Nullable;
+
 import groovy.lang.Closure;
 
 import java.util.Map;
@@ -162,6 +164,7 @@
          * An optional group id, to place the action in a given sub-menu.
          * @null This value can be null.
          */
+        @Nullable
         private final String mGroupId;
 
         /**
@@ -201,6 +204,7 @@
          * Returns the optional id of an existing group or null
          * @null This value can be null.
          */
+        @Nullable
         public String getGroupId() {
             return mGroupId;
         }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
index 7790411..cbfe763 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java
@@ -49,6 +49,10 @@
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.window.Window;
 
 import groovy.lang.Closure;
 import groovy.lang.ExpandoMetaClass;
@@ -69,11 +73,6 @@
 import java.util.List;
 import java.util.Map;
 
-/* TODO:
- * - create a logger object and pass it around.
- *
- */
-
 /**
  * The rule engine manages the groovy rules files and interacts with them.
  * There's one {@link RulesEngine} instance per layout editor.
@@ -814,6 +813,46 @@
         public IViewRule loadRule(String fqcn) {
             return RulesEngine.this.loadRule(fqcn, fqcn);
         }
+
+        public void displayAlert(String message) {
+            MessageDialog.openInformation(
+                    AdtPlugin.getDisplay().getActiveShell(),
+                    mFqcn,  // title
+                    message);
+        }
+
+        public String displayInput(String message, String value, final Closure filter) {
+            IInputValidator validator = null;
+            if (filter != null) {
+                validator = new IInputValidator() {
+                    public String isValid(String newText) {
+                        Object result = RulesEngine.this.callClosure(filter, newText);
+
+                        if (result instanceof String) {
+                            return (String) result;
+
+                        } else if (Boolean.FALSE.equals(result)) {
+                            // Returns an empty string to indicate an undescribed error
+                            return "";  //$NON-NLS-1$
+                        }
+
+                        // Return null to indicate success of validation
+                        return null;
+                    }
+                };
+            }
+
+            InputDialog d = new InputDialog(
+                        AdtPlugin.getDisplay().getActiveShell(),
+                        mFqcn,  // title
+                        message,
+                        value == null ? "" : value, //$NON-NLS-1$
+                        validator);
+            if (d.open() == Window.OK) {
+                return d.getValue();
+            }
+            return null;
+        }
     }
 
 }
diff --git a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
index 9589060..b0c0e50 100644
--- a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
+++ b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
@@ -112,19 +112,19 @@
                 "Updates an Android Virtual Device to match the folders of a new SDK." },
 
             { VERB_CREATE, OBJECT_PROJECT,
-                "Creates a new Android Project." },
+                "Creates a new Android project." },
             { VERB_UPDATE, OBJECT_PROJECT,
-                "Updates an Android Project (must have an AndroidManifest.xml)." },
+                "Updates an Android project (must already have an AndroidManifest.xml)." },
 
             { VERB_CREATE, OBJECT_TEST_PROJECT,
-                "Creates a new Android Test Project." },
+                "Creates a new Android project for a test package." },
             { VERB_UPDATE, OBJECT_TEST_PROJECT,
-                "Updates an Android Test Project (must have an AndroidManifest.xml)." },
+                "Updates the Android project for a test package (must already have an AndroidManifest.xml)." },
 
             { VERB_CREATE, OBJECT_LIB_PROJECT,
-                "Creates a new Android Library Project." },
+                "Creates a new Android library project." },
             { VERB_UPDATE, OBJECT_LIB_PROJECT,
-                "Updates an Android Library Project (must have an AndroidManifest.xml)." },
+                "Updates an Android library project (must already have an AndroidManifest.xml)." },
 /*
  * disabled until the feature is officially supported.
             { VERB_CREATE, OBJECT_EXPORT_PROJECT,
@@ -148,22 +148,22 @@
 
         define(Mode.STRING, false,
                 VERB_CREATE, OBJECT_AVD, "p", KEY_PATH,
-                "Location path of the directory where the new AVD will be created", null);
+                "Directory where the new AVD will be created", null);
         define(Mode.STRING, true,
                 VERB_CREATE, OBJECT_AVD, "n", KEY_NAME,
                 "Name of the new AVD", null);
         define(Mode.STRING, true,
                 VERB_CREATE, OBJECT_AVD, "t", KEY_TARGET_ID,
-                "Target id of the new AVD", null);
+                "Target ID of the new AVD", null);
         define(Mode.STRING, false,
                 VERB_CREATE, OBJECT_AVD, "s", KEY_SKIN,
-                "Skin of the new AVD", null);
+                "Skin for the new AVD", null);
         define(Mode.STRING, false,
                 VERB_CREATE, OBJECT_AVD, "c", KEY_SDCARD,
                 "Path to a shared SD card image, or size of a new sdcard for the new AVD", null);
         define(Mode.BOOLEAN, false,
                 VERB_CREATE, OBJECT_AVD, "f", KEY_FORCE,
-                "Force creation (override an existing AVD)", false);
+                "Forces creation (overwrites an existing AVD)", false);
 
         // --- delete avd ---
 
@@ -178,10 +178,10 @@
                 "Name of the AVD to move or rename", null);
         define(Mode.STRING, false,
                 VERB_MOVE, OBJECT_AVD, "r", KEY_RENAME,
-                "New name of the AVD to rename", null);
+                "New name of the AVD", null);
         define(Mode.STRING, false,
                 VERB_MOVE, OBJECT_AVD, "p", KEY_PATH,
-                "New location path of the directory where to move the AVD", null);
+                "Path to the AVD's new directory", null);
 
         // --- update avd ---
 
@@ -193,30 +193,31 @@
 
         define(Mode.BOOLEAN, false,
                 VERB_UPDATE, OBJECT_SDK, "u", KEY_NO_UI,
-                "Update from command-line, without any UI", false);
+                "Updates from command-line (does not display the GUI)", false);
 
         define(Mode.BOOLEAN, false,
                 VERB_UPDATE, OBJECT_SDK, "s", KEY_NO_HTTPS,
-                "Use HTTP instead of the default HTTPS for downloads", false);
+                "Uses HTTP instead of HTTPS (the default) for downloads", false);
 
         define(Mode.BOOLEAN, false,
                 VERB_UPDATE, OBJECT_SDK, "f", KEY_FORCE,
-                "Force replacing things that have been modified (samples, adb)", false);
+                "Forces replacement of a package or its parts, even if something has been modified",
+                false);
 
         define(Mode.STRING, false,
                 VERB_UPDATE, OBJECT_SDK, "t", KEY_FILTER,
-                "A coma-separated list of " + Arrays.toString(SdkRepository.NODES) +
-                " to limit update to specified types of packages",
+                "A filter that limits the update to the specified types of packages in the form of\n" +
+                "                a comma-separated list of " + Arrays.toString(SdkRepository.NODES),
                 null);
 
         define(Mode.BOOLEAN, false,
                 VERB_UPDATE, OBJECT_SDK, "o", KEY_OBSOLETE,
-                "Install obsolete packages",
+                "Installs obsolete packages",
                 false);
 
         define(Mode.BOOLEAN, false,
                 VERB_UPDATE, OBJECT_SDK, "n", KEY_DRY_MODE,
-                "Only simulates what would be updated but does not download/install anything",
+                "Simulates the update but does not download or install anything",
                 false);
 
         // --- create project ---
@@ -231,16 +232,16 @@
         define(Mode.STRING, true,
                 VERB_CREATE, OBJECT_PROJECT,
                 "p", KEY_PATH,
-                "Location path of new project", null);
+                "The new project's directory", null);
         define(Mode.STRING, true,
                 VERB_CREATE, OBJECT_PROJECT, "t", KEY_TARGET_ID,
-                "Target id of the new project", null);
+                "Target ID of the new project", null);
         define(Mode.STRING, true,
                 VERB_CREATE, OBJECT_PROJECT, "k", KEY_PACKAGE,
-                "Package name", null);
+                "Android package name for the application", null);
         define(Mode.STRING, true,
                 VERB_CREATE, OBJECT_PROJECT, "a", KEY_ACTIVITY,
-                "Activity name", null);
+                "Name of the default Activity that is created", null);
         define(Mode.STRING, false,
                 VERB_CREATE, OBJECT_PROJECT, "n", KEY_NAME,
                 "Project name", null);
@@ -250,29 +251,29 @@
         define(Mode.STRING, true,
                 VERB_CREATE, OBJECT_TEST_PROJECT,
                 "p", KEY_PATH,
-                "Location path of new project", null);
+                "The new project's directory", null);
         define(Mode.STRING, false,
                 VERB_CREATE, OBJECT_TEST_PROJECT, "n", KEY_NAME,
                 "Project name", null);
         define(Mode.STRING, true,
                 VERB_CREATE, OBJECT_TEST_PROJECT, "m", KEY_MAIN_PROJECT,
-                "Location path of the project to test, relative to the new project", null);
+                "Path to directory of the app under test, relative to the test project directory", null);
 
         // --- create lib-project ---
 
         define(Mode.STRING, true,
                 VERB_CREATE, OBJECT_LIB_PROJECT,
                 "p", KEY_PATH,
-                "Location path of new project", null);
+                "The new project's directory", null);
         define(Mode.STRING, true,
                 VERB_CREATE, OBJECT_LIB_PROJECT, "t", KEY_TARGET_ID,
-                "Target id of the new project", null);
+                "Target ID of the new project", null);
         define(Mode.STRING, false,
                 VERB_CREATE, OBJECT_LIB_PROJECT, "n", KEY_NAME,
                 "Project name", null);
         define(Mode.STRING, true,
                 VERB_CREATE, OBJECT_LIB_PROJECT, "k", KEY_PACKAGE,
-                "Package name", null);
+                "Android package name for the library", null);
 
         // --- create export-project ---
 /*
@@ -294,11 +295,11 @@
         define(Mode.STRING, true,
                 VERB_UPDATE, OBJECT_PROJECT,
                 "p", KEY_PATH,
-                "Location path of the project", null);
+                "The project's directory", null);
         define(Mode.STRING, false,
                 VERB_UPDATE, OBJECT_PROJECT,
                 "t", KEY_TARGET_ID,
-                "Target id to set for the project", null);
+                "Target ID to set for the project", null);
         define(Mode.STRING, false,
                 VERB_UPDATE, OBJECT_PROJECT,
                 "n", KEY_NAME,
@@ -306,33 +307,33 @@
         define(Mode.BOOLEAN, false,
                 VERB_UPDATE, OBJECT_PROJECT,
                 "s", KEY_SUBPROJECTS,
-                "Also update any projects in sub-folders, such as test projects.", false);
+                "Also updates any projects in sub-folders, such as test projects.", false);
         define(Mode.STRING, false,
                 VERB_UPDATE, OBJECT_PROJECT,
                 "l", KEY_LIBRARY,
-                "Location path of an Android Library to add, relative to the main project", null);
+                "Directory of an Android library to add, relative to this project's directory", null);
 
         // --- update test project ---
 
         define(Mode.STRING, true,
                 VERB_UPDATE, OBJECT_TEST_PROJECT,
                 "p", KEY_PATH,
-                "Location path of the project", null);
+                "The project's directory", null);
         define(Mode.STRING, true,
                 VERB_UPDATE, OBJECT_TEST_PROJECT,
                 "m", KEY_MAIN_PROJECT,
-                "Location path of the project to test, relative to the new project", null);
+                "Directory of the app under test, relative to the test project directory", null);
 
         // --- update lib project ---
 
         define(Mode.STRING, true,
                 VERB_UPDATE, OBJECT_LIB_PROJECT,
                 "p", KEY_PATH,
-                "Location path of the project", null);
+                "The project's directory", null);
         define(Mode.STRING, false,
                 VERB_UPDATE, OBJECT_LIB_PROJECT,
                 "t", KEY_TARGET_ID,
-                "Target id to set for the project", null);
+                "Target ID to set for the project", null);
 
         // --- update export project ---
 /*
diff --git a/testapps/basicLib/res/drawable-hdpi/icon.png b/testapps/basicLib/res/drawable-hdpi/icon.png
deleted file mode 100644
index 8074c4c..0000000
--- a/testapps/basicLib/res/drawable-hdpi/icon.png
+++ /dev/null
Binary files differ
diff --git a/testapps/basicLib/res/drawable-ldpi/icon.png b/testapps/basicLib/res/drawable-ldpi/icon.png
deleted file mode 100644
index 1095584..0000000
--- a/testapps/basicLib/res/drawable-ldpi/icon.png
+++ /dev/null
Binary files differ
diff --git a/testapps/basicLib/res/drawable-mdpi/icon.png b/testapps/basicLib/res/drawable/icon.png
similarity index 100%
rename from testapps/basicLib/res/drawable-mdpi/icon.png
rename to testapps/basicLib/res/drawable/icon.png
Binary files differ
diff --git a/testapps/basicProject/res/drawable-hdpi/icon.png b/testapps/basicProject/res/drawable-hdpi/icon.png
deleted file mode 100644
index 8074c4c..0000000
--- a/testapps/basicProject/res/drawable-hdpi/icon.png
+++ /dev/null
Binary files differ
diff --git a/testapps/basicProject/res/drawable-ldpi/icon.png b/testapps/basicProject/res/drawable-ldpi/icon.png
deleted file mode 100644
index 1095584..0000000
--- a/testapps/basicProject/res/drawable-ldpi/icon.png
+++ /dev/null
Binary files differ
diff --git a/testapps/basicProject/res/drawable-mdpi/icon.png b/testapps/basicProject/res/drawable/icon.png
similarity index 100%
rename from testapps/basicProject/res/drawable-mdpi/icon.png
rename to testapps/basicProject/res/drawable/icon.png
Binary files differ