aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--anttasks/.classpath8
-rw-r--r--anttasks/.project17
-rw-r--r--anttasks/Android.mk17
-rw-r--r--anttasks/src/Android.mk28
-rw-r--r--anttasks/src/com/android/ant/AndroidInitTask.java150
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/Device.java8
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java15
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java19
-rwxr-xr-xddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java9
-rw-r--r--ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java4
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java16
-rw-r--r--eclipse/features/com.android.ide.eclipse.adt/feature.xml7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java14
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java17
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerDeltaVisitor.java3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java226
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java135
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java156
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/MainLaunchConfigTab.java10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/preferences/BuildPreferencePage.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FolderDecorator.java7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ExportWizard.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ProjectCheckPage.java1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java68
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidJarLoader.java5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java6
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/IAndroidClassLoader.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java65
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/WidgetClassLoader.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java338
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java29
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java38
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestHelper.java54
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java14
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidXPathFactory.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/BaseProjectHelper.java49
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/XmlErrorHandler.java3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/AttrsXmlParser.java56
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java17
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidContentAssist.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidEditor.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptor.java9
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptorLabelProvider.java12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/DescriptorsUtils.java71
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/TextAttributeDescriptor.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/BasePullParser.java7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java39
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/CountryCodeQualifier.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/NetworkCodeQualifier.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/PixelDensityQualifier.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/MultiResourceFile.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectClassLoader.java3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectResources.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolder.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolderType.java18
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceManager.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/SingleResourceFile.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/FileWrapper.java5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/FolderWrapper.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IFileWrapper.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IFolderWrapper.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/templates/AndroidManifest.template1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/templates/uses-sdk.template1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/not_source_folder/jar/example/Class1.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/jar/example/Class1.java)0
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/not_source_folder/jar/example/Class2.java (renamed from eclipse/plugins/com.android.ide.eclipse.tests/unittests/jar/example/Class2.java)0
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/project/ProjectHelperTest.java5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/AndroidJarLoaderTest.java5
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/resources/AttrsXmlParserTest.java27
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/descriptors/DescriptorsUtilsTest.java12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/configurations/TextInputMethodQualifierTest.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/manager/ConfigMatchTest.java8
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/mock_attrs.xml24
-rwxr-xr-xeclipse/scripts/create_test_symlinks.sh34
-rw-r--r--emulator/qtools/dmtrace.cpp1
-rw-r--r--scripts/android_rules.xml227
-rw-r--r--scripts/build.template61
-rw-r--r--scripts/default.properties.template18
-rw-r--r--sdkmanager/app/.classpath2
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java580
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/Main.java222
-rw-r--r--sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java152
-rw-r--r--sdkmanager/app/tests/com/android/sdkmanager/CommandLineProcessorTest.java180
-rw-r--r--sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java105
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java30
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java40
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java3
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java14
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java37
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java19
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java346
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java95
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java22
-rw-r--r--sdkmanager/libs/sdkuilib/.classpath8
-rw-r--r--sdkmanager/libs/sdkuilib/.project17
-rw-r--r--sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java56
-rw-r--r--sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/VmSelector.java379
105 files changed, 3744 insertions, 878 deletions
diff --git a/anttasks/.classpath b/anttasks/.classpath
new file mode 100644
index 0000000..08ced21
--- /dev/null
+++ b/anttasks/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
+ <classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/ant/ant.jar"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/anttasks/.project b/anttasks/.project
new file mode 100644
index 0000000..aed1b61
--- /dev/null
+++ b/anttasks/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>ant-tasks</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/anttasks/Android.mk b/anttasks/Android.mk
new file mode 100644
index 0000000..15ee903
--- /dev/null
+++ b/anttasks/Android.mk
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+ANTTASKS_LOCAL_DIR := $(call my-dir)
+include $(ANTTASKS_LOCAL_DIR)/src/Android.mk
diff --git a/anttasks/src/Android.mk b/anttasks/src/Android.mk
new file mode 100644
index 0000000..dbaf2bc
--- /dev/null
+++ b/anttasks/src/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_JAVA_LIBRARIES := \
+ sdklib \
+ ant
+
+LOCAL_MODULE := anttasks
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/anttasks/src/com/android/ant/AndroidInitTask.java b/anttasks/src/com/android/ant/AndroidInitTask.java
new file mode 100644
index 0000000..84c1d27
--- /dev/null
+++ b/anttasks/src/com/android/ant/AndroidInitTask.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ant;
+
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.ISdkLog;
+import com.android.sdklib.SdkManager;
+import com.android.sdklib.project.ProjectProperties;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.ImportTask;
+
+import java.io.File;
+import java.util.ArrayList;
+
+/**
+ * Import Target Ant task. This task accomplishes:
+ * <ul>
+ * <li>Gets the project target hash string from {@link ProjectProperties#PROPERTY_TARGET},
+ * and resolves it.</li>
+ * <li>Sets up ant properties so that the rest of the Ant scripts finds:
+ * <ul>
+ * <li>Path to the underlying platform to access the build rules ('android-platform')<li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * This is used in build.xml/template.
+ *
+ */
+public class AndroidInitTask extends ImportTask {
+ private final static String ANDROID_RULES = "android_rules.xml";
+
+ private final static String PROPERTY_ANDROID_JAR = "android-jar";
+ private final static String PROPERTY_ANDROID_AIDL = "android-aidl";
+
+ @Override
+ public void execute() throws BuildException {
+ Project antProject = getProject();
+
+ // get the SDK location
+ String sdkLocation = antProject.getProperty(ProjectProperties.PROPERTY_SDK);
+
+ // check if it's valid and exists
+ if (sdkLocation == null || sdkLocation.length() == 0) {
+ throw new BuildException("SDK Location is not set.");
+ }
+
+ File sdk = new File(sdkLocation);
+ if (sdk.isDirectory() == false) {
+ throw new BuildException(String.format("SDK Location '%s' is not valid.", sdkLocation));
+ }
+
+ // get the target property value
+ String targetHashString = antProject.getProperty(ProjectProperties.PROPERTY_TARGET);
+ if (targetHashString == null) {
+ throw new BuildException("Android Target is not set.");
+ }
+
+ // load up the sdk targets.
+ final ArrayList<String> messages = new ArrayList<String>();
+ SdkManager manager = SdkManager.createManager(sdkLocation, new ISdkLog() {
+ public void error(Throwable t, String errorFormat, Object... args) {
+ if (errorFormat != null) {
+ messages.add(String.format("Error: " + errorFormat, args));
+ }
+ if (t != null) {
+ messages.add("Error: " + t.getMessage());
+ }
+ }
+
+ public void printf(String msgFormat, Object... args) {
+ messages.add(String.format(msgFormat, args));
+ }
+
+ public void warning(String warningFormat, Object... args) {
+ messages.add(String.format("Warning: " + warningFormat, args));
+ }
+ });
+
+ if (manager == null) {
+ // since we failed to parse the SDK, lets display the parsing output.
+ for (String msg : messages) {
+ System.out.println(msg);
+ }
+ throw new BuildException("Failed to parse SDK content.");
+ }
+
+ // resolve it
+ IAndroidTarget androidTarget = manager.getTargetFromHashString(targetHashString);
+
+ if (androidTarget == null) {
+ throw new BuildException(String.format(
+ "Unable to resolve target '%s'", targetHashString));
+ }
+
+ // display it
+ System.out.println("Project Target: " + androidTarget.getName());
+ if (androidTarget.isPlatform() == false) {
+ System.out.println("Vendor: " + androidTarget.getVendor());
+ }
+ System.out.println("Platform Version: " + androidTarget.getApiVersionName());
+ System.out.println("API level: " + androidTarget.getApiVersionNumber());
+
+ // sets up the properties.
+ String androidJar = androidTarget.getPath(IAndroidTarget.ANDROID_JAR);
+ String androidAidl = androidTarget.getPath(IAndroidTarget.ANDROID_AIDL);
+
+ antProject.setProperty(PROPERTY_ANDROID_JAR, androidJar);
+ antProject.setProperty(PROPERTY_ANDROID_AIDL, androidAidl);
+
+ // find the file to import, and import it.
+ String templateFolder = androidTarget.getPath(IAndroidTarget.TEMPLATES);
+
+ // make sure the file exists.
+ File templates = new File(templateFolder);
+ if (templates.isDirectory() == false) {
+ throw new BuildException(String.format("Template directory '%s' is missing.",
+ templateFolder));
+ }
+
+ // now check the rules file exists.
+ File rules = new File(templateFolder, ANDROID_RULES);
+ if (rules.isFile() == false) {
+ throw new BuildException(String.format("Build rules file '%s' is missing.",
+ templateFolder));
+ }
+
+ // set the file location to import
+ setFile(rules.getAbsolutePath());
+
+ // and import it
+ super.execute();
+ }
+}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java b/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
index a031a1b..8291f59 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
@@ -69,6 +69,9 @@ public final class Device implements IDevice {
/** Serial number of the device */
String serialNumber = null;
+ /** Name of the vm */
+ String mVmName = null;
+
/** State of the device. */
DeviceState state = null;
@@ -91,6 +94,11 @@ public final class Device implements IDevice {
return serialNumber;
}
+ public String getVmName() {
+ return mVmName;
+ }
+
+
/*
* (non-Javadoc)
* @see com.android.ddmlib.IDevice#getState()
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java b/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
index 4b81116..8547ac1 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/DeviceMonitor.java
@@ -349,7 +349,7 @@ final class DeviceMonitor {
}
if (device.getPropertyCount() == 0) {
- queryForBuild(device);
+ queryNewDeviceForInfo(device);
}
}
}
@@ -387,7 +387,7 @@ final class DeviceMonitor {
// look for their build info.
if (newDevice.isOnline()) {
- queryForBuild(newDevice);
+ queryNewDeviceForInfo(newDevice);
}
}
}
@@ -413,11 +413,20 @@ final class DeviceMonitor {
* Queries a device for its build info.
* @param device the device to query.
*/
- private void queryForBuild(Device device) {
+ private void queryNewDeviceForInfo(Device device) {
// TODO: do this in a separate thread.
try {
+ // first get the list of properties.
device.executeShellCommand(GetPropReceiver.GETPROP_COMMAND,
new GetPropReceiver(device));
+
+ // now get the emulator VM name (if applicable).
+ if (device.isEmulator()) {
+ EmulatorConsole console = EmulatorConsole.getConsole(device);
+ if (console != null) {
+ device.mVmName = console.getVmName();
+ }
+ }
} catch (IOException e) {
// if we can't get the build info, it doesn't matter too much
}
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java b/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java
index 4aac0e1..e00073c 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/EmulatorConsole.java
@@ -54,6 +54,7 @@ public final class EmulatorConsole {
private final static String HOST = "127.0.0.1"; //$NON-NLS-1$
private final static String COMMAND_PING = "help\r\n"; //$NON-NLS-1$
+ private final static String COMMAND_VM_NAME = "vm name\r\n"; //$NON-NLS-1$
private final static String COMMAND_KILL = "kill\r\n"; //$NON-NLS-1$
private final static String COMMAND_GSM_STATUS = "gsm status\r\n"; //$NON-NLS-1$
private final static String COMMAND_GSM_CALL = "gsm call %1$s\r\n"; //$NON-NLS-1$
@@ -307,6 +308,24 @@ public final class EmulatorConsole {
RemoveConsole(mPort);
}
}
+
+ public synchronized String getVmName() {
+ if (sendCommand(COMMAND_VM_NAME)) {
+ String[] result = readLines();
+ if (result != null && result.length == 2) { // this should be the name on first line,
+ // and ok on 2nd line
+ return result[0];
+ } else {
+ // try to see if there's a message after KO
+ Matcher m = RE_KO.matcher(result[result.length-1]);
+ if (m.matches()) {
+ return m.group(1);
+ }
+ }
+ }
+
+ return null;
+ }
/**
* Get the network status of the emulator.
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java b/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
index 106b76f..61d1ca4 100755
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
@@ -44,6 +44,15 @@ public interface IDevice {
* Returns the serial number of the device.
*/
public String getSerialNumber();
+
+ /**
+ * Returns the name of the VM the emulator is running.
+ * <p/>This is only valid if {@link #isEmulator()} returns true.
+ * <p/>If the emulator is not running any VM (for instance it's running from an Android source
+ * tree build), this method will return "<code>&lt;build&gt;</code>".
+ * @return the name of the VM or <code>null</code> if there isn't any.
+ */
+ public String getVmName();
/**
* Returns the state of the device.
diff --git a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
index cd4aa26..556fc9b 100644
--- a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
+++ b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
@@ -201,6 +201,10 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
throw new UnsupportedOperationException();
}
+ public String getVmName() {
+ return "";
+ }
+
}
/** An empty implementation of TestRunListener
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java
index adaa3c3..1331a09 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/DevicePanel.java
@@ -200,12 +200,22 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen
case DEVICE_COL_STATE:
return getStateString(device);
case DEVICE_COL_BUILD: {
+ String vmName = device.getVmName();
String debuggable = device.getProperty(Device.PROP_DEBUGGABLE);
String version = device.getProperty(Device.PROP_BUILD_VERSION);
- if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$
- return String.format("%1$s (debug)", version); //$NON-NLS-1$
+ if (device.isEmulator()) {
+ if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$
+ return String.format("%1$s [%2$s, debug]", vmName, //$NON-NLS-1$
+ version);
+ } else {
+ return String.format("%1$s [%2$s]", vmName, version); //$NON-NLS-1$
+ }
} else {
- return String.format("%1$s", version); //$NON-NLS-1$
+ if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$
+ return String.format("%1$s, debug", version); //$NON-NLS-1$
+ } else {
+ return String.format("%1$s", version); //$NON-NLS-1$
+ }
}
}
}
diff --git a/eclipse/features/com.android.ide.eclipse.adt/feature.xml b/eclipse/features/com.android.ide.eclipse.adt/feature.xml
index 191b76d..676a89e 100644
--- a/eclipse/features/com.android.ide.eclipse.adt/feature.xml
+++ b/eclipse/features/com.android.ide.eclipse.adt/feature.xml
@@ -40,6 +40,13 @@
<import plugin="org.eclipse.ui"/>
<import plugin="org.eclipse.ui.ide"/>
<import plugin="org.eclipse.ui.forms"/>
+ <import plugin="org.eclipse.gef"/>
+ <import plugin="org.eclipse.ui.browser"/>
+ <import plugin="org.eclipse.ui.views"/>
+ <import plugin="org.eclipse.wst.sse.core"/>
+ <import plugin="org.eclipse.wst.sse.ui"/>
+ <import plugin="org.eclipse.wst.xml.core"/>
+ <import plugin="org.eclipse.wst.xml.ui"/>
</requires>
<plugin
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java
index 61b3f4d..7304e5e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.adt;
+import com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerInitializer;
/**
@@ -42,8 +43,8 @@ public class AdtConstants {
/** Marker for Android Target errors.
* This is not cleared on each like other markers. Instead, it's cleared
- * when a ContainerClasspathInitialized has succeeded in creating an
- * {@link AndroidClasspathContainer}*/
+ * when an {@link AndroidClasspathContainerInitializer} has succeeded in creating an
+ * AndroidClasspathContainer */
public final static String MARKER_TARGET = AdtPlugin.PLUGIN_ID + ".targetProblem"; //$NON-NLS-1$
/** Build verbosity "Always". Those messages are always displayed. */
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
index d9c18cf..62bc7ed 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
@@ -1201,7 +1201,7 @@ public class AdtPlugin extends AbstractUIPlugin {
if (file.getFullPath().segmentCount() == 4) {
// check if we are inside the res folder.
String segment = file.getFullPath().segment(1);
- if (segment.equalsIgnoreCase(AndroidConstants.FD_RESOURCES)) {
+ if (segment.equalsIgnoreCase(SdkConstants.FD_RESOURCES)) {
// we are inside a res/ folder, get the actual ResourceFolder
ProjectResources resources = ResourceManager.getInstance().
getProjectResources(file.getProject());
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
index 4d16120..e38419a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
@@ -31,6 +31,7 @@ import com.android.jarutils.DebugKeyProvider.KeytoolException;
import com.android.jarutils.SignedJarBuilder.IZipEntryFilter;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
@@ -120,6 +121,10 @@ public class ApkBuilder extends BaseBuilder {
}
}
+ /**
+ * {@inheritDoc}
+ * @throws CoreException
+ */
public boolean visit(IResourceDelta delta) throws CoreException {
// no need to keep looking if we already know we need to convert
// to dex and make the final package.
@@ -589,7 +594,7 @@ public class ApkBuilder extends BaseBuilder {
* @param osBinPath the path to the output folder of the project
* @param osOutFilePath the path of the dex file to create.
* @param referencedJavaProjects the list of referenced projects for this project.
- * @return
+ *
* @throws CoreException
*/
private boolean executeDx(IJavaProject javaProject, String osBinPath, String osOutFilePath,
@@ -756,8 +761,7 @@ public class ApkBuilder extends BaseBuilder {
// now write the native libraries.
// First look if the lib folder is there.
- IResource libFolder = javaProject.getProject().findMember(
- AndroidConstants.FD_NATIVE_LIBS);
+ IResource libFolder = javaProject.getProject().findMember(SdkConstants.FD_NATIVE_LIBS);
if (libFolder != null && libFolder.exists() &&
libFolder.getType() == IResource.FOLDER) {
// look inside and put .so in lib/* by keeping the relative folder path.
@@ -829,7 +833,7 @@ public class ApkBuilder extends BaseBuilder {
* lib folder directly goes in this "lib" folder in the archive.
*
*
- * @param rooSegmentCount The number of segment of the path of the folder containing the
+ * @param rootSegmentCount The number of segment of the path of the folder containing the
* libraries. This is used to compute the path in the archive.
* @param jarBuilder the {@link SignedJarBuilder} used to create the archive.
* @param resource the IResource to write.
@@ -847,7 +851,7 @@ public class ApkBuilder extends BaseBuilder {
path = path.removeFirstSegments(rootSegmentCount);
// add it to the archive.
- IPath apkPath = new Path(AndroidConstants.FD_APK_NATIVE_LIBS);
+ IPath apkPath = new Path(SdkConstants.FD_APK_NATIVE_LIBS);
apkPath = apkPath.append(path);
// writes the file in the apk.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java
index 47ef626..aec703d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkDeltaVisitor.java
@@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.build;
import com.android.ide.eclipse.adt.build.BaseBuilder.BaseDeltaVisitor;
import com.android.ide.eclipse.common.AndroidConstants;
+import com.android.sdklib.SdkConstants;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
@@ -98,17 +99,17 @@ public class ApkDeltaVisitor extends BaseDeltaVisitor
mOutputPath = outputfolder.getFullPath();
}
- IResource assetFolder = builder.getProject().findMember(AndroidConstants.FD_ASSETS);
+ IResource assetFolder = builder.getProject().findMember(SdkConstants.FD_ASSETS);
if (assetFolder != null) {
mAssetPath = assetFolder.getFullPath();
}
- IResource resFolder = builder.getProject().findMember(AndroidConstants.FD_RESOURCES);
+ IResource resFolder = builder.getProject().findMember(SdkConstants.FD_RESOURCES);
if (resFolder != null) {
mResPath = resFolder.getFullPath();
}
- IResource libFolder = builder.getProject().findMember(AndroidConstants.FD_NATIVE_LIBS);
+ IResource libFolder = builder.getProject().findMember(SdkConstants.FD_NATIVE_LIBS);
if (libFolder != null) {
mLibFolder = libFolder.getFullPath();
}
@@ -126,8 +127,9 @@ public class ApkDeltaVisitor extends BaseDeltaVisitor
return mMakeFinalPackage;
}
- /*
- * (non-Javadoc)
+ /**
+ * {@inheritDoc}
+ * @throws CoreException
*
* @see org.eclipse.core.resources.IResourceDeltaVisitor
* #visit(org.eclipse.core.resources.IResourceDelta)
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java
index f94bdc7..534c123 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java
@@ -69,15 +69,15 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
/**
* First line of dual line aapt error.<br>
* "ERROR at line &lt;line&gt;: &lt;error&gt;"<br>
- * " (Occured while parsing &lt;path&gt;)"
+ * " (Occurred while parsing &lt;path&gt;)"
*/
private final static Pattern sPattern1Line1 = Pattern.compile(
"^ERROR\\s+at\\s+line\\s+(\\d+):\\s+(.*)$"); //$NON-NLS-1$
/**
* Second line of dual line aapt error.<br>
* "ERROR at line &lt;line&gt;: &lt;error&gt;"<br>
- * " (Occured while parsing &lt;path&gt;)"<br>
- * @see sPattern1Line1
+ * " (Occurred while parsing &lt;path&gt;)"<br>
+ * @see #sPattern1Line1
*/
private final static Pattern sPattern1Line2 = Pattern.compile(
"^\\s+\\(Occurred while parsing\\s+(.*)\\)$"); //$NON-NLS-1$
@@ -92,7 +92,7 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
* Second line of dual line aapt error.<br>
* "ERROR: &lt;error&gt;"<br>
* "Defined at file &lt;path&gt; line &lt;line&gt;"<br>
- * @see sPattern2Line1
+ * @see #sPattern2Line1
*/
private final static Pattern sPattern2Line2 = Pattern.compile(
"Defined\\s+at\\s+file\\s+(.+)\\s+line\\s+(\\d+)"); //$NON-NLS-1$
@@ -113,7 +113,7 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
* Second line of dual line aapt error.<br>
* "ERROR parsing XML file &lt;path&gt;"<br>
* "&lt;error&gt; at line &lt;line&gt;"<br>
- * @see sPattern4Line1
+ * @see #sPattern4Line1
*/
private final static Pattern sPattern4Line2 = Pattern.compile(
"^(.+)\\s+at\\s+line\\s+(\\d+)$"); //$NON-NLS-1$
@@ -263,7 +263,7 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
/**
* Adds a marker to the current project.
- * @param file the file to be marked
+ *
* @param markerId The id of the marker to add.
* @param message the message associated with the mark
* @param severity the severity of the marker.
@@ -292,12 +292,11 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
/**
* Removes markers from a container and its children.
- * @param container The container from which to delete the markers.
+ * @param folder The container from which to delete the markers.
* @param markerId The id of the markers to remove. If null, all marker of
* type <code>IMarker.PROBLEM</code> will be removed.
*/
- protected final void removeMarkersFromContainer(IContainer folder,
- String markerId) {
+ protected final void removeMarkersFromContainer(IContainer folder, String markerId) {
try {
if (folder.exists()) {
folder.deleteMarkers(markerId, true, IResource.DEPTH_INFINITE);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
index 1a4aa8c..9fc4348 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
@@ -1128,10 +1128,8 @@ public class PreCompilerBuilder extends BaseBuilder {
* Finish a file created/modified by an outside command line process.
* The file is marked as modified by Android, and the parent folder is refreshed, so that,
* in case the file didn't exist beforehand, the file appears in the package explorer.
- * @param file The file to "finish".
- * @param parent The parent container. Can be null (in which case it'll be
- * figured out from the file's IResource.
- * @param monitor a monitor to display progress.
+ * @param rFile The R file to "finish".
+ * @param manifestFile The manifest file to "finish".
* @throws CoreException
*/
private void finishJavaFilesAfterExternalModification(IFile rFile, IFile manifestFile)
@@ -1150,9 +1148,7 @@ public class PreCompilerBuilder extends BaseBuilder {
* The file is marked as modified by Android, and the parent folder is refreshed, so that,
* in case the file didn't exist beforehand, the file appears in the package explorer.
* @param file The file to "finish".
- * @param parent The parent container. Can be null (in which case it'll be
- * figured out from the file's IResource.
- * @param monitor a monitor to display progress.
+ * @param aidlFile The AIDL file to "finish".
* @throws CoreException
*/
private void finishFileAfterExternalModification(IFile file, IFile aidlFile)
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerDeltaVisitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerDeltaVisitor.java
index 33d5fa6..f4778d7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerDeltaVisitor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerDeltaVisitor.java
@@ -23,6 +23,7 @@ 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 com.android.sdklib.SdkConstants;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
@@ -154,7 +155,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements
// then we are not yet in a source or resource folder
mInRes = mInSrc = false;
- if (AndroidConstants.FD_RESOURCES.equalsIgnoreCase(segments[1])) {
+ if (SdkConstants.FD_RESOURCES.equalsIgnoreCase(segments[1])) {
// this is the resource folder that was modified. we want to
// see its content.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java
index 48ec7c3..2b7d01d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java
@@ -31,8 +31,12 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.debug.launching.DeviceChooserDialog.DeviceChooserResponse;
import com.android.ide.eclipse.adt.debug.ui.EmulatorConfigTab;
import com.android.ide.eclipse.adt.project.ProjectHelper;
+import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.project.AndroidManifestHelper;
-import com.android.sdklib.SdkConstants;
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkManager;
+import com.android.sdklib.vm.VmManager;
+import com.android.sdklib.vm.VmManager.VmInfo;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
@@ -58,6 +62,7 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -69,9 +74,9 @@ import java.util.regex.Pattern;
public final class AndroidLaunchController implements IDebugBridgeChangeListener,
IDeviceChangeListener, IClientChangeListener {
+ private static final String FLAG_VM = "-vm"; //$NON-NLS-1$
private static final String FLAG_NETDELAY = "-netdelay"; //$NON-NLS-1$
private static final String FLAG_NETSPEED = "-netspeed"; //$NON-NLS-1$
- private static final String FLAG_SKIN = "-skin"; //$NON-NLS-1$
private static final String FLAG_WIPE_DATA = "-wipe-data"; //$NON-NLS-1$
private static final String FLAG_NO_BOOT_ANIM = "-no-boot-anim"; //$NON-NLS-1$
@@ -223,11 +228,10 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
public boolean mNoBootAnim = LaunchConfigDelegate.DEFAULT_NO_BOOT_ANIM;
/**
- * Screen size parameters.
- * This value can be provided to the emulator directly for the option "-skin"
+ * Vm Name.
*/
- public String mSkin = null;
-
+ public String mVmName = null;
+
public String mNetworkSpeed = EmulatorConfigTab.getSpeed(
LaunchConfigDelegate.DEFAULT_SPEED);
public String mNetworkDelay = EmulatorConfigTab.getDelay(
@@ -258,12 +262,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
}
try {
- mSkin = config.getAttribute(LaunchConfigDelegate.ATTR_SKIN, mSkin);
- if (mSkin == null) {
- mSkin = SdkConstants.SKIN_DEFAULT;
- }
+ mVmName = config.getAttribute(LaunchConfigDelegate.ATTR_VM_NAME, mVmName);
} catch (CoreException e) {
- mSkin = SdkConstants.SKIN_DEFAULT;
}
int index = LaunchConfigDelegate.DEFAULT_SPEED;
@@ -526,11 +526,14 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
// set the launch mode to default.
wc.setAttribute(LaunchConfigDelegate.ATTR_LAUNCH_ACTION,
LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION);
-
+
// set default target mode
wc.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
LaunchConfigDelegate.DEFAULT_TARGET_MODE);
+ // default VM: None
+ wc.setAttribute(LaunchConfigDelegate.ATTR_VM_NAME, (String)null);
+
// set the default network speed
wc.setAttribute(LaunchConfigDelegate.ATTR_SPEED,
LaunchConfigDelegate.DEFAULT_SPEED);
@@ -539,9 +542,6 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
wc.setAttribute(LaunchConfigDelegate.ATTR_DELAY,
LaunchConfigDelegate.DEFAULT_DELAY);
- // default skin
- wc.setAttribute(LaunchConfigDelegate.ATTR_SKIN, SdkConstants.SKIN_DEFAULT);
-
// default wipe data mode
wc.setAttribute(LaunchConfigDelegate.ATTR_WIPE_DATA,
LaunchConfigDelegate.DEFAULT_WIPE_DATA);
@@ -627,32 +627,171 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
// set the debug mode
launchInfo.mDebugMode = mode.equals(ILaunchManager.DEBUG_MODE);
- // device chooser response.
+ // get the SDK
+ Sdk currentSdk = Sdk.getCurrent();
+ VmManager vmManager = currentSdk.getVmManager();
+
+ // get the project target
+ final IAndroidTarget projectTarget = currentSdk.getTarget(project);
+
+ // FIXME: check errors on missing sdk, vm manager, or project target.
+
+ // device chooser response object.
final DeviceChooserResponse response = new DeviceChooserResponse();
+ /*
+ * Launch logic:
+ * - Manually Mode
+ * Always display a UI that lets a user see the current running emulators/devices.
+ * The UI must show which devices are compatibles, and allow launching new emulators
+ * with compatible (and not yet running) VM.
+ * - Automatic Way
+ * * Preferred VM set.
+ * If Preferred VM is not running: launch it.
+ * Launch the application on the preferred VM.
+ * * No preferred VM.
+ * Count the number of compatible emulators/devices.
+ * If != 1, display a UI similar to manual mode.
+ * If == 1, launch the application on this VM/device.
+ */
+
if (config.mTargetMode == AndroidLaunchConfiguration.AUTO_TARGET_MODE) {
// if we are in automatic target mode, we need to find the current devices
Device[] devices = AndroidDebugBridge.getBridge().getDevices();
- // depending on the number of devices, we'll simulate an automatic choice
- // from the device chooser or simply show up the device chooser.
- if (devices.length == 0) {
- // if zero devices, we launch the device.
- AdtPlugin.printToConsole(project, "Automatic Target Mode: launching new emulator.");
+ // first check if we have a preferred VM name, and if it actually exists, and is valid
+ // (ie able to run the project).
+ // We need to check this in case the VM was recreated with a different target that is
+ // not compatible.
+ VmInfo preferredVm = null;
+ if (config.mVmName != null) {
+ preferredVm = vmManager.getVm(config.mVmName);
+ if (projectTarget.isCompatibleBaseFor(preferredVm.getTarget()) == false) {
+ preferredVm = null;
+
+ AdtPlugin.printErrorToConsole(project, String.format(
+ "Preferred VM '%1$s' is not compatible with the project target '%2$s'. Looking for a compatible VM...",
+ config.mVmName, projectTarget.getName()));
+ }
+ }
+
+ if (preferredVm != null) {
+ // look for a matching device
+ for (Device d : devices) {
+ String deviceVm = d.getVmName();
+ if (deviceVm != null && deviceVm.equals(config.mVmName)) {
+ response.mustContinue = true;
+ response.mustLaunchEmulator = false;
+ response.deviceToUse = d;
+
+ AdtPlugin.printToConsole(project, String.format(
+ "Automatic Target Mode: Preferred VM '%1$s' is available on emulator '%2$s'",
+ config.mVmName, d));
+
+ continueLaunch(response, project, launch, launchInfo, config);
+ return;
+ }
+ }
+
+ // at this point we have a valid preferred VM that is not running.
+ // We need to start it.
response.mustContinue = true;
response.mustLaunchEmulator = true;
+ response.vmToLaunch = preferredVm;
+
+ AdtPlugin.printToConsole(project, String.format(
+ "Automatic Target Mode: Preferred VM '%1$s' is not available. Launching new emulator.",
+ config.mVmName));
+
continueLaunch(response, project, launch, launchInfo, config);
return;
- } else if (devices.length == 1) {
+ }
+
+ // no (valid) preferred VM? look for one.
+ HashMap<Device, VmInfo> compatibleRunningVms = new HashMap<Device, VmInfo>();
+ boolean hasDevice = false; // if there's 1+ device running, we may force manual mode,
+ // as we cannot always detect proper compatibility with
+ // devices. This is the case if the project target is not
+ // a standard platform
+ for (Device d : devices) {
+ String deviceVm = d.getVmName();
+ if (deviceVm != null) { // physical devices return null.
+ VmInfo info = vmManager.getVm(deviceVm);
+ if (info != null && projectTarget.isCompatibleBaseFor(info.getTarget())) {
+ compatibleRunningVms.put(d, info);
+ }
+ } else {
+ if (projectTarget.isPlatform()) { // means this can run on any device as long
+ // as api level is high enough
+ String apiString = d.getProperty(SdkManager.PROP_VERSION_SDK);
+ try {
+ int apiNumber = Integer.parseInt(apiString);
+ if (apiNumber >= projectTarget.getApiVersionNumber()) {
+ // device is compatible with project
+ compatibleRunningVms.put(d, null);
+ continue;
+ }
+ } catch (NumberFormatException e) {
+ // do nothing, we'll consider it a non compatible device below.
+ }
+ }
+ hasDevice = true;
+ }
+ }
+
+ // depending on the number of devices, we'll simulate an automatic choice
+ // from the device chooser or simply show up the device chooser.
+ if (hasDevice == false && compatibleRunningVms.size() == 0) {
+ // if zero emulators/devices, we launch an emulator.
+ // We need to figure out which VM first.
+
+ // we are going to take the closest VM. ie a compatible VM that has the API level
+ // closest to the project target.
+ VmInfo[] vms = vmManager.getVms();
+ VmInfo defaultVm = null;
+ for (VmInfo vm : vms) {
+ if (projectTarget.isCompatibleBaseFor(vm.getTarget())) {
+ if (defaultVm == null ||
+ vm.getTarget().getApiVersionNumber() <
+ defaultVm.getTarget().getApiVersionNumber()) {
+ defaultVm = vm;
+ }
+ }
+ }
+
+ if (defaultVm != null) {
+ response.mustContinue = true;
+ response.mustLaunchEmulator = true;
+ response.vmToLaunch = defaultVm;
+
+ AdtPlugin.printToConsole(project, String.format(
+ "Automatic Target Mode: launching new emulator with compatible VM '%1$s'",
+ defaultVm.getName()));
+
+ continueLaunch(response, project, launch, launchInfo, config);
+ return;
+ } else {
+ // FIXME: ask the user if he wants to create a VM.
+ // we found no compatible VM.
+ AdtPlugin.printErrorToConsole(project, String.format(
+ "Failed to find a VM compatible with target '%1$s'. Launch aborted.",
+ projectTarget.getName()));
+ launch.stopLaunch();
+ return;
+ }
+ } else if (hasDevice == false && compatibleRunningVms.size() == 1) {
+ Entry<Device, VmInfo> e = compatibleRunningVms.entrySet().iterator().next();
response.mustContinue = true;
response.mustLaunchEmulator = false;
- response.deviceToUse = devices[0];
+ response.deviceToUse = e.getKey();
- if (response.deviceToUse.isEmulator()) {
- message = String.format("Automatic Target Mode: using existing emulator: %1$s",
- response.deviceToUse);
+ // get the VmInfo, if null, the device is a physical device.
+ VmInfo vmInfo = e.getValue();
+ if (vmInfo != null) {
+ message = String.format("Automatic Target Mode: using existing emulator '%1$s' running compatible VM '%2$s'",
+ response.deviceToUse, e.getValue().getName());
} else {
- message = String.format("Automatic Target Mode: using existing device: %1$s",
+ message = String.format("Automatic Target Mode: using device '%1$s'",
response.deviceToUse);
}
AdtPlugin.printToConsole(project, message);
@@ -662,8 +801,13 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
}
// if more than one device, we'll bring up the DeviceChooser dialog below.
- AdtPlugin.printToConsole(project,
- "Automatic Target Mode: user selection for 2+ devices.");
+ if (compatibleRunningVms.size() >= 2) {
+ message = "Automatic Target Mode: Several compatible targets. Please select a target device.";
+ } else if (hasDevice) {
+ message = "Automatic Target Mode: Unable to detect device compatibility. Please select a target device.";
+ }
+
+ AdtPlugin.printToConsole(project, message);
}
// bring up the device chooser.
@@ -671,7 +815,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
public void run() {
DeviceChooserDialog dialog = new DeviceChooserDialog(
AdtPlugin.getDisplay().getActiveShell());
- dialog.open(response, project, launch, launchInfo, config);
+ dialog.open(response, project, projectTarget, launch, launchInfo, config);
}
});
@@ -705,7 +849,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
synchronized (sListLock) {
mWaitingForEmulatorLaunches.add(launchInfo);
AdtPlugin.printToConsole(project, "Launching a new emulator.");
- boolean status = launchEmulator(config);
+ boolean status = launchEmulator(config, response.vmToLaunch);
if (status == false) {
// launching the emulator failed!
@@ -775,9 +919,6 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* the device requires it and it is not set in the manifest, the launch will be forced to
* "release" mode instead of "debug"</li>
* <ul>
- * @param launchInfo
- * @param device
- * @return
*/
private boolean checkBuildInfo(DelayedLaunchInfo launchInfo, Device device) {
if (device != null) {
@@ -1122,7 +1263,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
/**
* launches an application on a device or emulator
*
- * @param classToLaunch the fully-qualified name of the activity to launch
+ * @param info the {@link DelayedLaunchInfo} that indicates the activity to launch
* @param device the device or emulator to launch the application on
*/
private void launchApp(final DelayedLaunchInfo info, Device device) {
@@ -1182,7 +1323,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
}
}
- private boolean launchEmulator(AndroidLaunchConfiguration config) {
+ private boolean launchEmulator(AndroidLaunchConfiguration config, VmInfo vmToLaunch) {
// split the custom command line in segments
ArrayList<String> customArgs = new ArrayList<String>();
@@ -1212,10 +1353,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
ArrayList<String> list = new ArrayList<String>();
list.add(AdtPlugin.getOsAbsoluteEmulator());
- if (config.mSkin != null) {
- list.add(FLAG_SKIN);
- list.add(config.mSkin);
- }
+ list.add(FLAG_VM);
+ list.add(vmToLaunch.getName());
if (config.mNetworkSpeed != null) {
list.add(FLAG_NETSPEED);
@@ -1329,7 +1468,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* @param debugPort The port to connect the debugger to
* @param androidLaunch The associated AndroidLaunch object.
* @param monitor A Progress monitor
- * @see connectRemoveDebugger()
+ * @see #connectRemoteDebugger(int, AndroidLaunch, IProgressMonitor)
*/
public static void launchRemoteDebugger( final int debugPort, final AndroidLaunch androidLaunch,
final IProgressMonitor monitor) {
@@ -1352,7 +1491,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* This is sent from a non UI thread.
* @param bridge the new {@link AndroidDebugBridge} object.
*
- * @see IDebugBridgeChangeListener#serverChanged(AndroidDebugBridge)
+ * @see IDebugBridgeChangeListener#bridgeChanged(AndroidDebugBridge)
*/
public void bridgeChanged(AndroidDebugBridge bridge) {
// The adb server has changed. We cancel any pending launches.
@@ -1447,7 +1586,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* @param device the device that was updated.
* @param changeMask the mask indicating what changed.
*
- * @see IDeviceChangeListener#deviceChanged(Device)
+ * @see IDeviceChangeListener#deviceChanged(Device, int)
*/
public void deviceChanged(Device device, int changeMask) {
// We could check if any starting device we care about is now ready, but we can wait for
@@ -1622,7 +1761,6 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* Get the stderr/stdout outputs of a process and return when the process is done.
* Both <b>must</b> be read or the process will block on windows.
* @param process The process to get the ouput from
- * @throws InterruptedException
*/
private void grabEmulatorOutput(final Process process) {
// read the lines as they come. if null is returned, it's
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java
index 2cb11c3..19ec9a7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java
@@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt.debug.launching;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.Client;
import com.android.ddmlib.Device;
+import com.android.ddmlib.IDevice;
import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
import com.android.ddmlib.Device.DeviceState;
import com.android.ddmuilib.IImageLoader;
@@ -27,7 +28,10 @@ import com.android.ddmuilib.TableHelper;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController.AndroidLaunchConfiguration;
import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController.DelayedLaunchInfo;
+import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.vm.VmManager.VmInfo;
import org.eclipse.core.resources.IProject;
import org.eclipse.jface.preference.IPreferenceStore;
@@ -67,19 +71,26 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
private final static String PREFS_COL_SERIAL = "deviceChooser.serial"; //$NON-NLS-1$
private final static String PREFS_COL_STATE = "deviceChooser.state"; //$NON-NLS-1$
- private final static String PREFS_COL_BUILD = "deviceChooser.build"; //$NON-NLS-1$
+ private final static String PREFS_COL_VM = "deviceChooser.vm"; //$NON-NLS-1$
+ private final static String PREFS_COL_TARGET = "deviceChooser.target"; //$NON-NLS-1$
+ private final static String PREFS_COL_DEBUG = "deviceChooser.debug"; //$NON-NLS-1$
private Table mDeviceTable;
private TableViewer mViewer;
private Image mDeviceImage;
private Image mEmulatorImage;
+ private Image mMatchImage;
+ private Image mNoMatchImage;
+ private Image mWarningImage;
private Button mOkButton;
private Button mCreateButton;
private DeviceChooserResponse mResponse;
private DelayedLaunchInfo mLaunchInfo;
+ private IAndroidTarget mProjectTarget;
+ private Sdk mSdk;
/**
* Basic Content Provider for a table full of {@link Device} objects. The input is
@@ -111,13 +122,44 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
private class LabelProvider implements ITableLabelProvider {
public Image getColumnImage(Object element, int columnIndex) {
- if (columnIndex == 0 && element instanceof Device) {
- if (((Device)element).isEmulator()) {
- return mEmulatorImage;
+ if (element instanceof Device) {
+ Device device = (Device)element;
+ switch (columnIndex) {
+ case 0:
+ return device.isEmulator() ? mEmulatorImage : mDeviceImage;
+
+ case 2:
+ // check for compatibility.
+ if (device.isEmulator() == false) { // physical device
+ // get the api level of the device
+ try {
+ String apiValue = device.getProperty(
+ IDevice.PROP_BUILD_VERSION_NUMBER);
+ int api = Integer.parseInt(apiValue);
+ if (api >= mProjectTarget.getApiVersionNumber()) {
+ // if the project is compiling against an add-on, the optional
+ // API may be missing from the device.
+ return mProjectTarget.isPlatform() ?
+ mMatchImage : mWarningImage;
+ } else {
+ return mNoMatchImage;
+ }
+ } catch (NumberFormatException e) {
+ // lets consider the device non compatible
+ return mNoMatchImage;
+ }
+ } else {
+ // get the VmInfo
+ VmInfo info = mSdk.getVmManager().getVm(device.getVmName());
+ if (info == null) {
+ return mWarningImage;
+ }
+ return mProjectTarget.isCompatibleBaseFor(info.getTarget()) ?
+ mMatchImage : mNoMatchImage;
+ }
}
-
- return mDeviceImage;
}
+
return null;
}
@@ -128,15 +170,30 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
case 0:
return device.getSerialNumber();
case 1:
- return getStateString(device);
+ if (device.isEmulator()) {
+ return device.getVmName();
+ } else {
+ return "N/A"; // devices don't have VM names.
+ }
case 2:
- String debuggable = device.getProperty(Device.PROP_DEBUGGABLE);
- String version = device.getProperty(Device.PROP_BUILD_VERSION);
+ if (device.isEmulator()) {
+ VmInfo info = mSdk.getVmManager().getVm(device.getVmName());
+ if (info == null) {
+ return "?";
+ }
+ return info.getTarget().getFullName();
+ } else {
+ return device.getProperty(IDevice.PROP_BUILD_VERSION);
+ }
+ case 3:
+ String debuggable = device.getProperty(IDevice.PROP_DEBUGGABLE);
if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$
- return String.format("%1$s (debug)", version); //$NON-NLS-1$
+ return "Yes";
} else {
- return String.format("%1$s", version); //$NON-NLS-1$
+ return "";
}
+ case 4:
+ return getStateString(device);
}
}
@@ -164,6 +221,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
public static class DeviceChooserResponse {
public boolean mustContinue;
public boolean mustLaunchEmulator;
+ public VmInfo vmToLaunch;
public Device deviceToUse;
}
@@ -175,14 +233,18 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
* Prepare and display the dialog.
* @param response
* @param project
+ * @param projectTarget
* @param launch
* @param launchInfo
* @param config
*/
public void open(DeviceChooserResponse response, IProject project,
- AndroidLaunch launch, DelayedLaunchInfo launchInfo, AndroidLaunchConfiguration config) {
+ IAndroidTarget projectTarget, AndroidLaunch launch, DelayedLaunchInfo launchInfo,
+ AndroidLaunchConfiguration config) {
mResponse = response;
+ mProjectTarget = projectTarget;
mLaunchInfo = launchInfo;
+ mSdk = Sdk.getCurrent();
Shell parent = getParent();
Shell shell = new Shell(parent, getStyle());
@@ -218,6 +280,9 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
mEmulatorImage.dispose();
mDeviceImage.dispose();
+ mMatchImage.dispose();
+ mNoMatchImage.dispose();
+ mWarningImage.dispose();
AndroidLaunchController.getInstance().continueLaunch(response, project, launch,
launchInfo, config);
@@ -249,14 +314,22 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
SWT.LEFT, "AAA+AAAAAAAAAAAAAAAAAAA", //$NON-NLS-1$
PREFS_COL_SERIAL, store);
+ TableHelper.createTableColumn(mDeviceTable, "VM Name",
+ SWT.LEFT, "engineering", //$NON-NLS-1$
+ PREFS_COL_VM, store);
+
+ TableHelper.createTableColumn(mDeviceTable, "Target",
+ SWT.LEFT, "AAA+Android 9.9.9", //$NON-NLS-1$
+ PREFS_COL_TARGET, store);
+
+ TableHelper.createTableColumn(mDeviceTable, "Debug",
+ SWT.LEFT, "Debug", //$NON-NLS-1$
+ PREFS_COL_DEBUG, store);
+
TableHelper.createTableColumn(mDeviceTable, "State",
SWT.LEFT, "bootloader", //$NON-NLS-1$
PREFS_COL_STATE, store);
- TableHelper.createTableColumn(mDeviceTable, "Build Info",
- SWT.LEFT, "engineering", //$NON-NLS-1$
- PREFS_COL_BUILD, store);
-
// create the viewer for it
mViewer = new TableViewer(mDeviceTable);
mViewer.setContentProvider(new ContentProvider());
@@ -357,20 +430,42 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
}
private void loadImages() {
- IImageLoader loader = DdmsPlugin.getImageLoader();
+ IImageLoader ddmsLoader = DdmsPlugin.getImageLoader();
Display display = DdmsPlugin.getDisplay();
+ IImageLoader adtLoader = AdtPlugin.getImageLoader();
if (mDeviceImage == null) {
- mDeviceImage = ImageHelper.loadImage(loader, display,
+ mDeviceImage = ImageHelper.loadImage(ddmsLoader, display,
"device.png", //$NON-NLS-1$
ICON_WIDTH, ICON_WIDTH,
display.getSystemColor(SWT.COLOR_RED));
}
if (mEmulatorImage == null) {
- mEmulatorImage = ImageHelper.loadImage(loader, display,
+ mEmulatorImage = ImageHelper.loadImage(ddmsLoader, display,
"emulator.png", ICON_WIDTH, ICON_WIDTH, //$NON-NLS-1$
display.getSystemColor(SWT.COLOR_BLUE));
}
+
+ if (mMatchImage == null) {
+ mMatchImage = ImageHelper.loadImage(adtLoader, display,
+ "match.png", //$NON-NLS-1$
+ ICON_WIDTH, ICON_WIDTH,
+ display.getSystemColor(SWT.COLOR_GREEN));
+ }
+
+ if (mNoMatchImage == null) {
+ mNoMatchImage = ImageHelper.loadImage(adtLoader, display,
+ "error.png", //$NON-NLS-1$
+ ICON_WIDTH, ICON_WIDTH,
+ display.getSystemColor(SWT.COLOR_RED));
+ }
+
+ if (mWarningImage == null) {
+ mWarningImage = ImageHelper.loadImage(adtLoader, display,
+ "warning.png", //$NON-NLS-1$
+ ICON_WIDTH, ICON_WIDTH,
+ display.getSystemColor(SWT.COLOR_YELLOW));
+ }
}
@@ -438,7 +533,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
* @param device the device that was updated.
* @param changeMask the mask indicating what changed.
*
- * @see IDeviceChangeListener#deviceChanged(Device)
+ * @see IDeviceChangeListener#deviceChanged(Device, int)
*/
public void deviceChanged(final Device device, int changeMask) {
if ((changeMask & (Device.CHANGE_STATE | Device.CHANGE_BUILD_INFO)) != 0) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java
index 5d3e349..68deec3 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java
@@ -80,9 +80,8 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
*/
public static final String ATTR_ACTIVITY = AdtPlugin.PLUGIN_ID + ".activity"; //$NON-NLS-1$
- /** Skin to be used to launch the emulator */
- public static final String ATTR_SKIN = AdtPlugin.PLUGIN_ID + ".skin"; //$NON-NLS-1$
-
+ public static final String ATTR_VM_NAME = AdtPlugin.PLUGIN_ID + ".vm"; //$NON-NLS-1$
+
public static final String ATTR_SPEED = AdtPlugin.PLUGIN_ID + ".speed"; //$NON-NLS-1$
/**
@@ -317,6 +316,10 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
1 /* code, unused */, "Can't find the project!", null /* exception */));
}
+ /**
+ * {@inheritDoc}
+ * @throws CoreException
+ */
@Override
public ILaunch getLaunch(ILaunchConfiguration configuration, String mode)
throws CoreException {
@@ -406,8 +409,6 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
/**
* Returns the name of the activity.
- * @param configuration
- * @return
*/
private String getActivityName(ILaunchConfiguration configuration) {
String empty = "";
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java
index c7b340c..f4f5281 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java
@@ -22,7 +22,9 @@ import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.ddms.DdmsPlugin;
import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.SdkConstants;
+import com.android.sdklib.vm.VmManager;
+import com.android.sdklib.vm.VmManager.VmInfo;
+import com.android.sdkuilib.VmSelector;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
@@ -70,6 +72,11 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
{ "UMTS", "umts" }, //$NON-NLS-2$
};
+ private Button mAutoTargetButton;
+ private Button mManualTargetButton;
+
+ private VmSelector mPreferredVmSelector;
+
private Combo mSpeedCombo;
private Combo mDelayCombo;
@@ -78,18 +85,10 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
private Text mEmulatorCLOptions;
- private Combo mSkinCombo;
-
- private Button mAutoTargetButton;
-
- private Button mManualTargetButton;
-
private Button mWipeDataButton;
private Button mNoBootAnimButton;
- private IAndroidTarget mTarget;
-
/**
* Returns the emulator ready speed option value.
* @param value The index of the combo selection.
@@ -147,6 +146,11 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
targetModeGroup.setLayout(layout);
targetModeGroup.setFont(font);
+ mManualTargetButton = new Button(targetModeGroup, SWT.RADIO);
+ mManualTargetButton.setText("Manual");
+ // Since there are only 2 radio buttons, we can put a listener on only one (they
+ // are both called on select and unselect event.
+
// add the radio button
mAutoTargetButton = new Button(targetModeGroup, SWT.RADIO);
mAutoTargetButton.setText("Automatic");
@@ -159,11 +163,16 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
}
});
- mManualTargetButton = new Button(targetModeGroup, SWT.RADIO);
- mManualTargetButton.setText("Manual");
- // Since there are only 2 radio buttons, we can put a listener on only
- // one (they
- // are both called on select and unselect event.
+ new Label(targetModeGroup, SWT.NONE).setText("Preferred VM");
+ VmInfo[] vms = new VmInfo[0];
+ mPreferredVmSelector = new VmSelector(targetModeGroup, vms,
+ false /*allowMultipleSelection*/);
+ mPreferredVmSelector.setSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ updateLaunchConfigurationDialog();
+ }
+ });
// emulator size
mEmulatorOptionsGroup = new Group(topComp, SWT.NONE);
@@ -174,17 +183,6 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
mEmulatorOptionsGroup.setLayout(layout);
mEmulatorOptionsGroup.setFont(font);
- new Label(mEmulatorOptionsGroup, SWT.NONE).setText("Screen Size:");
-
- mSkinCombo = new Combo(mEmulatorOptionsGroup, SWT.READ_ONLY);
- mSkinCombo.addSelectionListener(new SelectionAdapter() {
- // called when selection changes
- @Override
- public void widgetSelected(SelectionEvent e) {
- updateLaunchConfigurationDialog();
- }
- });
-
// network options
new Label(mEmulatorOptionsGroup, SWT.NONE).setText("Network Speed:");
@@ -279,8 +277,9 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
* @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration)
*/
public void initializeFrom(ILaunchConfiguration configuration) {
- boolean value = LaunchConfigDelegate.DEFAULT_TARGET_MODE; // true ==
- // automatic
+ VmManager vmManager = Sdk.getCurrent().getVmManager();
+
+ boolean value = LaunchConfigDelegate.DEFAULT_TARGET_MODE; // true == automatic
try {
value = configuration.getAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE, value);
} catch (CoreException e) {
@@ -290,11 +289,12 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
mManualTargetButton.setSelection(!value);
// look for the project name to get its target.
- String projectName = "";
+ String stringValue = "";
try {
- projectName = configuration.getAttribute(
- IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, projectName);
+ stringValue = configuration.getAttribute(
+ IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, stringValue);
} catch (CoreException ce) {
+ // let's not do anything here, we'll use the default value
}
IProject project = null;
@@ -304,25 +304,41 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
if (projects != null) {
// look for the project whose name we read from the configuration.
for (IJavaProject p : projects) {
- if (p.getElementName().equals(projectName)) {
+ if (p.getElementName().equals(stringValue)) {
project = p.getProject();
break;
}
}
}
- mSkinCombo.removeAll();
+ // update the VM list
+ VmInfo[] vms = null;
+ if (vmManager != null) {
+ vms = vmManager.getVms();
+ }
+
+ IAndroidTarget projectTarget = null;
if (project != null) {
- mTarget = Sdk.getCurrent().getTarget(project);
- if (mTarget != null) {
- String[] skins = mTarget.getSkins();
- if (skins != null) {
- for (String skin : skins) {
- mSkinCombo.add(skin);
- }
- mSkinCombo.pack();
- }
- }
+ projectTarget = Sdk.getCurrent().getTarget(project);
+ } else {
+ vms = null; // no project? we don't want to display any "compatible" VMs.
+ }
+
+ mPreferredVmSelector.setVms(vms, projectTarget);
+
+ stringValue = "";
+ try {
+ stringValue = configuration.getAttribute(LaunchConfigDelegate.ATTR_VM_NAME,
+ stringValue);
+ } catch (CoreException e) {
+ // let's not do anything here, we'll use the default value
+ }
+
+ if (stringValue != null && stringValue.length() > 0 && vmManager != null) {
+ VmInfo targetVm = vmManager.getVm(stringValue);
+ mPreferredVmSelector.setSelection(targetVm);
+ } else {
+ mPreferredVmSelector.setSelection(null);
}
value = LaunchConfigDelegate.DEFAULT_WIPE_DATA;
@@ -342,23 +358,6 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
mNoBootAnimButton.setSelection(value);
int index = -1;
- try {
- String skin = configuration.getAttribute(LaunchConfigDelegate.ATTR_SKIN, (String)null);
- if (skin == null) {
- skin = SdkConstants.SKIN_DEFAULT;
- }
-
- index = getSkinIndex(skin);
- } catch (CoreException e) {
- index = getSkinIndex(SdkConstants.SKIN_DEFAULT);
- }
-
- if (index == -1) {
- mSkinCombo.select(0);
- updateLaunchConfigurationDialog();
- } else {
- mSkinCombo.select(index);
- }
index = LaunchConfigDelegate.DEFAULT_SPEED;
try {
@@ -405,8 +404,12 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
public void performApply(ILaunchConfigurationWorkingCopy configuration) {
configuration.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
mAutoTargetButton.getSelection());
- configuration.setAttribute(LaunchConfigDelegate.ATTR_SKIN,
- getSkinNameByIndex(mSkinCombo.getSelectionIndex()));
+ VmInfo vm = mPreferredVmSelector.getFirstSelected();
+ if (vm != null) {
+ configuration.setAttribute(LaunchConfigDelegate.ATTR_VM_NAME, vm.getName());
+ } else {
+ configuration.setAttribute(LaunchConfigDelegate.ATTR_VM_NAME, (String)null);
+ }
configuration.setAttribute(LaunchConfigDelegate.ATTR_SPEED,
mSpeedCombo.getSelectionIndex());
configuration.setAttribute(LaunchConfigDelegate.ATTR_DELAY,
@@ -425,8 +428,6 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
configuration.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
LaunchConfigDelegate.DEFAULT_TARGET_MODE);
- configuration.setAttribute(LaunchConfigDelegate.ATTR_SKIN,
- SdkConstants.SKIN_DEFAULT);
configuration.setAttribute(LaunchConfigDelegate.ATTR_SPEED,
LaunchConfigDelegate.DEFAULT_SPEED);
configuration.setAttribute(LaunchConfigDelegate.ATTR_DELAY,
@@ -440,33 +441,4 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
String emuOptions = store.getString(AdtPlugin.PREFS_EMU_OPTIONS);
configuration.setAttribute(LaunchConfigDelegate.ATTR_COMMANDLINE, emuOptions);
}
-
- private String getSkinNameByIndex(int index) {
- if (mTarget != null && index > 0) {
- String[] skins = mTarget.getSkins();
- if (skins != null && index < skins.length) {
- return skins[index];
- }
- }
-
- return null;
- }
-
- private int getSkinIndex(String name) {
- if (mTarget != null) {
- String[] skins = mTarget.getSkins();
- if (skins != null) {
- int index = 0;
- for (String skin : skins) {
- if (skin.equalsIgnoreCase(name)) {
- return index;
- }
- index++;
- }
- }
- }
-
- return -1;
- }
-
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/MainLaunchConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/MainLaunchConfigTab.java
index af7f2bb..6a40ed0 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/MainLaunchConfigTab.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/MainLaunchConfigTab.java
@@ -398,11 +398,13 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
config.setMappedResources(resources);
}
- /** Loads the ui with the activities of the specified project, and store the
- * activities in <code>mActivities</code>
+ /**
+ * Loads the ui with the activities of the specified project, and stores the
+ * activities in <code>mActivities</code>.
+ * <p/>
* First activity is selected by default if present.
- * @param project The project to load the activities from
- * @return The array of activities or null if none could be found.
+ *
+ * @param project The project to load the activities from.
*/
private void loadActivities(IProject project) {
if (project != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/preferences/BuildPreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/preferences/BuildPreferencePage.java
index 434269c..e64c2f4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/preferences/BuildPreferencePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/preferences/BuildPreferencePage.java
@@ -199,7 +199,7 @@ public class BuildPreferencePage extends FieldEditorPreferencePage implements
*/
private void handleException(Throwable t) {
String msg = t.getMessage();
- if (t == null) {
+ if (msg == null) {
Throwable cause = t.getCause();
if (cause != null) {
handleException(cause);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FolderDecorator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FolderDecorator.java
index 1ca89cd..7fc3318 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FolderDecorator.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FolderDecorator.java
@@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.project;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.common.AndroidConstants;
+import com.android.sdklib.SdkConstants;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
@@ -53,13 +54,13 @@ public class FolderDecorator implements ILightweightLabelDecorator {
// check the folder is directly under the project.
if (folder.getParent().getType() == IResource.PROJECT) {
String name = folder.getName();
- if (name.equals(AndroidConstants.FD_ASSETS)) {
+ if (name.equals(SdkConstants.FD_ASSETS)) {
decorate(decoration, " [Android assets]");
decoration.addOverlay(mDescriptor, IDecoration.TOP_RIGHT);
- } else if (name.equals(AndroidConstants.FD_RESOURCES)) {
+ } else if (name.equals(SdkConstants.FD_RESOURCES)) {
decorate(decoration, " [Android resources]");
decoration.addOverlay(mDescriptor, IDecoration.TOP_RIGHT);
- } else if (name.equals(AndroidConstants.FD_NATIVE_LIBS)) {
+ } else if (name.equals(SdkConstants.FD_NATIVE_LIBS)) {
decorate(decoration, " [Native Libraries]");
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ExportWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ExportWizard.java
index de4b339..399eac9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ExportWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ExportWizard.java
@@ -493,7 +493,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
}
// no more cause and still no message. display the first exception.
- return cause.getClass().getCanonicalName();
+ return t.getClass().getCanonicalName();
}
return message;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ProjectCheckPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ProjectCheckPage.java
index 3614be3..e161e18 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ProjectCheckPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/export/ProjectCheckPage.java
@@ -256,7 +256,6 @@ final class ProjectCheckPage extends ExportWizardPage {
/**
* Checks the parameters for correctness, and update the error message and buttons.
- * @return the current IProject of this launch config.
*/
private void handleProjectNameChange() {
setPageComplete(false);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java
index 2cafa01..4da216c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java
@@ -28,8 +28,12 @@ import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.ClasspathContainerInitializer;
import org.eclipse.jdt.core.IAccessRule;
import org.eclipse.jdt.core.IClasspathAttribute;
@@ -125,7 +129,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
*/
private static IClasspathContainer allocateAndroidContainer(String containerId,
IJavaProject javaProject) {
- IProject iProject = javaProject.getProject();
+ final IProject iProject = javaProject.getProject();
// remove potential MARKER_TARGETs.
try {
@@ -139,7 +143,9 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
}
- // first we check if the SDK has been loaded
+ // First we check if the SDK has been loaded.
+ // By passing the javaProject to getSdkLoadStatus(), we ensure that, should the SDK
+ // not be loaded yet, the classpath container will be resolved again once the SDK is loaded.
boolean sdkIsLoaded = AdtPlugin.getDefault().getSdkLoadStatus(javaProject) ==
LoadStatus.LOADED;
@@ -172,8 +178,14 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
String message = null;
boolean outputToConsole = true;
if (hashString == null || hashString.length() == 0) {
- message = String.format(
- "Project has no target set. Edit the project properties to set one.");
+ // if there is no hash string we only show this if the SDK is loaded.
+ // For a project opened at start-up with no target, this would be displayed twice,
+ // once when the project is opened, and once after the SDK has finished loading.
+ // By testing the sdk is loaded, we only show this once in the console.
+ if (sdkIsLoaded) {
+ message = String.format(
+ "Project has no target set. Edit the project properties to set one.");
+ }
} else if (sdkIsLoaded) {
message = String.format(
"Unable to resolve target '%s'", hashString);
@@ -187,23 +199,41 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
// and it's expected. (we do keep the error marker though).
outputToConsole = false;
}
-
- // log the error and put the marker on the project
- if (outputToConsole) {
- AdtPlugin.printBuildToConsole(AdtConstants.BUILD_ALWAYS, iProject, message);
- }
- IMarker marker = BaseProjectHelper.addMarker(iProject, AdtConstants.MARKER_TARGET, message,
- IMarker.SEVERITY_ERROR);
- // add a marker priority as this is an more important error than the error that will
- // spring from the lack of library
- try {
- marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
- } catch (CoreException e) {
- // just log the error
- AdtPlugin.log(e, "Error changing target marker priority.");
+ if (message != null) {
+ // log the error and put the marker on the project if we can.
+ if (outputToConsole) {
+ AdtPlugin.printBuildToConsole(AdtConstants.BUILD_ALWAYS, iProject, message);
+ }
+
+ try {
+ BaseProjectHelper.addMarker(iProject, AdtConstants.MARKER_TARGET, message, -1,
+ IMarker.SEVERITY_ERROR, IMarker.PRIORITY_HIGH);
+ } catch (CoreException e) {
+ // In some cases, the workspace may be locked for modification when we pass here.
+ // We schedule a new job to put the marker after.
+ final String fmessage = message;
+ Job markerJob = new Job("Android SDK: Resolving error markers") {
+ @SuppressWarnings("unchecked")
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ BaseProjectHelper.addMarker(iProject, AdtConstants.MARKER_TARGET,
+ fmessage, -1, IMarker.SEVERITY_ERROR, IMarker.PRIORITY_HIGH);
+ } catch (CoreException e2) {
+ return e2.getStatus();
+ }
+
+ return Status.OK_STATUS;
+ }
+ };
+
+ // build jobs are run after other interactive jobs
+ markerJob.setPriority(Job.BUILD);
+ markerJob.schedule();
+ }
}
-
+
// return a dummy container to replace the one we may have had before.
return new IClasspathContainer() {
public IClasspathEntry[] getClasspathEntries() {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidJarLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidJarLoader.java
index fad4f19..1f6ebf1 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidJarLoader.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidJarLoader.java
@@ -37,7 +37,8 @@ import javax.management.InvalidAttributeValueException;
public class AndroidJarLoader extends ClassLoader implements IAndroidClassLoader {
/**
- * Wrapper around a {@link Class} to provide the methods of {@link IClassDescriptor}.
+ * Wrapper around a {@link Class} to provide the methods of
+ * {@link IAndroidClassLoader.IClassDescriptor}.
*/
public final static class ClassWrapper implements IClassDescriptor {
private Class<?> mClass;
@@ -416,7 +417,7 @@ public class AndroidJarLoader extends ClassLoader implements IAndroidClassLoader
}
/**
- * Returns a {@link IClass} by its fully-qualified name.
+ * Returns a {@link IAndroidClassLoader.IClassDescriptor} by its fully-qualified name.
* @param className the fully-qualified name of the class to return.
* @throws ClassNotFoundException
*/
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java
index 232b9e8..dfe876f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java
@@ -82,11 +82,8 @@ public final class AndroidTargetParser {
/**
* Parses the framework, collects all interesting information and stores them in the
- * {@link FrameworkResourceManager} given to the constructor.
+ * {@link IAndroidTarget} given to the constructor.
*
- * @param osSdkPath the OS path of the SDK directory.
- * @param resourceManager the {@link FrameworkResourceManager} that will store the parsed
- * resources.
* @param monitor A progress monitor. Can be null. Caller is responsible for calling done.
* @return True if the SDK path was valid and parsing has been attempted.
*/
@@ -400,7 +397,6 @@ public final class AndroidTargetParser {
* Loads and collects the action and category default values from the framework.
* The values are added to the <code>actions</code> and <code>categories</code> lists.
*
- * @param osLibPath The OS path to the SDK tools/lib folder, ending with a separator.
* @param activityActions the list which will receive the activity action values.
* @param broadcastActions the list which will receive the broadcast action values.
* @param serviceActions the list which will receive the service action values.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/IAndroidClassLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/IAndroidClassLoader.java
index 50d319e..35057d1 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/IAndroidClassLoader.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/IAndroidClassLoader.java
@@ -64,7 +64,7 @@ public interface IAndroidClassLoader {
throws IOException, InvalidAttributeValueException, ClassFormatError;
/**
- * Returns a {@link IClass} by its fully-qualified name.
+ * Returns a {@link IClassDescriptor} by its fully-qualified name.
* @param className the fully-qualified name of the class to return.
* @throws ClassNotFoundException
*/
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
index 3b9d10e..19f8f45 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
@@ -18,17 +18,17 @@ package com.android.ide.eclipse.adt.sdk;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerInitializer;
+import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
import com.android.sdklib.project.ProjectProperties;
+import com.android.sdklib.project.ProjectProperties.PropertyType;
+import com.android.sdklib.vm.VmManager;
import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IProjectDescription;
-import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
@@ -46,21 +46,20 @@ import java.util.HashMap;
* To start using an SDK, call {@link #loadSdk(String)} which returns the instance of
* the Sdk object.
*
- * To get the list of platforms present in the SDK, call {@link #getPlatforms()}.
- * To get the list of add-ons present in the SDK, call {@link #getAddons()}.
- *
+ * To get the list of platforms or add-ons present in the SDK, call {@link #getTargets()}.
*/
public class Sdk {
- private final static String PROPERTY_PROJECT_TARGET = "androidTarget"; //$NON-NLS-1$
-
private static Sdk sCurrentSdk = null;
private final SdkManager mManager;
+ private final VmManager mVmManager;
+
private final HashMap<IProject, IAndroidTarget> mProjectMap =
new HashMap<IProject, IAndroidTarget>();
private final HashMap<IAndroidTarget, AndroidTargetData> mTargetMap =
new HashMap<IAndroidTarget, AndroidTargetData>();
private final String mDocBaseUrl;
+
/**
* Loads an SDK and returns an {@link Sdk} object if success.
@@ -74,18 +73,33 @@ public class Sdk {
final ArrayList<String> logMessages = new ArrayList<String>();
ISdkLog log = new ISdkLog() {
- public void error(String errorFormat, Object... arg) {
- logMessages.add(String.format(errorFormat, arg));
+ public void error(Throwable throwable, String errorFormat, Object... arg) {
+ if (errorFormat != null) {
+ logMessages.add(String.format(errorFormat, arg));
+ }
+
+ if (throwable != null) {
+ logMessages.add(throwable.getMessage());
+ }
}
public void warning(String warningFormat, Object... arg) {
logMessages.add(String.format(warningFormat, arg));
}
+ public void printf(String msgFormat, Object... arg) {
+ logMessages.add(String.format(msgFormat, arg));
+ }
};
// get an SdkManager object for the location
SdkManager manager = SdkManager.createManager(sdkLocation, log);
if (manager != null) {
- sCurrentSdk = new Sdk(manager);
+ VmManager vmManager = null;
+ try {
+ vmManager = new VmManager(manager, log);
+ } catch (AndroidLocationException e) {
+ log.error(e, "Error parsing the VMs");
+ }
+ sCurrentSdk = new Sdk(manager, vmManager);
return sCurrentSdk;
} else {
StringBuilder sb = new StringBuilder("Error Loading the SDK:\n");
@@ -106,6 +120,13 @@ public class Sdk {
}
/**
+ * Returns the location (OS path) of the current SDK.
+ */
+ public String getSdkLocation() {
+ return mManager.getLocation();
+ }
+
+ /**
* Returns the URL to the local documentation.
* Can return null if no documentation is found in the current SDK.
*
@@ -181,7 +202,8 @@ public class Sdk {
*/
public static String getProjectTargetHashString(IProject project) {
// load the default.properties from the project folder.
- ProjectProperties properties = ProjectProperties.load(project.getLocation().toOSString());
+ ProjectProperties properties = ProjectProperties.load(project.getLocation().toOSString(),
+ PropertyType.DEFAULT);
if (properties == null) {
AdtPlugin.log(IStatus.ERROR, "Failed to load properties file for project '%s'",
project.getName());
@@ -200,10 +222,12 @@ public class Sdk {
public static void setProjectTargetHashString(IProject project, String targetHashString) {
// because we don't want to erase other properties from default.properties, we first load
// them
- ProjectProperties properties = ProjectProperties.load(project.getLocation().toOSString());
+ ProjectProperties properties = ProjectProperties.load(project.getLocation().toOSString(),
+ PropertyType.DEFAULT);
if (properties == null) {
// doesn't exist yet? we create it.
- properties = ProjectProperties.create(project.getLocation().toOSString());
+ properties = ProjectProperties.create(project.getLocation().toOSString(),
+ PropertyType.DEFAULT);
}
// add/change the target hash string.
@@ -218,7 +242,7 @@ public class Sdk {
}
}
/**
- * Return the {@link PlatformData} for a given {@link IAndroidTarget}.
+ * Return the {@link AndroidTargetData} for a given {@link IAndroidTarget}.
*/
public AndroidTargetData getTargetData(IAndroidTarget target) {
synchronized (mTargetMap) {
@@ -226,8 +250,17 @@ public class Sdk {
}
}
- private Sdk(SdkManager manager) {
+ /**
+ * Returns the {@link VmManager}. If the VmManager failed to parse the VM folder, this could
+ * be <code>null</code>.
+ */
+ public VmManager getVmManager() {
+ return mVmManager;
+ }
+
+ private Sdk(SdkManager manager, VmManager vmManager) {
mManager = manager;
+ mVmManager = vmManager;
// pre-compute some paths
mDocBaseUrl = getDocumentationBaseUrl(mManager.getLocation() +
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/WidgetClassLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/WidgetClassLoader.java
index 8db09f2..0e60f8a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/WidgetClassLoader.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/WidgetClassLoader.java
@@ -323,7 +323,7 @@ public final class WidgetClassLoader implements IAndroidClassLoader {
}
/**
- * Returns a {@link IClass} by its fully-qualified name.
+ * Returns a {@link IAndroidClassLoader.IClassDescriptor} by its fully-qualified name.
* @param className the fully-qualified name of the class to return.
* @throws ClassNotFoundException
*/
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java
index 8044bcb..7fc94ef 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java
@@ -26,6 +26,7 @@ import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestHelper;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
import com.android.sdkuilib.SdkTargetSelector;
import org.eclipse.core.filesystem.URIUtil;
@@ -64,9 +65,11 @@ import java.util.regex.Pattern;
* NewAndroidProjectCreationPage is a project creation page that provides the
* following fields:
* <ul>
+ * <li> Project name
+ * <li> SDK Target
+ * <li> Application name
* <li> Package name
* <li> Activity name
- * <li> Location of the SDK
* </ul>
* Note: this class is public so that it can be accessed from unit tests.
* It is however an internal class. Its API may change without notice.
@@ -93,13 +96,14 @@ public class NewProjectCreationPage extends WizardPage {
private static final Pattern sProjectNamePattern = Pattern.compile("^[\\w][\\w. -]*$"); //$NON-NLS-1$
/** Last user-browsed location, static so that it be remembered for the whole session */
private static String sCustomLocationOsPath = ""; //$NON-NLS-1$
+ private static boolean sAutoComputeCustomLocation = true;
private final int MSG_NONE = 0;
private final int MSG_WARNING = 1;
private final int MSG_ERROR = 2;
private String mUserPackageName = ""; //$NON-NLS-1$
- private String mUserActivityName = ""; //$NON-NLS-1$
+ private String mUserActivityName = ""; //$NON-NLS-1$
private boolean mUserCreateActivityCheck = INITIAL_CREATE_ACTIVITY;
private String mSourceFolder = ""; //$NON-NLS-1$
@@ -114,6 +118,8 @@ public class NewProjectCreationPage extends WizardPage {
private Text mLocationPathField;
private Button mBrowseButton;
private Button mCreateActivityCheck;
+ private Text mMinSdkVersionField;
+ private SdkTargetSelector mSdkTargetSelector;
private boolean mInternalLocationPathUpdate;
protected boolean mInternalProjectNameUpdate;
@@ -122,7 +128,7 @@ public class NewProjectCreationPage extends WizardPage {
private boolean mInternalActivityNameUpdate;
protected boolean mProjectNameModifiedByUser;
protected boolean mApplicationNameModifiedByUser;
- private SdkTargetSelector mSdkTargetSelector;
+ private boolean mInternalMinSdkVersionUpdate;
/**
@@ -133,13 +139,6 @@ public class NewProjectCreationPage extends WizardPage {
public NewProjectCreationPage(String pageName) {
super(pageName);
setPageComplete(false);
- if (sCustomLocationOsPath == null ||
- sCustomLocationOsPath.length() == 0 ||
- !new File(sCustomLocationOsPath).isDirectory()) {
- // FIXME location of samples is pretty much impossible here.
- //sCustomLocationOsPath = AdtPlugin.getOsSdkSamplesFolder();
- sCustomLocationOsPath = File.listRoots()[0].getAbsolutePath();
- }
}
// --- Getters used by NewProjectWizard ---
@@ -170,6 +169,11 @@ public class NewProjectCreationPage extends WizardPage {
return mActivityNameField == null ? INITIAL_NAME : mActivityNameField.getText().trim();
}
+ /** Returns the value of the min sdk version field with spaces trimmed. */
+ public String getMinSdkVersion() {
+ return mMinSdkVersionField == null ? "" : mMinSdkVersionField.getText().trim();
+ }
+
/** Returns the value of the application name field with spaces trimmed. */
public String getApplicationName() {
// Return the name of the activity as default application name.
@@ -200,7 +204,7 @@ public class NewProjectCreationPage extends WizardPage {
* "src" constant. */
public String getSourceFolder() {
if (isNewProject() || mSourceFolder == null || mSourceFolder.length() == 0) {
- return AndroidConstants.FD_SOURCES;
+ return SdkConstants.FD_SOURCES;
} else {
return mSourceFolder;
}
@@ -389,9 +393,16 @@ public class NewProjectCreationPage extends WizardPage {
}
mSdkTargetSelector = new SdkTargetSelector(group, targets, false /*multi-selection*/);
+
+ // If there's only one target, select it
+ if (targets != null && targets.length == 1) {
+ mSdkTargetSelector.setSelection(targets[0]);
+ }
+
mSdkTargetSelector.setSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
+ updateLocationPathField(null);
setPageComplete(validatePage());
}
});
@@ -506,6 +517,25 @@ public class NewProjectCreationPage extends WizardPage {
onActivityNameFieldModified();
}
});
+
+ // min sdk version label
+ label = new Label(group, SWT.NONE);
+ label.setText("Min SDK Version:");
+ label.setFont(parent.getFont());
+ label.setToolTipText("The minimum SDK version number that the application requires. Must be an integer > 0. It can be empty.");
+
+ // min sdk version entry field
+ mMinSdkVersionField = new Text(group, SWT.BORDER);
+ data = new GridData(GridData.FILL_HORIZONTAL);
+ label.setToolTipText("The minimum SDK version number that the application requires. Must be an integer > 0. It can be empty.");
+ mMinSdkVersionField.setLayoutData(data);
+ mMinSdkVersionField.setFont(parent.getFont());
+ mMinSdkVersionField.addListener(SWT.Modify, new Listener() {
+ public void handleEvent(Event event) {
+ onMinSdkVersionFieldModified();
+ setPageComplete(validatePage());
+ }
+ });
}
@@ -588,7 +618,29 @@ public class NewProjectCreationPage extends WizardPage {
mInternalLocationPathUpdate = true;
if (custom_location) {
if (abs_dir != null) {
+ // We get here if the user selected a directory with the "Browse" button.
+ // Disable auto-compute of the custom location unless the user selected
+ // the exact same path.
+ sAutoComputeCustomLocation = sAutoComputeCustomLocation &&
+ abs_dir.equals(sCustomLocationOsPath);
sCustomLocationOsPath = TextProcessor.process(abs_dir);
+ } else if (sAutoComputeCustomLocation ||
+ !new File(sCustomLocationOsPath).isDirectory()) {
+ // By default select the samples directory of the current target
+ IAndroidTarget target = getSdkTarget();
+ if (target != null) {
+ sCustomLocationOsPath = target.getPath(IAndroidTarget.SAMPLES);
+ }
+
+ // If we don't have a target, select the base directory of the
+ // "universal sdk". If we don't even have that, use a root drive.
+ if (sCustomLocationOsPath == null || sCustomLocationOsPath.length() == 0) {
+ if (Sdk.getCurrent() != null) {
+ sCustomLocationOsPath = Sdk.getCurrent().getSdkLocation();
+ } else {
+ sCustomLocationOsPath = File.listRoots()[0].getAbsolutePath();
+ }
+ }
}
if (!mLocationPathField.getText().equals(sCustomLocationOsPath)) {
mLocationPathField.setText(sCustomLocationOsPath);
@@ -615,8 +667,13 @@ public class NewProjectCreationPage extends WizardPage {
private void onLocationPathFieldModified() {
if (!mInternalLocationPathUpdate) {
// When the updates doesn't come from updateLocationPathField, it must be the user
- // editing the field manually, in which case we want to save the value internally.
- sCustomLocationOsPath = getLocationPathFieldValue();
+ // editing the field manually, in which case we want to save the value internally
+ // and we disable auto-compute of the custom location (to avoid overriding the user
+ // value)
+ String newPath = getLocationPathFieldValue();
+ sAutoComputeCustomLocation = sAutoComputeCustomLocation &&
+ newPath.equals(sCustomLocationOsPath);
+ sCustomLocationOsPath = newPath;
extractNamesFromAndroidManifest();
setPageComplete(validatePage());
}
@@ -665,6 +722,31 @@ public class NewProjectCreationPage extends WizardPage {
}
/**
+ * Called when the min sdk version field has been modified.
+ *
+ * Ignore the internal modifications. When modified by the user, try to match
+ * a target with the same API level.
+ */
+ private void onMinSdkVersionFieldModified() {
+ if (mInternalMinSdkVersionUpdate) {
+ return;
+ }
+
+ try {
+ int version = Integer.parseInt(getMinSdkVersion());
+
+ for (IAndroidTarget target : mSdkTargetSelector.getTargets()) {
+ if (target.getApiVersionNumber() == version) {
+ mSdkTargetSelector.setSelection(target);
+ break;
+ }
+ }
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+ }
+
+ /**
* Called when the radio buttons are changed between the "create new project" and the
* "use existing source" mode. This reverts the fields to whatever the user manually
* entered before.
@@ -697,89 +779,132 @@ public class NewProjectCreationPage extends WizardPage {
* can actually be found in the custom user directory.
*/
private void extractNamesFromAndroidManifest() {
- if (!isNewProject()) {
- File f = new File(getProjectLocation());
- if (f.isDirectory()) {
- Path path = new Path(f.getPath());
- String osPath = path.append(AndroidConstants.FN_ANDROID_MANIFEST).toOSString();
- AndroidManifestHelper manifest = new AndroidManifestHelper(osPath);
- if (manifest.exists()) {
- String packageName = null;
- String activityName = null;
- try {
- packageName = manifest.getPackageName();
- activityName = manifest.getActivityName(1);
- } catch (Exception e) {
- // pass
- }
+ if (isNewProject()) {
+ return;
+ }
+ String projectLocation = getProjectLocation();
+ File f = new File(projectLocation);
+ if (!f.isDirectory()) {
+ return;
+ }
- if (packageName != null && packageName.length() > 0) {
- mPackageNameField.setText(packageName);
- }
+ Path path = new Path(f.getPath());
+ String osPath = path.append(AndroidConstants.FN_ANDROID_MANIFEST).toOSString();
+ AndroidManifestHelper manifest = new AndroidManifestHelper(osPath);
+ if (!manifest.exists()) {
+ return;
+ }
+
+ String packageName = null;
+ String activityName = null;
+ String minSdkVersion = null;
+ try {
+ packageName = manifest.getPackageName();
+ activityName = manifest.getActivityName(1);
+ minSdkVersion = manifest.getMinSdkVersion();
+ } catch (Exception e) {
+ // ignore exceptions
+ }
- if (activityName != null && activityName.length() > 0) {
- mInternalActivityNameUpdate = true;
- mInternalCreateActivityUpdate = true;
- mActivityNameField.setText(activityName);
- mCreateActivityCheck.setSelection(true);
- mInternalCreateActivityUpdate = false;
- mInternalActivityNameUpdate = false;
-
- // If project name and application names are empty, use the activity
- // name as a default. If the activity name has dots, it's a part of a
- // package specification and only the last identifier must be used.
- if (activityName.indexOf('.') != -1) {
- String[] ids = activityName.split(AndroidConstants.RE_DOT);
- activityName = ids[ids.length - 1];
- }
- if (mProjectNameField.getText().length() == 0 ||
- !mProjectNameModifiedByUser) {
- mInternalProjectNameUpdate = true;
- mProjectNameField.setText(activityName);
- mInternalProjectNameUpdate = false;
- }
- if (mApplicationNameField.getText().length() == 0 ||
- !mApplicationNameModifiedByUser) {
- mInternalApplicationNameUpdate = true;
- mApplicationNameField.setText(activityName);
- mInternalApplicationNameUpdate = false;
- }
- } else {
- mInternalActivityNameUpdate = true;
- mInternalCreateActivityUpdate = true;
- mActivityNameField.setText("");
- mCreateActivityCheck.setSelection(false);
- mInternalCreateActivityUpdate = false;
- mInternalActivityNameUpdate = false;
-
- // There is no activity name to use to fill in the project and application
- // name. However if there's a package name, we can use this as a base.
- if (packageName != null && packageName.length() > 0) {
- // Package name is a java identifier, so it's most suitable for
- // an application name.
-
- if (mApplicationNameField.getText().length() == 0 ||
- !mApplicationNameModifiedByUser) {
- mInternalApplicationNameUpdate = true;
- mApplicationNameField.setText(packageName);
- mInternalApplicationNameUpdate = false;
- }
-
- // For the project name, remove any dots
- packageName = packageName.replace('.', '_');
- if (mProjectNameField.getText().length() == 0 ||
- !mProjectNameModifiedByUser) {
- mInternalProjectNameUpdate = true;
- mProjectNameField.setText(packageName);
- mInternalProjectNameUpdate = false;
- }
-
- }
+
+ if (packageName != null && packageName.length() > 0) {
+ mPackageNameField.setText(packageName);
+ }
+
+ if (activityName != null && activityName.length() > 0) {
+ mInternalActivityNameUpdate = true;
+ mInternalCreateActivityUpdate = true;
+ mActivityNameField.setText(activityName);
+ mCreateActivityCheck.setSelection(true);
+ mInternalCreateActivityUpdate = false;
+ mInternalActivityNameUpdate = false;
+
+ // If project name and application names are empty, use the activity
+ // name as a default. If the activity name has dots, it's a part of a
+ // package specification and only the last identifier must be used.
+ if (activityName.indexOf('.') != -1) {
+ String[] ids = activityName.split(AndroidConstants.RE_DOT);
+ activityName = ids[ids.length - 1];
+ }
+ if (mProjectNameField.getText().length() == 0 ||
+ !mProjectNameModifiedByUser) {
+ mInternalProjectNameUpdate = true;
+ mProjectNameField.setText(activityName);
+ mInternalProjectNameUpdate = false;
+ }
+ if (mApplicationNameField.getText().length() == 0 ||
+ !mApplicationNameModifiedByUser) {
+ mInternalApplicationNameUpdate = true;
+ mApplicationNameField.setText(activityName);
+ mInternalApplicationNameUpdate = false;
+ }
+ } else {
+ mInternalActivityNameUpdate = true;
+ mInternalCreateActivityUpdate = true;
+ mActivityNameField.setText(""); //$NON-NLS-1$
+ mCreateActivityCheck.setSelection(false);
+ mInternalCreateActivityUpdate = false;
+ mInternalActivityNameUpdate = false;
+
+ // There is no activity name to use to fill in the project and application
+ // name. However if there's a package name, we can use this as a base.
+ if (packageName != null && packageName.length() > 0) {
+ // Package name is a java identifier, so it's most suitable for
+ // an application name.
+
+ if (mApplicationNameField.getText().length() == 0 ||
+ !mApplicationNameModifiedByUser) {
+ mInternalApplicationNameUpdate = true;
+ mApplicationNameField.setText(packageName);
+ mInternalApplicationNameUpdate = false;
+ }
+
+ // For the project name, remove any dots
+ packageName = packageName.replace('.', '_');
+ if (mProjectNameField.getText().length() == 0 ||
+ !mProjectNameModifiedByUser) {
+ mInternalProjectNameUpdate = true;
+ mProjectNameField.setText(packageName);
+ mInternalProjectNameUpdate = false;
+ }
+
+ }
+ }
+
+ // Select the target matching the manifest's sdk, if any
+ boolean foundTarget = false;
+ if (minSdkVersion != null) {
+ try {
+ int sdkVersion = Integer.parseInt(minSdkVersion);
+
+ for (IAndroidTarget target : mSdkTargetSelector.getTargets()) {
+ if (target.getApiVersionNumber() == sdkVersion) {
+ mSdkTargetSelector.setSelection(target);
+ foundTarget = true;
+ break;
}
}
+ } catch(NumberFormatException e) {
+ // ignore
+ }
+ }
+
+ if (!foundTarget) {
+ for (IAndroidTarget target : mSdkTargetSelector.getTargets()) {
+ if (projectLocation.startsWith(target.getLocation())) {
+ mSdkTargetSelector.setSelection(target);
+ foundTarget = true;
+ break;
+ }
}
}
+
+ if (!foundTarget) {
+ mInternalMinSdkVersionUpdate = true;
+ mMinSdkVersionField.setText(minSdkVersion == null ? "" : minSdkVersion); //$NON-NLS-1$
+ mInternalMinSdkVersionUpdate = false;
+ }
}
/**
@@ -805,6 +930,9 @@ public class NewProjectCreationPage extends WizardPage {
status |= validateActivityField();
}
if ((status & MSG_ERROR) == 0) {
+ status |= validateMinSdkVersionField();
+ }
+ if ((status & MSG_ERROR) == 0) {
status |= validateSourceFolder();
}
if (status == MSG_NONE) {
@@ -950,6 +1078,38 @@ public class NewProjectCreationPage extends WizardPage {
}
/**
+ * Validates the sdk target choice.
+ *
+ * @return The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE.
+ */
+ private int validateMinSdkVersionField() {
+
+ // If the min sdk version is empty, it is always accepted.
+ if (getMinSdkVersion().length() == 0) {
+ return MSG_NONE;
+ }
+
+ int version = -1;
+ try {
+ // If not empty, it must be a valid integer > 0
+ version = Integer.parseInt(getMinSdkVersion());
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+
+ if (version < 1) {
+ return setStatus("Min SDK Version must be an integer > 0.", MSG_ERROR);
+ }
+
+ if (getSdkTarget() != null && getSdkTarget().getApiVersionNumber() != version) {
+ return setStatus("The API level for the selected SDK target does not match the Min SDK version.",
+ MSG_WARNING);
+ }
+
+ return MSG_NONE;
+ }
+
+ /**
* Validates the activity name field.
*
* @return The wizard message type, one of MSG_ERROR, MSG_WARNING or MSG_NONE.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java
index a582217..607159a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java
@@ -22,6 +22,7 @@ import com.android.ide.eclipse.adt.project.ProjectHelper;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
@@ -85,32 +86,36 @@ public class NewProjectWizard extends Wizard implements INewWizard {
private static final String PARAM_IS_NEW_PROJECT = "IS_NEW_PROJECT"; //$NON-NLS-1$
private static final String PARAM_SRC_FOLDER = "SRC_FOLDER"; //$NON-NLS-1$
private static final String PARAM_SDK_TARGET = "SDK_TARGET"; //$NON-NLS-1$
+ private static final String PARAM_MIN_SDK_VERSION = "MIN_SDK_VERSION"; //$NON-NLS-1$
private static final String PH_ACTIVITIES = "ACTIVITIES"; //$NON-NLS-1$
+ private static final String PH_USES_SDK = "USES-SDK"; //$NON-NLS-1$
private static final String PH_INTENT_FILTERS = "INTENT_FILTERS"; //$NON-NLS-1$
private static final String PH_STRINGS = "STRINGS"; //$NON-NLS-1$
private static final String BIN_DIRECTORY =
- AndroidConstants.FD_BINARIES + AndroidConstants.WS_SEP;
+ SdkConstants.FD_OUTPUT + AndroidConstants.WS_SEP;
private static final String RES_DIRECTORY =
- AndroidConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
+ SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
private static final String ASSETS_DIRECTORY =
- AndroidConstants.FD_ASSETS + AndroidConstants.WS_SEP;
+ SdkConstants.FD_ASSETS + AndroidConstants.WS_SEP;
private static final String DRAWABLE_DIRECTORY =
- AndroidConstants.FD_DRAWABLE + AndroidConstants.WS_SEP;
+ SdkConstants.FD_DRAWABLE + AndroidConstants.WS_SEP;
private static final String LAYOUT_DIRECTORY =
- AndroidConstants.FD_LAYOUT + AndroidConstants.WS_SEP;
+ SdkConstants.FD_LAYOUT + AndroidConstants.WS_SEP;
private static final String VALUES_DIRECTORY =
- AndroidConstants.FD_VALUES + AndroidConstants.WS_SEP;
+ SdkConstants.FD_VALUES + AndroidConstants.WS_SEP;
private static final String TEMPLATES_DIRECTORY = "templates/"; //$NON-NLS-1$
private static final String TEMPLATE_MANIFEST = TEMPLATES_DIRECTORY
+ "AndroidManifest.template"; //$NON-NLS-1$
private static final String TEMPLATE_ACTIVITIES = TEMPLATES_DIRECTORY
+ "activity.template"; //$NON-NLS-1$
+ private static final String TEMPLATE_USES_SDK = TEMPLATES_DIRECTORY
+ + "uses-sdk.template"; //$NON-NLS-1$
private static final String TEMPLATE_INTENT_LAUNCHER = TEMPLATES_DIRECTORY
+ "launcher_intent_filter.template"; //$NON-NLS-1$
-
+
private static final String TEMPLATE_STRINGS = TEMPLATES_DIRECTORY
+ "strings.template"; //$NON-NLS-1$
private static final String TEMPLATE_STRING = TEMPLATES_DIRECTORY
@@ -235,6 +240,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
parameters.put(PARAM_IS_NEW_PROJECT, mMainPage.isNewProject());
parameters.put(PARAM_SRC_FOLDER, mMainPage.getSourceFolder());
parameters.put(PARAM_SDK_TARGET, mMainPage.getSdkTarget());
+ parameters.put(PARAM_MIN_SDK_VERSION, mMainPage.getMinSdkVersion());
if (mMainPage.isCreateActivity()) {
// An activity name can be of the form ".package.Class" or ".Class".
@@ -449,6 +455,15 @@ public class NewProjectWizard extends Wizard implements INewWizard {
// remove the activity(ies) from the manifest
manifestTemplate = manifestTemplate.replaceAll(PH_ACTIVITIES, "");
}
+
+ String minSdkVersion = (String) parameters.get(PARAM_MIN_SDK_VERSION);
+ if (minSdkVersion != null && minSdkVersion.length() > 0) {
+ String usesSdkTemplate = AdtPlugin.readEmbeddedTextFile(TEMPLATE_USES_SDK);
+ String usesSdk = replaceParameters(usesSdkTemplate, parameters);
+ manifestTemplate = manifestTemplate.replaceAll(PH_USES_SDK, usesSdk);
+ } else {
+ manifestTemplate = manifestTemplate.replaceAll(PH_USES_SDK, "");
+ }
// Save in the project as UTF-8
InputStream stream = new ByteArrayInputStream(
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
index f3f7b79..0e780a9 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
@@ -130,48 +130,14 @@ public class AndroidConstants {
public final static String FN_TRACEVIEW = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
"traceview.exe" : "traceview"; //$NON-NLS-1$ //$NON-NLS-2$
- /** Folder Names for Android Projects . */
-
- /* Resources folder name, i.e. "res". */
- public final static String FD_RESOURCES = "res"; //$NON-NLS-1$
- /** Assets folder name, i.e. "assets" */
- public final static String FD_ASSETS = "assets"; //$NON-NLS-1$
- /** Default source folder name, i.e. "src" */
- public final static String FD_SOURCES = "src"; //$NON-NLS-1$
- /** Default native library folder name inside the project, i.e. "libs"
- * While the folder inside the .apk is "lib", we call that one libs because
- * that's what we use in ant for both .jar and .so and we need to make the 2 development ways
- * compatible. */
- public final static String FD_NATIVE_LIBS = "libs"; //$NON-NLS-1$
- /** Native lib folder inside the APK: "lib" */
- public final static String FD_APK_NATIVE_LIBS = "lib"; //$NON-NLS-1$
- /** Default bin folder name, i.e. "bin" */
- public final static String FD_BINARIES = "bin"; //$NON-NLS-1$
- /** Default anim resource folder name, i.e. "anim" */
- public final static String FD_ANIM = "anim"; //$NON-NLS-1$
- /** Default color resource folder name, i.e. "color" */
- public final static String FD_COLOR = "color"; //$NON-NLS-1$
- /** Default drawable resource folder name, i.e. "drawable" */
- public final static String FD_DRAWABLE = "drawable"; //$NON-NLS-1$
- /** Default layout resource folder name, i.e. "layout" */
- public final static String FD_LAYOUT = "layout"; //$NON-NLS-1$
- /** Default menu resource folder name, i.e. "menu" */
- public final static String FD_MENU = "menu"; //$NON-NLS-1$
- /** Default values resource folder name, i.e. "values" */
- public final static String FD_VALUES = "values"; //$NON-NLS-1$
- /** Default xml resource folder name, i.e. "xml" */
- public final static String FD_XML = "xml"; //$NON-NLS-1$
- /** Default raw resource folder name, i.e. "raw" */
- public final static String FD_RAW = "raw"; //$NON-NLS-1$
-
/** Absolute path of the workspace root, i.e. "/" */
public final static String WS_ROOT = WS_SEP;
/** Absolute path of the resource folder, eg "/res".<br> This is a workspace path. */
- public final static String WS_RESOURCES = WS_SEP + FD_RESOURCES;
+ public final static String WS_RESOURCES = WS_SEP + SdkConstants.FD_RESOURCES;
/** Absolute path of the resource folder, eg "/assets".<br> This is a workspace path. */
- public final static String WS_ASSETS = WS_SEP + FD_ASSETS;
+ 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$
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestHelper.java
index 2db9e9b..cd238d2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestHelper.java
@@ -97,7 +97,7 @@ public class AndroidManifestHelper {
*/
public String getPackageName() {
try {
- return getPackageNameInternal(mXPath, getSource());
+ return mXPath.evaluate("/manifest/@package", getSource()); //$NON-NLS-1$
} catch (XPathExpressionException e1) {
// If the XPath failed to evaluate, we'll return null.
} catch (Exception e) {
@@ -111,17 +111,39 @@ public class AndroidManifestHelper {
}
/**
+ * Returns the minSdkVersion defined in the manifest file.
+ *
+ * @return A String object with the package or null if any error happened.
+ */
+ public String getMinSdkVersion() {
+ try {
+ return mXPath.evaluate("/manifest/uses-sdk/@" //$NON-NLS-1$
+ + AndroidXPathFactory.DEFAULT_NS_PREFIX
+ + ":minSdkVersion", getSource()); //$NON-NLS-1$
+ } catch (XPathExpressionException e1) {
+ // If the XPath failed to evaluate, we'll return null.
+ } catch (Exception e) {
+ // if this happens this is due to the resource being out of sync.
+ // so we must refresh it and do it again
+
+ // for any other kind of exception we must return null as well;
+ }
+
+ return null;
+ }
+ /**
* Returns the i-th activity defined in the manifest file.
*
- * @param manifest The manifest's IFile object.
* @param index The 1-based index of the activity to return.
- * @param xpath An optional xpath object. If null is provided a new one will
- * be created.
* @return A String object with the activity or null if any error happened.
*/
public String getActivityName(int index) {
try {
- return getActivityNameInternal(index, mXPath, getSource());
+ return mXPath.evaluate("/manifest/application/activity[" //$NON-NLS-1$
+ + index
+ + "]/@" //$NON-NLS-1$
+ + AndroidXPathFactory.DEFAULT_NS_PREFIX +":name", //$NON-NLS-1$
+ getSource());
} catch (XPathExpressionException e1) {
// If the XPath failed to evaluate, we'll return null.
} catch (Exception e) {
@@ -216,26 +238,4 @@ public class AndroidManifestHelper {
return null;
}
- /**
- * Performs the actual XPath evaluation to get the package name.
- * Extracted so that we can share it with AndroidManifestFromProject.
- */
- private static String getPackageNameInternal(XPath xpath, InputSource source)
- throws XPathExpressionException {
- return xpath.evaluate("/manifest/@package", source); //$NON-NLS-1$
- }
-
- /**
- * Performs the actual XPath evaluation to get the activity name.
- * Extracted so that we can share it with AndroidManifestFromProject.
- */
- private static String getActivityNameInternal(int index, XPath xpath, InputSource source)
- throws XPathExpressionException {
- return xpath.evaluate("/manifest/application/activity[" //$NON-NLS-1$
- + index
- + "]/@" //$NON-NLS-1$
- + AndroidXPathFactory.DEFAULT_NS_PREFIX +":name", //$NON-NLS-1$
- source);
- }
-
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
index cb98525..2866ce2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
@@ -319,7 +319,7 @@ public class AndroidManifestParser {
* @see org.xml.sax.helpers.DefaultHandler#error(org.xml.sax.SAXParseException)
*/
@Override
- public void error(SAXParseException e) throws SAXException {
+ public void error(SAXParseException e) {
if (mMarkErrors) {
handleError(e, e.getLineNumber());
}
@@ -329,7 +329,7 @@ public class AndroidManifestParser {
* @see org.xml.sax.helpers.DefaultHandler#fatalError(org.xml.sax.SAXParseException)
*/
@Override
- public void fatalError(SAXParseException e) throws SAXException {
+ public void fatalError(SAXParseException e) {
if (mMarkErrors) {
handleError(e, e.getLineNumber());
}
@@ -348,7 +348,6 @@ public class AndroidManifestParser {
/**
* Processes the activity node.
* @param attributes the attributes for the activity node.
- * @throws CoreException
*/
private void processActivityNode(Attributes attributes) {
// lets get the activity name, and add it to the list
@@ -381,7 +380,6 @@ public class AndroidManifestParser {
* @param attributes the attributes for the activity node.
* @param superClassName the fully qualified name of the super class that this
* node is representing
- * @throws CoreException
*/
private void processNode(Attributes attributes, String superClassName) {
// lets get the class name, and check it if required.
@@ -567,12 +565,11 @@ public class AndroidManifestParser {
/**
* Parses the manifest file, collects data, and checks for errors.
* @param javaProject The java project. Required.
- * @param manifestFile
+ * @param manifestFile The manifest file to parse.
* @param errorListener the {@link XmlErrorListener} object being notified of the presence
* of errors. Optional.
* @return an {@link AndroidManifestParser} or null if the parsing failed.
* @throws CoreException
- * @see {@link #parse(IJavaProject, IFile, XmlErrorListener, boolean, boolean)}
*/
public static AndroidManifestParser parseForError(IJavaProject javaProject, IFile manifestFile,
XmlErrorListener errorListener) throws CoreException {
@@ -581,12 +578,9 @@ public class AndroidManifestParser {
/**
* Parses the manifest file, and collects data.
- * @param manifestFile
- * @param errorListener the {@link XmlErrorListener} object being notified of the presence
- * of errors. Optional.
+ * @param manifestFile The manifest file to parse.
* @return an {@link AndroidManifestParser} or null if the parsing failed.
* @throws CoreException
- * @see {@link #parse(IJavaProject, IFile, XmlErrorListener, boolean, boolean)}
*/
public static AndroidManifestParser parseForData(IFile manifestFile) throws CoreException {
return parse(null /* javaProject */, manifestFile, null /* errorListener */,
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidXPathFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidXPathFactory.java
index 530c89e..8544b25 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidXPathFactory.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidXPathFactory.java
@@ -39,7 +39,7 @@ public class AndroidXPathFactory {
/**
* Construct the context with the prefix associated with the android namespace.
- * @param prefix the Prefix
+ * @param androidPrefix the Prefix
*/
public AndroidNamespaceContext(String androidPrefix) {
mAndroidPrefix = androidPrefix;
@@ -71,7 +71,7 @@ public class AndroidXPathFactory {
/**
* Creates a new XPath object, specifying which prefix in the query is used for the
* android namespace.
- * @param prefix The namespace prefix.
+ * @param androidPrefix The namespace prefix.
*/
public static XPath newXPath(String androidPrefix) {
XPath xpath = sFactory.newXPath();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/BaseProjectHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/BaseProjectHelper.java
index c69e875..bd8b444 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/BaseProjectHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/BaseProjectHelper.java
@@ -82,11 +82,13 @@ public final class BaseProjectHelper {
}
/**
- * Adds a marker to a file on a specific line
+ * Adds a marker to a file on a specific line. This methods catches thrown
+ * {@link CoreException}, and returns null instead.
* @param file the file to be marked
* @param markerId The id of the marker to add.
* @param message the message associated with the mark
- * @param lineNumber the line number where to put the mark
+ * @param lineNumber the line number where to put the mark. If line is < 1, it puts the marker
+ * on line 1.
* @param severity the severity of the marker.
* @return the IMarker that was added or null if it failed to add one.
*/
@@ -96,7 +98,7 @@ public final class BaseProjectHelper {
IMarker marker = file.createMarker(markerId);
marker.setAttribute(IMarker.MESSAGE, message);
marker.setAttribute(IMarker.SEVERITY, severity);
- if (lineNumber == -1) {
+ if (lineNumber < 1) {
lineNumber = 1;
}
marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
@@ -108,7 +110,7 @@ public final class BaseProjectHelper {
return marker;
} catch (CoreException e) {
- AdtPlugin.log(e, "Failed to add marker '%1$s' to '%2$s'",
+ AdtPlugin.log(e, "Failed to add marker '%1$s' to '%2$s'", //$NON-NLS-1$
markerId, file.getFullPath());
}
@@ -116,7 +118,8 @@ public final class BaseProjectHelper {
}
/**
- * Adds a marker to a resource.
+ * Adds a marker to a resource. This methods catches thrown {@link CoreException},
+ * and returns null instead.
* @param resource the file to be marked
* @param markerId The id of the marker to add.
* @param message the message associated with the mark
@@ -129,7 +132,7 @@ public final class BaseProjectHelper {
IMarker marker = resource.createMarker(markerId);
marker.setAttribute(IMarker.MESSAGE, message);
marker.setAttribute(IMarker.SEVERITY, severity);
-
+
// on Windows, when adding a marker to a project, it takes a refresh for the marker
// to show. In order to fix this we're forcing a refresh of elements receiving
// markers (and only the element, not its children), to force the marker display.
@@ -137,13 +140,43 @@ public final class BaseProjectHelper {
return marker;
} catch (CoreException e) {
- AdtPlugin.log(e, "Failed to add marker '%1$s' to '%2$s'",
+ AdtPlugin.log(e, "Failed to add marker '%1$s' to '%2$s'", //$NON-NLS-1$
markerId, resource.getFullPath());
}
return null;
}
-
+
+ /**
+ * Adds a marker to a resource. This method does not catch {@link CoreException} and instead
+ * throw them.
+ * @param resource the file to be marked
+ * @param markerId The id of the marker to add.
+ * @param message the message associated with the mark
+ * @param lineNumber the line number where to put the mark if != -1.
+ * @param severity the severity of the marker.
+ * @param priority the priority of the marker
+ * @return the IMarker that was added.
+ * @throws CoreException
+ */
+ public final static IMarker addMarker(IResource resource, String markerId,
+ String message, int lineNumber, int severity, int priority) throws CoreException {
+ IMarker marker = resource.createMarker(markerId);
+ marker.setAttribute(IMarker.MESSAGE, message);
+ marker.setAttribute(IMarker.SEVERITY, severity);
+ if (lineNumber != -1) {
+ marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
+ }
+ marker.setAttribute(IMarker.PRIORITY, priority);
+
+ // on Windows, when adding a marker to a project, it takes a refresh for the marker
+ // to show. In order to fix this we're forcing a refresh of elements receiving
+ // markers (and only the element, not its children), to force the marker display.
+ resource.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor());
+
+ return marker;
+ }
+
/**
* Tests that a class name is valid for usage in the manifest.
* <p/>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/XmlErrorHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/XmlErrorHandler.java
index 26fbf42..fda55c4 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/XmlErrorHandler.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/XmlErrorHandler.java
@@ -62,6 +62,7 @@ public class XmlErrorHandler extends DefaultHandler {
/**
* Xml Error call back
* @param exception the parsing exception
+ * @throws SAXException
*/
@Override
public void error(SAXParseException exception) throws SAXException {
@@ -71,6 +72,7 @@ public class XmlErrorHandler extends DefaultHandler {
/**
* Xml Fatal Error call back
* @param exception the parsing exception
+ * @throws SAXException
*/
@Override
public void fatalError(SAXParseException exception) throws SAXException {
@@ -80,6 +82,7 @@ public class XmlErrorHandler extends DefaultHandler {
/**
* Xml Warning call back
* @param exception the parsing exception
+ * @throws SAXException
*/
@Override
public void warning(SAXParseException exception) throws SAXException {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/AttrsXmlParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/AttrsXmlParser.java
index 3176c8e..3875e81 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/AttrsXmlParser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/AttrsXmlParser.java
@@ -20,6 +20,7 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo;
import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo.Format;
import com.android.ide.eclipse.common.resources.ViewClassInfo.LayoutParamsInfo;
+import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils;
import org.eclipse.core.runtime.IStatus;
import org.w3c.dom.Document;
@@ -228,7 +229,7 @@ public final class AttrsXmlParser {
}
mStyleMap.put(name, style);
if (lastComment != null) {
- style.setJavaDoc(formatJavadoc(lastComment.getNodeValue()));
+ style.setJavaDoc(parseJavadoc(lastComment.getNodeValue()));
}
}
}
@@ -263,14 +264,15 @@ public final class AttrsXmlParser {
}
if (info != null) {
if (lastComment != null) {
- info.setJavaDoc(formatJavadoc(lastComment.getNodeValue()));
+ info.setJavaDoc(parseJavadoc(lastComment.getNodeValue()));
+ info.setDeprecatedDoc(parseDeprecatedDoc(lastComment.getNodeValue()));
}
}
}
}
return info;
}
-
+
/**
* Finds all the attributes for a particular style node,
* e.g. a declare-styleable of name "TextView" or "LinearLayout_Layout".
@@ -431,16 +433,23 @@ public final class AttrsXmlParser {
}
/**
- * Formats the javadoc.
+ * Parses the javadoc comment.
* Only keeps the first sentence.
- * Removes and simplifies links and references.
+ * <p/>
+ * This does not remove nor simplify links and references. Such a transformation
+ * is done later at "display" time in {@link DescriptorsUtils#formatTooltip(String)} and co.
*/
- private String formatJavadoc(String comment) {
+ private String parseJavadoc(String comment) {
if (comment == null) {
return null;
}
+
// sanitize & collapse whitespace
comment = comment.replaceAll("\\s+", " "); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // Explicitly remove any @deprecated tags since they are handled separately.
+ comment = comment.replaceAll("(?:\\{@deprecated[^}]*\\}|@deprecated[^@}]*)", "");
+
// take everything up to the first dot that is followed by a space or the end of the line.
// I love regexps :-). For the curious, the regexp is:
// - start of line
@@ -456,6 +465,41 @@ public final class AttrsXmlParser {
// - followed by a space (?= non-capturing zero-width positive look-ahead)
// - anything else is ignored
comment = comment.replaceFirst("^\\s*(.*?(?:$|(?<![a-zA-Z]\\.[a-zA-Z])\\.(?=\\s))).*", "$1"); //$NON-NLS-1$ //$NON-NLS-2$
+
return comment;
}
+
+
+ /**
+ * Parses the javadoc and extract the first @deprecated tag, if any.
+ * Returns null if there's no @deprecated tag.
+ * The deprecated tag can be of two forms:
+ * - {+@deprecated ...text till the next bracket }
+ * Note: there should be no space or + between { and @. I need one in this comment otherwise
+ * this method will be tagged as deprecated ;-)
+ * - @deprecated ...text till the next @tag or end of the comment.
+ * In both cases the comment can be multi-line.
+ */
+ private String parseDeprecatedDoc(String comment) {
+ // Skip if we can't even find the tag in the comment.
+ if (comment == null) {
+ return null;
+ }
+
+ // sanitize & collapse whitespace
+ comment = comment.replaceAll("\\s+", " "); //$NON-NLS-1$ //$NON-NLS-2$
+
+ int pos = comment.indexOf("{@deprecated");
+ if (pos >= 0) {
+ comment = comment.substring(pos + 12 /* len of {@deprecated */);
+ comment = comment.replaceFirst("^([^}]*).*", "$1");
+ } else if ((pos = comment.indexOf("@deprecated")) >= 0) {
+ comment = comment.substring(pos + 11 /* len of @deprecated */);
+ comment = comment.replaceFirst("^(.*?)(?:@.*|$)", "$1");
+ } else {
+ return null;
+ }
+
+ return comment.trim();
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java
index 6cff62c..efa5981 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java
@@ -55,8 +55,10 @@ public class DeclareStyleableInfo {
private String[] mEnumValues;
/** Values for flag. null for other types. */
private String[] mFlagValues;
- /** Short javadoc */
+ /** Short javadoc (i.e. the first sentence). */
private String mJavaDoc;
+ /** Documentation for deprecated attributes. Null if not deprecated. */
+ private String mDeprecatedDoc;
/**
* @param name The XML Name of the attribute
@@ -74,6 +76,7 @@ public class DeclareStyleableInfo {
mEnumValues = info.mEnumValues;
mFlagValues = info.mFlagValues;
mJavaDoc = info.mJavaDoc;
+ mDeprecatedDoc = info.mDeprecatedDoc;
}
/** Returns the XML Name of the attribute */
@@ -93,10 +96,14 @@ public class DeclareStyleableInfo {
public String[] getFlagValues() {
return mFlagValues;
}
- /** Returns a short javadoc */
+ /** Returns a short javadoc, .i.e. the first sentence. */
public String getJavaDoc() {
return mJavaDoc;
}
+ /** Returns the documentation for deprecated attributes. Null if not deprecated. */
+ public String getDeprecatedDoc() {
+ return mDeprecatedDoc;
+ }
/** Sets the values for enums. null for other types. */
public void setEnumValues(String[] values) {
@@ -106,10 +113,14 @@ public class DeclareStyleableInfo {
public void setFlagValues(String[] values) {
mFlagValues = values;
}
- /** Sets a short javadoc */
+ /** Sets a short javadoc, .i.e. the first sentence. */
public void setJavaDoc(String javaDoc) {
mJavaDoc = javaDoc;
}
+ /** Sets the documentation for deprecated attributes. Null if not deprecated. */
+ public void setDeprecatedDoc(String deprecatedDoc) {
+ mDeprecatedDoc = deprecatedDoc;
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidContentAssist.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidContentAssist.java
index d1b4547..50d3d28 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidContentAssist.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidContentAssist.java
@@ -91,7 +91,12 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
/**
* Constructor for AndroidContentAssist
- * @param rootElementDescriptors The valid root elements of the XML hierarchy
+ * @param descriptorId An id for {@link AndroidTargetData#getDescriptorProvider(int)}.
+ * The Id can be one of {@link AndroidTargetData#DESCRIPTOR_MANIFEST},
+ * {@link AndroidTargetData#DESCRIPTOR_LAYOUT},
+ * {@link AndroidTargetData#DESCRIPTOR_MENU},
+ * or {@link AndroidTargetData#DESCRIPTOR_XML}.
+ * All other values will throw an {@link IllegalArgumentException} later at runtime.
*/
public AndroidContentAssist(int descriptorId) {
mDescriptorId = descriptorId;
@@ -723,7 +728,6 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
/**
* Computes (if needed) and returns the root descriptor.
- * @return
*/
private ElementDescriptor getRootDescriptor() {
if (mRootDescriptor == null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidEditor.java
index 78e0401..dca7db0 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidEditor.java
@@ -722,7 +722,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang
}
/**
- * Returns the {@link PlatformData} for the edited file.
+ * Returns the {@link AndroidTargetData} for the edited file.
*/
public AndroidTargetData getTargetData() {
IProject project = getProject();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptor.java
index 2c779b2..70d03a1 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptor.java
@@ -38,6 +38,7 @@ public abstract class AttributeDescriptor {
private String mXmlLocalName;
private ElementDescriptor mParent;
private final String mNsUri;
+ private boolean mDeprecated;
/**
* Creates a new {@link AttributeDescriptor}
@@ -70,6 +71,14 @@ public abstract class AttributeDescriptor {
return mParent;
}
+ public void setDeprecated(boolean isDeprecated) {
+ mDeprecated = isDeprecated;
+ }
+
+ public boolean isDeprecated() {
+ return mDeprecated;
+ }
+
/**
* Returns an optional icon for the attribute.
* <p/>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptorLabelProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptorLabelProvider.java
index 05ae922..2729565 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptorLabelProvider.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/AttributeDescriptorLabelProvider.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.editors.descriptors;
+import com.android.ide.eclipse.editors.IconFactory;
import com.android.ide.eclipse.editors.uimodel.UiAbstractTextAttributeNode;
import org.eclipse.jface.viewers.ILabelProvider;
@@ -35,6 +36,17 @@ public class AttributeDescriptorLabelProvider implements ILabelProvider {
}
public Image getImage(Object element) {
+ if (element instanceof UiAbstractTextAttributeNode) {
+ UiAbstractTextAttributeNode node = (UiAbstractTextAttributeNode) element;
+ if (node.getDescriptor().isDeprecated()) {
+ String v = node.getCurrentValue();
+ if (v != null && v.length() > 0) {
+ IconFactory factory = IconFactory.getInstance();
+ return factory.getIcon("warning"); //$NON-NLS-1$
+ }
+ }
+ }
+
return null;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/DescriptorsUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/DescriptorsUtils.java
index c84bf57..09f1478 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/DescriptorsUtils.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/DescriptorsUtils.java
@@ -92,8 +92,9 @@ public final class DescriptorsUtils {
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
* @param infos The array of {@link AttributeInfo} to read and append to attributes
- * @param requiredAttributes An optional list of attributes to mark as "required" (i.e. append
- * a "*" to their UI name as a hint for the user.)
+ * @param requiredAttributes An optional set of attributes to mark as "required" (i.e. append
+ * a "*" to their UI name as a hint for the user.) If not null, must contains
+ * entries in the form "elem-name/attr-name". Elem-name can be "*".
* @param overrides A map [attribute name => TextAttributeDescriptor creator]. A creator
* can either by a Class<? extends TextAttributeDescriptor> or an instance of
* {@link ITextAttributeCreator} that instantiates the right TextAttributeDescriptor.
@@ -101,16 +102,15 @@ public final class DescriptorsUtils {
public static void appendAttributes(ArrayList<AttributeDescriptor> attributes,
String elementXmlName,
String nsUri, AttributeInfo[] infos,
- String[] requiredAttributes,
+ Set<String> requiredAttributes,
Map<String, Object> overrides) {
for (AttributeInfo info : infos) {
boolean required = false;
if (requiredAttributes != null) {
- for(String attr_name : requiredAttributes) {
- if (attr_name.equals(info.getName())) {
- required = true;
- break;
- }
+ String attr_name = info.getName();
+ if (requiredAttributes.contains("*/" + attr_name) ||
+ requiredAttributes.contains(elementXmlName + "/" + attr_name)) {
+ required = true;
}
}
appendAttribute(attributes, elementXmlName, nsUri, info, required, overrides);
@@ -144,7 +144,26 @@ public final class DescriptorsUtils {
if (required) {
uiName += "*"; //$NON-NLS-1$
}
- String tooltip = formatTooltip(info.getJavaDoc()); // tooltip
+
+ String tooltip = null;
+ String rawTooltip = info.getJavaDoc();
+ if (rawTooltip == null) {
+ rawTooltip = "";
+ }
+
+ String deprecated = info.getDeprecatedDoc();
+ if (deprecated != null) {
+ if (rawTooltip.length() > 0) {
+ rawTooltip += "@@"; //$NON-NLS-1$ insert a break
+ }
+ rawTooltip += "* Deprecated";
+ if (deprecated.length() != 0) {
+ rawTooltip += ": " + deprecated; //$NON-NLS-1$
+ }
+ if (deprecated.length() == 0 || !deprecated.endsWith(".")) { //$NON-NLS-1$
+ rawTooltip += "."; //$NON-NLS-1$
+ }
+ }
// Add the known types to the tooltip
Format[] formats_list = info.getFormats();
@@ -154,11 +173,14 @@ public final class DescriptorsUtils {
HashSet<Format> formats_set = new HashSet<Format>();
StringBuilder sb = new StringBuilder();
- if (tooltip != null) {
- sb.append(tooltip);
- sb.append(" "); //$NON-NLS-1$
+ if (rawTooltip != null && rawTooltip.length() > 0) {
+ sb.append(rawTooltip);
+ sb.append(" "); //$NON-NLS-1$
}
- sb.append("["); //$NON-NLS-1$
+ if (sb.length() > 0) {
+ sb.append("@@"); //$NON-NLS-1$ @@ inserts a break before the types
+ }
+ sb.append("["); //$NON-NLS-1$
for (int i = 0; i < flen; i++) {
Format f = formats_list[i];
formats_set.add(f);
@@ -172,19 +194,21 @@ public final class DescriptorsUtils {
sb.append("]"); //$NON-NLS-1$
if (required) {
- sb.append(". Required.");
+ sb.append(".@@* "); //$NON-NLS-1$ @@ inserts a break.
+ sb.append("Required.");
}
// The extra space at the end makes the tooltip more readable on Windows.
sb.append(" "); //$NON-NLS-1$
- tooltip = sb.toString();
+ rawTooltip = sb.toString();
+ tooltip = formatTooltip(rawTooltip);
// Create a specialized attribute if we can
if (overrides != null) {
for (Entry<String, Object> entry: overrides.entrySet()) {
String key = entry.getKey();
- String elements[] = key.split("/");
+ String elements[] = key.split("/"); //$NON-NLS-1$
String overrideAttrLocalName = null;
if (elements.length < 1) {
continue;
@@ -193,7 +217,7 @@ public final class DescriptorsUtils {
elements = null;
} else {
overrideAttrLocalName = elements[elements.length - 1];
- elements = elements[0].split(",");
+ elements = elements[0].split(","); //$NON-NLS-1$
}
if (overrideAttrLocalName == null ||
@@ -204,7 +228,8 @@ public final class DescriptorsUtils {
boolean ok_element = elements.length < 1;
if (!ok_element) {
for (String element : elements) {
- if (element.equals("*") || element.equals(elementXmlName)) {
+ if (element.equals("*") //$NON-NLS-1$
+ || element.equals(elementXmlName)) {
ok_element = true;
break;
}
@@ -271,8 +296,12 @@ public final class DescriptorsUtils {
// By default a simple text field is used
if (attr == null) {
+ if (tooltip == null) {
+ tooltip = formatTooltip(rawTooltip);
+ }
attr = new TextAttributeDescriptor(xmlLocalName, uiName, nsUri, tooltip);
}
+ attr.setDeprecated(info.getDeprecatedDoc() != null);
attributes.add(attr);
}
@@ -582,6 +611,8 @@ public final class DescriptorsUtils {
Pattern p_code = Pattern.compile("<code>(.+?)</code>(.*)"); //$NON-NLS-1$
// Detects @blah@, used in hard-coded tooltip descriptors
Pattern p_elem = Pattern.compile("@([\\w -]+)@(.*)"); //$NON-NLS-1$
+ // Detects a buffer that starts by @@ (request for a break)
+ Pattern p_break = Pattern.compile("@@(.*)"); //$NON-NLS-1$
// Detects a buffer that starts by @ < or { (one that was not matched above)
Pattern p_open = Pattern.compile("([@<\\{])(.*)"); //$NON-NLS-1$
// Detects everything till the next potential separator, i.e. @ < or {
@@ -616,6 +647,10 @@ public final class DescriptorsUtils {
if (text != null) {
currentLength += text.length() - 2;
}
+ } else if ((m = p_break.matcher(javadoc)).matches()) {
+ spans.add(BREAK);
+ currentLength = 0;
+ javadoc = m.group(1);
} else if ((m = p_open.matcher(javadoc)).matches()) {
s = m.group(1);
javadoc = m.group(2);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/TextAttributeDescriptor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/TextAttributeDescriptor.java
index 632471d..a9d2b2e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/TextAttributeDescriptor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/TextAttributeDescriptor.java
@@ -95,6 +95,10 @@ public class TextAttributeDescriptor extends AttributeDescriptor implements IPro
}
public String getCategory() {
+ if (isDeprecated()) {
+ return "Deprecated";
+ }
+
ElementDescriptor parent = getParent();
if (parent != null) {
return parent.getUiName();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/BasePullParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/BasePullParser.java
index c512625..381539b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/BasePullParser.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/BasePullParser.java
@@ -20,7 +20,6 @@ import com.android.layoutlib.api.IXmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
@@ -178,7 +177,7 @@ public abstract class BasePullParser implements IXmlPullParser {
return mParsingState;
}
- public int nextTag() throws XmlPullParserException, IOException {
+ public int nextTag() throws XmlPullParserException {
int eventType = next();
if (eventType != START_TAG && eventType != END_TAG) {
throw new XmlPullParserException("expected start or end tag", this, null);
@@ -186,7 +185,7 @@ public abstract class BasePullParser implements IXmlPullParser {
return eventType;
}
- public String nextText() throws XmlPullParserException, IOException {
+ public String nextText() throws XmlPullParserException {
if (getEventType() != START_TAG) {
throw new XmlPullParserException("parser must be on START_TAG to read next text", this,
null);
@@ -208,7 +207,7 @@ public abstract class BasePullParser implements IXmlPullParser {
}
}
- public int nextToken() throws XmlPullParserException, IOException {
+ public int nextToken() throws XmlPullParserException {
return next();
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java
index 87a14ad..a0b30ec 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java
@@ -33,8 +33,10 @@ import org.eclipse.core.runtime.IStatus;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
import java.util.TreeSet;
import java.util.Map.Entry;
@@ -166,7 +168,13 @@ public final class AndroidManifestDescriptors implements IDescriptorProvider {
"android", //$NON-NLS-1$
AndroidConstants.NS_RESOURCES);
+ // -- setup the required attributes overrides --
+
+ Set<String> required = new HashSet<String>();
+ required.add("provider/authorities"); //$NON-NLS-1$
+
// -- setup the various attribute format overrides --
+
// The key for each override is "element1,element2,.../attr-xml-local-name" or
// "*/attr-xml-local-name" to match the attribute in any element.
@@ -181,7 +189,7 @@ public final class AndroidManifestDescriptors implements IDescriptorProvider {
tooltip);
}
});
-
+
overrides.put("*/theme", ThemeAttributeDescriptor.class); //$NON-NLS-1$
overrides.put("*/permission", ListAttributeDescriptor.class); //$NON-NLS-1$
overrides.put("*/targetPackage", PackageAttributeDescriptor.class); //$NON-NLS-1$
@@ -212,8 +220,12 @@ public final class AndroidManifestDescriptors implements IDescriptorProvider {
// --
- inflateElement(manifestMap, overrides, elementDescs,
- MANIFEST_ELEMENT, "AndroidManifest"); //$NON-NLS-1$
+ inflateElement(manifestMap,
+ overrides,
+ required,
+ elementDescs,
+ MANIFEST_ELEMENT,
+ "AndroidManifest"); //$NON-NLS-1$
insertAttribute(MANIFEST_ELEMENT, PACKAGE_ATTR_DESC);
sanityCheck(manifestMap, MANIFEST_ELEMENT);
@@ -312,16 +324,17 @@ public final class AndroidManifestDescriptors implements IDescriptorProvider {
* "Inflates" the properties of an {@link ElementDescriptor} from the styleable declaration.
* <p/>
* This first creates all the attributes for the given ElementDescriptor.
- * It then find all children of the descriptor, inflate them recursively and set them
+ * It then finds all children of the descriptor, inflate them recursively and set them
* as child to this ElementDescriptor.
*
- * @param styleMap The input styleable map for manifest elements & attributes
+ * @param styleMap The input styleable map for manifest elements & attributes.
* @param overrides A list of attribute overrides (to customize the type of the attribute
- * descriptors)
+ * descriptors).
+ * @param requiredAttributes Set of attributes to be marked as required.
* @param existingElementDescs A map of already created element descriptors, keyed by
* XML local name. This is used to use the static elements created initially by this
* class, which are referenced directly by editors (so that reloading an SDK won't
- * break these references)
+ * break these references).
* @param elemDesc The current {@link ElementDescriptor} to inflate.
* @param styleName The name of the {@link ElementDescriptor} to inflate. Its XML local name
* will be guessed automatically from the style name.
@@ -329,6 +342,7 @@ public final class AndroidManifestDescriptors implements IDescriptorProvider {
private void inflateElement(
Map<String, DeclareStyleableInfo> styleMap,
Map<String, Object> overrides,
+ Set<String> requiredAttributes,
HashMap<String, ElementDescriptor> existingElementDescs,
ElementDescriptor elemDesc,
String styleName) {
@@ -342,7 +356,9 @@ public final class AndroidManifestDescriptors implements IDescriptorProvider {
DescriptorsUtils.appendAttributes(attrDescs,
elemDesc.getXmlLocalName(),
AndroidConstants.NS_RESOURCES,
- style.getAttributes(), null, overrides);
+ style.getAttributes(),
+ requiredAttributes,
+ overrides);
elemDesc.setTooltip(style.getJavaDoc());
elemDesc.setAttributes(attrDescs.toArray(new AttributeDescriptor[attrDescs.size()]));
}
@@ -373,7 +389,12 @@ public final class AndroidManifestDescriptors implements IDescriptorProvider {
}
children.add(child);
- inflateElement(styleMap, overrides, existingElementDescs, child, childStyleName);
+ inflateElement(styleMap,
+ overrides,
+ requiredAttributes,
+ existingElementDescs,
+ child,
+ childStyleName);
}
}
elemDesc.setChildren(children.toArray(new ElementDescriptor[children.size()]));
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/CountryCodeQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/CountryCodeQualifier.java
index 1d01260..9a61d17 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/CountryCodeQualifier.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/CountryCodeQualifier.java
@@ -66,7 +66,7 @@ public final class CountryCodeQualifier extends ResourceQualifier {
/**
* Returns the folder name segment for the given value. This is equivalent to calling
* {@link #toString()} on a {@link CountryCodeQualifier} object.
- * @param value the value of the qualifier, as returned by {@link #getCode()}.
+ * @param code the value of the qualifier, as returned by {@link #getCode()}.
*/
public static String getFolderSegment(int code) {
if (code != DEFAULT_CODE && code >= 100 && code <=999) { // code is 3 digit.) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/NetworkCodeQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/NetworkCodeQualifier.java
index ce527a4..7e30901 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/NetworkCodeQualifier.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/NetworkCodeQualifier.java
@@ -66,7 +66,7 @@ public final class NetworkCodeQualifier extends ResourceQualifier {
/**
* Returns the folder name segment for the given value. This is equivalent to calling
* {@link #toString()} on a {@link NetworkCodeQualifier} object.
- * @param value the value of the qualifier, as returned by {@link #getCode()}.
+ * @param code the value of the qualifier, as returned by {@link #getCode()}.
*/
public static String getFolderSegment(int code) {
if (code != DEFAULT_CODE && code >= 1 && code <= 999) { // code is 1-3 digit.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/PixelDensityQualifier.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/PixelDensityQualifier.java
index 0fd05bf..c47bb83 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/PixelDensityQualifier.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/configurations/PixelDensityQualifier.java
@@ -39,7 +39,7 @@ public final class PixelDensityQualifier extends ResourceQualifier {
/**
* Creates and returns a qualifier from the given folder segment. If the segment is incorrect,
* <code>null</code> is returned.
- * @param segment the folder segment from which to create a qualifier.
+ * @param folderSegment the folder segment from which to create a qualifier.
* @return a new {@link CountryCodeQualifier} object or <code>null</code>
*/
public static PixelDensityQualifier getQualifier(String folderSegment) {
@@ -66,7 +66,7 @@ public final class PixelDensityQualifier extends ResourceQualifier {
/**
* Returns the folder name segment for the given value. This is equivalent to calling
* {@link #toString()} on a {@link NetworkCodeQualifier} object.
- * @param value the value of the qualifier, as returned by {@link #getCode()}.
+ * @param value the value of the qualifier, as returned by {@link #getValue()}.
*/
public static String getFolderSegment(int value) {
if (value != DEFAULT_DENSITY) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/MultiResourceFile.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/MultiResourceFile.java
index 72438a6..3812791 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/MultiResourceFile.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/MultiResourceFile.java
@@ -132,7 +132,7 @@ public final class MultiResourceFile extends ResourceFile implements IValueResou
/**
* Adds a resource item to the list
* @param resType The type of the resource
- * @param name The name of the resource.
+ * @param value The value of the resource.
*/
public void addResourceValue(String resType, ResourceValue value) {
ResourceType type = ResourceType.getEnum(resType);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectClassLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectClassLoader.java
index 183af27..8b6c3c1 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectClassLoader.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectClassLoader.java
@@ -107,7 +107,6 @@ public final class ProjectClassLoader extends ClassLoader {
* @param parent the root of the file.
* @param segments the segments containing the path of the file
* @param index the offset at which to start looking into segments.
- * @return
* @throws FileNotFoundException
*/
private File getFile(File parent, String[] segments, int index)
@@ -168,8 +167,6 @@ public final class ProjectClassLoader extends ClassLoader {
/**
* Loads a class from the 3rd party jar present in the project
- * @param name
- * @return
* @throws ClassNotFoundException
*/
private Class<?> loadClassFromJar(String name) throws ClassNotFoundException {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectResources.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectResources.java
index b0881fa..40e4e3b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectResources.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ProjectResources.java
@@ -393,8 +393,6 @@ public class ProjectResources implements IResourceRepository {
/**
* Resolves a compiled resource id of type int[] into the resource name.
- * @param id
- * @return
*/
public String resolveResourceValue(int[] id) {
if (mStyleableValueToNameMap != null) {
@@ -407,9 +405,6 @@ public class ProjectResources implements IResourceRepository {
/**
* Returns the value of a resource by its type and name.
- * @param type
- * @param name
- * @return
*/
public Integer getResourceValue(String type, String name) {
if (mResourceValueMap != null) {
@@ -444,8 +439,7 @@ public class ProjectResources implements IResourceRepository {
/**
* Returns the list of regions used in the resources with the given language.
- * @param currentLanguage the current language the region must be associated with
- * @return
+ * @param currentLanguage the current language the region must be associated with.
*/
public Set<String> getRegions(String currentLanguage) {
Set<String> set = new HashSet<String>();
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolder.java
index 6db0d94..98f5b39 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolder.java
@@ -185,7 +185,7 @@ public final class ResourceFolder extends Resource {
/**
* Returns the {@link ResourceFile} matching a given name.
- * @param file The name of the file to return.
+ * @param filename The name of the file to return.
* @return the {@link ResourceFile} or <code>null</code> if no match was found.
*/
public ResourceFile getFile(String filename) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolderType.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolderType.java
index bd93301..5fc7393 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolderType.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceFolderType.java
@@ -16,21 +16,21 @@
package com.android.ide.eclipse.editors.resources.manager;
-import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
+import com.android.sdklib.SdkConstants;
/**
* Enum representing a type of resource folder.
*/
public enum ResourceFolderType {
- ANIM(AndroidConstants.FD_ANIM),
- COLOR(AndroidConstants.FD_COLOR),
- DRAWABLE(AndroidConstants.FD_DRAWABLE),
- LAYOUT(AndroidConstants.FD_LAYOUT),
- MENU(AndroidConstants.FD_MENU),
- RAW(AndroidConstants.FD_RAW),
- VALUES(AndroidConstants.FD_VALUES),
- XML(AndroidConstants.FD_XML);
+ ANIM(SdkConstants.FD_ANIM),
+ COLOR(SdkConstants.FD_COLOR),
+ DRAWABLE(SdkConstants.FD_DRAWABLE),
+ LAYOUT(SdkConstants.FD_LAYOUT),
+ MENU(SdkConstants.FD_MENU),
+ RAW(SdkConstants.FD_RAW),
+ VALUES(SdkConstants.FD_VALUES),
+ XML(SdkConstants.FD_XML);
private final String mName;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceManager.java
index 9c5f0fc..6099008 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceManager.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/ResourceManager.java
@@ -30,6 +30,7 @@ import com.android.ide.eclipse.editors.resources.manager.files.IAbstractFolder;
import com.android.ide.eclipse.editors.resources.manager.files.IFileWrapper;
import com.android.ide.eclipse.editors.resources.manager.files.IFolderWrapper;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
@@ -269,8 +270,7 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
/**
* Loads and returns the resources for a given {@link IAndroidTarget}
- * @param osFilePath the path to the folder containing all the versions of the framework
- * resources
+ * @param androidTarget the target from which to load the framework resources
*/
public ProjectResources loadFrameworkResources(IAndroidTarget androidTarget) {
String osResourcesPath = androidTarget.getPath(IAndroidTarget.RESOURCES);
@@ -329,7 +329,7 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
return;
}
- IFolder resourceFolder = project.getFolder(AndroidConstants.FD_RESOURCES);
+ IFolder resourceFolder = project.getFolder(SdkConstants.FD_RESOURCES);
ProjectResources projectResources = mMap.get(project);
if (projectResources == null) {
@@ -478,7 +478,7 @@ public final class ResourceManager implements IProjectListener, IFolderListener,
* @return true if the path is under /project res/
*/
private boolean isInResFolder(IPath path) {
- return AndroidConstants.FD_RESOURCES.equalsIgnoreCase(path.segment(1));
+ return SdkConstants.FD_RESOURCES.equalsIgnoreCase(path.segment(1));
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/SingleResourceFile.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/SingleResourceFile.java
index 1211236..32b1107 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/SingleResourceFile.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/SingleResourceFile.java
@@ -115,8 +115,6 @@ public class SingleResourceFile extends ResourceFile {
/**
* Returns the name of the resources.
- * @param type
- * @return
*/
private String getResourceName(ResourceType type) {
// get the name from the filename.
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/FileWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/FileWrapper.java
index 0a14214..d99cb13 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/FileWrapper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/FileWrapper.java
@@ -17,7 +17,6 @@
package com.android.ide.eclipse.editors.resources.manager.files;
import org.eclipse.core.resources.IFile;
-import org.eclipse.core.runtime.CoreException;
import java.io.File;
import java.io.FileInputStream;
@@ -45,7 +44,7 @@ public class FileWrapper implements IAbstractFile {
mFile = file;
}
- public InputStream getContents() throws CoreException {
+ public InputStream getContents() {
try {
return new FileInputStream(mFile);
} catch (FileNotFoundException e) {
@@ -74,7 +73,7 @@ public class FileWrapper implements IAbstractFile {
}
if (obj instanceof File) {
- return mFile.equals((File)obj);
+ return mFile.equals(obj);
}
return super.equals(obj);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/FolderWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/FolderWrapper.java
index 8afea33..9ad7460 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/FolderWrapper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/FolderWrapper.java
@@ -59,7 +59,7 @@ public class FolderWrapper implements IAbstractFolder {
}
if (obj instanceof File) {
- return mFolder.equals((File)obj);
+ return mFolder.equals(obj);
}
return super.equals(obj);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IFileWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IFileWrapper.java
index 441c65b..f0f5f2d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IFileWrapper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IFileWrapper.java
@@ -55,7 +55,7 @@ public class IFileWrapper implements IAbstractFile {
}
if (obj instanceof IFile) {
- return mFile.equals((IFile)obj);
+ return mFile.equals(obj);
}
return super.equals(obj);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IFolderWrapper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IFolderWrapper.java
index 92b5c07..b1fa3ef 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IFolderWrapper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/files/IFolderWrapper.java
@@ -61,7 +61,7 @@ public class IFolderWrapper implements IAbstractFolder {
}
if (obj instanceof IFolder) {
- return mFolder.equals((IFolder)obj);
+ return mFolder.equals(obj);
}
return super.equals(obj);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java
index 772fb52..0729881 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/NewItemSelectionDialog.java
@@ -17,7 +17,6 @@
package com.android.ide.eclipse.editors.ui.tree;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java
index b7dffdd..4a05b1e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java
@@ -79,8 +79,7 @@ import java.util.HashMap;
* To use this, instantiate somewhere in the UI and then:
* <ul>
* <li>Use {@link #setConfiguration(String)} or {@link #setConfiguration(FolderConfiguration)}.
- * <li>Retrieve the configuration using {@link #getConfiguration(FolderConfiguration)} and
- * test it using {@link FolderConfiguration#isValid()}.
+ * <li>Retrieve the configuration using {@link #getConfiguration(FolderConfiguration)}.
* </ul>
*/
public class ConfigurationSelector extends Composite {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
index b27dd4f..cc643be 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
@@ -30,6 +30,7 @@ import com.android.ide.eclipse.editors.resources.descriptors.ResourcesDescriptor
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.ConfigurationState;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
@@ -243,7 +244,7 @@ class NewXmlFileCreationPage extends WizardPage {
/** Absolute destination folder root, e.g. "/res/" */
private static String sResFolderAbs = AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP;
/** Relative destination folder root, e.g. "res/" */
- private static String sResFolderRel = AndroidConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
+ private static String sResFolderRel = SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
private IProject mProject;
private Text mProjectTextField;
@@ -629,7 +630,7 @@ class NewXmlFileCreationPage extends WizardPage {
// Disregard this folder selection if it doesn't point to /res/something
if (wsFolderPath != null &&
wsFolderPath.segmentCount() > 1 &&
- AndroidConstants.FD_RESOURCES.equals(wsFolderPath.segment(0))) {
+ SdkConstants.FD_RESOURCES.equals(wsFolderPath.segment(0))) {
score += 2;
} else {
wsFolderPath = null;
@@ -1002,7 +1003,7 @@ class NewXmlFileCreationPage extends WizardPage {
String fileName = getFileName();
if (fileName == null || fileName.length() == 0) {
error = "A destination file name is required.";
- } else if (fileName != null && !fileName.endsWith(AndroidConstants.DOT_XML)) {
+ } else if (!fileName.endsWith(AndroidConstants.DOT_XML)) {
error = String.format("The filename must end with %1$s.", AndroidConstants.DOT_XML);
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java
index d3ff334..6913ce0 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java
@@ -17,7 +17,6 @@
package com.android.ide.eclipse.editors.wizards;
import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.resources.IResourceRepository;
import com.android.ide.eclipse.common.resources.ResourceItem;
import com.android.ide.eclipse.common.resources.ResourceType;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/AndroidManifest.template b/eclipse/plugins/com.android.ide.eclipse.adt/templates/AndroidManifest.template
index b4faae6..b43e75f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/templates/AndroidManifest.template
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/templates/AndroidManifest.template
@@ -6,4 +6,5 @@
<application android:icon="@drawable/icon" android:label="APPLICATION_NAME">
ACTIVITIES
</application>
+USES-SDK
</manifest>
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/templates/uses-sdk.template b/eclipse/plugins/com.android.ide.eclipse.adt/templates/uses-sdk.template
new file mode 100644
index 0000000..8adae71
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/templates/uses-sdk.template
@@ -0,0 +1 @@
+ <uses-sdk android:minSdkVersion="MIN_SDK_VERSION" />
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/jar/example/Class1.java b/eclipse/plugins/com.android.ide.eclipse.tests/not_source_folder/jar/example/Class1.java
index 3cf1027..3cf1027 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/jar/example/Class1.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/not_source_folder/jar/example/Class1.java
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/jar/example/Class2.java b/eclipse/plugins/com.android.ide.eclipse.tests/not_source_folder/jar/example/Class2.java
index 4d15c47..4d15c47 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/jar/example/Class2.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/not_source_folder/jar/example/Class2.java
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/project/ProjectHelperTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/project/ProjectHelperTest.java
index 4700821..5a89d01 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/project/ProjectHelperTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/project/ProjectHelperTest.java
@@ -60,8 +60,9 @@ public class ProjectHelperTest extends TestCase {
ProjectHelper.fixProjectClasspathEntries(javaProject);
IClasspathEntry[] fixedEntries = javaProject.getRawClasspath();
- assertEquals(2, fixedEntries.length);
+ assertEquals(3, fixedEntries.length);
assertEquals("Project/src", fixedEntries[0].getPath().toString());
- assertEquals(CONTAINER_ID, fixedEntries[1].getPath().toString());
+ assertEquals(OLD_CONTAINER_ID, fixedEntries[1].getPath().toString());
+ assertEquals(CONTAINER_ID, fixedEntries[2].getPath().toString());
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/AndroidJarLoaderTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/AndroidJarLoaderTest.java
index f3d9b79..872938b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/AndroidJarLoaderTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/sdk/AndroidJarLoaderTest.java
@@ -45,6 +45,8 @@ public class AndroidJarLoaderTest extends TestCase {
@Override
public void tearDown() throws Exception {
+ mFrameworkClassLoader = null;
+ System.gc();
}
/** Preloads classes. They should load just fine. */
@@ -74,8 +76,9 @@ public class AndroidJarLoaderTest extends TestCase {
Class<?> c = _findClass(mFrameworkClassLoader, "jar.example.Class2"); //$NON-NLS-1$
assertEquals("jar.example.Class2", c.getName()); //$NON-NLS-1$
HashMap<String, Class<?>> map = getPrivateClassCache();
+ assertTrue(map.containsKey("jar.example.Class1")); //$NON-NLS-1$
assertTrue(map.containsKey("jar.example.Class2")); //$NON-NLS-1$
- assertEquals(1, map.size());
+ assertEquals(2, map.size());
}
/** call the protected method findClass */
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/resources/AttrsXmlParserTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/resources/AttrsXmlParserTest.java
index 76ebfc3..8338453 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/resources/AttrsXmlParserTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/resources/AttrsXmlParserTest.java
@@ -94,6 +94,33 @@ public class AttrsXmlParserTest extends TestCase {
assertEquals(Integer.valueOf(0), valueMap.get("horizontal"));
assertEquals(Integer.valueOf(1), valueMap.get("vertical"));
}
+
+ public final void testDeprecated() throws Exception {
+ mParser.preload();
+
+ DeclareStyleableInfo dep = mParser.getDeclareStyleableList().get("DeprecatedTest");
+ assertNotNull(dep);
+
+ AttributeInfo[] attrs = dep.getAttributes();
+ assertEquals(4, attrs.length);
+
+ assertEquals("deprecated-inline", attrs[0].getName());
+ assertEquals("In-line deprecated.", attrs[0].getDeprecatedDoc());
+ assertEquals("Deprecated comments using delimiters.", attrs[0].getJavaDoc());
+
+ assertEquals("deprecated-multiline", attrs[1].getName());
+ assertEquals("Multi-line version of deprecated that works till the next tag.",
+ attrs[1].getDeprecatedDoc());
+ assertEquals("Deprecated comments on their own line.", attrs[1].getJavaDoc());
+
+ assertEquals("deprecated-not", attrs[2].getName());
+ assertEquals(null, attrs[2].getDeprecatedDoc());
+ assertEquals("This attribute is not deprecated.", attrs[2].getJavaDoc());
+
+ assertEquals("deprecated-no-javadoc", attrs[3].getName());
+ assertEquals("There is no other javadoc here.", attrs[3].getDeprecatedDoc());
+ assertEquals("", attrs[3].getJavaDoc());
+ }
//---- access to private methods
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/descriptors/DescriptorsUtilsTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/descriptors/DescriptorsUtilsTest.java
index 5506459..69c3ed8 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/descriptors/DescriptorsUtilsTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/descriptors/DescriptorsUtilsTest.java
@@ -99,28 +99,26 @@ public class DescriptorsUtilsTest extends TestCase {
ElementDescriptor desc = new ElementDescriptor("application");
desc.setSdkUrl(DescriptorsUtils.MANIFEST_SDK_URL + "TagApplication");
String docBaseUrl = "http://base";
- assertEquals("<form><p></p></form>", DescriptorsUtils.formatFormText("", desc, docBaseUrl));
+ assertEquals("<form><li style=\"image\" value=\"image\"></li></form>", DescriptorsUtils.formatFormText("", desc, docBaseUrl));
- assertEquals("<form><p><a href=\"http://base/reference/android/R.styleable.html#TagApplication\">application</a></p></form>",
+ assertEquals("<form><li style=\"image\" value=\"image\"><a href=\"http://base/reference/android/R.styleable.html#TagApplication\">application</a></li></form>",
DescriptorsUtils.formatFormText(
"<code>application</code>",
desc, docBaseUrl));
- assertEquals("<form><p><b>android.content.Intent</b></p></form>",
+ assertEquals("<form><li style=\"image\" value=\"image\"><b>android.content.Intent</b></li></form>",
DescriptorsUtils.formatFormText(
"{@link android.content.Intent}",
desc, docBaseUrl));
- assertEquals("<form><p><a href=\"http://base/reference/android/R.styleable.html#AndroidManifestPermission\">AndroidManifestPermission</a></p></form>",
+ assertEquals("<form><li style=\"image\" value=\"image\"><a href=\"http://base/reference/android/R.styleable.html#AndroidManifestPermission\">AndroidManifestPermission</a></li></form>",
DescriptorsUtils.formatFormText(
"{@link #AndroidManifestPermission}",
desc, docBaseUrl));
- assertEquals("<form><p><a href=\"http://base/reference/android/R.styleable.html#AndroidManifestPermission\">\"permission\"</a></p></form>",
+ assertEquals("<form><li style=\"image\" value=\"image\"><a href=\"http://base/reference/android/R.styleable.html#AndroidManifestPermission\">\"permission\"</a></li></form>",
DescriptorsUtils.formatFormText(
"{@link #AndroidManifestPermission &lt;permission&gt;}",
desc, docBaseUrl));
-
}
-
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/configurations/TextInputMethodQualifierTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/configurations/TextInputMethodQualifierTest.java
index 9e01081..28f7871 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/configurations/TextInputMethodQualifierTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/configurations/TextInputMethodQualifierTest.java
@@ -54,11 +54,11 @@ public class TextInputMethodQualifierTest extends TestCase {
}
public void testNoKey() {
- assertEquals(true, timq.checkAndSet("nokey", config)); //$NON-NLS-1$
+ assertEquals(true, timq.checkAndSet("nokeys", config)); //$NON-NLS-1$
assertTrue(config.getTextInputMethodQualifier() != null);
assertEquals(TextInputMethodQualifier.TextInputMethod.NOKEY,
config.getTextInputMethodQualifier().getValue());
- assertEquals("nokey", config.getTextInputMethodQualifier().toString()); //$NON-NLS-1$
+ assertEquals("nokeys", config.getTextInputMethodQualifier().toString()); //$NON-NLS-1$
}
public void testFailures() {
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/manager/ConfigMatchTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/manager/ConfigMatchTest.java
index 0c1a508..25a86c3 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/manager/ConfigMatchTest.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/manager/ConfigMatchTest.java
@@ -41,7 +41,7 @@ public class ConfigMatchTest extends TestCase {
private static final String MISC2_FILENAME = "bar.xml"; //$NON-NLS-1$
private ProjectResources mResources;
- private ArrayList<ResourceQualifier> mQualifierList;
+ private ResourceQualifier[] mQualifierList;
private FolderConfiguration config4;
private FolderConfiguration config3;
private FolderConfiguration config2;
@@ -60,7 +60,7 @@ public class ConfigMatchTest extends TestCase {
qualifierListField.setAccessible(true);
// get the actual list.
- mQualifierList = (ArrayList<ResourceQualifier>)qualifierListField.get(manager);
+ mQualifierList = (ResourceQualifier[])qualifierListField.get(manager);
// create the project resources.
mResources = new ProjectResources(false /* isFrameworkRepository */);
@@ -191,10 +191,10 @@ public class ConfigMatchTest extends TestCase {
FolderConfiguration config = new FolderConfiguration();
// those must be of the same length
- assertEquals(qualifierValues.length, mQualifierList.size());
+ assertEquals(qualifierValues.length, mQualifierList.length);
int index = 0;
-
+
for (ResourceQualifier qualifier : mQualifierList) {
String value = qualifierValues[index++];
if (value != null) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/mock_attrs.xml b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/mock_attrs.xml
index e51604c..aa9a1f7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/mock_attrs.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/mock_attrs.xml
@@ -18,7 +18,7 @@
-->
<resources>
<!-- WARNING !!! THIS IS A MOCK FILE. DO NOT USE FOR DOCUMENTATION PURPOSES.
- This file has been trimmed down to only extract a number of interest cases
+ This file has been trimmed down to only extract a number of interesting cases
for unit tests.
What this contains:
@@ -314,5 +314,27 @@
<attr name="collapseColumns" format="string" />
</declare-styleable>
+ <!-- Test for deprecated attributes. -->
+ <declare-styleable name="DeprecatedTest">
+ <!-- Deprecated comments using delimiters.
+ Ignored. {@deprecated In-line deprecated.} {@ignore Ignored}.
+ -->
+ <attr name="deprecated-inline" />
+
+ <!-- Deprecated comments on their own line.
+ @deprecated Multi-line version of deprecated
+ that works till the next tag.
+ @ignore This tag must be ignored
+ -->
+ <attr name="deprecated-multiline" />
+
+ <!-- This attribute is not deprecated. -->
+ <attr name="deprecated-not" />
+
+ <!-- {@deprecated There is no other javadoc here. } -->
+ <attr name="deprecated-no-javadoc" format="boolean" />
+
+ </declare-styleable>
+
</resources>
diff --git a/eclipse/scripts/create_test_symlinks.sh b/eclipse/scripts/create_test_symlinks.sh
index 1479e04..931dce8 100755
--- a/eclipse/scripts/create_test_symlinks.sh
+++ b/eclipse/scripts/create_test_symlinks.sh
@@ -11,32 +11,38 @@ function back() {
echo $1 | sed 's@[^/]*@..@g'
}
+HOST=`uname`
+if [ "${HOST:0:6}" == "CYGWIN" ]; then
+ # We can't use symlinks under Cygwin
+ function cpdir() { # $1=dest $2=source
+ rsync -avW --delete-after $2 $1
+ }
+
+else
+ # For all other systems which support symlinks
+ function cpdir() { # $1=dest $2=source
+ ln -svf `back $1`/$2 $1
+ }
+fi
+
BASE="development/tools/eclipse/plugins/com.android.ide.eclipse.tests"
DEST=$BASE
BACK=`back $DEST`
-
HOST=`uname`
if [ "$HOST" == "Linux" ]; then
- DIR="ln -svf"
ln -svf $BACK/out/host/linux-x86/framework/kxml2-2.3.0.jar "$DEST/"
elif [ "$HOST" == "Darwin" ]; then
- DIR="ln -svf"
ln -svf $BACK/out/host/darwin-x86/framework/kxml2-2.3.0.jar "$DEST/"
elif [ "${HOST:0:6}" == "CYGWIN" ]; then
- DIR="rsync -avW --delete-after"
- JAR="kxml2-2.3.0.jar"
- if [ ! -f "$DEST/$JAR" ]; then
- # Get the jar from ADT if we can, otherwise download it.
- if [ -f "$DEST/../com.android.ide.eclipse.adt/$JAR" ]; then
- cp "$DEST/../com.android.ide.eclipse.adt/$JAR" "$DEST/$JAR"
- else
- wget -O "$DEST/$JAR" "http://internap.dl.sourceforge.net/sourceforge/kxml/$JAR"
- fi
- chmod a+rx "$DEST/$JAR"
+
+ if [ ! -f "$DEST/kxml2-2.3.0.jar" ]; then
+ cp -v "prebuilt/common/kxml2/kxml2-2.3.0.jar" "$DEST/"
+ chmod -v a+rx "$DEST"/*.jar
fi
+
else
echo "Unsupported platform ($HOST). Nothing done."
fi
@@ -44,5 +50,5 @@ fi
# create link to ddmlib tests
DEST=$BASE/unittests/com/android
BACK=`back $DEST`
-$DIR $BACK/development/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib $DEST/
+cpdir $DEST development/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib
diff --git a/emulator/qtools/dmtrace.cpp b/emulator/qtools/dmtrace.cpp
index a27193a..6d9250a 100644
--- a/emulator/qtools/dmtrace.cpp
+++ b/emulator/qtools/dmtrace.cpp
@@ -2,6 +2,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <unistd.h>
#include <inttypes.h>
#include <string.h>
#include "dmtrace.h"
diff --git a/scripts/android_rules.xml b/scripts/android_rules.xml
index bed5f24..1331696 100644
--- a/scripts/android_rules.xml
+++ b/scripts/android_rules.xml
@@ -1,112 +1,73 @@
<?xml version="1.0" ?>
<project name="android_rules" default="debug">
- <!-- No user servicable parts below. -->
-
- <property name="outdir-main" value="../${outdir}" />
-
- <property name="android-tools" value="${sdk-folder}/tools" />
- <property name="android-platform" value="${sdk-folder}/platforms/${target-folder}" />
- <property name="android-framework" value="${android-platform}/framework.aidl" />
- <property name="android-jar" value="${android-platform}/android.jar" />
+ <property name="android-tools" value="${sdk-location}/tools" />
<!-- Input directories -->
- <property name="resource-dir" value="res" />
- <property name="asset-dir" value="assets" />
- <property name="srcdir" value="src" />
- <condition property="srcdir-ospath"
- value="${basedir}\${srcdir}"
- else="${basedir}/${srcdir}" >
- <os family="windows"/>
- </condition>
+ <property name="source-folder" value="src" />
+ <property name="resource-folder" value="res" />
+ <property name="asset-folder" value="assets" />
+ <property name="source-location" value="${basedir}/${source-folder}" />
+ <available file="${basedir}/${asset-folder}" property="has.asset.folder"/>
<!-- folder for the 3rd party java libraries -->
<property name="external-libs" value="libs" />
- <condition property="external-libs-ospath"
- value="${basedir}\${external-libs}"
- else="${basedir}/${external-libs}" >
- <os family="windows"/>
- </condition>
+ <property name="external-libs-location" value="${basedir}/${external-libs}"/>
<!-- folder for the native libraries -->
<property name="native-libs" value="libs" />
- <condition property="native-libs-ospath"
- value="${basedir}\${native-libs}"
- else="${basedir}/${native-libs}" >
- <os family="windows"/>
- </condition>
+ <property name="native-libs-location" value="${basedir}/${native-libs}"/>
<!-- Output directories -->
- <property name="outdir-classes" value="${outdir}/classes" />
- <condition property="outdir-classes-ospath"
- value="${basedir}\${outdir-classes}"
- else="${basedir}/${outdir-classes}" >
- <os family="windows"/>
- </condition>
- <condition property="outdir-main-classes"
- value="${outdir-main}/classes">
- <available file="${outdir-main}/classes"
- type="dir"/>
- </condition>
+ <property name="out-folder" value="bin" />
+ <property name="out-classes" value="${out-folder}/classes" />
+ <property name="out-classes-location" value="${basedir}/${out-classes}"/>
+ <!-- out folders for a parent project if this project is an instrumentation project -->
+ <property name="main-out-folder" value="../${out-folder}" />
+ <property name="main-out-classes" value="${main-out-folder}/classes"/>
<!-- Create R.java in the source directory -->
- <property name="outdir-r" value="src" />
+ <property name="r-folder" value="${source-folder}" />
<!-- Intermediate files -->
<property name="dex-file" value="classes.dex" />
- <property name="intermediate-dex" value="${outdir}/${dex-file}" />
- <condition property="intermediate-dex-ospath"
+ <property name="intermediate-dex" value="${out-folder}/${dex-file}" />
+ <!-- dx does not properly support incorrect / or \ based on the platform
+ and Ant cannot convert them because the parameter is not a valid path.
+ Because of this we have to compute different paths depending on the platform. -->
+ <condition property="intermediate-dex-location"
value="${basedir}\${intermediate-dex}"
else="${basedir}/${intermediate-dex}" >
<os family="windows"/>
</condition>
<!-- The final package file to generate -->
- <property name="resources-package" value="${outdir}/${ant.project.name}.ap_" />
- <condition property="resources-package-ospath"
- value="${basedir}\${resources-package}"
- else="${basedir}/${resources-package}" >
- <os family="windows"/>
- </condition>
+ <property name="resources-package" value="${out-folder}/${ant.project.name}.ap_"/>
+ <property name="resources-package-location" value="${basedir}/${resources-package}"/>
- <property name="out-debug-package" value="${outdir}/${ant.project.name}-debug.apk" />
- <condition property="out-debug-package-ospath"
- value="${basedir}\${out-debug-package}"
- else="${basedir}/${out-debug-package}" >
- <os family="windows"/>
- </condition>
+ <property name="out-debug-package" value="${out-folder}/${ant.project.name}-debug.apk"/>
+ <property name="out-debug-package-location" value="${basedir}/${out-debug-package}"/>
- <property name="out-unsigned-package" value="${outdir}/${ant.project.name}-unsigned.apk" />
- <condition property="out-unsigned-package-ospath"
- value="${basedir}\${out-unsigned-package}"
- else="${basedir}/${out-unsigned-package}" >
- <os family="windows"/>
- </condition>
+ <property name="out-unsigned-package" value="${out-folder}/${ant.project.name}-unsigned.apk"/>
+ <property name="out-unsigned-package-location" value="${basedir}/${out-unsigned-package}"/>
<!-- Tools -->
- <condition property="aapt" value="${android-tools}/aapt.exe" else="${android-tools}/aapt" >
- <os family="windows"/>
- </condition>
- <condition property="aidl" value="${android-tools}/aidl.exe" else="${android-tools}/aidl" >
- <os family="windows"/>
- </condition>
- <condition property="adb" value="${android-tools}/adb.exe" else="${android-tools}/adb" >
- <os family="windows"/>
- </condition>
- <condition property="dx" value="${android-tools}/dx.bat" else="${android-tools}/dx" >
- <os family="windows"/>
- </condition>
- <condition property="apk-builder" value="${android-tools}/apkbuilder.bat" else="${android-tools}/apkbuilder" >
- <os family="windows"/>
- </condition>
+ <condition property="exe" value="exe" else=""><os family="windows"/></condition>
+ <condition property="bat" value="bat" else=""><os family="windows"/></condition>
- <!-- Rules -->
+ <property name="aapt" value="${android-tools}/aapt${exe}"/>
+ <property name="aidl" value="${android-tools}/aidl${exe}"/>
+ <property name="adb" value="${android-tools}/adb${exe}"/>
+ <property name="dx" value="${android-tools}/dx${bat}"/>
+ <property name="apk-builder" value="${android-tools}/apkbuilder${bat}"/>
+
+ <!-- rules -->
<!-- Create the output directories if they don't exist yet. -->
<target name="dirs">
<echo>Creating output directories if needed...</echo>
- <mkdir dir="${outdir}" />
- <mkdir dir="${outdir-classes}" />
+ <mkdir dir="${out-folder}" />
+ <mkdir dir="${out-classes}" />
</target>
<!-- Generate the R.java file for this project's resources. -->
@@ -116,13 +77,13 @@
<arg value="package" />
<arg value="-m" />
<arg value="-J" />
- <arg value="${outdir-r}" />
+ <arg path="${r-folder}" />
<arg value="-M" />
- <arg value="AndroidManifest.xml" />
+ <arg path="AndroidManifest.xml" />
<arg value="-S" />
- <arg value="${resource-dir}" />
+ <arg path="${resource-folder}" />
<arg value="-I" />
- <arg value="${android-jar}" />
+ <arg path="${android-jar}" />
</exec>
</target>
@@ -130,9 +91,9 @@
<target name="aidl" depends="dirs">
<echo>Compiling aidl files into Java classes...</echo>
<apply executable="${aidl}" failonerror="true">
- <arg value="-p${android-framework}" />
- <arg value="-I${srcdir}" />
- <fileset dir="${srcdir}">
+ <arg value="-p${android-aidl}" />
+ <arg value="-I${source-folder}" />
+ <fileset dir="${source-folder}">
<include name="**/*.aidl"/>
</fileset>
</apply>
@@ -141,113 +102,102 @@
<!-- Compile this project's .java files into .class files. -->
<target name="compile" depends="dirs, resource-src, aidl">
<javac encoding="ascii" target="1.5" debug="true" extdirs=""
- srcdir="${srcdir}"
- destdir="${outdir-classes}"
+ srcdir="${source-folder}"
+ destdir="${out-classes}"
bootclasspath="${android-jar}">
<classpath>
<fileset dir="${external-libs}" includes="*.jar"/>
- <pathelement path="${outdir-main-classes}"/>
+ <pathelement path="${main-out-classes}"/>
</classpath>
</javac>
</target>
<!-- Convert this project's .class files into .dex files. -->
<target name="dex" depends="compile">
- <echo>Converting compiled files and external libraries into ${outdir}/${dex-file}...</echo>
+ <echo>Converting compiled files and external libraries into ${out-folder}/${dex-file}...</echo>
<apply executable="${dx}" failonerror="true" parallel="true">
<arg value="--dex" />
- <arg value="--output=${intermediate-dex-ospath}" />
- <arg path="${outdir-classes-ospath}" />
+ <arg value="--output=${intermediate-dex-location}" />
+ <arg path="${out-classes-location}" />
<fileset dir="${external-libs}" includes="*.jar"/>
</apply>
</target>
<!-- Put the project's resources into the output package file. -->
- <target name="package-res-and-assets">
+ <target name="package-res-and-assets" if="has.asset.folder">
<echo>Packaging resources and assets...</echo>
<exec executable="${aapt}" failonerror="true">
<arg value="package" />
<arg value="-f" />
<arg value="-M" />
- <arg value="AndroidManifest.xml" />
+ <arg path="AndroidManifest.xml" />
<arg value="-S" />
- <arg value="${resource-dir}" />
+ <arg path="${resource-folder}" />
<arg value="-A" />
- <arg value="${asset-dir}" />
+ <arg path="${asset-folder}" />
<arg value="-I" />
- <arg value="${android-jar}" />
+ <arg path="${android-jar}" />
<arg value="-F" />
<arg value="${resources-package}" />
</exec>
</target>
- <!-- Same as package-res-and-assets, but without "-A ${asset-dir}" -->
- <target name="package-res-no-assets">
+ <!-- Same as package-res-and-assets, but without "-A ${asset-folder}" -->
+ <target name="package-res-no-assets" unless="has.asset.folder">
<echo>Packaging resources...</echo>
<exec executable="${aapt}" failonerror="true">
<arg value="package" />
<arg value="-f" />
<arg value="-M" />
- <arg value="AndroidManifest.xml" />
+ <arg path="AndroidManifest.xml" />
<arg value="-S" />
- <arg value="${resource-dir}" />
+ <arg path="${resource-folder}" />
<!-- No assets directory -->
<arg value="-I" />
- <arg value="${android-jar}" />
+ <arg path="${android-jar}" />
<arg value="-F" />
- <arg value="${resources-package}" />
+ <arg path="${resources-package}" />
</exec>
</target>
- <!-- Invoke the proper target depending on whether or not
- an assets directory is present. -->
- <!-- TODO: find a nicer way to include the "-A ${asset-dir}" argument
- only when the assets dir exists. -->
- <target name="package-res">
- <available file="${asset-dir}" type="dir"
- property="res-target" value="and-assets" />
- <property name="res-target" value="no-assets" />
- <antcall target="package-res-${res-target}" />
- </target>
-
<!-- Package the application and sign it with a debug key.
This is the default target when building. It is used for debug. -->
- <target name="debug" depends="dex, package-res">
+ <target name="debug" depends="dex, package-res-and-assets, package-res-no-assets">
<echo>Packaging ${out-debug-package}, and signing it with a debug key...</echo>
<exec executable="${apk-builder}" failonerror="true">
- <arg value="${out-debug-package-ospath}" />
+ <arg value="${out-debug-package-location}" />
<arg value="-z" />
- <arg value="${resources-package-ospath}" />
+ <arg path="${resources-package-location}" />
<arg value="-f" />
- <arg value="${intermediate-dex-ospath}" />
+ <arg path="${intermediate-dex-location}" />
<arg value="-rf" />
- <arg value="${srcdir-ospath}" />
+ <arg path="${source-location}" />
<arg value="-rj" />
- <arg value="${external-libs-ospath}" />
+ <arg path="${external-libs-location}" />
<arg value="-nf" />
- <arg value="${native-libs-ospath}" />
+ <arg path="${native-libs-location}" />
</exec>
</target>
<!-- Package the application without signing it.
This allows for the application to be signed later with an official publishing key. -->
- <target name="release" depends="dex, package-res">
+ <target name="release" depends="dex, package-res-and-assets, package-res-no-assets">
<echo>Packaging ${out-unsigned-package} for release...</echo>
<exec executable="${apk-builder}" failonerror="true">
- <arg value="${out-unsigned-package-ospath}" />
+ <arg value="${out-unsigned-package-location}" />
<arg value="-u" />
<arg value="-z" />
- <arg value="${resources-package-ospath}" />
+ <arg path="${resources-package-location}" />
<arg value="-f" />
- <arg value="${intermediate-dex-ospath}" />
+ <arg path="${intermediate-dex-location}" />
<arg value="-rf" />
- <arg value="${srcdir-ospath}" />
+ <arg path="${source-location}" />
<arg value="-rj" />
- <arg value="${external-libs-ospath}" />
+ <arg path="${external-libs-location}" />
<arg value="-nf" />
- <arg value="${native-libs-ospath}" />
+ <arg path="${native-libs-location}" />
</exec>
- <echo>It will need to be signed with jarsigner before being published.</echo>
+ <echo>It will need to be signed with jarsigner before it is published.</echo>
</target>
<!-- Install the package on the default emulator -->
@@ -255,7 +205,7 @@
<echo>Installing ${out-debug-package} onto default emulator...</echo>
<exec executable="${adb}" failonerror="true">
<arg value="install" />
- <arg value="${out-debug-package}" />
+ <arg path="${out-debug-package}" />
</exec>
</target>
@@ -264,7 +214,7 @@
<exec executable="${adb}" failonerror="true">
<arg value="install" />
<arg value="-r" />
- <arg value="${out-debug-package}" />
+ <arg path="${out-debug-package}" />
</exec>
</target>
@@ -273,8 +223,25 @@
<echo>Uninstalling ${application-package} from the default emulator...</echo>
<exec executable="${adb}" failonerror="true">
<arg value="uninstall" />
- <arg value="${application-package}" />
+ <arg path="${application-package}" />
</exec>
</target>
-
+
+ <target name="help">
+ <!-- displays starts at col 13
+ |13 80| -->
+ <echo>Android Ant Build. Available targets:</echo>
+ <echo> help: Displays this help.</echo>
+ <echo> debug: Builds the application and sign it with a debug key.</echo>
+ <echo> release: Builds the application. The generated apk file must be</echo>
+ <echo> signed before it is published.</echo>
+ <echo> install: Installs the debug package onto a running emulator or</echo>
+ <echo> device. This can only be used if the application has </echo>
+ <echo> not yet been installed.</echo>
+ <echo> reinstall: Installs the debug package on a running emulator or</echo>
+ <echo> device that already has the application.</echo>
+ <echo> The signatures must match.</echo>
+ <echo> uninstall: uninstall the application from a running emulator or</echo>
+ <echo> device.</echo>
+ </target>
</project>
diff --git a/scripts/build.template b/scripts/build.template
index f04f1d8..c1afef8 100644
--- a/scripts/build.template
+++ b/scripts/build.template
@@ -1,30 +1,51 @@
<?xml version="1.0" ?>
-<project name="ACTIVITY_NAME" default="debug">
+<project name="ACTIVITY_NAME" default="help">
+
+ <!-- The local.properties file is created and updated by the 'android' tool.
+ It contain the path to the SDK. It should *NOT* be checked in in Version
+ Control Systems. -->
+ <property file="local.properties"/>
<!-- The build.properties file can be created by you and is never touched
- by activitycreator. If you want to manually set properties, this is
- the best place to set them. -->
- <property file="build.properties"/>
+ by the 'android' tool. This is the place to change some of the default property values
+ used by the Ant rules.
+ Here are some properties you may want to change/update:
+
+ application-package
+ the name of your application package as defined in the manifest. Used by the
+ 'uninstall' rule.
+ source-folder
+ the name of the source folder. Default is 'src'.
+ out-folder
+ the name of the output folder. Default is 'bin'.
- <!-- The default.properties file is created and updated by activitycreator.
- It will set any properties not already defined by build.properties. -->
- <property file="default.properties"/>
+ Properties related to the SDK location or the project target should be updated
+ using the 'android' tool with the 'update' action.
- <!-- ************************************************************************************* -->
- <!-- These settings were written by activitycreator.
- Do not change them unless you really know what you are doing. -->
+ This file is an integral part of the build system for your application and
+ should be checked in in Version Control Systems.
- <!-- Application Package Name -->
- <property name="application-package" value="PACKAGE" />
+ -->
+ <property file="build.properties"/>
- <!-- The intermediates directory, Eclipse uses "bin"
- for its own output, so we do the same. -->
- <property name="outdir" value="bin" />
-
- <!-- ************************************************************************************* -->
- <!-- Import the default Android build rules.
- This requires ant 1.6.0 or above. -->
+ <!-- The default.properties file is created and updated by the 'android' tool, as well
+ as ADT.
+ This file is an integral part of the build system for your application and
+ should be checked in in Version Control Systems. -->
+ <property file="default.properties"/>
- <import file="${sdk-folder}/tools/lib/android_rules.xml" />
+ <!-- Custom Android task to deal with the project target, and import the proper rules.
+ This requires ant 1.6.0 or above. -->
+ <path id="android.antlibs">
+ <pathelement path="${sdk-location}/tools/lib/anttasks.jar" />
+ <pathelement path="${sdk-location}/tools/lib/sdklib.jar" />
+ <pathelement path="${sdk-location}/tools/lib/androidprefs.jar" />
+ </path>
+
+ <taskdef name="androidinit" classname="com.android.ant.AndroidInitTask"
+ classpathref="android.antlibs"/>
+ <!-- Class the Android Init task that will import the proper rule file containing
+ all the Ant targets -->
+ <androidinit />
</project>
diff --git a/scripts/default.properties.template b/scripts/default.properties.template
deleted file mode 100644
index 63df494..0000000
--- a/scripts/default.properties.template
+++ /dev/null
@@ -1,18 +0,0 @@
-# This file is automatically generated by activitycreator.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-# Instead customize values in a "build.properties" file.
-
-# location of the SDK
-sdk-folder=ANDROID_SDK_FOLDER
-
-# target mode. Value can be "platform" or "add-on"
-target-mode=TARGET_MODE
-
-# target API level.
-target-api=TARGET_API
-
-# target name, if target-mode=add-on
-target-name=TARGET_NAME
-
-# target platform. This is either the target itself or the platform the add-on is based on.
-target-folder=TARGET_FOLDER
diff --git a/sdkmanager/app/.classpath b/sdkmanager/app/.classpath
index 45c59d3..cbd9d37 100644
--- a/sdkmanager/app/.classpath
+++ b/sdkmanager/app/.classpath
@@ -1,9 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="tests"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
<classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/>
<classpathentry combineaccessrules="false" kind="src" path="/SdkUiLib"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java b/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
new file mode 100644
index 0000000..ef3d0ee
--- /dev/null
+++ b/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdkmanager;
+
+import java.util.HashMap;
+import java.util.Map.Entry;
+
+/**
+ * Parses the command-line and stores flags needed or requested.
+ * <p/>
+ * This is a base class. To be useful you want to:
+ * <ul>
+ * <li>override it.
+ * <li>pass an action array to the constructor.
+ * <li>define flags for your actions.
+ * </ul>
+ * <p/>
+ * To use, call {@link #parseArgs(String[])} and then call {@link #getValue(String, String)}.
+ */
+public class CommandLineProcessor {
+
+ /** Internal action name for all global flags. */
+ public final static String GLOBAL_FLAG = "global";
+ /** Internal action name for internally hidden flags.
+ * This is currently used to store the requested action name. */
+ public final static String INTERNAL_FLAG = "internal";
+
+ /** The global help flag. */
+ public static final String KEY_HELP = "help";
+ /** The global verbose flag. */
+ public static final String KEY_VERBOSE = "verbose";
+ /** The internal action flag. */
+ public static final String KEY_ACTION = "action";
+
+ /** List of available actions.
+ * <p/>
+ * Each entry must be a 2-string array with first the action name and then
+ * a description.
+ */
+ private final String[][] mActions;
+ /** The hash of all defined arguments.
+ * <p/>
+ * The key is a string "action/longName".
+ */
+ private final HashMap<String, Arg> mArguments = new HashMap<String, Arg>();
+
+ public CommandLineProcessor(String[][] actions) {
+ mActions = actions;
+
+ define(MODE.STRING, false, INTERNAL_FLAG, null, KEY_ACTION, "Selected Action", null);
+
+ define(MODE.BOOLEAN, false, GLOBAL_FLAG, "v", KEY_VERBOSE, "Verbose mode", false);
+ define(MODE.BOOLEAN, false, GLOBAL_FLAG, "h", KEY_HELP, "This help", false);
+ }
+
+ //------------------
+ // Helpers to get flags values
+
+ /** Helper that returns true if --verbose was requested. */
+ public boolean isVerbose() {
+ return ((Boolean) getValue(GLOBAL_FLAG, KEY_VERBOSE)).booleanValue();
+ }
+
+ /** Helper that returns true if --help was requested. */
+ public boolean isHelpRequested() {
+ return ((Boolean) getValue(GLOBAL_FLAG, KEY_HELP)).booleanValue();
+ }
+
+ /** Helper that returns the requested action name. */
+ public String getActionRequested() {
+ return (String) getValue(INTERNAL_FLAG, KEY_ACTION);
+ }
+
+ //------------------
+
+ /**
+ * Raw access to parsed parameter values.
+ * @param action The action name, including {@link #GLOBAL_FLAG} and {@link #INTERNAL_FLAG}
+ * @param longFlagName The long flag name for the given action.
+ * @return The current value object stored in the parameter, which depends on the argument mode.
+ */
+ public Object getValue(String action, String longFlagName) {
+ String key = action + "/" + longFlagName;
+ Arg arg = mArguments.get(key);
+ return arg.getCurrentValue();
+ }
+
+ /**
+ * Internal setter for raw parameter value.
+ * @param action The action name, including {@link #GLOBAL_FLAG} and {@link #INTERNAL_FLAG}
+ * @param longFlagName The long flag name for the given action.
+ * @param value The new current value object stored in the parameter, which depends on the
+ * argument mode.
+ */
+ protected void setValue(String action, String longFlagName, Object value) {
+ String key = action + "/" + longFlagName;
+ Arg arg = mArguments.get(key);
+ arg.setCurrentValue(value);
+ }
+
+ /**
+ * Parses the command-line arguments.
+ * <p/>
+ * This method will exit and not return if a parsing error arise.
+ *
+ * @param args The arguments typically received by a main method.
+ */
+ public void parseArgs(String[] args) {
+ String needsHelp = null;
+ String action = null;
+
+ int n = args.length;
+ for (int i = 0; i < n; i++) {
+ Arg arg = null;
+ String a = args[i];
+ if (a.startsWith("--")) {
+ arg = findLongArg(action, a.substring(2));
+ } else if (a.startsWith("-")) {
+ arg = findShortArg(action, a.substring(1));
+ }
+
+ // Not a keyword and we don't have an action yet, this should be an action
+ if (arg == null && action == null) {
+
+ if (a.startsWith("-")) {
+ // Got a keyword but not valid for global flags
+ needsHelp = String.format(
+ "Flag '%1$s' is not a valid global flag. Did you mean to specify it after the action name?",
+ a, action);
+ break;
+ }
+
+ for (String[] actionDesc : mActions) {
+ if (actionDesc[0].equals(a)) {
+ action = a;
+ break;
+ }
+ }
+
+ if (action == null) {
+ needsHelp = String.format(
+ "Expected action name after global parameters but found %1$s instead.",
+ a);
+ break;
+ }
+ } else if (arg == null && action != null) {
+ // Got a keyword but not valid for the current action
+ needsHelp = String.format(
+ "Flag '%1$s' is not valid for action '%2$s'.",
+ a, action);
+ break;
+
+ } else if (arg != null) {
+ // Process keyword
+ String error = null;
+ if (arg.getMode().needsExtra()) {
+ if (++i >= n) {
+ needsHelp = String.format("Missing argument for flag %1$s.", a);
+ break;
+ }
+
+ error = arg.getMode().process(arg, args[i]);
+ } else {
+ error = arg.getMode().process(arg, null);
+
+ // If we just toggled help, we want to exit now without printing any error.
+ // We do this test here only when a Boolean flag is toggled since booleans
+ // are the only flags that don't take parameters and help is a boolean.
+ if (isHelpRequested()) {
+ printHelpAndExit(null);
+ // The call above should terminate however in unit tests we override
+ // it so we still need to return here.
+ return;
+ }
+ }
+
+ if (error != null) {
+ needsHelp = String.format("Invalid usage for flag %1$s: %2$s.", a, error);
+ break;
+ }
+ }
+ }
+
+ if (needsHelp == null) {
+ if (action == null) {
+ needsHelp = "Missing action name.";
+ } else {
+ // Validate that all mandatory arguments are non-null for this action
+ for (Entry<String, Arg> entry : mArguments.entrySet()) {
+ Arg arg = entry.getValue();
+ if (arg.getAction().equals(action)) {
+ if (arg.isMandatory() && arg.getCurrentValue() == null) {
+ needsHelp = String.format("The parameter --%1$s must be defined for action '%2$s'",
+ arg.getLongArg(),
+ action);
+ break;
+ }
+ }
+ }
+
+ setValue(INTERNAL_FLAG, KEY_ACTION, action);
+ }
+ }
+
+ if (needsHelp != null) {
+ printHelpAndExitForAction(action, needsHelp);
+ }
+ }
+
+ /**
+ * Finds an {@link Arg} given an action name and a long flag name.
+ * @return The {@link Arg} found or null.
+ */
+ protected Arg findLongArg(String action, String longName) {
+ if (action == null) {
+ action = GLOBAL_FLAG;
+ }
+ String key = action + "/" + longName;
+ return mArguments.get(key);
+ }
+
+ /**
+ * Finds an {@link Arg} given an action name and a short flag name.
+ * @return The {@link Arg} found or null.
+ */
+ protected Arg findShortArg(String action, String shortName) {
+ if (action == null) {
+ action = GLOBAL_FLAG;
+ }
+
+ for (Entry<String, Arg> entry : mArguments.entrySet()) {
+ Arg arg = entry.getValue();
+ if (arg.getAction().equals(action)) {
+ if (shortName.equals(arg.getShortArg())) {
+ return arg;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Prints the help/usage and exits.
+ *
+ * @param errorFormat Optional error message to print prior to usage using String.format
+ * @param args Arguments for String.format
+ */
+ public void printHelpAndExit(String errorFormat, Object... args) {
+ printHelpAndExitForAction(null /*actionFilter*/, errorFormat, args);
+ }
+
+ /**
+ * Prints the help/usage and exits.
+ *
+ * @param actionFilter If null, displays help for all actions. If not null, display help only
+ * for that specific action. In all cases also display general usage and action list.
+ * @param errorFormat Optional error message to print prior to usage using String.format
+ * @param args Arguments for String.format
+ */
+ public void printHelpAndExitForAction(String actionFilter, String errorFormat, Object... args) {
+ if (errorFormat != null) {
+ stderr(errorFormat, args);
+ }
+
+ /*
+ * usage should fit in 80 columns
+ * 12345678901234567890123456789012345678901234567890123456789012345678901234567890
+ */
+ stdout("\n" +
+ "Usage:\n" +
+ " android [global options] action [action options]\n" +
+ "\n" +
+ "Global options:");
+ listOptions(GLOBAL_FLAG);
+
+ stdout("\nValid actions:");
+ for (String[] action : mActions) {
+ String filler = "";
+ int len = action[0].length();
+ if (len < 10) {
+ filler = " ".substring(len);
+ }
+
+ stdout("- %1$s:%2$s %3$s", action[0], filler, action[1]);
+ }
+
+ for (String[] action : mActions) {
+ if (actionFilter == null || actionFilter.equals(action[0])) {
+ stdout("\nAction \"%1$s\":", action[0]);
+ stdout(" %1$s", action[1]);
+ stdout("Options:");
+ listOptions(action[0]);
+ }
+ }
+
+ exit();
+ }
+
+ /**
+ * Internal helper to print all the option flags for a given action name.
+ */
+ protected void listOptions(String action) {
+ int numOptions = 0;
+ for (Entry<String, Arg> entry : mArguments.entrySet()) {
+ Arg arg = entry.getValue();
+ if (arg.getAction().equals(action)) {
+
+ String value = null;
+ if (arg.getDefaultValue() instanceof String[]) {
+ value = "";
+ for (String v : (String[]) arg.getDefaultValue()) {
+ if (value.length() > 0) {
+ value += "|";
+ }
+ value += v;
+ }
+ } else if (arg.getDefaultValue() != null) {
+ value = arg.getDefaultValue().toString();
+ }
+
+ stdout(" -%1$s %2$-10s %3$s%4$s",
+ arg.getShortArg(),
+ "--" + arg.getLongArg(),
+ arg.getDescription(),
+ value == null ? "" : " (" + value + ")");
+ numOptions++;
+ }
+ }
+
+ if (numOptions == 0) {
+ stdout(" No options");
+ }
+ }
+
+ //----
+
+ /**
+ * The mode of an argument specifies the type of variable it represents,
+ * whether an extra parameter is required after the flag and how to parse it.
+ */
+ static enum MODE {
+ /** Argument value is a Boolean. Default value is a Boolean. */
+ BOOLEAN {
+ @Override
+ public boolean needsExtra() {
+ return false;
+ }
+ @Override
+ public String process(Arg arg, String extra) {
+ // Toggle the current value
+ arg.setCurrentValue(! ((Boolean) arg.getCurrentValue()).booleanValue());
+ return null;
+ }
+ },
+
+ /** Argument value is an Integer. Default value is an Integer. */
+ INTEGER {
+ @Override
+ public boolean needsExtra() {
+ return true;
+ }
+ @Override
+ public String process(Arg arg, String extra) {
+ try {
+ arg.setCurrentValue(Integer.parseInt(extra));
+ return null;
+ } catch (NumberFormatException e) {
+ return String.format("Failed to parse '%1$s' as an integer: %2%s",
+ extra, e.getMessage());
+ }
+ }
+ },
+
+ /** Argument value is a String. Default value is a String[]. */
+ ENUM {
+ @Override
+ public boolean needsExtra() {
+ return true;
+ }
+ @Override
+ public String process(Arg arg, String extra) {
+ StringBuilder desc = new StringBuilder();
+ String[] values = (String[]) arg.getDefaultValue();
+ for (String value : values) {
+ if (value.equals(extra)) {
+ arg.setCurrentValue(extra);
+ return null;
+ }
+
+ if (desc.length() != 0) {
+ desc.append(", ");
+ }
+ desc.append(value);
+ }
+
+ return String.format("'%1$s' is not one of %2$s", extra, desc.toString());
+ }
+ },
+
+ /** Argument value is a String. Default value is a null. */
+ STRING {
+ @Override
+ public boolean needsExtra() {
+ return true;
+ }
+ @Override
+ public String process(Arg arg, String extra) {
+ arg.setCurrentValue(extra);
+ return null;
+ }
+ };
+
+ /**
+ * Returns true if this mode requires an extra parameter.
+ */
+ public abstract boolean needsExtra();
+
+ /**
+ * Processes the flag for this argument.
+ *
+ * @param arg The argument being processed.
+ * @param extra The extra parameter. Null if {@link #needsExtra()} returned false.
+ * @return An error string or null if there's no error.
+ */
+ public abstract String process(Arg arg, String extra);
+ }
+
+ /**
+ * An argument accepted by the command-line, also called "a flag".
+ * Arguments must have a short version (one letter), a long version name and a description.
+ * They can have a default value, or it can be null.
+ * Depending on the {@link MODE}, the default value can be a Boolean, an Integer, a String
+ * or a String array (in which case the first item is the current by default.)
+ */
+ static class Arg {
+ private final String mAction;
+ private final String mShortName;
+ private final String mLongName;
+ private final String mDescription;
+ private final Object mDefaultValue;
+ private Object mCurrentValue;
+ private final MODE mMode;
+ private final boolean mMandatory;
+
+ /**
+ * Creates a new argument flag description.
+ *
+ * @param mode The {@link MODE} for the argument.
+ * @param mandatory True if this argument is mandatory for this action.
+ * @param action The action name. Can be #GLOBAL_FLAG or #INTERNAL_FLAG.
+ * @param shortName The one-letter short argument name. Cannot be empty nor null.
+ * @param longName The long argument name. Cannot be empty nor null.
+ * @param description The description. Cannot be null.
+ * @param defaultValue The default value (or values), which depends on the selected {@link MODE}.
+ */
+ public Arg(MODE mode,
+ boolean mandatory,
+ String action,
+ String shortName,
+ String longName,
+ String description,
+ Object defaultValue) {
+ mMode = mode;
+ mMandatory = mandatory;
+ mAction = action;
+ mShortName = shortName;
+ mLongName = longName;
+ mDescription = description;
+ mDefaultValue = defaultValue;
+ if (defaultValue instanceof String[]) {
+ mCurrentValue = ((String[])defaultValue)[0];
+ } else {
+ mCurrentValue = mDefaultValue;
+ }
+ }
+
+ public boolean isMandatory() {
+ return mMandatory;
+ }
+
+ public String getShortArg() {
+ return mShortName;
+ }
+
+ public String getLongArg() {
+ return mLongName;
+ }
+
+ public String getDescription() {
+ return mDescription;
+ }
+
+ public String getAction() {
+ return mAction;
+ }
+
+ public Object getDefaultValue() {
+ return mDefaultValue;
+ }
+
+ public Object getCurrentValue() {
+ return mCurrentValue;
+ }
+
+ public void setCurrentValue(Object currentValue) {
+ mCurrentValue = currentValue;
+ }
+
+ public MODE getMode() {
+ return mMode;
+ }
+ }
+
+ /**
+ * Internal helper to define a new argument for a give action.
+ *
+ * @param mode The {@link MODE} for the argument.
+ * @param action The action name. Can be #GLOBAL_FLAG or #INTERNAL_FLAG.
+ * @param shortName The one-letter short argument name. Cannot be empty nor null.
+ * @param longName The long argument name. Cannot be empty nor null.
+ * @param description The description. Cannot be null.
+ * @param defaultValue The default value (or values), which depends on the selected {@link MODE}.
+ */
+ protected void define(MODE mode,
+ boolean mandatory,
+ String action,
+ String shortName, String longName,
+ String description, Object defaultValue) {
+ assert(mandatory || mode == MODE.BOOLEAN); // a boolean mode cannot be mandatory
+
+ String key = action + "/" + longName;
+ mArguments.put(key, new Arg(mode, mandatory,
+ action, shortName, longName, description, defaultValue));
+ }
+
+ /**
+ * Exits in case of error.
+ * This is protected so that it can be overridden in unit tests.
+ */
+ protected void exit() {
+ System.exit(1);
+ }
+
+ /**
+ * Prints a line to stdout.
+ * This is protected so that it can be overridden in unit tests.
+ *
+ * @param format The string to be formatted. Cannot be null.
+ * @param args Format arguments.
+ */
+ protected void stdout(String format, Object...args) {
+ System.out.println(String.format(format, args));
+ }
+
+ /**
+ * Prints a line to stderr.
+ * This is protected so that it can be overridden in unit tests.
+ *
+ * @param format The string to be formatted. Cannot be null.
+ * @param args Format arguments.
+ */
+ protected void stderr(String format, Object...args) {
+ System.err.println(String.format(format, args));
+ }
+}
diff --git a/sdkmanager/app/src/com/android/sdkmanager/Main.java b/sdkmanager/app/src/com/android/sdkmanager/Main.java
index 72bd2aa..3bcb9a3 100644
--- a/sdkmanager/app/src/com/android/sdkmanager/Main.java
+++ b/sdkmanager/app/src/com/android/sdkmanager/Main.java
@@ -23,6 +23,8 @@ import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
+import com.android.sdklib.project.ProjectCreator;
+import com.android.sdklib.project.ProjectCreator.OutputLevel;
import com.android.sdklib.vm.HardwareProperties;
import com.android.sdklib.vm.VmManager;
import com.android.sdklib.vm.HardwareProperties.HardwareProperty;
@@ -35,31 +37,20 @@ import java.util.List;
import java.util.Map;
/**
- * Main class for the 'android' application
- *
+ * Main class for the 'android' application.
*/
class Main {
private final static String TOOLSDIR = "com.android.sdkmanager.toolsdir";
- private final static String ARG_LIST_TARGET = "target";
- private final static String ARG_LIST_VM = "vm";
-
private final static String[] BOOLEAN_YES_REPLIES = new String[] { "yes", "y" };
private final static String[] BOOLEAN_NO_REPLIES = new String[] { "no", "n" };
private String mSdkFolder;
+ private ISdkLog mSdkLog;
private SdkManager mSdkManager;
private VmManager mVmManager;
-
- /* --list parameters */
- private String mListObject;
-
- /* --create parameters */
- private boolean mCreateVm;
- private int mCreateTargetId;
- private IAndroidTarget mCreateTarget;
- private String mCreateName;
+ private SdkCommandLine mSdkCommandLine;
public static void main(String[] args) {
new Main().run(args);
@@ -71,7 +62,7 @@ class Main {
*/
private void run(String[] args) {
init();
- parseArgs(args);
+ mSdkCommandLine.parseArgs(args);
parseSdk();
doAction();
}
@@ -81,70 +72,41 @@ class Main {
* doing basic parsing of the SDK.
*/
private void init() {
+ mSdkCommandLine = new SdkCommandLine();
+
/* We get passed a property for the tools dir */
String toolsDirProp = System.getProperty(TOOLSDIR);
if (toolsDirProp == null) {
// for debugging, it's easier to override using the process environment
toolsDirProp = System.getenv(TOOLSDIR);
}
- if (toolsDirProp == null) {
- printHelpAndExit("ERROR: The tools directory property is not set, please make sure you are executing android or android.bat");
- }
-
- // got back a level for the SDK folder
- File tools = new File(toolsDirProp);
- mSdkFolder = tools.getParent();
-
- }
- /**
- * Parses command-line arguments, or prints help/usage and exits if error.
- * @param args arguments passed to the program
- */
- private void parseArgs(String[] args) {
- final int numArgs = args.length;
-
- try {
- int argPos = 0;
- for (; argPos < numArgs; argPos++) {
- final String arg = args[argPos];
- if (arg.equals("-l") || arg.equals("--list")) {
- mListObject = args[++argPos];
- } else if (arg.equals("-c") || arg.equals("--create")) {
- mCreateVm = true;
- parseCreateArgs(args, ++argPos);
+ if (toolsDirProp != null) {
+ // got back a level for the SDK folder
+ File tools;
+ if (toolsDirProp.length() > 0) {
+ tools = new File(toolsDirProp);
+ mSdkFolder = tools.getParent();
+ } else {
+ try {
+ tools = new File(".").getCanonicalFile();
+ mSdkFolder = tools.getParent();
+ } catch (IOException e) {
+ // Will print an error below since mSdkFolder is not defined
}
}
- } catch (ArrayIndexOutOfBoundsException e) {
- /* Any OOB triggers help */
- printHelpAndExit("ERROR: Not enough arguments.");
}
- }
- private void parseCreateArgs(String[] args, int argPos) {
- final int numArgs = args.length;
-
- try {
- for (; argPos < numArgs; argPos++) {
- final String arg = args[argPos];
- if (arg.equals("-t") || arg.equals("--target")) {
- String targetId = args[++argPos];
- try {
- // get the target id
- mCreateTargetId = Integer.parseInt(targetId);
- } catch (NumberFormatException e) {
- printHelpAndExit("ERROR: Target Id is not a number");
- }
- } else if (arg.equals("-n") || arg.equals("--name")) {
- mCreateName = args[++argPos];
- } else {
- printHelpAndExit("ERROR: '%s' unknown argument for --create mode",
- args[argPos]);
- }
+ if (mSdkFolder == null) {
+ String os = System.getProperty("os.name");
+ String cmd = "android";
+ if (os.startsWith("Windows")) {
+ cmd += ".bat";
}
- } catch (ArrayIndexOutOfBoundsException e) {
- /* Any OOB triggers help */
- printHelpAndExit("ERROR: Not enough arguments for --create");
+
+ mSdkCommandLine.printHelpAndExit(
+ "ERROR: The tools directory property is not set, please make sure you are executing %1$s",
+ cmd);
}
}
@@ -152,10 +114,15 @@ class Main {
* Does the basic SDK parsing required for all actions
*/
private void parseSdk() {
- mSdkManager = SdkManager.createManager(mSdkFolder, new ISdkLog() {
- public void error(String errorFormat, Object... args) {
- System.err.printf("Error: " + errorFormat, args);
- System.err.println("");
+ mSdkLog = new ISdkLog() {
+ public void error(Throwable t, String errorFormat, Object... args) {
+ if (errorFormat != null) {
+ System.err.printf("Error: " + errorFormat, args);
+ System.err.println("");
+ }
+ if (t != null) {
+ System.err.print("Error: " + t.getMessage());
+ }
}
public void warning(String warningFormat, Object... args) {
@@ -165,10 +132,15 @@ class Main {
System.out.println("");
}
}
- });
+
+ public void printf(String msgFormat, Object... args) {
+ System.out.printf(msgFormat, args);
+ }
+ };
+ mSdkManager = SdkManager.createManager(mSdkFolder, mSdkLog);
if (mSdkManager == null) {
- printHelpAndExit("ERROR: Unable to parse SDK content.");
+ mSdkCommandLine.printHelpAndExit("ERROR: Unable to parse SDK content.");
}
}
@@ -176,19 +148,37 @@ class Main {
* Actually do an action...
*/
private void doAction() {
- if (mListObject != null) {
+ String action = mSdkCommandLine.getActionRequested();
+
+ if (SdkCommandLine.ACTION_LIST.equals(action)) {
// list action.
- if (ARG_LIST_TARGET.equals(mListObject)) {
+ if (SdkCommandLine.ARG_TARGET.equals(mSdkCommandLine.getListFilter())) {
displayTargetList();
- } else if (ARG_LIST_VM.equals(mListObject)) {
+ } else if (SdkCommandLine.ARG_VM.equals(mSdkCommandLine.getListFilter())) {
displayVmList();
} else {
- printHelpAndExit("'%s' is not a valid --list option", mListObject);
+ displayTargetList();
+ displayVmList();
}
- } else if (mCreateVm) {
+ } else if (SdkCommandLine.ACTION_NEW_VM.equals(action)) {
createVm();
+ } else if (SdkCommandLine.ACTION_NEW_PROJECT.equals(action)) {
+ // get the target and try to resolve it.
+ int targetId = mSdkCommandLine.getNewProjectTargetId();
+ IAndroidTarget[] targets = mSdkManager.getTargets();
+ if (targetId < 1 || targetId > targets.length) {
+ mSdkCommandLine.printHelpAndExit("ERROR: Wrong target id.");
+ }
+ IAndroidTarget target = targets[targetId - 1];
+
+ ProjectCreator creator = new ProjectCreator(mSdkFolder,
+ OutputLevel.NORMAL, mSdkLog);
+
+ creator.createProject(mSdkCommandLine.getNewProjectLocation(),
+ mSdkCommandLine.getNewProjectName(), mSdkCommandLine.getNewProjectPackage(),
+ mSdkCommandLine.getNewProjectActivity(), target, true);
} else {
- printHelpAndExit(null);
+ mSdkCommandLine.printHelpAndExit(null);
}
}
@@ -274,7 +264,7 @@ class Main {
index++;
}
} catch (AndroidLocationException e) {
- printHelpAndExit(e.getMessage());
+ mSdkCommandLine.printHelpAndExit(e.getMessage());
}
}
@@ -283,11 +273,14 @@ class Main {
*/
private void createVm() {
// find a matching target
- if (mCreateTargetId >= 1 && mCreateTargetId <= mSdkManager.getTargets().length) {
- mCreateTarget = mSdkManager.getTargets()[mCreateTargetId-1]; // target it is 1-based
+ int targetId = mSdkCommandLine.getNewVmTargetId();
+ IAndroidTarget target = null;
+
+ if (targetId >= 1 && targetId <= mSdkManager.getTargets().length) {
+ target = mSdkManager.getTargets()[targetId-1]; // target it is 1-based
} else {
- printHelpAndExit(
- "ERROR: Target Id is not a valid Id. Check android --list target for the list of targets.");
+ mSdkCommandLine.printHelpAndExit(
+ "ERROR: Target Id is not a valid Id. Check 'android list target' for the list of targets.");
}
try {
@@ -295,19 +288,24 @@ class Main {
String vmRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_VMS;
Map<String, String> hardwareConfig = null;
- if (mCreateTarget.isPlatform()) {
+ if (target.isPlatform()) {
try {
- hardwareConfig = promptForHardware(mCreateTarget);
+ hardwareConfig = promptForHardware(target);
} catch (IOException e) {
- printHelpAndExit(e.getMessage());
+ mSdkCommandLine.printHelpAndExit(e.getMessage());
}
}
- VmManager.createVm(vmRoot, mCreateName, mCreateTarget, null /*skinName*/,
- null /*sdcardPath*/, 0 /*sdcardSize*/, hardwareConfig,
+ VmManager.createVm(vmRoot,
+ mSdkCommandLine.getNewVmName(),
+ target,
+ null /*skinName*/,
+ null /*sdcardPath*/,
+ 0 /*sdcardSize*/,
+ hardwareConfig,
null /* sdklog */);
} catch (AndroidLocationException e) {
- printHelpAndExit(e.getMessage());
+ mSdkCommandLine.printHelpAndExit(e.getMessage());
}
}
@@ -325,7 +323,7 @@ class Main {
System.out.print(String.format("Do you which to create a custom hardware profile [%s]",
defaultAnswer));
- result = readLine(readLineBuffer);
+ result = readLine(readLineBuffer).trim();
// handle default:
if (result.length() == 0) {
result = defaultAnswer;
@@ -391,8 +389,7 @@ class Main {
break;
case INTEGER:
try {
- @SuppressWarnings("unused")
- int value = Integer.parseInt(result);
+ Integer.parseInt(result);
map.put(property.getName(), result);
i++; // valid reply, move to next property
} catch (NumberFormatException e) {
@@ -414,9 +411,8 @@ class Main {
}
/**
- * Read the line from the input stream.
+ * Reads the line from the input stream.
* @param buffer
- * @return
* @throws IOException
*/
private String readLine(byte[] buffer) throws IOException {
@@ -434,7 +430,12 @@ class Main {
return new String(buffer, 0, count) + secondHalf;
}
- return new String(buffer, 0, count - 1); // -1 to not include the carriage return
+ // ignore end whitespace
+ while (count > 0 && (buffer[count-1] == '\r' || buffer[count-1] == '\n')) {
+ count--;
+ }
+
+ return new String(buffer, 0, count);
}
/**
@@ -442,6 +443,7 @@ class Main {
* @throws IOException If the value is not a boolean string.
*/
private boolean getBooleanReply(String reply) throws IOException {
+
for (String valid : BOOLEAN_YES_REPLIES) {
if (valid.equalsIgnoreCase(reply)) {
return true;
@@ -456,32 +458,4 @@ class Main {
throw new IOException(String.format("%s is not a valid reply", reply));
}
-
- /**
- * Prints the help/usage and exits.
- * @param errorFormat Optional error message to print prior to usage using String.format
- * @param args Arguments for String.format
- */
- private void printHelpAndExit(String errorFormat, Object... args) {
- if (errorFormat != null) {
- System.err.println(String.format(errorFormat, args));
- }
-
- /*
- * usage should fit in 80 columns
- * 12345678901234567890123456789012345678901234567890123456789012345678901234567890
- */
- final String usage = "\n" +
- "Usage:\n" +
- " android --list [target|vm]\n" +
- " android --create --target <target id> --name <name>\n" +
- "\n" +
- "Options:\n" +
- " -l [target|vm], --list [target|vm]\n" +
- " Outputs the available targets or Virtual Machines and their Ids.\n" +
- "\n";
-
- System.out.println(usage);
- System.exit(1);
- }
} \ No newline at end of file
diff --git a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
new file mode 100644
index 0000000..918c534
--- /dev/null
+++ b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdkmanager;
+
+import com.android.sdklib.SdkManager;
+
+
+/**
+ * Specific command-line flags for the {@link SdkManager}.
+ */
+public class SdkCommandLine extends CommandLineProcessor {
+
+ public static final String ARG_ALIAS = "alias";
+ public static final String ARG_ACTIVITY = "activity";
+ public static final String ARG_VM = "vm";
+ public static final String ARG_TARGET = "target";
+ public static final String ARG_ALL = "all";
+
+ public static final String KEY_IN = "in";
+ public static final String KEY_ACTIVITY = ARG_ACTIVITY;
+ public static final String KEY_PACKAGE = "package";
+ public static final String KEY_MODE = "mode";
+ public static final String KEY_TARGET_ID = ARG_TARGET;
+ public static final String KEY_NAME = "name";
+ public static final String KEY_OUT = "out";
+ public static final String KEY_FILTER = "filter";
+
+ public final static String ACTION_LIST = "list";
+ public final static String ACTION_NEW_VM = ARG_VM;
+ public final static String ACTION_NEW_PROJECT = "project";
+ public final static String ACTION_UPDATE_PROJECT = "update";
+
+ private final static String[][] ACTIONS = {
+ { ACTION_LIST,
+ "Lists existing targets or VMs." },
+ { ACTION_NEW_VM,
+ "Creates a new VM." },
+ { ACTION_NEW_PROJECT,
+ "Creates a new project using a template." },
+ { ACTION_UPDATE_PROJECT,
+ "Updates a new project from existing source (must have an AndroidManifest.xml)." },
+ };
+
+ public SdkCommandLine() {
+ super(ACTIONS);
+
+ define(MODE.ENUM, false, ACTION_LIST, "f", KEY_FILTER,
+ "List filter", new String[] { ARG_ALL, ARG_TARGET, ARG_VM });
+
+ define(MODE.STRING, false, ACTION_NEW_VM, "o", KEY_OUT,
+ "Location path of new VM", null);
+ define(MODE.STRING, true, ACTION_NEW_VM, "n", KEY_NAME,
+ "Name of the new VM", null);
+ define(MODE.INTEGER, true, ACTION_NEW_VM, "t", KEY_TARGET_ID,
+ "Target id of the new VM", null);
+
+ define(MODE.ENUM, true, ACTION_NEW_PROJECT, "m", KEY_MODE,
+ "Project mode", new String[] { ARG_ACTIVITY, ARG_ALIAS });
+ define(MODE.STRING, false, ACTION_NEW_PROJECT, "o", KEY_OUT,
+ "Location path of new project", null);
+ define(MODE.STRING, true, ACTION_NEW_PROJECT, "n", KEY_NAME,
+ "Name of the new project", null);
+ define(MODE.INTEGER, true, ACTION_NEW_PROJECT, "t", KEY_TARGET_ID,
+ "Target id of the new project", null);
+ define(MODE.STRING, true, ACTION_NEW_PROJECT, "p", KEY_PACKAGE,
+ "Package name", null);
+ define(MODE.STRING, true, ACTION_NEW_PROJECT, "a", KEY_ACTIVITY,
+ "Activity name", null);
+
+ define(MODE.STRING, false, ACTION_UPDATE_PROJECT, "i", KEY_IN,
+ "Directory location of the project", null);
+ define(MODE.STRING, true, ACTION_UPDATE_PROJECT, "t", KEY_TARGET_ID,
+ "Target id to set for the project", null);
+ }
+
+ // -- some helpers for list action flags
+
+ /** Helper to retrieve the --filter for the list action. */
+ public String getListFilter() {
+ return ((String) getValue(ACTION_LIST, KEY_FILTER));
+ }
+
+ // -- some helpers for vm action flags
+
+ /** Helper to retrieve the --out location for the new vm action. */
+ public String getNewVmLocation() {
+ return ((String) getValue(ACTION_NEW_VM, KEY_OUT));
+ }
+
+ /** Helper to retrieve the --target id for the new vm action. */
+ public int getNewVmTargetId() {
+ return ((Integer) getValue(ACTION_NEW_VM, KEY_TARGET_ID)).intValue();
+ }
+
+ /** Helper to retrieve the --name for the new vm action. */
+ public String getNewVmName() {
+ return ((String) getValue(ACTION_NEW_VM, KEY_NAME));
+ }
+
+ // -- some helpers for project action flags
+
+ /** Helper to retrieve the --out location for the new project action. */
+ public String getNewProjectLocation() {
+ return ((String) getValue(ACTION_NEW_PROJECT, KEY_OUT));
+ }
+
+ /** Helper to retrieve the --target id for the new project action. */
+ public int getNewProjectTargetId() {
+ return ((Integer) getValue(ACTION_NEW_PROJECT, KEY_TARGET_ID)).intValue();
+ }
+
+ /** Helper to retrieve the --name for the new project action. */
+ public String getNewProjectName() {
+ return ((String) getValue(ACTION_NEW_PROJECT, KEY_NAME));
+ }
+
+ /** Helper to retrieve the --package for the new project action. */
+ public String getNewProjectPackage() {
+ return ((String) getValue(ACTION_NEW_PROJECT, KEY_PACKAGE));
+ }
+
+ /** Helper to retrieve the --activity for the new project action. */
+ public String getNewProjectActivity() {
+ return ((String) getValue(ACTION_NEW_PROJECT, KEY_ACTIVITY));
+ }
+
+ // -- some helpers for update action flags
+
+ /** Helper to retrieve the --out location for the update project action. */
+ public String getUpdateProjectLocation() {
+ return ((String) getValue(ACTION_UPDATE_PROJECT, KEY_OUT));
+ }
+
+ /** Helper to retrieve the --target id for the update project action. */
+ public int getUpdateProjectTargetId() {
+ return ((Integer) getValue(ACTION_UPDATE_PROJECT, KEY_TARGET_ID)).intValue();
+ }
+}
diff --git a/sdkmanager/app/tests/com/android/sdkmanager/CommandLineProcessorTest.java b/sdkmanager/app/tests/com/android/sdkmanager/CommandLineProcessorTest.java
new file mode 100644
index 0000000..e74cdbd
--- /dev/null
+++ b/sdkmanager/app/tests/com/android/sdkmanager/CommandLineProcessorTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdkmanager;
+
+import junit.framework.TestCase;
+
+
+public class CommandLineProcessorTest extends TestCase {
+
+ /**
+ * A mock version of the {@link CommandLineProcessor} class that does not
+ * exits and captures its stdout/stderr output.
+ */
+ public static class MockCommandLineProcessor extends CommandLineProcessor {
+ private boolean mExitCalled;
+ private boolean mHelpCalled;
+ private String mStdOut = "";
+ private String mStdErr = "";
+
+ public MockCommandLineProcessor() {
+ super(new String[][] {
+ { "action1", "Some action" },
+ { "action2", "Another action" },
+ });
+ define(MODE.STRING, false /*mandatory*/,
+ "action1", "1", "first", "non-mandatory flag", null);
+ define(MODE.STRING, true /*mandatory*/,
+ "action1", "2", "second", "mandatory flag", null);
+ }
+
+ @Override
+ public void printHelpAndExitForAction(String actionFilter,
+ String errorFormat, Object... args) {
+ mHelpCalled = true;
+ super.printHelpAndExitForAction(actionFilter, errorFormat, args);
+ }
+
+ @Override
+ protected void exit() {
+ mExitCalled = true;
+ }
+
+ @Override
+ protected void stdout(String format, Object... args) {
+ String s = String.format(format, args);
+ mStdOut += s + "\n";
+ // don't call super to avoid printing stuff
+ }
+
+ @Override
+ protected void stderr(String format, Object... args) {
+ String s = String.format(format, args);
+ mStdErr += s + "\n";
+ // don't call super to avoid printing stuff
+ }
+
+ public boolean wasHelpCalled() {
+ return mHelpCalled;
+ }
+
+ public boolean wasExitCalled() {
+ return mExitCalled;
+ }
+
+ public String getStdOut() {
+ return mStdOut;
+ }
+
+ public String getStdErr() {
+ return mStdErr;
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public final void testPrintHelpAndExit() {
+ MockCommandLineProcessor c = new MockCommandLineProcessor();
+ assertFalse(c.wasExitCalled());
+ assertFalse(c.wasHelpCalled());
+ assertTrue(c.getStdOut().equals(""));
+ assertTrue(c.getStdErr().equals(""));
+ c.printHelpAndExit(null);
+ assertTrue(c.getStdOut().indexOf("-v") != -1);
+ assertTrue(c.getStdOut().indexOf("--verbose") != -1);
+ assertTrue(c.getStdErr().equals(""));
+ assertTrue(c.wasExitCalled());
+
+ c = new MockCommandLineProcessor();
+ assertFalse(c.wasExitCalled());
+ assertTrue(c.getStdOut().equals(""));
+ assertTrue(c.getStdErr().indexOf("Missing parameter") == -1);
+
+ c.printHelpAndExit("Missing %s", "parameter");
+ assertTrue(c.wasExitCalled());
+ assertFalse(c.getStdOut().equals(""));
+ assertTrue(c.getStdErr().indexOf("Missing parameter") != -1);
+ }
+
+ public final void testVerbose() {
+ MockCommandLineProcessor c = new MockCommandLineProcessor();
+
+ assertFalse(c.isVerbose());
+ c.parseArgs(new String[] { "-v" });
+ assertTrue(c.isVerbose());
+ assertTrue(c.wasExitCalled());
+ assertTrue(c.wasHelpCalled());
+ assertTrue(c.getStdErr().indexOf("Missing action name.") != -1);
+
+ c = new MockCommandLineProcessor();
+ c.parseArgs(new String[] { "--verbose" });
+ assertTrue(c.isVerbose());
+ assertTrue(c.wasExitCalled());
+ assertTrue(c.wasHelpCalled());
+ assertTrue(c.getStdErr().indexOf("Missing action name.") != -1);
+ }
+
+ public final void testHelp() {
+ MockCommandLineProcessor c = new MockCommandLineProcessor();
+
+ c.parseArgs(new String[] { "-h" });
+ assertTrue(c.wasExitCalled());
+ assertTrue(c.wasHelpCalled());
+ assertTrue(c.getStdErr().indexOf("Missing action name.") == -1);
+
+ c = new MockCommandLineProcessor();
+ c.parseArgs(new String[] { "--help" });
+ assertTrue(c.wasExitCalled());
+ assertTrue(c.wasHelpCalled());
+ assertTrue(c.getStdErr().indexOf("Missing action name.") == -1);
+ }
+
+ public final void testMandatory() {
+ MockCommandLineProcessor c = new MockCommandLineProcessor();
+
+ c.parseArgs(new String[] { "action1", "-1", "value1", "-2", "value2" });
+ assertFalse(c.wasExitCalled());
+ assertFalse(c.wasHelpCalled());
+ assertEquals("", c.getStdErr());
+ assertEquals("value1", c.getValue("action1", "first"));
+ assertEquals("value2", c.getValue("action1", "second"));
+
+ c = new MockCommandLineProcessor();
+ c.parseArgs(new String[] { "action1", "-2", "value2" });
+ assertFalse(c.wasExitCalled());
+ assertFalse(c.wasHelpCalled());
+ assertEquals("", c.getStdErr());
+ assertEquals(null, c.getValue("action1", "first"));
+ assertEquals("value2", c.getValue("action1", "second"));
+
+ c = new MockCommandLineProcessor();
+ c.parseArgs(new String[] { "action1" });
+ assertTrue(c.wasExitCalled());
+ assertTrue(c.wasHelpCalled());
+ assertTrue(c.getStdErr().indexOf("must be defined") != -1);
+ assertEquals(null, c.getValue("action1", "first"));
+ assertEquals(null, c.getValue("action1", "second"));
+ }
+}
diff --git a/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java b/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java
new file mode 100644
index 0000000..b943b98
--- /dev/null
+++ b/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdkmanager;
+
+import junit.framework.TestCase;
+
+public class SdkCommandLineTest extends TestCase {
+
+ /**
+ * A mock version of the {@link SdkCommandLine} class that does not
+ * exits and discards its stdout/stderr output.
+ */
+ public static class MockSdkCommandLine extends SdkCommandLine {
+ private boolean mExitCalled;
+ private boolean mHelpCalled;
+
+ public MockSdkCommandLine() {
+ }
+
+ @Override
+ public void printHelpAndExitForAction(String actionFilter,
+ String errorFormat, Object... args) {
+ mHelpCalled = true;
+ super.printHelpAndExitForAction(actionFilter, errorFormat, args);
+ }
+
+ @Override
+ protected void exit() {
+ mExitCalled = true;
+ }
+
+ @Override
+ protected void stdout(String format, Object... args) {
+ // discard
+ }
+
+ @Override
+ protected void stderr(String format, Object... args) {
+ // discard
+ }
+
+ public boolean wasExitCalled() {
+ return mExitCalled;
+ }
+
+ public boolean wasHelpCalled() {
+ return mHelpCalled;
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /** Test list with long name and verbose */
+ public final void testList_Long_Verbose() {
+ MockSdkCommandLine c = new MockSdkCommandLine();
+ assertEquals("all", c.getListFilter());
+ c.parseArgs(new String[] { "-v", "list", "--filter", "vm" });
+ assertFalse(c.wasHelpCalled());
+ assertFalse(c.wasExitCalled());
+ assertEquals("vm", c.getListFilter());
+ assertTrue(c.isVerbose());
+ }
+
+ /** Test list with short name and no verbose */
+ public final void testList_Short() {
+ MockSdkCommandLine c = new MockSdkCommandLine();
+ assertEquals("all", c.getListFilter());
+ c.parseArgs(new String[] { "list", "-f", "vm" });
+ assertFalse(c.wasHelpCalled());
+ assertFalse(c.wasExitCalled());
+ assertEquals("vm", c.getListFilter());
+ }
+
+ /** Test list with long name and missing parameter */
+ public final void testList_Long_MissingParam() {
+ MockSdkCommandLine c = new MockSdkCommandLine();
+ assertEquals("all", c.getListFilter());
+ c.parseArgs(new String[] { "list", "--filter" });
+ assertTrue(c.wasHelpCalled());
+ assertTrue(c.wasExitCalled());
+ assertEquals("all", c.getListFilter());
+ }
+}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java
index 5759613..2a2efe7 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java
@@ -95,6 +95,10 @@ final class AddOnTarget implements IAndroidTarget {
}
}
+ public String getLocation() {
+ return mLocation;
+ }
+
public String getName() {
return mName;
}
@@ -103,6 +107,10 @@ final class AddOnTarget implements IAndroidTarget {
return mVendor;
}
+ public String getFullName() {
+ return String.format("%1$s (%2$s)", mName, mVendor);
+ }
+
public String getDescription() {
return mDescription;
}
@@ -140,6 +148,28 @@ final class AddOnTarget implements IAndroidTarget {
return mLibraries;
}
+ public boolean isCompatibleBaseFor(IAndroidTarget target) {
+ // basic test
+ if (target == this) {
+ return true;
+ }
+
+ // if the receiver has no optional library, then anything with api version number >= to
+ // the receiver is compatible.
+ if (mLibraries.length == 0) {
+ return target.getApiVersionNumber() >= getApiVersionNumber();
+ }
+
+ // Otherwise, target is only compatible if the vendor and name are equals with the api
+ // number greater or equal (ie target is a newer version of this add-on).
+ if (target.isPlatform() == false) {
+ return (mVendor.equals(target.getVendor()) && mName.equals(target.getName()) &&
+ target.getApiVersionNumber() >= getApiVersionNumber());
+ }
+
+ return false;
+ }
+
public String hashString() {
return String.format(ADD_ON_FORMAT, mVendor, mName, mBasePlatform.getApiVersionNumber());
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
index e5d45b2..0e2b109 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java
@@ -22,23 +22,41 @@ package com.android.sdklib;
*/
public interface IAndroidTarget extends Comparable<IAndroidTarget> {
+ /** OS Path to the "android.jar" file. */
public static int ANDROID_JAR = 1;
+ /** OS Path to the "framework.aidl" file. */
public static int ANDROID_AIDL = 2;
+ /** OS Path to "images" folder which contains the emulator system images. */
public static int IMAGES = 3;
+ /** OS Path to the "samples" folder which contains sample projects. */
public static int SAMPLES = 4;
+ /** OS Path to the "skins" folder which contains the emulator skins. */
public static int SKINS = 5;
+ /** OS Path to the "templates" folder which contains the templates for new projects. */
public static int TEMPLATES = 6;
+ /** OS Path to the "data" folder which contains data & libraries for the SDK tools. */
public static int DATA = 7;
+ /** OS Path to the "attrs.xml" file. */
public static int ATTRIBUTES = 8;
+ /** OS Path to the "attrs_manifest.xml" file. */
public static int MANIFEST_ATTRIBUTES = 9;
+ /** OS Path to the "data/layoutlib.jar" library. */
public static int LAYOUT_LIB = 10;
+ /** OS Path to the "data/res" folder. */
public static int RESOURCES = 11;
+ /** OS Path to the "data/fonts" folder. */
public static int FONTS = 12;
+ /** OS Path to the "data/widgets.txt" file. */
public static int WIDGETS = 13;
+ /** OS Path to the "data/activity_actions.txt" file. */
public static int ACTIONS_ACTIVITY = 14;
+ /** OS Path to the "data/broadcast_actions.txt" file. */
public static int ACTIONS_BROADCAST = 15;
+ /** OS Path to the "data/service_actions.txt" file. */
public static int ACTIONS_SERVICE = 16;
+ /** OS Path to the "data/categories.txt" file. */
public static int CATEGORIES = 17;
+ /** OS Path to the "sources" folder. */
public static int SOURCES = 18;
public interface IOptionalLibrary {
@@ -48,6 +66,11 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> {
}
/**
+ * Returns the target location.
+ */
+ String getLocation();
+
+ /**
* Returns the name of the vendor of the target.
*/
String getVendor();
@@ -58,6 +81,12 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> {
String getName();
/**
+ * Returns the full name of the target, possibly including vendor name.
+ * @return
+ */
+ String getFullName();
+
+ /**
* Returns the description of the target.
*/
String getDescription();
@@ -80,7 +109,7 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> {
/**
* Returns the path of a platform component.
* @param pathId the id representing the path to return. Any of the constants defined in the
- * {@link ITargetDataProvider} interface can be used.
+ * {@link IAndroidTarget} interface can be used.
*/
String getPath(int pathId);
@@ -96,6 +125,15 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> {
IOptionalLibrary[] getOptionalLibraries();
/**
+ * Returns whether the given target is compatible with the receiver.
+ * <p/>A target is considered compatible if applications developed for the receiver can run on
+ * the given target.
+ *
+ * @param target the IAndroidTarget to test.
+ */
+ boolean isCompatibleBaseFor(IAndroidTarget target);
+
+ /**
* Returns a string able to uniquely identify a target.
* Typically the target will encode information such as api level, whether it's a platform
* or add-on, and if it's an add-on vendor and add-on name.
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java
index 3eda37f..8cbe44a 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java
@@ -21,5 +21,6 @@ package com.android.sdklib;
*/
public interface ISdkLog {
void warning(String warningFormat, Object... args);
- void error(String errorFormat, Object... args);
+ void error(Throwable t, String errorFormat, Object... args);
+ void printf(String msgFormat, Object... args);
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java
index f5a1f6d..59fa81c 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/PlatformTarget.java
@@ -95,6 +95,10 @@ final class PlatformTarget implements IAndroidTarget {
public String getName() {
return mName;
}
+
+ public String getFullName() {
+ return mName;
+ }
/*
* (non-Javadoc)
@@ -136,7 +140,17 @@ final class PlatformTarget implements IAndroidTarget {
public IOptionalLibrary[] getOptionalLibraries() {
return null;
}
+
+ public boolean isCompatibleBaseFor(IAndroidTarget target) {
+ // basic test
+ if (target == this) {
+ return true;
+ }
+ // target is compatible wit the receiver as long as its api version number is greater or
+ // equal.
+ return target.getApiVersionNumber() >= mApiVersionNumber;
+ }
public String hashString() {
return String.format(PLATFORM_HASH, mApiVersionNumber);
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
index 78d1fda..ede0d86 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
@@ -64,6 +64,40 @@ public final class SdkConstants {
/** Skin layout file */
public final static String FN_SKIN_LAYOUT = "layout";//$NON-NLS-1$
+ /* Folder Names for Android Projects . */
+
+ /** Resources folder name, i.e. "res". */
+ public final static String FD_RESOURCES = "res"; //$NON-NLS-1$
+ /** Assets folder name, i.e. "assets" */
+ public final static String FD_ASSETS = "assets"; //$NON-NLS-1$
+ /** Default source folder name, i.e. "src" */
+ public final static String FD_SOURCES = "src"; //$NON-NLS-1$
+ /** Default native library folder name inside the project, i.e. "libs"
+ * While the folder inside the .apk is "lib", we call that one libs because
+ * that's what we use in ant for both .jar and .so and we need to make the 2 development ways
+ * compatible. */
+ public final static String FD_NATIVE_LIBS = "libs"; //$NON-NLS-1$
+ /** Native lib folder inside the APK: "lib" */
+ public final static String FD_APK_NATIVE_LIBS = "lib"; //$NON-NLS-1$
+ /** Default output folder name, i.e. "bin" */
+ public final static String FD_OUTPUT = "bin"; //$NON-NLS-1$
+ /** Default anim resource folder name, i.e. "anim" */
+ public final static String FD_ANIM = "anim"; //$NON-NLS-1$
+ /** Default color resource folder name, i.e. "color" */
+ public final static String FD_COLOR = "color"; //$NON-NLS-1$
+ /** Default drawable resource folder name, i.e. "drawable" */
+ public final static String FD_DRAWABLE = "drawable"; //$NON-NLS-1$
+ /** Default layout resource folder name, i.e. "layout" */
+ public final static String FD_LAYOUT = "layout"; //$NON-NLS-1$
+ /** Default menu resource folder name, i.e. "menu" */
+ public final static String FD_MENU = "menu"; //$NON-NLS-1$
+ /** Default values resource folder name, i.e. "values" */
+ public final static String FD_VALUES = "values"; //$NON-NLS-1$
+ /** Default xml resource folder name, i.e. "xml" */
+ public final static String FD_XML = "xml"; //$NON-NLS-1$
+ /** Default raw resource folder name, i.e. "raw" */
+ public final static String FD_RAW = "raw"; //$NON-NLS-1$
+
/* Folder Names for the Android SDK */
/** Name of the SDK platforms folder. */
@@ -90,13 +124,12 @@ public final class SdkConstants {
public final static String FD_RES = "res";
/** Name of the SDK font folder, i.e. "fonts" */
public final static String FD_FONTS = "fonts";
- /** Default values resource folder name, i.e. "values" */
- public final static String FD_VALUES = "values";
/** Name of the android sources directory */
public static final String FD_ANDROID_SOURCES = "sources";
/** Name of the addon libs folder. */
public final static String FD_ADDON_LIBS = "libs";
+
/* Folder path relative to the SDK root */
/** Path of the documentation directory relative to the sdk folder.
* This is an OS path, ending with a separator. */
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
index 67b8499..b4de51a 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
@@ -36,8 +36,8 @@ import java.util.regex.Pattern;
*/
public final class SdkManager {
- private final static String PROP_VERSION_SDK = "ro.build.version.sdk";
- private final static String PROP_VERSION_RELEASE = "ro.build.version.release";
+ public final static String PROP_VERSION_SDK = "ro.build.version.sdk";
+ public final static String PROP_VERSION_RELEASE = "ro.build.version.release";
private final static String ADDON_NAME = "name";
private final static String ADDON_VENDOR = "vendor";
@@ -73,7 +73,7 @@ public final class SdkManager {
return manager;
} catch (IllegalArgumentException e) {
if (log != null) {
- log.error(e.getMessage());
+ log.error(e, "Error parsing the sdk.");
}
}
@@ -188,13 +188,14 @@ public final class SdkManager {
// looks like apiNumber does not parse to a number.
// Ignore this platform.
if (log != null) {
- log.error("Ignoring platform '%1$s': %2$s is not a valid number in %3$s.",
+ log.error(null,
+ "Ignoring platform '%1$s': %2$s is not a valid number in %3$s.",
platform.getName(), PROP_VERSION_SDK, SdkConstants.FN_BUILD_PROP);
}
}
}
} else if (log != null) {
- log.error("Ignoring platform '%1$s': %2$s is missing.", platform.getName(),
+ log.error(null, "Ignoring platform '%1$s': %2$s is missing.", platform.getName(),
SdkConstants.FN_BUILD_PROP);
}
@@ -281,7 +282,7 @@ public final class SdkManager {
if (baseTarget == null) {
if (log != null) {
- log.error(
+ log.error(null,
"Ignoring add-on '%1$s': Unable to find base platform with API level %2$d",
addon.getName(), apiValue);
}
@@ -292,7 +293,7 @@ public final class SdkManager {
// looks like apiNumber does not parse to a number.
// Ignore this add-on.
if (log != null) {
- log.error(
+ log.error(null,
"Ignoring add-on '%1$s': %2$s is not a valid number in %3$s.",
addon.getName(), ADDON_API, SdkConstants.FN_BUILD_PROP);
}
@@ -331,7 +332,7 @@ public final class SdkManager {
return target;
}
} else if (log != null) {
- log.error("Ignoring add-on '%1$s': %2$s is missing.", addon.getName(),
+ log.error(null, "Ignoring add-on '%1$s': %2$s is missing.", addon.getName(),
SdkConstants.FN_MANIFEST_INI);
}
@@ -340,7 +341,7 @@ public final class SdkManager {
private void displayAddonManifestError(ISdkLog log, String addonName, String valueName) {
if (log != null) {
- log.error("Ignoring add-on '%1$s': '%2$s' is missing from %3$s.",
+ log.error(null, "Ignoring add-on '%1$s': '%2$s' is missing from %3$s.",
addonName, valueName, SdkConstants.FN_MANIFEST_INI);
}
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java
new file mode 100644
index 0000000..1184fc2
--- /dev/null
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdklib.project;
+
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.ISdkLog;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.project.ProjectProperties.PropertyType;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Creates the basic files needed to get an Android project up and running. Also
+ * allows creation of IntelliJ project files.
+ *
+ * @hide
+ */
+public class ProjectCreator {
+
+ private final static String PH_JAVA_FOLDER = "PACKAGE_PATH";
+ private final static String PH_PACKAGE = "PACKAGE";
+ private final static String PH_ACTIVITY_NAME = "ACTIVITY_NAME";
+
+ private final static String FOLDER_TESTS = "tests";
+
+ public enum OutputLevel {
+ SILENT, NORMAL, VERBOSE;
+ }
+
+ private static class ProjectCreateException extends Exception {
+ /** default UID. This will not be serialized anyway. */
+ private static final long serialVersionUID = 1L;
+
+ ProjectCreateException(String message) {
+ super(message);
+ }
+
+ ProjectCreateException(Throwable t, String format, Object... args) {
+ super(format != null ? String.format(format, args) : format, t);
+ }
+
+ ProjectCreateException(String format, Object... args) {
+ super(String.format(format, args));
+ }
+ }
+
+ private final OutputLevel mLevel;
+
+ private final ISdkLog mLog;
+ private final String mSdkFolder;
+
+ public ProjectCreator(String sdkFolder, OutputLevel level, ISdkLog log) {
+ mSdkFolder = sdkFolder;
+ mLevel = level;
+ mLog = log;
+ }
+
+ /**
+ * Creates a new project.
+ * @param folderPath the folder of the project to create. This folder must exist.
+ * @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 target the project target.
+ * @param isTestProject whether the project to create is a test project.
+ */
+ public void createProject(String folderPath, String projectName,
+ String packageName, String activityName, IAndroidTarget target,
+ boolean isTestProject) {
+
+ // check project folder exists.
+ File projectFolder = new File(folderPath);
+ if (projectFolder.isDirectory() == false) {
+ mLog.error(null, "Folder '%s' does not exist. Aborting...", folderPath);
+ return;
+ }
+
+ try {
+ // first create the project properties.
+
+ // location of the SDK goes in localProperty
+ ProjectProperties localProperties = ProjectProperties.create(folderPath,
+ PropertyType.LOCAL);
+ localProperties.setProperty(ProjectProperties.PROPERTY_SDK, mSdkFolder);
+ localProperties.save();
+
+ // target goes in default properties
+ ProjectProperties defaultProperties = ProjectProperties.create(folderPath,
+ PropertyType.DEFAULT);
+ defaultProperties.setAndroidTarget(target);
+ defaultProperties.save();
+
+ // create the map for place-holders of values to replace in the templates
+ final HashMap<String, String> keywords = new HashMap<String, String>();
+
+ // create the required folders.
+ // compute src folder path
+ final String packagePath =
+ stripString(packageName.replace(".", File.separator),
+ File.separatorChar);
+
+ // put this path in the place-holder map for project files that needs to list
+ // files manually.
+ keywords.put(PH_JAVA_FOLDER, packagePath);
+
+ keywords.put(PH_PACKAGE, packageName);
+ if (activityName != null) {
+ keywords.put(PH_ACTIVITY_NAME, activityName);
+ }
+
+ // create the source folder and the java package folders.
+ final String srcFolderPath = SdkConstants.FD_SOURCES + File.separator + packagePath;
+ File sourceFolder = createDirs(projectFolder, srcFolderPath);
+ String javaTemplate = "java_file.template";
+ String activityFileName = activityName + ".java";
+ if (isTestProject) {
+ javaTemplate = "java_tests_file.template";
+ activityFileName = activityName + "Test.java";
+ }
+ installTemplate(javaTemplate, new File(sourceFolder, activityFileName),
+ keywords, target);
+
+ // create other useful folders
+ File resourceFodler = createDirs(projectFolder, SdkConstants.FD_RESOURCES);
+ createDirs(projectFolder, SdkConstants.FD_OUTPUT);
+ createDirs(projectFolder, SdkConstants.FD_NATIVE_LIBS);
+
+ if (isTestProject == false) {
+ /* Make res files only for non test projects */
+ File valueFolder = createDirs(resourceFodler, SdkConstants.FD_VALUES);
+ installTemplate("strings.template", new File(valueFolder, "strings.xml"),
+ keywords, target);
+
+ File layoutFolder = createDirs(resourceFodler, SdkConstants.FD_LAYOUT);
+ installTemplate("layout.template", new File(layoutFolder, "main.xml"),
+ keywords, target);
+ }
+
+ /* Make AndroidManifest.xml and build.xml files */
+ String manifestTemplate = "AndroidManifest.template";
+ if (isTestProject) {
+ manifestTemplate = "AndroidManifest.tests.template";
+ }
+
+ installTemplate(manifestTemplate, new File(projectFolder, "AndroidManifest.xml"),
+ keywords, target);
+
+ installTemplate("build.template", new File(projectFolder, "build.xml"), keywords);
+
+ // if this is not a test project, then we create one.
+ if (isTestProject == false) {
+ // create the test project folder.
+ createDirs(projectFolder, FOLDER_TESTS);
+ File testProjectFolder = new File(folderPath, FOLDER_TESTS);
+
+ createProject(testProjectFolder.getAbsolutePath(), projectName, packageName,
+ activityName, target, true /*isTestProject*/);
+ }
+ } catch (ProjectCreateException e) {
+ mLog.error(e, null);
+ } catch (IOException e) {
+ mLog.error(e, null);
+ }
+ }
+
+ /**
+ * Installs a new file that is based on a template file provided by a given target.
+ * Each match of each key from the place-holder map in the template will be replaced with its
+ * corresponding value in the created file.
+ *
+ * @param templateName the name of to the template file
+ * @param dest the path to the destination file, relative to the project
+ * @param placeholderMap a map of (place-holder, value) to create the file from the template.
+ * @param target the Target of the project that will be providing the template.
+ * @throws ProjectCreateException
+ */
+ private void installTemplate(String templateName, File destFile,
+ Map<String, String> placeholderMap, IAndroidTarget target)
+ throws ProjectCreateException {
+ // query the target for its template directory
+ String templateFolder = target.getPath(IAndroidTarget.TEMPLATES);
+ final String sourcePath = templateFolder + File.separator + templateName;
+
+ installFullPathTemplate(sourcePath, destFile, placeholderMap);
+ }
+
+ /**
+ * Installs a new file that is based on a template file provided by the tools folder.
+ * Each match of each key from the place-holder map in the template will be replaced with its
+ * corresponding value in the created file.
+ *
+ * @param templateName the name of to the template file
+ * @param dest the path to the destination file, relative to the project
+ * @param placeholderMap a map of (place-holder, value) to create the file from the template.
+ * @throws ProjectCreateException
+ */
+ private void installTemplate(String templateName, File destFile,
+ Map<String, String> placeholderMap)
+ throws ProjectCreateException {
+ // query the target for its template directory
+ String templateFolder = mSdkFolder + File.separator + SdkConstants.OS_SDK_TOOLS_LIB_FOLDER;
+ final String sourcePath = templateFolder + File.separator + templateName;
+
+ installFullPathTemplate(sourcePath, destFile, placeholderMap);
+ }
+
+ /**
+ * Installs a new file that is based on a template.
+ * Each match of each key from the place-holder map in the template will be replaced with its
+ * corresponding value in the created file.
+ *
+ * @param sourcePath the full path to the source template file
+ * @param destFile the destination file
+ * @param placeholderMap a map of (place-holder, value) to create the file from the template.
+ * @throws ProjectCreateException
+ */
+ private void installFullPathTemplate(String sourcePath, File destFile,
+ Map<String, String> placeholderMap) throws ProjectCreateException {
+ try {
+ BufferedWriter out = new BufferedWriter(new FileWriter(destFile));
+ BufferedReader in = new BufferedReader(new FileReader(sourcePath));
+ String line;
+
+ while ((line = in.readLine()) != null) {
+ for (String key : placeholderMap.keySet()) {
+ line = line.replace(key, placeholderMap.get(key));
+ }
+
+ out.write(line);
+ out.newLine();
+ }
+
+ out.close();
+ in.close();
+ } catch (Exception e) {
+ throw new ProjectCreateException(e, "Could not access %1$s: %2$s",
+ destFile, e.getMessage());
+ }
+
+ println("Added file %1$s", destFile);
+ }
+
+
+ /**
+ * Prints a message unless silence is enabled.
+ * @param format Format for String.format
+ * @param args Arguments for String.format
+ */
+ private void println(String format, Object... args) {
+ if (mLevel == OutputLevel.VERBOSE) {
+ System.out.println(String.format(format, args));
+ }
+ }
+
+ /**
+ * Creates a new folder, along with any parent folders that do not exists.
+ *
+ * @param parent the parent folder
+ * @param name the name of the directory to create.
+ * @throws ProjectCreateException
+ */
+ private File createDirs(File parent, String name) throws ProjectCreateException {
+ final File newFolder = new File(parent, name);
+ boolean existedBefore = true;
+
+ if (!newFolder.exists()) {
+ if (!newFolder.mkdirs()) {
+ throw new ProjectCreateException("Could not create directory: %1$s", newFolder);
+ }
+ existedBefore = false;
+ }
+
+ if (newFolder.isDirectory()) {
+ if (!newFolder.canWrite()) {
+ throw new ProjectCreateException("Path is not writable: %1$s", newFolder);
+ }
+ } else {
+ throw new ProjectCreateException("Path is not a directory: %1$s", newFolder);
+ }
+
+ if (!existedBefore) {
+ try {
+ println("Created directory %1$s", newFolder.getCanonicalPath());
+ } catch (IOException e) {
+ throw new ProjectCreateException(
+ "Could not determine canonical path of created directory", e);
+ }
+ }
+
+ return newFolder;
+ }
+
+ /**
+ * Strips the string of beginning and trailing characters (multiple
+ * characters will be stripped, example stripString("..test...", '.')
+ * results in "test";
+ *
+ * @param s the string to strip
+ * @param strip the character to strip from beginning and end
+ * @return the stripped string or the empty string if everything is stripped.
+ */
+ private static String stripString(String s, char strip) {
+ final int sLen = s.length();
+ int newStart = 0, newEnd = sLen - 1;
+
+ while (newStart < sLen && s.charAt(newStart) == strip) {
+ newStart++;
+ }
+ while (newEnd >= 0 && s.charAt(newEnd) == strip) {
+ newEnd--;
+ }
+
+ /*
+ * newEnd contains a char we want, and substring takes end as being
+ * exclusive
+ */
+ newEnd++;
+
+ if (newStart >= sLen || newEnd < 0) {
+ return "";
+ }
+
+ return s.substring(newStart, newEnd);
+ }
+}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java
index c0c1fe3..473f284 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java
@@ -16,6 +16,7 @@
package com.android.sdklib.project;
+import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkManager;
import java.io.File;
@@ -32,38 +33,87 @@ import java.util.Map.Entry;
public final class ProjectProperties {
/** The property name for the project target */
public final static String PROPERTY_TARGET = "target";
- public final static String PROPERTY_SDK = "sdk-folder";
+ public final static String PROPERTY_SDK = "sdk-location";
- private final static String PROPERTIES_FILE = "default.properties";
+ public static enum PropertyType {
+ BUILD("build.properties", BUILD_HEADER),
+ DEFAULT("default.properties", DEFAULT_HEADER),
+ LOCAL("local.properties", LOCAL_HEADER);
+
+ private final String mFilename;
+ private final String mHeader;
+
+ PropertyType(String filename, String header) {
+ mFilename = filename;
+ mHeader = header;
+ }
+ }
- private final static String PROP_HEADER =
+ private final static String LOCAL_HEADER =
+// 1-------10--------20--------30--------40--------50--------60--------70--------80
"# This file is automatically generated by Android Tools.\n" +
"# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n" +
- "# For customized properties when using Ant, set new values\n" +
- "# in a \"build.properties\" file.\n\n";
+ "# \n" +
+ "# This file must *NOT* be checked in Version Control Systems,\n" +
+ "# as it contains information specific to your local configuration.\n" +
+ "\n";
+
+ private final static String DEFAULT_HEADER =
+// 1-------10--------20--------30--------40--------50--------60--------70--------80
+ "# This file is automatically generated by Android Tools.\n" +
+ "# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n" +
+ "# \n" +
+ "# This file must be checked in Version Control Systems.\n" +
+ "# \n" +
+ "# To customize properties used by the Ant build system use,\n" +
+ "# \"build.properties\", and override values to adapt the script to your" +
+ "# project structure.\n" +
+ "\n";
+
+ private final static String BUILD_HEADER =
+// 1-------10--------20--------30--------40--------50--------60--------70--------80
+ "# This file is used to override default values used by the Ant build system.\n" +
+ "# \n" +
+ "# This file must be checked in Version Control Systems, as it is" +
+ "# integral to the build system of your project.\n" +
+ "# \n" +
+ "# Use this file to change values like:\n" +
+ "# application-package\n:" +
+ "# the name of your application package as defined in the manifest.\n" +
+ "# Used by the 'uninstall' rule.\n"+
+ "# source-folder\n:" +
+ "# the name of the source folder. Default is 'src'.\n" +
+ "# out-folder\n:" +
+ "# the name of the output folder. Default is 'bin'\n" +
+ "\n";
private final static Map<String, String> COMMENT_MAP = new HashMap<String, String>();
static {
- COMMENT_MAP.put(PROPERTY_TARGET, "# Project target.\n");
- COMMENT_MAP.put(PROPERTY_SDK, "# location of the SDK. Only used by Ant.\n");
+// 1-------10--------20--------30--------40--------50--------60--------70--------80
+ COMMENT_MAP.put(PROPERTY_TARGET,
+ "# Project target.\n");
+ COMMENT_MAP.put(PROPERTY_SDK, "# location of the SDK. This is only used by Ant\n" +
+ "# For customization when using a Version Control System, please read the\n" +
+ "# header note.\n");
}
private final String mProjectFolderOsPath;
private final Map<String, String> mProperties;
+ private final PropertyType mType;
/**
* Loads a project properties file and return a {@link ProjectProperties} object
* containing the properties
* @param projectFolderOsPath the project folder.
*/
- public static ProjectProperties load(String projectFolderOsPath) {
+ public static ProjectProperties load(String projectFolderOsPath, PropertyType type) {
File projectFolder = new File(projectFolderOsPath);
if (projectFolder.isDirectory()) {
- File defaultFile = new File(projectFolder, PROPERTIES_FILE);
+ File defaultFile = new File(projectFolder, type.mFilename);
if (defaultFile.isFile()) {
Map<String, String> map = SdkManager.parsePropertyFile(defaultFile, null /* log */);
if (map != null) {
- return new ProjectProperties(projectFolderOsPath, map);
+ return new ProjectProperties(projectFolderOsPath, map, type);
}
}
}
@@ -71,13 +121,14 @@ public final class ProjectProperties {
}
/**
- * Creates a new project properties file, with no properties.
+ * Creates a new project properties object, with no properties.
* <p/>The file is not created until {@link #save()} is called.
* @param projectFolderOsPath the project folder.
+ * @param type
*/
- public static ProjectProperties create(String projectFolderOsPath) {
+ public static ProjectProperties create(String projectFolderOsPath, PropertyType type) {
// create and return a ProjectProperties with an empty map.
- return new ProjectProperties(projectFolderOsPath, new HashMap<String, String>());
+ return new ProjectProperties(projectFolderOsPath, new HashMap<String, String>(), type);
}
/**
@@ -90,6 +141,15 @@ public final class ProjectProperties {
}
/**
+ * Sets the target property to the given {@link IAndroidTarget} object.
+ * @param target the Android target.
+ */
+ public void setAndroidTarget(IAndroidTarget target) {
+ assert mType == PropertyType.DEFAULT;
+ mProperties.put(PROPERTY_TARGET, target.hashString());
+ }
+
+ /**
* Returns the value of a property.
* @param name the name of the property.
* @return the property value or null if the property is not set.
@@ -103,12 +163,12 @@ public final class ProjectProperties {
* @throws IOException
*/
public void save() throws IOException {
- File toSave = new File(mProjectFolderOsPath, PROPERTIES_FILE);
+ File toSave = new File(mProjectFolderOsPath, mType.mFilename);
FileWriter writer = new FileWriter(toSave);
// write the header
- writer.write(PROP_HEADER);
+ writer.write(mType.mHeader);
// write the properties.
for (Entry<String, String> entry : mProperties.entrySet()) {
@@ -128,9 +188,12 @@ public final class ProjectProperties {
* Use {@link #load(String)} or {@link #create(String)} to instantiate.
* @param projectFolderOsPath
* @param map
+ * @param type
*/
- private ProjectProperties(String projectFolderOsPath, Map<String, String> map) {
+ private ProjectProperties(String projectFolderOsPath, Map<String, String> map,
+ PropertyType type) {
mProjectFolderOsPath = projectFolderOsPath;
mProperties = map;
+ mType = type;
}
}
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java
index a9f1b17..a28561d 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java
@@ -76,9 +76,27 @@ public final class VmManager {
buildVmList(sdk);
}
+ /**
+ * Returns the existing VMs.
+ * @return a newly allocated arrays containing all the VMs.
+ */
public VmInfo[] getVms() {
return mVmList.toArray(new VmInfo[mVmList.size()]);
}
+
+ /**
+ * Returns the {@link VmInfo} matching the given <var>name</var>.
+ * @return the matching VmInfo or <code>null</code> if none were found.
+ */
+ public VmInfo getVm(String name) {
+ for (VmInfo info : mVmList) {
+ if (info.name.equals(name)) {
+ return info;
+ }
+ }
+
+ return null;
+ }
/**
* Creates a new VM.
@@ -101,7 +119,7 @@ public final class VmManager {
File rootDirectory = new File(parentFolder);
if (rootDirectory.isDirectory() == false) {
if (log != null) {
- log.error("%s does not exists.", parentFolder);
+ log.error(null, "%s does not exists.", parentFolder);
}
return;
}
@@ -109,7 +127,7 @@ public final class VmManager {
File vmFolder = new File(parentFolder, name + ".avm");
if (vmFolder.exists()) {
if (log != null) {
- log.error("%s already exists.", vmFolder.getAbsolutePath());
+ log.error(null, "%s already exists.", vmFolder.getAbsolutePath());
}
return;
}
diff --git a/sdkmanager/libs/sdkuilib/.classpath b/sdkmanager/libs/sdkuilib/.classpath
new file mode 100644
index 0000000..eb5af7e
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sdkmanager/libs/sdkuilib/.project b/sdkmanager/libs/sdkuilib/.project
new file mode 100644
index 0000000..da430c8
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>SdkUiLib</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java
index ddc492e..fc951f2 100644
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java
@@ -40,6 +40,11 @@ import java.util.ArrayList;
/**
* The SDK target selector is a table that is added to the given parent composite.
+ * <p/>
+ * To use, create it using {@link #SdkTargetSelector(Composite, IAndroidTarget[], boolean)} then
+ * call {@link #setSelection(IAndroidTarget)}, {@link #setSelectionListener(SelectionListener)}
+ * and finally use {@link #getFirstSelected()} or {@link #getAllSelected()} to retrieve the
+ * selection.
*/
public class SdkTargetSelector {
@@ -49,6 +54,14 @@ public class SdkTargetSelector {
private Table mTable;
private Label mDescription;
+ /**
+ * Creates a new SDK Target Selector.
+ *
+ * @param parent The parent composite where the selector will be added.
+ * @param targets The list of targets. This is <em>not</em> copied, the caller must not modify.
+ * @param allowMultipleSelection True if more than one SDK target can be selected at the same
+ * time.
+ */
public SdkTargetSelector(Composite parent, IAndroidTarget[] targets,
boolean allowMultipleSelection) {
mTargets = targets;
@@ -81,14 +94,25 @@ public class SdkTargetSelector {
column1.setText("Vendor");
final TableColumn column2 = new TableColumn(mTable, SWT.NONE);
column2.setText("API Level");
+ final TableColumn column3 = new TableColumn(mTable, SWT.NONE);
+ column3.setText("SDK");
- adjustColumnsWidth(mTable, column0, column1, column2);
+ adjustColumnsWidth(mTable, column0, column1, column2, column3);
setupSelectionListener(mTable);
fillTable(mTable);
setupTooltip(mTable);
}
/**
+ * Returns the list of known targets.
+ * <p/>
+ * This is not a copy. Callers must <em>not</em> modify this array.
+ */
+ public IAndroidTarget[] getTargets() {
+ return mTargets;
+ }
+
+ /**
* Sets a selection listener. Set it to null to remove it.
* The listener will be called <em>after</em> this table processed its selection
* events so that the caller can see the updated state.
@@ -107,20 +131,33 @@ public class SdkTargetSelector {
/**
* Sets the current target selection.
+ * <p/>
+ * If the selection is actually changed, this will invoke the selection listener
+ * (if any) with a null event.
+ *
* @param target the target to be selection
* @return true if the target could be selected, false otherwise.
*/
public boolean setSelection(IAndroidTarget target) {
boolean found = false;
+ boolean modified = false;
for (TableItem i : mTable.getItems()) {
if ((IAndroidTarget) i.getData() == target) {
found = true;
- i.setChecked(true);
- } else {
+ if (!i.getChecked()) {
+ modified = true;
+ i.setChecked(true);
+ }
+ } else if (i.getChecked()) {
+ modified = true;
i.setChecked(false);
}
}
+ if (modified && mSelectionListener != null) {
+ mSelectionListener.widgetSelected(null);
+ }
+
return found;
}
@@ -166,15 +203,17 @@ public class SdkTargetSelector {
private void adjustColumnsWidth(final Table table,
final TableColumn column0,
final TableColumn column1,
- final TableColumn column2) {
+ final TableColumn column2,
+ final TableColumn column3) {
// Add a listener to resize the column to the full width of the table
table.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Rectangle r = table.getClientArea();
- column0.setWidth(r.width * 3 / 10); // 30%
- column1.setWidth(r.width * 5 / 10); // 50%
- column2.setWidth(r.width * 2 / 10); // 20%
+ 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%
}
});
}
@@ -238,6 +277,7 @@ public class SdkTargetSelector {
* <li>column 0: sdk name
* <li>column 1: sdk vendor
* <li>column 2: sdk api name
+ * <li>column 3: sdk version
* </ul>
*/
private void fillTable(final Table table) {
@@ -249,6 +289,7 @@ public class SdkTargetSelector {
item.setText(0, target.getName());
item.setText(1, target.getVendor());
item.setText(2, target.getApiVersionName());
+ item.setText(3, Integer.toString(target.getApiVersionNumber()));
}
} else {
table.setEnabled(false);
@@ -257,6 +298,7 @@ public class SdkTargetSelector {
item.setText(0, "--");
item.setText(1, "No target available");
item.setText(2, "--");
+ item.setText(3, "--");
}
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/VmSelector.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/VmSelector.java
new file mode 100644
index 0000000..dcc0b9e
--- /dev/null
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/VmSelector.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdkuilib;
+
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.vm.VmManager.VmInfo;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+import java.util.ArrayList;
+
+
+/**
+ * The VM selector is a table that is added to the given parent composite.
+ * <p/>
+ * To use, create it using {@link #VmSelector(Composite, VmInfo[], boolean)} then
+ * call {@link #setSelection(VmInfo)}, {@link #setSelectionListener(SelectionListener)}
+ * and finally use {@link #getFirstSelected()} or {@link #getAllSelected()} to retrieve the
+ * selection.
+ */
+public final class VmSelector {
+
+ private VmInfo[] mVms;
+ private final boolean mAllowMultipleSelection;
+ private SelectionListener mSelectionListener;
+ private Table mTable;
+ private Label mDescription;
+
+ /**
+ * Creates a new SDK Target Selector.
+ *
+ * @param parent The parent composite where the selector will be added.
+ * @param vms The list of vms. This is <em>not</em> copied, the caller must not modify.
+ * @param allowMultipleSelection True if more than one SDK target can be selected at the same
+ * time.
+ */
+ public VmSelector(Composite parent, VmInfo[] vms, boolean allowMultipleSelection) {
+ mVms = vms;
+
+ // Layout has 1 column
+ Composite group = new Composite(parent, SWT.NONE);
+ group.setLayout(new GridLayout());
+ group.setLayoutData(new GridData(GridData.FILL_BOTH));
+ group.setFont(parent.getFont());
+
+ mAllowMultipleSelection = allowMultipleSelection;
+ mTable = new Table(group, SWT.CHECK | SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER);
+ mTable.setHeaderVisible(true);
+ mTable.setLinesVisible(false);
+
+ GridData data = new GridData();
+ data.grabExcessVerticalSpace = true;
+ data.grabExcessHorizontalSpace = true;
+ data.horizontalAlignment = GridData.FILL;
+ data.verticalAlignment = GridData.FILL;
+ mTable.setLayoutData(data);
+
+ mDescription = new Label(group, SWT.WRAP);
+ mDescription.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ // create the table columns
+ final TableColumn column0 = new TableColumn(mTable, SWT.NONE);
+ column0.setText("VM Name");
+ final TableColumn column1 = new TableColumn(mTable, SWT.NONE);
+ column1.setText("Target Name");
+ final TableColumn column2 = new TableColumn(mTable, SWT.NONE);
+ column2.setText("API Level");
+ final TableColumn column3 = new TableColumn(mTable, SWT.NONE);
+ column3.setText("SDK");
+
+ adjustColumnsWidth(mTable, column0, column1, column2, column3);
+ setupSelectionListener(mTable);
+ fillTable(mTable, null /* target filter */);
+ setupTooltip(mTable);
+ }
+
+ /**
+ * Sets a new set of VM, with an optional filter.
+ * <p/>This must be called from the UI thread.
+ *
+ * @param vms The list of vms. This is <em>not</em> copied, the caller must not modify.
+ * @param filter An IAndroidTarget. If non-null, only VM whose target are compatible with the
+ * filter target will displayed an available for selection.
+ */
+ public void setVms(VmInfo[] vms, IAndroidTarget filter) {
+ mVms = vms;
+ fillTable(mTable, filter);
+ }
+
+ /**
+ * Returns the list of known Vms.
+ * <p/>
+ * This is not a copy. Callers must <em>not</em> modify this array.
+ */
+ public VmInfo[] getVms() {
+ return mVms;
+ }
+
+ /**
+ * Sets a selection listener. Set it to null to remove it.
+ * The listener will be called <em>after</em> this table processed its selection
+ * events so that the caller can see the updated state.
+ * <p/>
+ * The event's item contains a {@link TableItem}.
+ * The {@link TableItem#getData()} contains an {@link IAndroidTarget}.
+ * <p/>
+ * It is recommended that the caller uses the {@link #getFirstSelected()} and
+ * {@link #getAllSelected()} methods instead.
+ *
+ * @param selectionListener The new listener or null to remove it.
+ */
+ public void setSelectionListener(SelectionListener selectionListener) {
+ mSelectionListener = selectionListener;
+ }
+
+ /**
+ * Sets the current target selection.
+ * <p/>
+ * If the selection is actually changed, this will invoke the selection listener
+ * (if any) with a null event.
+ *
+ * @param target the target to be selection
+ * @return true if the target could be selected, false otherwise.
+ */
+ public boolean setSelection(VmInfo target) {
+ boolean found = false;
+ boolean modified = false;
+ for (TableItem i : mTable.getItems()) {
+ if ((VmInfo) i.getData() == target) {
+ found = true;
+ if (!i.getChecked()) {
+ modified = true;
+ i.setChecked(true);
+ }
+ } else if (i.getChecked()) {
+ modified = true;
+ i.setChecked(false);
+ }
+ }
+
+ if (modified && mSelectionListener != null) {
+ mSelectionListener.widgetSelected(null);
+ }
+
+ return found;
+ }
+
+ /**
+ * Returns all selected items.
+ * This is useful when the table is in multiple-selection mode.
+ *
+ * @see #getFirstSelected()
+ * @return An array of selected items. The list can be empty but not null.
+ */
+ public VmInfo[] getAllSelected() {
+ ArrayList<IAndroidTarget> list = new ArrayList<IAndroidTarget>();
+ for (TableItem i : mTable.getItems()) {
+ if (i.getChecked()) {
+ list.add((IAndroidTarget) i.getData());
+ }
+ }
+ return list.toArray(new VmInfo[list.size()]);
+ }
+
+ /**
+ * Returns the first selected item.
+ * This is useful when the table is in single-selection mode.
+ *
+ * @see #getAllSelected()
+ * @return The first selected item or null.
+ */
+ public VmInfo getFirstSelected() {
+ for (TableItem i : mTable.getItems()) {
+ if (i.getChecked()) {
+ return (VmInfo) i.getData();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Adds a listener to adjust the columns width when the parent is resized.
+ * <p/>
+ * If we need something more fancy, we might want to use this:
+ * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co
+ */
+ private void adjustColumnsWidth(final Table table,
+ final TableColumn column0,
+ final TableColumn column1,
+ final TableColumn column2,
+ final TableColumn column3) {
+ // Add a listener to resize the column to the full width of the table
+ table.addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Rectangle r = table.getClientArea();
+ column0.setWidth(r.width * 30 / 100); // 30%
+ column1.setWidth(r.width * 45 / 100); // 45%
+ column2.setWidth(r.width * 15 / 100); // 15%
+ column3.setWidth(r.width * 10 / 100); // 10%
+ }
+ });
+ }
+
+
+ /**
+ * Creates a selection listener that will check or uncheck the whole line when
+ * double-clicked (aka "the default selection").
+ */
+ private void setupSelectionListener(final Table table) {
+ // Add a selection listener that will check/uncheck items when they are double-clicked
+ table.addSelectionListener(new SelectionListener() {
+ /** Default selection means double-click on "most" platforms */
+ public void widgetDefaultSelected(SelectionEvent e) {
+ if (e.item instanceof TableItem) {
+ TableItem i = (TableItem) e.item;
+ i.setChecked(!i.getChecked());
+ enforceSingleSelection(i);
+ updateDescription(i);
+ }
+
+ if (mSelectionListener != null) {
+ mSelectionListener.widgetDefaultSelected(e);
+ }
+ }
+
+ public void widgetSelected(SelectionEvent e) {
+ if (e.item instanceof TableItem) {
+ TableItem i = (TableItem) e.item;
+ enforceSingleSelection(i);
+ updateDescription(i);
+ }
+
+ if (mSelectionListener != null) {
+ mSelectionListener.widgetSelected(e);
+ }
+ }
+
+ /**
+ * If we're not in multiple selection mode, uncheck all other
+ * items when this one is selected.
+ */
+ private void enforceSingleSelection(TableItem item) {
+ if (!mAllowMultipleSelection && item.getChecked()) {
+ Table parentTable = item.getParent();
+ for (TableItem i2 : parentTable.getItems()) {
+ if (i2 != item && i2.getChecked()) {
+ i2.setChecked(false);
+ }
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Fills the table with all VM.
+ * The table columns are:
+ * <ul>
+ * <li>column 0: sdk name
+ * <li>column 1: sdk vendor
+ * <li>column 2: sdk api name
+ * <li>column 3: sdk version
+ * </ul>
+ */
+ private void fillTable(final Table table, IAndroidTarget filter) {
+ table.removeAll();
+ if (mVms != null && mVms.length > 0) {
+ table.setEnabled(true);
+ for (VmInfo vm : mVms) {
+ if (filter == null || filter.isCompatibleBaseFor(vm.getTarget())) {
+ TableItem item = new TableItem(table, SWT.NONE);
+ item.setData(vm);
+ item.setText(0, vm.getName());
+ IAndroidTarget target = vm.getTarget();
+ item.setText(1, target.getFullName());
+ item.setText(2, target.getApiVersionName());
+ item.setText(3, Integer.toString(target.getApiVersionNumber()));
+ }
+ }
+ }
+
+ if (table.getItemCount() == 0) {
+ table.setEnabled(false);
+ TableItem item = new TableItem(table, SWT.NONE);
+ item.setData(null);
+ item.setText(0, "--");
+ item.setText(1, "No VM available");
+ item.setText(2, "--");
+ item.setText(3, "--");
+ }
+ }
+
+ /**
+ * Sets up a tooltip that displays the current item description.
+ * <p/>
+ * Displaying a tooltip over the table looks kind of odd here. Instead we actually
+ * display the description in a label under the table.
+ */
+ private void setupTooltip(final Table table) {
+ /*
+ * Reference:
+ * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet125.java?view=markup
+ */
+
+ final Listener listener = new Listener() {
+ public void handleEvent(Event event) {
+
+ switch(event.type) {
+ case SWT.KeyDown:
+ case SWT.MouseExit:
+ case SWT.MouseDown:
+ return;
+
+ case SWT.MouseHover:
+ updateDescription(table.getItem(new Point(event.x, event.y)));
+ break;
+
+ case SWT.Selection:
+ if (event.item instanceof TableItem) {
+ updateDescription((TableItem) event.item);
+ }
+ break;
+
+ default:
+ return;
+ }
+
+ }
+ };
+
+ table.addListener(SWT.Dispose, listener);
+ table.addListener(SWT.KeyDown, listener);
+ table.addListener(SWT.MouseMove, listener);
+ table.addListener(SWT.MouseHover, listener);
+ }
+
+ /**
+ * Updates the description label with the path of the item's VM, if any.
+ */
+ private void updateDescription(TableItem item) {
+ if (item != null) {
+ Object data = item.getData();
+ if (data instanceof VmInfo) {
+ String newTooltip = ((VmInfo) data).getPath();
+ mDescription.setText(newTooltip == null ? "" : newTooltip); //$NON-NLS-1$
+ }
+ }
+ }
+}