From 114be912894a344748a010a212abf09b274defd1 Mon Sep 17 00:00:00 2001
From: Brett Chabot <>
Date: Tue, 24 Mar 2009 17:42:09 -0700
Subject: [PATCH 001/127] Automated import from
//branches/donutburger/...@140465,140465
---
.../plugins/com.android.ide.eclipse.adt/plugin.xml | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index c18c72f05..2ab64256f 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -506,7 +506,7 @@
delegateDescription="Removes the Android JAR from the Bootstrap Classpath"
id="com.android.ide.eclipse.adt.launch.JUnitLaunchConfigDelegate.launchAndroidJunit"
modes="run,debug"
- name="Android JUnit"
+ name="Android JUnit Test"
type="org.eclipse.jdt.junit.launchconfig">
@@ -516,7 +516,7 @@
delegate="com.android.ide.eclipse.adt.launch.junit.AndroidJUnitLaunchConfigDelegate"
id="com.android.ide.eclipse.adt.junit.launchConfigurationType"
modes="run,debug"
- name="Android Instrumentation"
+ name="Android JUnit Test"
public="true"
sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"
sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer">
@@ -534,7 +534,7 @@
point="org.eclipse.debug.ui.launchConfigurationTabGroups">
@@ -544,7 +544,7 @@
class="com.android.ide.eclipse.adt.launch.junit.AndroidJUnitLaunchShortcut"
icon="icons/android.png"
id="com.android.ide.eclipse.adt.junit.launchShortcut"
- label="Android Instrumentation"
+ label="Android JUnit Test"
modes="run,debug">
From 1d0652bf7b68f1e404976a4a5d02184553c71dbb Mon Sep 17 00:00:00 2001
From: Brett Chabot <>
Date: Tue, 24 Mar 2009 17:42:42 -0700
Subject: [PATCH 002/127] Automated import from
//branches/donutburger/...@140466,140466
---
.../AndroidJUnitLaunchConfigDelegate.java | 60 ++++++++++++++++++-
.../AndroidJUnitLaunchConfigurationTab.java | 8 +++
.../junit/AndroidJUnitLaunchShortcut.java | 30 ++++++++--
.../ide/eclipse/common/AndroidConstants.java | 3 +
.../common/project/AndroidManifestParser.java | 32 +++++++---
5 files changed, 117 insertions(+), 16 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
index 05cc6ae73..a624b0001 100755
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
@@ -18,10 +18,11 @@ package com.android.ide.eclipse.adt.launch.junit;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.launch.AndroidLaunch;
+import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration;
import com.android.ide.eclipse.adt.launch.AndroidLaunchController;
import com.android.ide.eclipse.adt.launch.IAndroidLaunchAction;
import com.android.ide.eclipse.adt.launch.LaunchConfigDelegate;
-import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration;
+import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
@@ -31,6 +32,7 @@ import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry;
@@ -46,6 +48,7 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
/** Launch config attribute that stores instrumentation runner */
static final String ATTR_INSTR_NAME = AdtPlugin.PLUGIN_ID + ".instrumentation"; //$NON-NLS-1$
+ static final String INSTRUMENTATION_OK = null;
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
@Override
@@ -87,7 +90,8 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
* Helper method to return the set of instrumentations for the Android project
*
* @param project the {@link IProject} to get instrumentations for
- * @return null if no error occurred parsing instrumentations
+ * @return null if error occurred parsing instrumentations, otherwise returns array of
+ * instrumentation class names
*/
static String[] getInstrumentationsForProject(IProject project) {
if (project != null) {
@@ -117,4 +121,56 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_RUNNER_KIND,
TestKindRegistry.JUNIT3_TEST_KIND_ID);
}
+
+ /**
+ * Helper method to determine if specified instrumentation can be used as a test runner
+ *
+ * @param project the {@link IJavaProject} to validate
+ * @param instrumentation the instrumentation class name to validate
+ * @return INSTRUMENTATION_OK if valid, otherwise returns error message
+ */
+ static String validateInstrumentationRunner(IJavaProject project, String instrumentation) {
+ AndroidManifestParser manifestParser;
+ try {
+ manifestParser = AndroidManifestParser.parse(
+ project, null /* errorListener */,
+ true /* gatherData */, false /* markErrors */);
+ // check if this instrumentation is the standard test runner
+ if (!instrumentation.equals(AndroidConstants.CLASS_INSTRUMENTATION_RUNNER)) {
+ // check if it extends the standard test runner
+ String result = BaseProjectHelper.testClassForManifest(project,
+ instrumentation, AndroidConstants.CLASS_INSTRUMENTATION_RUNNER, true);
+ if (result != BaseProjectHelper.TEST_CLASS_OK) {
+ return String.format("The instrumentation runner must be of type %s",
+ AndroidConstants.CLASS_INSTRUMENTATION_RUNNER);
+ }
+ }
+ if (!hasTestRunnerLibrary(manifestParser)) {
+ return String.format("%s does not not use the %s library",
+ project.getProject().getName(), AndroidConstants.LIBRARY_TEST_RUNNER);
+ }
+ } catch (CoreException e) {
+ String err = String.format("Error parsing AndroidManifest for %s",
+ project.getProject().getName());
+ AdtPlugin.log(e, err);
+ return err;
+ }
+ return INSTRUMENTATION_OK;
+ }
+
+ /**
+ * Helper method to determine if given manifest has a AndroidConstants.LIBRARY_TEST_RUNNER
+ * library reference
+ *
+ * @param manifestParser the {@link AndroidManifestParser} to search
+ * @return true if test runner library found, false otherwise
+ */
+ static boolean hasTestRunnerLibrary(AndroidManifestParser manifestParser) {
+ for (String lib : manifestParser.getUsesLibraries()) {
+ if (lib.equals(AndroidConstants.LIBRARY_TEST_RUNNER)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
index 5fbda983d..aa59a5157 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
@@ -691,10 +691,18 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
private void validateInstrumentation(IJavaProject javaProject) {
if (mInstrumentations == null || mInstrumentations.length < 1) {
setErrorMessage("Specified project has no defined instrumentations");
+ return;
}
String instrumentation = getSelectedInstrumentation();
if (instrumentation == null) {
setErrorMessage("Instrumentation not specified");
+ return;
+ }
+ String result = AndroidJUnitLaunchConfigDelegate.validateInstrumentationRunner(
+ javaProject, instrumentation);
+ if (result != AndroidJUnitLaunchConfigDelegate.INSTRUMENTATION_OK) {
+ setErrorMessage(result);
+ return;
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java
index e03f2822b..30649e2e8 100755
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java
@@ -16,6 +16,9 @@
package com.android.ide.eclipse.adt.launch.junit;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.common.AndroidConstants;
+
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
@@ -34,7 +37,7 @@ public class AndroidJUnitLaunchShortcut extends JUnitLaunchShortcut {
/**
* Creates a default Android JUnit launch configuration. Sets the instrumentation runner to the
- * first instrumentation found in the AndroidManifest.
+ * first instrumentation found in the AndroidManifest.
*/
@Override
protected ILaunchConfigurationWorkingCopy createLaunchConfiguration(IJavaElement element)
@@ -43,10 +46,27 @@ public class AndroidJUnitLaunchShortcut extends JUnitLaunchShortcut {
IProject project = element.getResource().getProject();
String[] instrumentations =
AndroidJUnitLaunchConfigDelegate.getInstrumentationsForProject(project);
- if (instrumentations != null && instrumentations.length > 0) {
- // just pick the first runner
- config.setAttribute(AndroidJUnitLaunchConfigDelegate.ATTR_INSTR_NAME,
- instrumentations[0]);
+ boolean runnerFound = false;
+ if (instrumentations != null) {
+ // just pick the first valid runner
+ for (String instr : instrumentations) {
+ if (AndroidJUnitLaunchConfigDelegate.validateInstrumentationRunner(
+ element.getJavaProject(), instr) ==
+ AndroidJUnitLaunchConfigDelegate.INSTRUMENTATION_OK) {
+
+ config.setAttribute(AndroidJUnitLaunchConfigDelegate.ATTR_INSTR_NAME,
+ instr);
+ runnerFound = true;
+ break;
+ }
+ }
+ }
+ if (!runnerFound) {
+ // TODO: put this in a string properties
+ String msg = String.format("ERROR: Application does not specify a %s instrumentation or does not declare uses-library %s",
+ AndroidConstants.CLASS_INSTRUMENTATION_RUNNER,
+ AndroidConstants.LIBRARY_TEST_RUNNER);
+ AdtPlugin.printErrorToConsole(project, msg);
}
AndroidJUnitLaunchConfigDelegate.setJUnitDefaults(config);
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
index 5abfd811d..1da753c7b 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
@@ -180,6 +180,8 @@ public class AndroidConstants {
public final static String CLASS_BROADCASTRECEIVER = "android.content.BroadcastReceiver"; //$NON-NLS-1$
public final static String CLASS_CONTENTPROVIDER = "android.content.ContentProvider"; //$NON-NLS-1$
public final static String CLASS_INSTRUMENTATION = "android.app.Instrumentation"; //$NON-NLS-1$
+ public final static String CLASS_INSTRUMENTATION_RUNNER =
+ "android.test.InstrumentationTestRunner"; //$NON-NLS-1$
public final static String CLASS_BUNDLE = "android.os.Bundle"; //$NON-NLS-1$
public final static String CLASS_R = "android.R"; //$NON-NLS-1$
public final static String CLASS_MANIFEST_PERMISSION = "android.Manifest$permission"; //$NON-NLS-1$
@@ -215,4 +217,5 @@ public class AndroidConstants {
/** The base URL where to find the Android class & manifest documentation */
public static final String CODESITE_BASE_URL = "http://code.google.com/android"; //$NON-NLS-1$
+ public static final String LIBRARY_TEST_RUNNER = "android.test.runner"; // $NON-NLS-1$
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
index 42c881b15..fe11e694c 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
@@ -255,12 +255,8 @@ public class AndroidManifestParser {
} catch (NumberFormatException e) {
handleError(e, -1 /* lineNumber */);
}
- } else if (NODE_INSTRUMENTATION.equals(localName)) {
- value = getAttributeValue(attributes, ATTRIBUTE_NAME,
- true /* hasNamespace */);
- if (value != null) {
- mInstrumentations.add(value);
- }
+ } else if (NODE_INSTRUMENTATION.equals(localName)) {
+ processInstrumentationNode(attributes);
}
break;
case LEVEL_ACTIVITY:
@@ -449,6 +445,25 @@ public class AndroidManifestParser {
addProcessName(processName);
}
}
+
+ /**
+ * Processes the instrumentation nodes.
+ * @param attributes the attributes for the activity node.
+ * node is representing
+ */
+ private void processInstrumentationNode(Attributes attributes) {
+ // lets get the class name, and check it if required.
+ String instrumentationName = getAttributeValue(attributes, ATTRIBUTE_NAME,
+ true /* hasNamespace */);
+ if (instrumentationName != null) {
+ String instrClassName = combinePackageAndClassName(mPackage, instrumentationName);
+ mInstrumentations.add(instrClassName);
+ if (mMarkErrors) {
+ checkClass(instrClassName, AndroidConstants.CLASS_INSTRUMENTATION,
+ true /* testVisibility */);
+ }
+ }
+ }
/**
* Checks that a class is valid and can be used in the Android Manifest.
@@ -484,8 +499,7 @@ public class AndroidManifestParser {
} catch (CoreException e) {
}
}
- }
-
+ }
}
/**
@@ -583,7 +597,7 @@ public class AndroidManifestParser {
return null;
}
-
+
/**
* Parses the Android Manifest, and returns an object containing the result of the parsing.
*
From 02c11da47f8916cf50095b10c16478a136a21aa5 Mon Sep 17 00:00:00 2001
From: Xavier Ducrohet <>
Date: Tue, 24 Mar 2009 18:14:09 -0700
Subject: [PATCH 003/127] Automated import from
//branches/donutburger/...@140696,140696
---
.../editors/layout/GraphicalLayoutEditor.java | 154 ++++++++++++------
1 file changed, 102 insertions(+), 52 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java
index 9c529e5da..12d49fe27 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java
@@ -61,8 +61,10 @@ import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.LanguageReg
import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.MobileCodeVerifier;
import com.android.layoutlib.api.ILayoutLog;
import com.android.layoutlib.api.ILayoutResult;
+import com.android.layoutlib.api.IProjectCallback;
import com.android.layoutlib.api.IResourceValue;
import com.android.layoutlib.api.IStyleResourceValue;
+import com.android.layoutlib.api.IXmlPullParser;
import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
import com.android.sdklib.IAndroidTarget;
@@ -222,7 +224,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
// updateUiFromFramework will reset language/region combo, so we must call
// setConfiguration after, or the settext on language/region will be lost.
if (mEditedConfig != null) {
- setConfiguration(mEditedConfig);
+ setConfiguration(mEditedConfig, false /*force*/);
}
// make sure we remove the custom view loader, since its parent class loader is the
@@ -867,7 +869,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
@Override
void editNewFile(FolderConfiguration configuration) {
// update the configuration UI
- setConfiguration(configuration);
+ setConfiguration(configuration, true /*force*/);
// enable the create button if the current and edited config are not equals
mCreateButton.setEnabled(mEditedConfig.equals(mCurrentConfig) == false);
@@ -975,18 +977,14 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
int themeIndex = mThemeCombo.getSelectionIndex();
if (themeIndex != -1) {
String theme = mThemeCombo.getItem(themeIndex);
-
- // change the string if it's a custom theme to make sure we can
- // differentiate them
- if (themeIndex >= mPlatformThemeCount) {
- theme = "*" + theme; //$NON-NLS-1$
- }
// Render a single object as described by the ViewElementDescriptor.
WidgetPullParser parser = new WidgetPullParser(descriptor);
- ILayoutResult result = bridge.bridge.computeLayout(parser,
+ ILayoutResult result = computeLayout(bridge, parser,
null /* projectKey */,
- 300 /* width */, 300 /* height */, theme,
+ 300 /* width */, 300 /* height */, 160 /*density*/,
+ 160.f /*xdpi*/, 160.f /*ydpi*/, theme,
+ themeIndex >= mPlatformThemeCount /*isProjectTheme*/,
configuredProjectResources, frameworkResources, projectCallback,
null /* logger */);
@@ -1073,11 +1071,14 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
/**
* Update the UI controls state with a given {@link FolderConfiguration}.
- * If a qualifier is not present in the {@link FolderConfiguration} object, the UI control
- * is not modified. However if the value in the control is not the default value, a warning
- * icon is showed.
+ * If force is set to true the UI will be changed to exactly reflect
+ * config, otherwise, if a qualifier is not present in config,
+ * the UI control is not modified. However if the value in the control is not the default value,
+ * a warning icon is shown.
+ * @param config The {@link FolderConfiguration} to set.
+ * @param force Whether the UI should be changed to exactly match the received configuration.
*/
- void setConfiguration(FolderConfiguration config) {
+ void setConfiguration(FolderConfiguration config, boolean force) {
mDisableUpdates = true; // we do not want to trigger onXXXChange when setting new values in the widgets.
mEditedConfig = config;
@@ -1088,6 +1089,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
if (countryQualifier != null) {
mCountry.setText(String.format("%1$d", countryQualifier.getCode()));
mCurrentConfig.setCountryCodeQualifier(countryQualifier);
+ } else if (force) {
+ mCountry.setText(""); //$NON-NLS-1$
+ mCurrentConfig.setCountryCodeQualifier(null);
} else if (mCountry.getText().length() > 0) {
mCountryIcon.setImage(mWarningImage);
}
@@ -1097,6 +1101,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
if (networkQualifier != null) {
mNetwork.setText(String.format("%1$d", networkQualifier.getCode()));
mCurrentConfig.setNetworkCodeQualifier(networkQualifier);
+ } else if (force) {
+ mNetwork.setText(""); //$NON-NLS-1$
+ mCurrentConfig.setNetworkCodeQualifier(null);
} else if (mNetwork.getText().length() > 0) {
mNetworkIcon.setImage(mWarningImage);
}
@@ -1106,6 +1113,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
if (languageQualifier != null) {
mLanguage.setText(languageQualifier.getValue());
mCurrentConfig.setLanguageQualifier(languageQualifier);
+ } else if (force) {
+ mLanguage.setText(""); //$NON-NLS-1$
+ mCurrentConfig.setLanguageQualifier(null);
} else if (mLanguage.getText().length() > 0) {
mLanguageIcon.setImage(mWarningImage);
}
@@ -1115,6 +1125,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
if (regionQualifier != null) {
mRegion.setText(regionQualifier.getValue());
mCurrentConfig.setRegionQualifier(regionQualifier);
+ } else if (force) {
+ mRegion.setText(""); //$NON-NLS-1$
+ mCurrentConfig.setRegionQualifier(null);
} else if (mRegion.getText().length() > 0) {
mRegionIcon.setImage(mWarningImage);
}
@@ -1125,6 +1138,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
mOrientation.select(
ScreenOrientation.getIndex(orientationQualifier.getValue()) + 1);
mCurrentConfig.setScreenOrientationQualifier(orientationQualifier);
+ } else if (force) {
+ mOrientation.select(0);
+ mCurrentConfig.setScreenOrientationQualifier(null);
} else if (mOrientation.getSelectionIndex() != 0) {
mOrientationIcon.setImage(mWarningImage);
}
@@ -1134,6 +1150,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
if (densityQualifier != null) {
mDensity.setText(String.format("%1$d", densityQualifier.getValue()));
mCurrentConfig.setPixelDensityQualifier(densityQualifier);
+ } else if (force) {
+ mDensity.setText(""); //$NON-NLS-1$
+ mCurrentConfig.setPixelDensityQualifier(null);
} else if (mDensity.getText().length() > 0) {
mDensityIcon.setImage(mWarningImage);
}
@@ -1143,6 +1162,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
if (touchQualifier != null) {
mTouch.select(TouchScreenType.getIndex(touchQualifier.getValue()) + 1);
mCurrentConfig.setTouchTypeQualifier(touchQualifier);
+ } else if (force) {
+ mTouch.select(0);
+ mCurrentConfig.setTouchTypeQualifier(null);
} else if (mTouch.getSelectionIndex() != 0) {
mTouchIcon.setImage(mWarningImage);
}
@@ -1152,6 +1174,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
if (keyboardQualifier != null) {
mKeyboard.select(KeyboardState.getIndex(keyboardQualifier.getValue()) + 1);
mCurrentConfig.setKeyboardStateQualifier(keyboardQualifier);
+ } else if (force) {
+ mKeyboard.select(0);
+ mCurrentConfig.setKeyboardStateQualifier(null);
} else if (mKeyboard.getSelectionIndex() != 0) {
mKeyboardIcon.setImage(mWarningImage);
}
@@ -1161,6 +1186,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
if (inputQualifier != null) {
mTextInput.select(TextInputMethod.getIndex(inputQualifier.getValue()) + 1);
mCurrentConfig.setTextInputMethodQualifier(inputQualifier);
+ } else if (force) {
+ mTextInput.select(0);
+ mCurrentConfig.setTextInputMethodQualifier(null);
} else if (mTextInput.getSelectionIndex() != 0) {
mTextInputIcon.setImage(mWarningImage);
}
@@ -1171,6 +1199,9 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
mNavigation.select(
NavigationMethod.getIndex(navigationQualifiter.getValue()) + 1);
mCurrentConfig.setNavigationMethodQualifier(navigationQualifiter);
+ } else if (force) {
+ mNavigation.select(0);
+ mCurrentConfig.setNavigationMethodQualifier(null);
} else if (mNavigation.getSelectionIndex() != 0) {
mNavigationIcon.setImage(mWarningImage);
}
@@ -1181,6 +1212,10 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
mSize1.setText(String.format("%1$d", sizeQualifier.getValue1()));
mSize2.setText(String.format("%1$d", sizeQualifier.getValue2()));
mCurrentConfig.setScreenDimensionQualifier(sizeQualifier);
+ } else if (force) {
+ mSize1.setText(""); //$NON-NLS-1$
+ mSize2.setText(""); //$NON-NLS-1$
+ mCurrentConfig.setScreenDimensionQualifier(null);
} else if (mSize1.getText().length() > 0 && mSize2.getText().length() > 0) {
mSizeIcon.setImage(mWarningImage);
}
@@ -1607,7 +1642,7 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
// at this point, we have not opened a new file.
// update the configuration icons with the new edited config.
- setConfiguration(mEditedConfig);
+ setConfiguration(mEditedConfig, false /*force*/);
// enable the create button if the current and edited config are not equals
mCreateButton.setEnabled(mEditedConfig.equals(mCurrentConfig) == false);
@@ -1794,45 +1829,16 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
// Compute the layout
UiElementPullParser parser = new UiElementPullParser(getModel());
Rectangle rect = getBounds();
- ILayoutResult result = null;
- if (bridge.apiLevel >= 3) {
- // call the new api with proper theme differentiator and
- // density/dpi support.
- boolean isProjectTheme = themeIndex >= mPlatformThemeCount;
+ boolean isProjectTheme = themeIndex >= mPlatformThemeCount;
- // FIXME pass the density/dpi from somewhere (resource config or skin).
- result = bridge.bridge.computeLayout(parser,
- iProject /* projectKey */,
- rect.width, rect.height, 160, 160.f, 160.f,
- theme, isProjectTheme,
- mConfiguredProjectRes, frameworkResources, mProjectCallback,
- mLogger);
- } else if (bridge.apiLevel == 2) {
- // api with boolean for separation of project/framework theme
- boolean isProjectTheme = themeIndex >= mPlatformThemeCount;
+ // FIXME pass the density/dpi from somewhere (resource config or skin).
+ ILayoutResult result = computeLayout(bridge, parser,
+ iProject /* projectKey */,
+ rect.width, rect.height, 160, 160.f, 160.f,
+ theme, isProjectTheme,
+ mConfiguredProjectRes, frameworkResources, mProjectCallback,
+ mLogger);
- result = bridge.bridge.computeLayout(parser,
- iProject /* projectKey */,
- rect.width, rect.height, theme, isProjectTheme,
- mConfiguredProjectRes, frameworkResources, mProjectCallback,
- mLogger);
- } else {
- // oldest api with no density/dpi, and project theme boolean mixed
- // into the theme name.
-
- // change the string if it's a custom theme to make sure we can
- // differentiate them
- if (themeIndex >= mPlatformThemeCount) {
- theme = "*" + theme; //$NON-NLS-1$
- }
-
- result = bridge.bridge.computeLayout(parser,
- iProject /* projectKey */,
- rect.width, rect.height, theme,
- mConfiguredProjectRes, frameworkResources, mProjectCallback,
- mLogger);
- }
-
// update the UiElementNode with the layout info.
if (result.getSuccess() == ILayoutResult.SUCCESS) {
model.setEditData(result.getImage());
@@ -2383,4 +2389,48 @@ public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor
return null;
}
+
+ /**
+ * Computes a layout by calling the correct computeLayout method of ILayoutBridge based on
+ * the implementation API level.
+ */
+ @SuppressWarnings("deprecation")
+ private ILayoutResult computeLayout(LayoutBridge bridge,
+ IXmlPullParser layoutDescription, Object projectKey,
+ int screenWidth, int screenHeight, int density, float xdpi, float ydpi,
+ String themeName, boolean isProjectTheme,
+ Map> projectResources,
+ Map> frameworkResources,
+ IProjectCallback projectCallback, ILayoutLog logger) {
+
+ if (bridge.apiLevel >= 3) {
+ // newer api with boolean for separation of project/framework theme,
+ // and density support.
+ return bridge.bridge.computeLayout(layoutDescription,
+ projectKey, screenWidth, screenHeight, density, xdpi, ydpi,
+ themeName, isProjectTheme,
+ projectResources, frameworkResources, projectCallback,
+ logger);
+ } else if (bridge.apiLevel == 2) {
+ // api with boolean for separation of project/framework theme
+ return bridge.bridge.computeLayout(layoutDescription,
+ projectKey, screenWidth, screenHeight, themeName, isProjectTheme,
+ mConfiguredProjectRes, frameworkResources, mProjectCallback,
+ mLogger);
+ } else {
+ // oldest api with no density/dpi, and project theme boolean mixed
+ // into the theme name.
+
+ // change the string if it's a custom theme to make sure we can
+ // differentiate them
+ if (isProjectTheme) {
+ themeName = "*" + themeName; //$NON-NLS-1$
+ }
+
+ return bridge.bridge.computeLayout(layoutDescription,
+ projectKey, screenWidth, screenHeight, themeName,
+ mConfiguredProjectRes, frameworkResources, mProjectCallback,
+ mLogger);
+ }
+ }
}
From 9c03440f19481e49cef8142c66b0bda76c59a74d Mon Sep 17 00:00:00 2001
From: Xavier Ducrohet <>
Date: Tue, 24 Mar 2009 18:15:02 -0700
Subject: [PATCH 004/127] Automated import from
//branches/donutburger/...@140697,140697
---
.../launch/AndroidLaunchConfiguration.java | 31 ++++++++++++++++---
.../adt/launch/AndroidLaunchController.java | 16 ++++++++--
.../adt/launch/LaunchConfigDelegate.java | 3 +-
.../com/android/sdklib/avd/AvdManager.java | 20 ++++++++++--
4 files changed, 60 insertions(+), 10 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java
index 448cda6b5..3e610db08 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java
@@ -32,8 +32,30 @@ public class AndroidLaunchConfiguration {
*/
public int mLaunchAction = LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION;
- public static final boolean AUTO_TARGET_MODE = true;
+ public enum TargetMode {
+ AUTO(true), MANUAL(false);
+
+ private boolean mValue;
+ TargetMode(boolean value) {
+ mValue = value;
+ }
+
+ public boolean getValue() {
+ return mValue;
+ }
+
+ public static TargetMode getMode(boolean value) {
+ for (TargetMode mode : values()) {
+ if (mode.mValue == value) {
+ return mode;
+ }
+ }
+
+ return null;
+ }
+ }
+
/**
* Target selection mode.
*
@@ -41,7 +63,7 @@ public class AndroidLaunchConfiguration {
*
false: manual mode
*
*/
- public boolean mTargetMode = LaunchConfigDelegate.DEFAULT_TARGET_MODE;
+ public TargetMode mTargetMode = LaunchConfigDelegate.DEFAULT_TARGET_MODE;
/**
* Indicates whether the emulator should be called with -wipe-data
@@ -81,8 +103,9 @@ public class AndroidLaunchConfiguration {
}
try {
- mTargetMode = config.getAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
- mTargetMode);
+ boolean value = config.getAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
+ mTargetMode.getValue());
+ mTargetMode = TargetMode.getMode(value);
} catch (CoreException e) {
// nothing to be done here, we'll use the default value
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
index 499cca704..5cf966904 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
@@ -29,11 +29,13 @@ import com.android.ddmlib.MultiLineReceiver;
import com.android.ddmlib.SyncService;
import com.android.ddmlib.SyncService.SyncResult;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration.TargetMode;
import com.android.ide.eclipse.adt.launch.DelayedLaunchInfo.InstallRetryMode;
import com.android.ide.eclipse.adt.launch.DeviceChooserDialog.DeviceChooserResponse;
import com.android.ide.eclipse.adt.project.ProjectHelper;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
+import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkManager;
import com.android.sdklib.avd.AvdManager;
@@ -236,7 +238,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
// set default target mode
wc.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
- LaunchConfigDelegate.DEFAULT_TARGET_MODE);
+ LaunchConfigDelegate.DEFAULT_TARGET_MODE.getValue());
// default AVD: None
wc.setAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, (String) null);
@@ -332,6 +334,16 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
Sdk currentSdk = Sdk.getCurrent();
AvdManager avdManager = currentSdk.getAvdManager();
+ // reload the AVDs to make sure we are up to date
+ try {
+ avdManager.reloadAvds();
+ } catch (AndroidLocationException e1) {
+ // this happens if the AVD Manager failed to find the folder in which the AVDs are
+ // stored. This is unlikely to happen, but if it does, we should force to go manual
+ // to allow using physical devices.
+ config.mTargetMode = TargetMode.MANUAL;
+ }
+
// get the project target
final IAndroidTarget projectTarget = currentSdk.getTarget(project);
@@ -356,7 +368,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* If == 1, launch the application on this AVD/device.
*/
- if (config.mTargetMode == AndroidLaunchConfiguration.AUTO_TARGET_MODE) {
+ if (config.mTargetMode == TargetMode.AUTO) {
// if we are in automatic target mode, we need to find the current devices
IDevice[] devices = AndroidDebugBridge.getBridge().getDevices();
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
index 80f62eaa8..db9a4aceb 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
@@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.launch;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration.TargetMode;
import com.android.ide.eclipse.adt.project.ProjectHelper;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
@@ -51,7 +52,7 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
/** Target mode parameters: true is automatic, false is manual */
public static final String ATTR_TARGET_MODE = AdtPlugin.PLUGIN_ID + ".target"; //$NON-NLS-1$
- public static final boolean DEFAULT_TARGET_MODE = true; //automatic mode
+ public static final TargetMode DEFAULT_TARGET_MODE = TargetMode.AUTO;
/**
* Launch action:
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
index 65cbbe356..93577e42b 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
@@ -177,7 +177,7 @@ public final class AvdManager {
public AvdManager(SdkManager sdk, ISdkLog sdkLog) throws AndroidLocationException {
mSdk = sdk;
mSdkLog = sdkLog;
- buildAvdList();
+ buildAvdList(mAvdList);
}
/**
@@ -201,6 +201,20 @@ public final class AvdManager {
return null;
}
+
+ /**
+ * Reloads the AVD list.
+ * @throws AndroidLocationException if there was an error finding the location of the
+ * AVD folder.
+ */
+ public void reloadAvds() throws AndroidLocationException {
+ // build the list in a temp list first, in case the method throws an exception.
+ // It's better than deleting the whole list before reading the new one.
+ ArrayList list = new ArrayList();
+ buildAvdList(list);
+ mAvdList.clear();
+ mAvdList.addAll(list);
+ }
/**
* Creates a new AVD. It is expected that there is no existing AVD with this name already.
@@ -620,7 +634,7 @@ public final class AvdManager {
}
}
- private void buildAvdList() throws AndroidLocationException {
+ private void buildAvdList(ArrayList list) throws AndroidLocationException {
// get the Android prefs location.
String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;
@@ -664,7 +678,7 @@ public final class AvdManager {
for (File avd : avds) {
AvdInfo info = parseAvdInfo(avd);
if (info != null) {
- mAvdList.add(info);
+ list.add(info);
if (avdListDebug) {
mSdkLog.printf("[AVD LIST DEBUG] Added AVD '%s'\n", info.getPath());
}
From 024d1c2375a09cadf7809c454fb04c190116815d Mon Sep 17 00:00:00 2001
From: Andy McFadden <>
Date: Tue, 24 Mar 2009 18:15:56 -0700
Subject: [PATCH 005/127] Automated import from
//branches/donutburger/...@140700,140700
---
.../android/ddmlib/AndroidDebugBridge.java | 1 +
.../src/com/android/ddmlib/HandleHeap.java | 41 +++++
.../src/com/android/ddmlib/HandleHello.java | 37 +++-
.../com/android/ddmlib/HandleProfiling.java | 163 ++++++++++++++++++
4 files changed, 241 insertions(+), 1 deletion(-)
create mode 100644 tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java
index 795bf886a..0957171c6 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/AndroidDebugBridge.java
@@ -194,6 +194,7 @@ public final class AndroidDebugBridge {
HandleThread.register(monitorThread);
HandleHeap.register(monitorThread);
HandleWait.register(monitorThread);
+ HandleProfiling.register(monitorThread);
}
/**
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java
index 5752b861a..51116387e 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHeap.java
@@ -32,6 +32,7 @@ final class HandleHeap extends ChunkHandler {
public static final int CHUNK_HPEN = type("HPEN");
public static final int CHUNK_HPSG = type("HPSG");
public static final int CHUNK_HPGC = type("HPGC");
+ public static final int CHUNK_HPDU = type("HPDU");
public static final int CHUNK_REAE = type("REAE");
public static final int CHUNK_REAQ = type("REAQ");
public static final int CHUNK_REAL = type("REAL");
@@ -98,6 +99,8 @@ final class HandleHeap extends ChunkHandler {
client.update(Client.CHANGE_HEAP_DATA);
} else if (type == CHUNK_HPSG) {
handleHPSG(client, data);
+ } else if (type == CHUNK_HPDU) {
+ handleHPDU(client, data);
} else if (type == CHUNK_REAQ) {
handleREAQ(client, data);
client.update(Client.CHANGE_HEAP_ALLOCATION_STATUS);
@@ -220,6 +223,44 @@ final class HandleHeap extends ChunkHandler {
client.sendAndConsume(packet, mInst);
}
+ /**
+ * Sends an HPDU request to the client.
+ *
+ * We will get an HPDU response when the heap dump has completed. On
+ * failure we get a generic failure response.
+ *
+ * @param fileName name of output file (on device)
+ */
+ public static void sendHPDU(Client client, String fileName)
+ throws IOException {
+ ByteBuffer rawBuf = allocBuffer(4 + fileName.length() * 2);
+ JdwpPacket packet = new JdwpPacket(rawBuf);
+ ByteBuffer buf = getChunkDataBuf(rawBuf);
+
+ buf.putInt(fileName.length());
+ putString(buf, fileName);
+
+ finishChunkPacket(packet, CHUNK_HPDU, buf.position());
+ Log.d("ddm-heap", "Sending " + name(CHUNK_HPDU) + " '" + fileName +"'");
+ client.sendAndConsume(packet, mInst);
+ }
+
+ /*
+ * Handle notification of completion of a HeaP DUmp.
+ */
+ private void handleHPDU(Client client, ByteBuffer data) {
+ byte result;
+
+ result = data.get();
+
+ if (result == 0) {
+ Log.i("ddm-heap", "Heap dump request has finished");
+ // TODO: stuff
+ } else {
+ Log.w("ddm-heap", "Heap dump request failed (check device log)");
+ }
+ }
+
/**
* Sends a REAE (REcent Allocation Enable) request to the client.
*/
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java
index 5ba5aeb8f..fb9697cce 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java
@@ -20,11 +20,12 @@ import java.io.IOException;
import java.nio.ByteBuffer;
/**
- * Handle the "hello" chunk (HELO).
+ * Handle the "hello" chunk (HELO) and feature discovery.
*/
final class HandleHello extends ChunkHandler {
public static final int CHUNK_HELO = ChunkHandler.type("HELO");
+ public static final int CHUNK_FEAT = ChunkHandler.type("FEAT");
private static final HandleHello mInst = new HandleHello();
@@ -65,6 +66,8 @@ final class HandleHello extends ChunkHandler {
if (type == CHUNK_HELO) {
assert isReply;
handleHELO(client, data);
+ } else if (type == CHUNK_FEAT) {
+ handleFEAT(client, data);
} else {
handleUnknownChunk(client, type, data, isReply, msgId);
}
@@ -126,5 +129,37 @@ final class HandleHello extends ChunkHandler {
+ " ID=0x" + Integer.toHexString(packet.getId()));
client.sendAndConsume(packet, mInst);
}
+
+ /**
+ * Handle a reply to our FEAT request.
+ */
+ private static void handleFEAT(Client client, ByteBuffer data) {
+ int featureCount;
+ int i;
+
+ featureCount = data.getInt();
+ for (i = 0; i < featureCount; i++) {
+ int len = data.getInt();
+ String feature = getString(data, len);
+
+ Log.d("ddm-hello", "Feature: " + feature);
+ }
+ }
+
+ /**
+ * Send a FEAT request to the client.
+ */
+ public static void sendFEAT(Client client) throws IOException {
+ ByteBuffer rawBuf = allocBuffer(0);
+ JdwpPacket packet = new JdwpPacket(rawBuf);
+ ByteBuffer buf = getChunkDataBuf(rawBuf);
+
+ // no data
+
+ finishChunkPacket(packet, CHUNK_FEAT, buf.position());
+ Log.d("ddm-heap", "Sending " + name(CHUNK_FEAT));
+ client.sendAndConsume(packet, mInst);
+ }
+
}
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java
new file mode 100644
index 000000000..e8e810325
--- /dev/null
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleProfiling.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ddmlib;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Handle heap status updates.
+ */
+final class HandleProfiling extends ChunkHandler {
+
+ public static final int CHUNK_MPRS = type("MPRS");
+ public static final int CHUNK_MPRE = type("MPRE");
+ public static final int CHUNK_MPRQ = type("MPRQ");
+
+ private static final HandleProfiling mInst = new HandleProfiling();
+
+ private HandleProfiling() {}
+
+ /**
+ * Register for the packets we expect to get from the client.
+ */
+ public static void register(MonitorThread mt) {
+ mt.registerChunkHandler(CHUNK_MPRE, mInst);
+ mt.registerChunkHandler(CHUNK_MPRQ, mInst);
+ }
+
+ /**
+ * Client is ready.
+ */
+ @Override
+ public void clientReady(Client client) throws IOException {}
+
+ /**
+ * Client went away.
+ */
+ @Override
+ public void clientDisconnected(Client client) {}
+
+ /**
+ * Chunk handler entry point.
+ */
+ @Override
+ public void handleChunk(Client client, int type, ByteBuffer data,
+ boolean isReply, int msgId) {
+
+ Log.d("ddm-prof", "handling " + ChunkHandler.name(type));
+
+ if (type == CHUNK_MPRE) {
+ handleMPRE(client, data);
+ } else if (type == CHUNK_MPRQ) {
+ handleMPRQ(client, data);
+ } else {
+ handleUnknownChunk(client, type, data, isReply, msgId);
+ }
+ }
+
+ /**
+ * Send a MPRS (Method PRofiling Start) request to the client.
+ *
+ * @param fileName is the name of the file to which profiling data
+ * will be written (on the device); it will have ".trace"
+ * appended if necessary
+ * @param bufferSize is the desired buffer size in bytes (8MB is good)
+ * @param flags should be zero
+ */
+ public static void sendMPRS(Client client, String fileName, int bufferSize,
+ int flags) throws IOException {
+
+ ByteBuffer rawBuf = allocBuffer(3*4 + fileName.length() * 2);
+ JdwpPacket packet = new JdwpPacket(rawBuf);
+ ByteBuffer buf = getChunkDataBuf(rawBuf);
+
+ buf.putInt(bufferSize);
+ buf.putInt(flags);
+ buf.putInt(fileName.length());
+ putString(buf, fileName);
+
+ finishChunkPacket(packet, CHUNK_MPRS, buf.position());
+ Log.d("ddm-prof", "Sending " + name(CHUNK_MPRS) + " '" + fileName
+ + "', size=" + bufferSize + ", flags=" + flags);
+ client.sendAndConsume(packet, mInst);
+ }
+
+ /**
+ * Send a MPRE (Method PRofiling End) request to the client.
+ */
+ public static void sendMPRE(Client client) throws IOException {
+ ByteBuffer rawBuf = allocBuffer(0);
+ JdwpPacket packet = new JdwpPacket(rawBuf);
+ ByteBuffer buf = getChunkDataBuf(rawBuf);
+
+ // no data
+
+ finishChunkPacket(packet, CHUNK_MPRE, buf.position());
+ Log.d("ddm-prof", "Sending " + name(CHUNK_MPRE));
+ client.sendAndConsume(packet, mInst);
+ }
+
+ /**
+ * Handle notification that method profiling has finished writing
+ * data to disk.
+ */
+ private void handleMPRE(Client client, ByteBuffer data) {
+ byte result;
+
+ result = data.get();
+
+ if (result == 0) {
+ Log.i("ddm-prof", "Method profiling has finished");
+ } else {
+ Log.w("ddm-prof", "Method profiling has failed (check device log)");
+ }
+
+ // TODO: stuff
+ }
+
+ /**
+ * Send a MPRQ (Method PRofiling Query) request to the client.
+ */
+ public static void sendMPRQ(Client client) throws IOException {
+ ByteBuffer rawBuf = allocBuffer(0);
+ JdwpPacket packet = new JdwpPacket(rawBuf);
+ ByteBuffer buf = getChunkDataBuf(rawBuf);
+
+ // no data
+
+ finishChunkPacket(packet, CHUNK_MPRQ, buf.position());
+ Log.d("ddm-prof", "Sending " + name(CHUNK_MPRQ));
+ client.sendAndConsume(packet, mInst);
+ }
+
+ /**
+ * Receive response to query.
+ */
+ private void handleMPRQ(Client client, ByteBuffer data) {
+ byte result;
+
+ result = data.get();
+
+ if (result == 0) {
+ Log.i("ddm-prof", "Method profiling is not running");
+ } else {
+ Log.i("ddm-prof", "Method profiling is running");
+ }
+ }
+}
+
From 7798a83bdf87605b9e6b9487a41d681872cc6dbc Mon Sep 17 00:00:00 2001
From: Xavier Ducrohet <>
Date: Tue, 24 Mar 2009 18:20:02 -0700
Subject: [PATCH 006/127] Automated import from
//branches/donutburger/...@140722,140722
---
.../ide/eclipse/adt/launch/EmulatorConfigTab.java | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java
index b898f63c5..3789153e4 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java
@@ -17,6 +17,7 @@
package com.android.ide.eclipse.adt.launch;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration.TargetMode;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.ddms.DdmsPlugin;
@@ -292,14 +293,15 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
public void initializeFrom(ILaunchConfiguration configuration) {
AvdManager avdManager = Sdk.getCurrent().getAvdManager();
- boolean value = LaunchConfigDelegate.DEFAULT_TARGET_MODE; // true == automatic
+ TargetMode mode = LaunchConfigDelegate.DEFAULT_TARGET_MODE; // true == automatic
try {
- value = configuration.getAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE, value);
+ mode = TargetMode.getMode(configuration.getAttribute(
+ LaunchConfigDelegate.ATTR_TARGET_MODE, mode.getValue()));
} catch (CoreException e) {
// let's not do anything here, we'll use the default value
}
- mAutoTargetButton.setSelection(value);
- mManualTargetButton.setSelection(!value);
+ mAutoTargetButton.setSelection(mode.getValue());
+ mManualTargetButton.setSelection(!mode.getValue());
// look for the project name to get its target.
String stringValue = "";
@@ -354,7 +356,7 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
mPreferredAvdSelector.setSelection(null);
}
- value = LaunchConfigDelegate.DEFAULT_WIPE_DATA;
+ boolean value = LaunchConfigDelegate.DEFAULT_WIPE_DATA;
try {
value = configuration.getAttribute(LaunchConfigDelegate.ATTR_WIPE_DATA, value);
} catch (CoreException e) {
@@ -440,7 +442,7 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
*/
public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
configuration.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
- LaunchConfigDelegate.DEFAULT_TARGET_MODE);
+ LaunchConfigDelegate.DEFAULT_TARGET_MODE.getValue());
configuration.setAttribute(LaunchConfigDelegate.ATTR_SPEED,
LaunchConfigDelegate.DEFAULT_SPEED);
configuration.setAttribute(LaunchConfigDelegate.ATTR_DELAY,
From f934a160fdf11489b8a6f7ec6beb55fb0c009cbb Mon Sep 17 00:00:00 2001
From: Dianne Hackborn <>
Date: Tue, 24 Mar 2009 18:36:43 -0700
Subject: [PATCH 007/127] Automated import from
//branches/donutburger/...@140818,140818
---
apps/CustomLocale/NOTICE | 190 +++++++++++++++++++++++++++++++++++++++
apps/Development/NOTICE | 190 +++++++++++++++++++++++++++++++++++++++
apps/SdkSetup/NOTICE | 190 +++++++++++++++++++++++++++++++++++++++
apps/SpareParts/NOTICE | 190 +++++++++++++++++++++++++++++++++++++++
apps/launchperf/NOTICE | 190 +++++++++++++++++++++++++++++++++++++++
5 files changed, 950 insertions(+)
create mode 100644 apps/CustomLocale/NOTICE
create mode 100644 apps/Development/NOTICE
create mode 100644 apps/SdkSetup/NOTICE
create mode 100644 apps/SpareParts/NOTICE
create mode 100644 apps/launchperf/NOTICE
diff --git a/apps/CustomLocale/NOTICE b/apps/CustomLocale/NOTICE
new file mode 100644
index 000000000..c5b1efa7a
--- /dev/null
+++ b/apps/CustomLocale/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/apps/Development/NOTICE b/apps/Development/NOTICE
new file mode 100644
index 000000000..c5b1efa7a
--- /dev/null
+++ b/apps/Development/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/apps/SdkSetup/NOTICE b/apps/SdkSetup/NOTICE
new file mode 100644
index 000000000..c5b1efa7a
--- /dev/null
+++ b/apps/SdkSetup/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/apps/SpareParts/NOTICE b/apps/SpareParts/NOTICE
new file mode 100644
index 000000000..c5b1efa7a
--- /dev/null
+++ b/apps/SpareParts/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/apps/launchperf/NOTICE b/apps/launchperf/NOTICE
new file mode 100644
index 000000000..c5b1efa7a
--- /dev/null
+++ b/apps/launchperf/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
From bbf8acf406c98b1e8a41a4b2c29d2285224ece8b Mon Sep 17 00:00:00 2001
From: Ricky Ng-Adam <>
Date: Tue, 24 Mar 2009 20:14:25 -0700
Subject: [PATCH 008/127] Automated import from
//branches/donutburger/...@141413,141413
---
.../common/project/AndroidManifestParser.java | 36 ++++++---
.../ide/eclipse/tests/AdtTestData.java | 29 ++++++-
.../project/AndroidManifestParserTest.java | 75 ++++++++-----------
.../data/AndroidManifest-instrumentation.xml | 18 +++++
.../data/AndroidManifest-testapp.xml | 17 +++++
5 files changed, 119 insertions(+), 56 deletions(-)
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-instrumentation.xml
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-testapp.xml
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
index fe11e694c..fa7e9b9f4 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.common.project;
+import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.XmlErrorHandler.XmlErrorListener;
import com.android.sdklib.SdkConstants;
@@ -579,7 +580,6 @@ public class AndroidManifestParser {
ManifestHandler manifestHandler = new ManifestHandler(manifestFile,
errorListener, gatherData, javaProject, markErrors);
-
parser.parse(new InputSource(manifestFile.getContents()), manifestHandler);
// get the result from the handler
@@ -590,10 +590,15 @@ public class AndroidManifestParser {
manifestHandler.getApiLevelRequirement(), manifestHandler.getInstrumentations(),
manifestHandler.getUsesLibraries());
} catch (ParserConfigurationException e) {
+ AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
+ "Bad parser configuration for %s", manifestFile.getFullPath());
} catch (SAXException e) {
+ AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
+ "Parser exception for %s", manifestFile.getFullPath());
} catch (IOException e) {
- } finally {
- }
+ AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
+ "I/O error for %s", manifestFile.getFullPath());
+ }
return null;
}
@@ -633,11 +638,15 @@ public class AndroidManifestParser {
manifestHandler.getApiLevelRequirement(), manifestHandler.getInstrumentations(),
manifestHandler.getUsesLibraries());
} catch (ParserConfigurationException e) {
+ AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
+ "Bad parser configuration for %s", manifestFile.getAbsolutePath());
} catch (SAXException e) {
+ AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
+ "Parser exception for %s", manifestFile.getAbsolutePath());
} catch (IOException e) {
- } finally {
- }
-
+ AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
+ "I/O error for %s", manifestFile.getAbsolutePath());
+ }
return null;
}
@@ -660,10 +669,12 @@ public class AndroidManifestParser {
boolean gatherData,
boolean markErrors)
throws CoreException {
+
+ IFile manifestFile = getManifest(javaProject.getProject());
+
try {
SAXParser parser = sParserFactory.newSAXParser();
-
- IFile manifestFile = getManifest(javaProject.getProject());
+
if (manifestFile != null) {
ManifestHandler manifestHandler = new ManifestHandler(manifestFile,
errorListener, gatherData, javaProject, markErrors);
@@ -678,10 +689,15 @@ public class AndroidManifestParser {
manifestHandler.getInstrumentations(), manifestHandler.getUsesLibraries());
}
} catch (ParserConfigurationException e) {
+ AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
+ "Bad parser configuration for %s", manifestFile.getFullPath());
} catch (SAXException e) {
+ AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
+ "Parser exception for %s", manifestFile.getFullPath());
} catch (IOException e) {
- } finally {
- }
+ AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
+ "I/O error for %s", manifestFile.getFullPath());
+ }
return null;
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java
index 262ef65a1..d86d585a3 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java
@@ -15,7 +15,13 @@
*/
package com.android.ide.eclipse.tests;
+import com.android.ide.eclipse.common.AndroidConstants;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.Platform;
+
import java.io.File;
+import java.io.IOException;
import java.net.URL;
import java.util.logging.Logger;
@@ -45,12 +51,29 @@ public class AdtTestData {
// accessed normally
mOsRootDataPath = System.getProperty("test_data");
if (mOsRootDataPath == null) {
- sLogger.info("Cannot find test_data directory, init to class loader");
+ sLogger.info("Cannot find test_data environment variable, init to class loader");
URL url = this.getClass().getClassLoader().getResource("data"); //$NON-NLS-1$
- mOsRootDataPath = url.getFile();
+
+ if (Platform.isRunning()) {
+ sLogger.info("Running as an Eclipse Plug-in JUnit test, using FileLocator");
+ try {
+ mOsRootDataPath = FileLocator.resolve(url).getFile();
+ } catch (IOException e) {
+ sLogger.warning("IOException while using FileLocator, reverting to url");
+ mOsRootDataPath = url.getFile();
+ }
+ } else {
+ sLogger.info("Running as an plain JUnit test, using url as-is");
+ mOsRootDataPath = url.getFile();
+ }
}
+
+ if (mOsRootDataPath.equals(AndroidConstants.WS_SEP + "data")) {
+ sLogger.warning("Resource data not found using class loader!, Defaulting to no path");
+ }
+
if (!mOsRootDataPath.endsWith(File.separator)) {
- sLogger.info("Fixing test_data env variable does not end with path separator");
+ sLogger.info("Fixing test_data env variable (does not end with path separator)");
mOsRootDataPath = mOsRootDataPath.concat(File.separator);
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java
index 516e448e6..7e8b0af10 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java
@@ -16,17 +16,20 @@
package com.android.ide.eclipse.common.project;
-import junit.framework.TestCase;
+import com.android.ide.eclipse.tests.AdtTestData;
-import com.android.ide.eclipse.mock.FileMock;
+import junit.framework.TestCase;
/**
* Tests for {@link AndroidManifestParser}
*/
public class AndroidManifestParserTest extends TestCase {
- private AndroidManifestParser mManifest;
+ private AndroidManifestParser mManifestTestApp;
+ private AndroidManifestParser mManifestInstrumentation;
- private static final String PACKAGE_NAME = "com.android.testapp"; //$NON-NLS-1$
+ private static final String INSTRUMENTATION_XML = "AndroidManifest-instrumentation.xml"; //$NON-NLS-1$
+ private static final String TESTAPP_XML = "AndroidManifest-testapp.xml"; //$NON-NLS-1$
+ private static final String PACKAGE_NAME = "com.android.testapp"; //$NON-NLS-1$
private static final String ACTIVITY_NAME = "com.android.testapp.MainActivity"; //$NON-NLS-1$
private static final String LIBRARY_NAME = "android.test.runner"; //$NON-NLS-1$
private static final String INSTRUMENTATION_NAME = "android.test.InstrumentationTestRunner"; //$NON-NLS-1$
@@ -35,60 +38,46 @@ public class AndroidManifestParserTest extends TestCase {
protected void setUp() throws Exception {
super.setUp();
- // create the test data
- StringBuilder sb = new StringBuilder();
- sb.append("\n"); //$NON-NLS-1$
- sb.append("\n"); //$NON-NLS-1$
- sb.append(" \n"); //$NON-NLS-1$
- sb.append(" \n"); //$NON-NLS-1$
- sb.append(" \n"); //$NON-NLS-1$
- sb.append(" \n"); //$NON-NLS-1$
- sb.append(" \"\n"); //$NON-NLS-1$
- sb.append(" \n"); //$NON-NLS-1$
- sb.append(" \n"); //$NON-NLS-1$
- sb.append(" \n"); //$NON-NLS-1$
- sb.append(" \n"); //$NON-NLS-1$
- sb.append(" "); //$NON-NLS-1$
- sb.append(" \n");
- sb.append("\n"); //$NON-NLS-1$
-
- FileMock mockFile = new FileMock("AndroidManifest.xml", sb.toString().getBytes());
+ String testFilePath = AdtTestData.getInstance().getTestFilePath(
+ TESTAPP_XML);
+ mManifestTestApp = AndroidManifestParser.parseForData(testFilePath);
+ assertNotNull(mManifestTestApp);
- mManifest = AndroidManifestParser.parseForData(mockFile);
- assertNotNull(mManifest);
+ testFilePath = AdtTestData.getInstance().getTestFilePath(
+ INSTRUMENTATION_XML);
+ mManifestInstrumentation = AndroidManifestParser.parseForData(testFilePath);
+ assertNotNull(mManifestInstrumentation);
}
+ public void testGetInstrumentationInformation() {
+ assertEquals(1, mManifestInstrumentation.getInstrumentations().length);
+ assertEquals(INSTRUMENTATION_NAME, mManifestTestApp.getInstrumentations()[0]);
+ }
+
public void testGetPackage() {
- assertEquals("com.android.testapp", mManifest.getPackage());
+ assertEquals(PACKAGE_NAME, mManifestTestApp.getPackage());
}
public void testGetActivities() {
- assertEquals(1, mManifest.getActivities().length);
- assertEquals(ACTIVITY_NAME, mManifest.getActivities()[0]);
+ assertEquals(1, mManifestTestApp.getActivities().length);
+ assertEquals(ACTIVITY_NAME, mManifestTestApp.getActivities()[0]);
}
public void testGetLauncherActivity() {
- assertEquals(ACTIVITY_NAME, mManifest.getLauncherActivity());
+ assertEquals(ACTIVITY_NAME, mManifestTestApp.getLauncherActivity());
}
public void testGetUsesLibraries() {
- assertEquals(1, mManifest.getUsesLibraries().length);
- assertEquals(LIBRARY_NAME, mManifest.getUsesLibraries()[0]);
+ assertEquals(1, mManifestTestApp.getUsesLibraries().length);
+ assertEquals(LIBRARY_NAME, mManifestTestApp.getUsesLibraries()[0]);
}
public void testGetInstrumentations() {
- assertEquals(1, mManifest.getInstrumentations().length);
- assertEquals(INSTRUMENTATION_NAME, mManifest.getInstrumentations()[0]);
+ assertEquals(1, mManifestTestApp.getInstrumentations().length);
+ assertEquals(INSTRUMENTATION_NAME, mManifestTestApp.getInstrumentations()[0]);
+ }
+
+ public void testGetPackageName() {
+ assertEquals(PACKAGE_NAME, mManifestTestApp.getPackage());
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-instrumentation.xml b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-instrumentation.xml
new file mode 100644
index 000000000..b380f967e
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-instrumentation.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-testapp.xml b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-testapp.xml
new file mode 100644
index 000000000..8ae70121c
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/data/AndroidManifest-testapp.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+ "
+
+
+
+
+ "
+
+
\ No newline at end of file
From 8ee14c4a389f1dd488b96d55cb4833ab0db8c71b Mon Sep 17 00:00:00 2001
From: Brett Chabot <>
Date: Tue, 24 Mar 2009 20:21:04 -0700
Subject: [PATCH 009/127] Automated import from
//branches/donutburger/...@141463,141463
---
.../eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java | 6 +++---
.../{RemoteADTTestRunner.java => RemoteAdtTestRunner.java} | 4 +++-
2 files changed, 6 insertions(+), 4 deletions(-)
rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/{RemoteADTTestRunner.java => RemoteAdtTestRunner.java} (98%)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
index 4dfe37d1d..b88026329 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
@@ -20,7 +20,7 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.launch.DelayedLaunchInfo;
import com.android.ide.eclipse.adt.launch.IAndroidLaunchAction;
import com.android.ide.eclipse.adt.launch.junit.runtime.AndroidJUnitLaunchInfo;
-import com.android.ide.eclipse.adt.launch.junit.runtime.RemoteADTTestRunner;
+import com.android.ide.eclipse.adt.launch.junit.runtime.RemoteAdtTestRunner;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
@@ -166,7 +166,7 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
private final VMRunnerConfiguration mRunConfig;
private final ILaunch mLaunch;
private final AndroidJUnitLaunchInfo mJUnitInfo;
- private RemoteADTTestRunner mTestRunner = null;
+ private RemoteAdtTestRunner mTestRunner = null;
private boolean mIsTerminated = false;
TestRunnerProcess(VMRunnerConfiguration runConfig, ILaunch launch,
@@ -256,7 +256,7 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
*/
@Override
public void run() {
- mTestRunner = new RemoteADTTestRunner();
+ mTestRunner = new RemoteAdtTestRunner();
mTestRunner.runTests(mRunConfig.getProgramArguments(), mJUnitInfo);
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteADTTestRunner.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java
similarity index 98%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteADTTestRunner.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java
index 6834c089a..0a6a3daee 100755
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteADTTestRunner.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java
@@ -34,7 +34,7 @@ import org.eclipse.jdt.internal.junit.runner.TestReferenceFailure;
* @see org.eclipse.jdt.internal.junit.runner.RemoteTestRunner for more details on the protocol
*/
@SuppressWarnings("restriction")
-public class RemoteADTTestRunner extends RemoteTestRunner {
+public class RemoteAdtTestRunner extends RemoteTestRunner {
private AndroidJUnitLaunchInfo mLaunchInfo;
private TestExecution mExecution;
@@ -97,6 +97,8 @@ public class RemoteADTTestRunner extends RemoteTestRunner {
// error occurred during test collection.
reportError(collector.getErrorMessage());
// abort here
+ notifyTestRunEnded(0);
+ return;
}
notifyTestRunStarted(collector.getTestCaseCount());
collector.sendTrees(this);
From e818ff0de5215f58aa836d652411ac274a7bb013 Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Tue, 24 Mar 2009 20:23:44 -0700
Subject: [PATCH 010/127] Automated import from
//branches/donutburger/...@141482,141482
---
.../META-INF/MANIFEST.MF | 4 +-
.../com.android.ide.eclipse.adt/plugin.xml | 44 +-
.../extractstring/ExtractStringAction.java | 161 ++++
.../ExtractStringContribution.java | 53 ++
.../ExtractStringDescriptor.java | 71 ++
.../extractstring/ExtractStringInputPage.java | 177 ++++
.../ExtractStringRefactoring.java | 890 ++++++++++++++++++
.../extractstring/ExtractStringWizard.java | 42 +
8 files changed, 1434 insertions(+), 8 deletions(-)
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringContribution.java
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringDescriptor.java
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
index 55c18bb26..c0dfcefd5 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
@@ -41,7 +41,9 @@ Require-Bundle: com.android.ide.eclipse.ddms,
org.eclipse.wst.xml.core,
org.eclipse.wst.xml.ui,
org.eclipse.jdt.junit,
- org.eclipse.jdt.junit.runtime
+ org.eclipse.jdt.junit.runtime,
+ org.eclipse.ltk.core.refactoring,
+ org.eclipse.ltk.ui.refactoring
Eclipse-LazyStart: true
Export-Package: com.android.ide.eclipse.adt,
com.android.ide.eclipse.adt.build;x-friends:="com.android.ide.eclipse.tests",
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index 2ab64256f..2bf633d32 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -470,7 +470,7 @@
point="org.eclipse.ui.actionSets">
-
-
-
-
+
+
+
+
@@ -565,4 +574,25 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java
new file mode 100644
index 000000000..528701573
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.refactorings.extractstring;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.ITypeRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+import org.eclipse.ui.PlatformUI;
+
+/*
+ * Quick Reference Link:
+ * http://www.eclipse.org/articles/article.php?file=Article-Unleashing-the-Power-of-Refactoring/index.html
+ * and
+ * http://www.ibm.com/developerworks/opensource/library/os-ecjdt/
+ */
+
+/**
+ * Action executed when the "Extract String" menu item is invoked.
+ *
+ * The intent of the action is to start a refactoring that extracts a source string and
+ * replaces it by an Android string resource ID.
+ *
+ * Workflow:
+ *
+ *
The action is currently located in the Refactoring menu in the main menu.
+ *
TODO: extend the popup refactoring menu in a Java or Android XML file.
+ *
The action is only enabled if the selection is 1 character or more. That is at least part
+ * of the string must be selected, it's not enough to just move the insertion point. This is
+ * a limitation due to {@link #selectionChanged(IAction, ISelection)} not being called when
+ * the insertion point is merely moved. TODO: address this limitation.
+ *
The action gets the current {@link ISelection}. It also knows the current
+ * {@link IWorkbenchWindow}. However for the refactoring we are also interested in having the
+ * actual resource file. By looking at the Active Window > Active Page > Active Editor we
+ * can get the {@link IEditorInput} and find the {@link ICompilationUnit} (aka Java file)
+ * that is being edited.
+ *
TODO: change this to find the {@link IFile} being manipulated. The {@link ICompilationUnit}
+ * can be inferred using {@link JavaCore#createCompilationUnitFrom(IFile)}. This will allow
+ * us to be able to work with a selection from an Android XML file later.
+ *
The action creates a new {@link ExtractStringRefactoring} and make it run on in a new
+ * {@link ExtractStringWizard}.
+ *
+ */
+public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
+
+ /** Keep track of the current workbench window. */
+ private IWorkbenchWindow mWindow;
+ private ITextSelection mSelection;
+ private ICompilationUnit mUnit;
+
+ /**
+ * Keep track of the current workbench window.
+ */
+ public void init(IWorkbenchWindow window) {
+ mWindow = window;
+ }
+
+ public void dispose() {
+ // Nothing to do
+ }
+
+ /**
+ * Examine the selection to determine if the action should be enabled or not.
+ *
+ * Keep a link to the relevant selection structure (i.e. a part of the Java AST).
+ */
+ public void selectionChanged(IAction action, ISelection selection) {
+
+ // Note, two kinds of selections are returned here:
+ // ITextSelection on a Java source window
+ // IStructuredSelection in the outline or navigator
+ // This simply deals with the refactoring based on a non-empty selection.
+ // At that point, just enable the action and later decide if it's valid when it actually
+ // runs since we don't have access to the AST yet.
+
+ mSelection = null;
+ mUnit = null;
+
+ if (selection instanceof ITextSelection) {
+ mSelection = (ITextSelection) selection;
+ if (mSelection.getLength() > 0) {
+ mUnit = getCompilationUnit();
+ }
+
+ // Keep for debugging purposes
+ //System.out.println(String.format("-- Selection: %d + %d = %s",
+ // mSelection.getOffset(),
+ // mSelection.getLength(),
+ // mSelection.getText()));
+ }
+
+ action.setEnabled(mSelection != null && mUnit != null);
+ }
+
+ /**
+ * Create a new instance of our refactoring and a wizard to configure it.
+ */
+ public void run(IAction action) {
+ if (mSelection != null && mUnit != null) {
+ ExtractStringRefactoring ref = new ExtractStringRefactoring(mUnit, mSelection);
+ RefactoringWizard wizard = new ExtractStringWizard(ref, "Extract Android String");
+ RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
+ try {
+ op.run(mWindow.getShell(), wizard.getDefaultPageTitle());
+ } catch (InterruptedException e) {
+ // Interrupted. Pass.
+ }
+ }
+ }
+
+ /**
+ * Returns the active {@link ICompilationUnit} or null.
+ */
+ private ICompilationUnit getCompilationUnit() {
+ IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (wwin != null) {
+ IWorkbenchPage page = wwin.getActivePage();
+ if (page != null) {
+ IEditorPart editor = page.getActiveEditor();
+ if (editor != null) {
+ IEditorInput input = editor.getEditorInput();
+ if (input != null) {
+ ITypeRoot typeRoot = JavaUI.getEditorInputTypeRoot(input);
+ // The type root can be either a .class or a .java (aka compilation unit).
+ // We want the compilation unit kind.
+ if (typeRoot instanceof ICompilationUnit) {
+ return (ICompilationUnit) typeRoot;
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringContribution.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringContribution.java
new file mode 100644
index 000000000..465e1a36c
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringContribution.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.refactorings.extractstring;
+
+import org.eclipse.ltk.core.refactoring.RefactoringContribution;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+
+import java.util.Map;
+
+/**
+ * @see ExtractStringDescriptor
+ */
+public class ExtractStringContribution extends RefactoringContribution {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ltk.core.refactoring.RefactoringContribution#createDescriptor(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.util.Map, int)
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public RefactoringDescriptor createDescriptor(
+ String id,
+ String project,
+ String description,
+ String comment,
+ Map arguments,
+ int flags)
+ throws IllegalArgumentException {
+ return new ExtractStringDescriptor(project, description, comment, arguments);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Map retrieveArgumentMap(RefactoringDescriptor descriptor) {
+ if (descriptor instanceof ExtractStringDescriptor) {
+ return ((ExtractStringDescriptor) descriptor).getArguments();
+ }
+ return super.retrieveArgumentMap(descriptor);
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringDescriptor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringDescriptor.java
new file mode 100644
index 000000000..6e999e942
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringDescriptor.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.refactorings.extractstring;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+import java.util.Map;
+
+/**
+ * A descriptor that allows an {@link ExtractStringRefactoring} to be created from
+ * a previous instance of itself.
+ */
+public class ExtractStringDescriptor extends RefactoringDescriptor {
+
+ public static final String ID =
+ "com.android.ide.eclipse.adt.refactoring.extract.string"; //$NON-NLS-1$
+
+ private final Map mArguments;
+
+ public ExtractStringDescriptor(String project, String description, String comment,
+ Map arguments) {
+ super(ID, project, description, comment,
+ RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE //flags
+ );
+ mArguments = arguments;
+ }
+
+ public Map getArguments() {
+ return mArguments;
+ }
+
+ /**
+ * Creates a new refactoring instance for this refactoring descriptor based on
+ * an argument map. The argument map is created by the refactoring itself in
+ * {@link ExtractStringRefactoring#createChange(org.eclipse.core.runtime.IProgressMonitor)}
+ *
+ * This is apparently used to replay a refactoring.
+ *
+ * {@inheritDoc}
+ *
+ * @throws CoreException
+ */
+ @Override
+ public Refactoring createRefactoring(RefactoringStatus status) throws CoreException {
+ try {
+ ExtractStringRefactoring ref = new ExtractStringRefactoring(mArguments);
+ return ref;
+ } catch (NullPointerException e) {
+ status.addFatalError("Failed to recreate ExtractStringRefactoring from descriptor");
+ return null;
+ }
+ }
+
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
new file mode 100644
index 000000000..cb449f02e
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.refactorings.extractstring;
+
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.ltk.ui.refactoring.UserInputWizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * @see ExtractStringRefactoring
+ */
+class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage {
+
+ public ExtractStringInputPage() {
+ super("ExtractStringInputPage"); //$NON-NLS-1$
+ }
+
+ private Label mStringLabel;
+ private Text mNewIdTextField;
+ private Label mFileLabel;
+
+ /**
+ * Create the UI for the refactoring wizard.
+ *
+ * Note that at that point the initial conditions have been checked in
+ * {@link ExtractStringRefactoring}.
+ */
+ public void createControl(Composite parent) {
+
+ final ExtractStringRefactoring ref = getOurRefactoring();
+
+ Composite content = new Composite(parent, SWT.NONE);
+
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ content.setLayout(layout);
+
+ // line 1: String found in selection
+
+ Label label = new Label(content, SWT.NONE);
+ label.setText("String:");
+
+ String selectedString = ref.getTokenString();
+
+ mStringLabel = new Label(content, SWT.NONE);
+ mStringLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mStringLabel.setText(selectedString != null ? selectedString : "");
+
+ // TODO provide an option to replace all occurences of this string instead of
+ // just the one.
+
+ // line 2 : Textfield for new ID
+
+ label = new Label(content, SWT.NONE);
+ label.setText("Replace by R.string.");
+
+ mNewIdTextField = new Text(content, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ mNewIdTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mNewIdTextField.setText(guessId(selectedString));
+
+ ref.setReplacementStringId(mNewIdTextField.getText().trim());
+
+ mNewIdTextField.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ if (validatePage(ref)) {
+ ref.setReplacementStringId(mNewIdTextField.getText().trim());
+ }
+ }
+ });
+
+ // line 3: selection of the output file
+ // TODO add a file field/chooser combo to let the user select the file to edit.
+
+ label = new Label(content, SWT.NONE);
+ label.setText("Resource file:");
+
+ mFileLabel = new Label(content, SWT.NONE);
+ mFileLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mFileLabel.setText("/res/values/strings.xml");
+ ref.setTargetFile(mFileLabel.getText());
+
+ // line 4: selection of the res config
+ // TODO add the Configuration Selector to decide with strings.xml to change
+
+ label = new Label(content, SWT.NONE);
+ label.setText("Configuration:");
+
+ label = new Label(content, SWT.NONE);
+ label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ label.setText("default");
+
+ validatePage(ref);
+ setControl(content);
+ }
+
+ private String guessId(String text) {
+ // make lower case
+ text = text.toLowerCase();
+
+ // everything not alphanumeric becomes an underscore
+ text = text.replaceAll("[^a-zA-Z0-9]+", "_"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // the id must be a proper Java identifier, so it can't start with a number
+ if (text.length() > 0 && !Character.isJavaIdentifierStart(text.charAt(0))) {
+ text = "_" + text; //$NON-NLS-1$
+ }
+ return text;
+ }
+
+ private ExtractStringRefactoring getOurRefactoring() {
+ return (ExtractStringRefactoring) getRefactoring();
+ }
+
+ private boolean validatePage(ExtractStringRefactoring ref) {
+ String text = mNewIdTextField.getText().trim();
+ boolean success = true;
+
+ // Analyze fatal errors.
+
+ if (text == null || text.length() < 1) {
+ setErrorMessage("Please provide a resource ID to replace with.");
+ success = false;
+ } else {
+ for (int i = 0; i < text.length(); i++) {
+ char c = text.charAt(i);
+ boolean ok = i == 0 ?
+ Character.isJavaIdentifierStart(c) :
+ Character.isJavaIdentifierPart(c);
+ if (!ok) {
+ setErrorMessage(String.format(
+ "The resource ID must be a valid Java identifier. The character %1$c at position %2$d is not acceptable.",
+ c, i+1));
+ success = false;
+ break;
+ }
+ }
+ }
+
+ // Analyze info & warnings.
+
+ if (success) {
+ if (ref.isResIdDuplicate(mFileLabel.getText(), text)) {
+ setErrorMessage(null);
+ setMessage(
+ String.format("Warning: There's already a string item called '%1$s' in %2$s.",
+ text, mFileLabel.getText()));
+ } else {
+ setMessage(null);
+ setErrorMessage(null);
+ }
+ }
+
+ setPageComplete(success);
+ return success;
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
new file mode 100644
index 000000000..715503c1f
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
@@ -0,0 +1,890 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.refactorings.extractstring;
+
+import com.android.ide.eclipse.common.AndroidConstants;
+import com.android.ide.eclipse.common.project.AndroidManifestParser;
+import com.android.ide.eclipse.common.project.AndroidXPathFactory;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourceAttributes;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.IBuffer;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.ToolFactory;
+import org.eclipse.jdt.core.compiler.IScanner;
+import org.eclipse.jdt.core.compiler.ITerminalSymbols;
+import org.eclipse.jdt.core.compiler.InvalidInputException;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.StringLiteral;
+import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.ChangeDescriptor;
+import org.eclipse.ltk.core.refactoring.CompositeChange;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.TextFileChange;
+import org.eclipse.text.edits.InsertEdit;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.ReplaceEdit;
+import org.eclipse.text.edits.TextEdit;
+import org.eclipse.text.edits.TextEditGroup;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+
+/**
+ * This refactoring extracts a string from a file and replaces it by an Android resource ID
+ * such as R.string.foo.
+ *
+ * There are a number of scenarios, which are not all supported yet. The workflow works as
+ * such:
+ *
+ *
User selects a string in a Java (TODO: or XML file) and invokes
+ * the {@link ExtractStringAction}.
+ *
The action finds the {@link ICompilationUnit} being edited as well as the current
+ * {@link ITextSelection}. The action creates a new instance of this refactoring as
+ * well as an {@link ExtractStringWizard} and runs the operation.
+ *
TODO: to support refactoring from an XML file, the action should give the {@link IFile}
+ * and then here we would have to determine whether it's a suitable Android XML file or a
+ * suitable Java file.
+ * TODO: enumerate the exact valid contexts in Android XML files, e.g. attributes in layout
+ * files or text elements (e.g. foo) for values, etc.
+ *
Step 1 of the refactoring is to check the preliminary conditions. Right now we check
+ * that the java source is not read-only and is in sync. We also try to find a string under
+ * the selection. If this fails, the refactoring is aborted.
+ *
TODO: Find the string in an XML file based on selection.
+ *
On success, the wizard is shown, which let the user input the new ID to use.
+ *
The wizard sets the user input values into this refactoring instance, e.g. the new string
+ * ID, the XML file to update, etc. The wizard does use the utility method
+ * {@link #isResIdDuplicate(String, String)} to check whether the new ID is already defined
+ * in the target XML file.
+ *
Once Preview or Finish is selected in the wizard, the
+ * {@link #checkFinalConditions(IProgressMonitor)} is called to double-check the user input
+ * and compute the actual changes.
+ *
When all changes are computed, {@link #createChange(IProgressMonitor)} is invoked.
+ *
+ *
+ * The list of changes are:
+ *
+ *
If the target XML does not exist, create it with the new string ID.
+ *
If the target XML exists, find the node and add the new string ID right after.
+ * If the node is , it needs to be opened.
+ *
Create an AST rewriter to edit the source Java file and replace all occurences by the
+ * new computed R.string.foo. Also need to rewrite imports to import R as needed.
+ * If there's already a conflicting R included, we need to insert the FQCN instead.
+ *
TODO: If the source is an XML file, determine if we need to change an attribute or a
+ * a text element.
+ *
TODO: Have a pref in the wizard: [x] Change other XML Files
+ *
TODO: Have a pref in the wizard: [x] Change other Java Files
+ *
+ */
+class ExtractStringRefactoring extends Refactoring {
+
+ /** The compilation unit, a.k.a. the Java file model. */
+ private final ICompilationUnit mUnit;
+ private final int mSelectionStart;
+ private final int mSelectionEnd;
+ /** The actual string selected, after UTF characters have been escaped, good for display. */
+ private String mTokenString;
+ /** Start position of the string token in the source buffer. */
+ private int mTokenStart;
+ /** End position of the string token in the source buffer. */
+ private int mTokenEnd;
+ private String mXmlStringId;
+ private String mTargetXmlFileWsPath;
+ private HashMap> mResIdCache;
+ private XPath mXPath;
+ private ArrayList mChanges;
+
+ public ExtractStringRefactoring(Map arguments)
+ throws NullPointerException {
+ mUnit = (ICompilationUnit) JavaCore.create(arguments.get("CU")); //$NON-NLS-1$
+ mSelectionStart = Integer.parseInt(arguments.get("sel-start")); //$NON-NLS-1$
+ mSelectionEnd = Integer.parseInt(arguments.get("sel-end")); //$NON-NLS-1$
+ mTokenStart = Integer.parseInt(arguments.get("tok-start")); //$NON-NLS-1$
+ mTokenEnd = Integer.parseInt(arguments.get("tok-end")); //$NON-NLS-1$
+ mTokenString = arguments.get("tok-esc"); //$NON-NLS-1$
+ }
+
+ private Map createArgumentMap() {
+ HashMap args = new HashMap();
+ args.put("CU", mUnit.getHandleIdentifier()); //$NON-NLS-1$
+ args.put("sel-start", Integer.toString(mSelectionStart)); //$NON-NLS-1$
+ args.put("sel-end", Integer.toString(mSelectionEnd)); //$NON-NLS-1$
+ args.put("tok-start", Integer.toString(mTokenStart)); //$NON-NLS-1$
+ args.put("tok-end", Integer.toString(mTokenEnd)); //$NON-NLS-1$
+ args.put("tok-esc", mTokenString); //$NON-NLS-1$
+ return args;
+ }
+
+ public ExtractStringRefactoring(ICompilationUnit unit, ITextSelection selection) {
+ mUnit = unit;
+
+ mSelectionStart = selection.getOffset();
+ mSelectionEnd = mSelectionStart + selection.getLength();
+ }
+
+ /**
+ * @see org.eclipse.ltk.core.refactoring.Refactoring#getName()
+ */
+ @Override
+ public String getName() {
+ return "Extract Android String";
+ }
+
+ /**
+ * Gets the actual string selected, after UTF characters have been escaped,
+ * good for display.
+ */
+ public String getTokenString() {
+ return mTokenString;
+ }
+
+ /**
+ * Step 1 of 3 of the refactoring:
+ * Checks that the current selection meets the initial condition before the ExtractString
+ * wizard is shown. The check is supposed to be lightweight and quick. Note that at that
+ * point the wizard has not been created yet.
+ *
+ * Here we scan the source buffer to find the token matching the selection.
+ * The check is successful is a Java string literal is selected, the source is in sync
+ * and is not read-only.
+ *
+ * This is also used to extract the string to be modified, so that we can display it in
+ * the refactoring wizard.
+ *
+ * @see org.eclipse.ltk.core.refactoring.Refactoring#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor)
+ *
+ * @throws CoreException
+ */
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor monitor)
+ throws CoreException, OperationCanceledException {
+
+ mTokenString = null;
+ mTokenStart = -1;
+ mTokenEnd = -1;
+
+ RefactoringStatus status = new RefactoringStatus();
+
+ try {
+ monitor.beginTask("Checking preconditions...", 3);
+
+ if (!extraChecks(monitor, status)) {
+ return status;
+ }
+
+ try {
+ IBuffer buffer = mUnit.getBuffer();
+
+ IScanner scanner = ToolFactory.createScanner(
+ false, //tokenizeComments
+ false, //tokenizeWhiteSpace
+ false, //assertMode
+ false //recordLineSeparator
+ );
+ scanner.setSource(buffer.getCharacters());
+ monitor.worked(1);
+
+ for(int token = scanner.getNextToken();
+ token != ITerminalSymbols.TokenNameEOF;
+ token = scanner.getNextToken()) {
+ if (scanner.getCurrentTokenStartPosition() <= mSelectionStart &&
+ scanner.getCurrentTokenEndPosition() >= mSelectionEnd) {
+ // found the token, but only keep of the right type
+ if (token == ITerminalSymbols.TokenNameStringLiteral) {
+ mTokenString = new String(scanner.getCurrentTokenSource());
+ mTokenStart = scanner.getCurrentTokenStartPosition();
+ mTokenEnd = scanner.getCurrentTokenEndPosition();
+ }
+ break;
+ } else if (scanner.getCurrentTokenStartPosition() > mSelectionEnd) {
+ // scanner is past the selection, abort.
+ break;
+ }
+ }
+ } catch (JavaModelException e1) {
+ // Error in mUnit.getBuffer. Ignore.
+ } catch (InvalidInputException e2) {
+ // Error in scanner.getNextToken. Ignore.
+ } finally {
+ monitor.worked(1);
+ }
+
+ if (mTokenString != null) {
+ // As a literal string, the token should have surrounding quotes. Remove them.
+ int len = mTokenString.length();
+ if (len > 0 &&
+ mTokenString.charAt(0) == '"' &&
+ mTokenString.charAt(len - 1) == '"') {
+ mTokenString = mTokenString.substring(1, len - 1);
+ }
+ // We need a non-empty string literal
+ if (mTokenString.length() == 0) {
+ mTokenString = null;
+ }
+ }
+
+ if (mTokenString == null) {
+ status.addFatalError("Please select a Java string literal.");
+ }
+
+ monitor.worked(1);
+ } finally {
+ monitor.done();
+ }
+
+ return status;
+ }
+
+ /**
+ * Tests from org.eclipse.jdt.internal.corext.refactoringChecks#validateEdit()
+ * Might not be useful.
+ *
+ * @return False if caller should abort, true if caller should continue.
+ */
+ private boolean extraChecks(IProgressMonitor monitor, RefactoringStatus status) {
+ //
+ IResource res = mUnit.getPrimary().getResource();
+ if (res == null || res.getType() != IResource.FILE) {
+ status.addFatalError("Cannot access resource; only regular files can be used.");
+ return false;
+ }
+ monitor.worked(1);
+
+ // check whether the source file is in sync
+ if (!res.isSynchronized(IResource.DEPTH_ZERO)) {
+ status.addFatalError("The file is not synchronized. Please save it first.");
+ return false;
+ }
+ monitor.worked(1);
+
+ // make sure we can write to it.
+ ResourceAttributes resAttr = res.getResourceAttributes();
+ if (mUnit.isReadOnly() || resAttr == null || resAttr.isReadOnly()) {
+ status.addFatalError("The file is read-only, please make it writeable first.");
+ return false;
+ }
+ monitor.worked(1);
+
+ return true;
+ }
+
+ /**
+ * Step 2 of 3 of the refactoring:
+ * Check the conditions once the user filled values in the refactoring wizard,
+ * then prepare the changes to be applied.
+ *
+ * In this case, most of the sanity checks are done by the wizard so essentially this
+ * should only be called if the wizard positively validated the user input.
+ *
+ * Here we do check that the target resource XML file either does not exists or
+ * is not read-only.
+ *
+ * @see org.eclipse.ltk.core.refactoring.Refactoring#checkFinalConditions(IProgressMonitor)
+ *
+ * @throws CoreException
+ */
+ @Override
+ public RefactoringStatus checkFinalConditions(IProgressMonitor monitor)
+ throws CoreException, OperationCanceledException {
+ RefactoringStatus status = new RefactoringStatus();
+
+ try {
+ monitor.beginTask("Checking post-conditions...", 3);
+
+ if (mXmlStringId == null || mXmlStringId.length() <= 0) {
+ // this is not supposed to happen
+ status.addFatalError("Missing replacement string ID");
+ } else if (mTargetXmlFileWsPath == null || mTargetXmlFileWsPath.length() <= 0) {
+ // this is not supposed to happen
+ status.addFatalError("Missing target xml file path");
+ }
+ monitor.worked(1);
+
+ // Either that resource must not exist or it must be a writeable file.
+ IResource targetXml = getTargetXmlResource(mTargetXmlFileWsPath);
+ if (targetXml != null) {
+ if (targetXml.getType() != IResource.FILE) {
+ status.addFatalError(
+ String.format("XML file '%1$s' is not a file.", mTargetXmlFileWsPath));
+ } else {
+ ResourceAttributes attr = targetXml.getResourceAttributes();
+ if (attr != null && attr.isReadOnly()) {
+ status.addFatalError(
+ String.format("XML file '%1$s' is read-only.",
+ mTargetXmlFileWsPath));
+ }
+ }
+ }
+ monitor.worked(1);
+
+ if (status.hasError()) {
+ return status;
+ }
+
+ mChanges = new ArrayList();
+
+
+ // Prepare the change for the XML file.
+
+ if (!isResIdDuplicate(mTargetXmlFileWsPath, mXmlStringId)) {
+ // We actually change it only if the ID doesn't exist yet
+ TextFileChange xmlChange = new TextFileChange(getName(), (IFile) targetXml);
+ xmlChange.setTextType("xml"); //$NON-NLS-1$
+ TextEdit edit = createXmlEdit((IFile) targetXml, mXmlStringId, mTokenString);
+ if (edit == null) {
+ status.addFatalError(String.format("Failed to modify file %1$s",
+ mTargetXmlFileWsPath));
+ }
+ xmlChange.setEdit(edit);
+ mChanges.add(xmlChange);
+ }
+ monitor.worked(1);
+
+ if (status.hasError()) {
+ return status;
+ }
+
+ // Prepare the change to the Java compilation unit
+ List changes = computeJavaChanges(mUnit, mXmlStringId, mTokenString, status,
+ SubMonitor.convert(monitor, 1));
+ if (changes != null) {
+ mChanges.addAll(changes);
+ }
+
+ monitor.worked(1);
+ } finally {
+ monitor.done();
+ }
+
+ return status;
+ }
+
+ /**
+ * Internal helper that actually prepares the {@link TextEdit} that adds the given
+ * ID to the given XML File.
+ *
+ * This does not actually modify the file.
+ *
+ * @param xmlFile The file resource to modify.
+ * @param replacementStringId The new ID to insert.
+ * @param oldString The old string, which will be the value in the XML string.
+ * @return A new {@link TextEdit} that describes how to change the file.
+ */
+ private TextEdit createXmlEdit(IFile xmlFile, String replacementStringId, String oldString) {
+
+ if (!xmlFile.exists()) {
+ // The XML file does not exist. Simply create it.
+ StringBuilder content = new StringBuilder();
+ content.append("\n"); //$NON-NLS-1$
+ content.append("\n"); //$NON-NLS-1$
+ content.append(" "). //$NON-NLS-1$
+ append(oldString).
+ append("\n"); //$NON-NLS-1$
+ content.append("\n"); //$NON-NLS-1$
+
+ return new InsertEdit(0, content.toString());
+ }
+
+ // The file exist. Attempt to parse it as a valid XML document.
+ try {
+ int[] indices = new int[2];
+ if (findXmlOpeningTagPos(xmlFile.getContents(), "resources", indices)) { //$NON-NLS-1$
+ // Indices[1] indicates whether we found > or />. It can only be 1 or 2.
+ // Indices[0] is the position of the first character of either > or />.
+ //
+ // Note: we don't even try to adapt our formatting to the existing structure (we
+ // could by capturing whatever whitespace is after the closing bracket and
+ // applying it here before our tag, unless we were dealing with an empty
+ // resource tag.)
+
+ int offset = indices[0];
+ int len = indices[1];
+ StringBuilder content = new StringBuilder();
+ content.append(">\n"); //$NON-NLS-1$
+ content.append(" "). //$NON-NLS-1$
+ append(oldString).
+ append(""); //$NON-NLS-1$
+ if (len == 2) {
+ content.append("\n"); //$NON-NLS-1$
+ }
+
+ return new ReplaceEdit(offset, len, content.toString());
+ }
+
+ } catch (CoreException e) {
+ // Failed to read file. Ignore. Will return null below.
+ }
+
+ return null;
+ }
+
+ /**
+ * Parse an XML input stream, looking for an opening tag.
+ *
+ * If found, returns the character offet in the buffer of the closing bracket of that
+ * tag, e.g. the position of > in "". The first character is at offset 0.
+ *
+ * The implementation here relies on a simple character-based parser. No DOM nor SAX
+ * parsing is used, due to the simplified nature of the task: we just want the first
+ * opening tag, which in our case should be the document root. We deal however with
+ * with the tag being commented out, so comments are skipped. We assume the XML doc
+ * is sane, e.g. we don't expect the tag to appear in the middle of a string. But
+ * again since in fact we want the root element, that's unlikely to happen.
+ *
+ * We need to deal with the case where the element is written as , in
+ * which case the caller will want to replace /> by ">...". To do that we return
+ * two values: the first offset of the closing tag (e.g. / or >) and the length, which
+ * can only be 1 or 2. If it's 2, the caller have to deal with /> instead of just >.
+ *
+ * @param contents An existing buffer to parse.
+ * @param tag The tag to look for.
+ * @param indices The return values: [0] is the offset of the closing bracket and [1] is
+ * the length which can be only 1 for > and 2 for />
+ * @return True if we found the tag, in which case indices can be used.
+ */
+ private boolean findXmlOpeningTagPos(InputStream contents, String tag, int[] indices) {
+
+ BufferedReader br = new BufferedReader(new InputStreamReader(contents));
+ StringBuilder sb = new StringBuilder(); // scratch area
+
+ tag = "<" + tag;
+ int tagLen = tag.length();
+ int maxLen = tagLen < 3 ? 3 : tagLen;
+
+ try {
+ int offset = 0;
+ int i = 0;
+ char searching = '<'; // we want opening tags
+ boolean capture = false;
+ boolean inComment = false;
+ boolean inTag = false;
+ while ((i = br.read()) != -1) {
+ char c = (char) i;
+ if (c == searching) {
+ capture = true;
+ }
+ if (capture) {
+ sb.append(c);
+ int len = sb.length();
+ if (inComment && c == '>') {
+ // is the comment being closed?
+ if (len >= 3 && sb.substring(len-3).equals("-->")) { //$NON-NLS-1$
+ // yes, comment is closing, stop capturing
+ capture = false;
+ inComment = false;
+ sb.setLength(0);
+ }
+ } else if (inTag && c == '>') {
+ // we're capturing in our tag, waiting for the closing >, we just got it
+ // so we're totally done here. Simply detect whether it's /> or >.
+ indices[0] = offset;
+ indices[1] = 1;
+ if (sb.charAt(len - 2) == '/') {
+ indices[0]--;
+ indices[1]++;
+ }
+ return true;
+
+ } else if (!inComment && !inTag) {
+ // not a comment and not our tag yet, so we're capturing because a
+ // tag is being opened but we don't know which one yet.
+
+ // look for either the opening or a comment or
+ // the opening of our tag.
+ if (len == 3 && sb.equals("<--")) { //$NON-NLS-1$
+ inComment = true;
+ } else if (len == tagLen && sb.toString().equals(tag)) {
+ inTag = true;
+ }
+
+ // if we're not interested in this tag yet, deal with when to stop
+ // capturing: the opening tag ends with either any kind of whitespace
+ // or with a > or maybe there's a PI that starts with
+ if (!inComment && !inTag) {
+ if (c == '>' || c == '?' || c == ' ' || c == '\n' || c == '\r') {
+ // stop capturing
+ capture = false;
+ sb.setLength(0);
+ }
+ }
+ }
+
+ if (capture && len > maxLen) {
+ // in any case we don't need to capture more than the size of our tag
+ // or the comment opening tag
+ sb.deleteCharAt(0);
+ }
+ }
+ offset++;
+ }
+ } catch (IOException e) {
+ // Ignore.
+ } finally {
+ try {
+ br.close();
+ } catch (IOException e) {
+ // oh come on...
+ }
+ }
+
+ return false;
+ }
+
+ private List computeJavaChanges(ICompilationUnit unit,
+ String xmlStringId,
+ String tokenString,
+ RefactoringStatus status,
+ SubMonitor subMonitor) {
+
+ // Get the Android package name from the Android Manifest. We need it to create
+ // the FQCN of the R class.
+ String packageName = null;
+ String error = null;
+ IProject proj = unit.getJavaProject().getProject();
+ IResource manifestFile = proj.findMember(AndroidConstants.FN_ANDROID_MANIFEST);
+ if (manifestFile == null || manifestFile.getType() != IResource.FILE) {
+ error = "File not found";
+ } else {
+ try {
+ AndroidManifestParser manifest = AndroidManifestParser.parseForData(
+ (IFile) manifestFile);
+ if (manifest == null) {
+ error = "Invalid content";
+ } else {
+ packageName = manifest.getPackage();
+ if (packageName == null) {
+ error = "Missing package definition";
+ }
+ }
+ } catch (CoreException e) {
+ error = e.getLocalizedMessage();
+ }
+ }
+
+ if (error != null) {
+ status.addFatalError(
+ String.format("Failed to parse file %1$s: %2$s.",
+ manifestFile.getFullPath(), error));
+ return null;
+ }
+
+ // TODO in a future version we might want to collect various Java files that
+ // need to be updated in the same project and process them all together.
+ // To do that we need to use an ASTRequestor and parser.createASTs, kind of
+ // like this:
+ //
+ // ASTRequestor requestor = new ASTRequestor() {
+ // @Override
+ // public void acceptAST(ICompilationUnit sourceUnit, CompilationUnit astNode) {
+ // super.acceptAST(sourceUnit, astNode);
+ // // TODO process astNode
+ // }
+ // };
+ // ...
+ // parser.createASTs(compilationUnits, bindingKeys, requestor, monitor)
+ //
+ // and then add multiple TextFileChange to the changes arraylist.
+
+ // Right now the changes array will contain one TextFileChange at most.
+ ArrayList changes = new ArrayList();
+
+ // This is the unit that will be modified.
+ TextFileChange change = new TextFileChange(getName(), (IFile) unit.getResource());
+ change.setTextType("java"); //$NON-NLS-1$
+
+ // Create an AST for this compilation unit
+ ASTParser parser = ASTParser.newParser(AST.JLS3);
+ parser.setProject(unit.getJavaProject());
+ parser.setSource(unit);
+ parser.setResolveBindings(true);
+ ASTNode node = parser.createAST(subMonitor.newChild(1));
+
+ // The ASTNode must be a CompilationUnit, by design
+ if (!(node instanceof CompilationUnit)) {
+ status.addFatalError(String.format("Internal error: ASTNode class %s", //$NON-NLS-1$
+ node.getClass()));
+ return null;
+ }
+
+ // ImportRewrite will allow us to add the new type to the imports and will resolve
+ // what the Java source must reference, e.g. the FQCN or just the simple name.
+ ImportRewrite ir = ImportRewrite.create((CompilationUnit) node, true);
+ String Rqualifier = packageName + ".R"; //$NON-NLS-1$
+ Rqualifier = ir.addImport(Rqualifier);
+
+ // Rewrite the AST itself via an ASTVisitor
+ AST ast = node.getAST();
+ ASTRewrite ar = ASTRewrite.create(ast);
+ ReplaceStringsVisitor visitor = new ReplaceStringsVisitor(ast, ar,
+ tokenString, Rqualifier, xmlStringId);
+ node.accept(visitor);
+
+ // Finally prepare the change set
+ try {
+ MultiTextEdit edit = new MultiTextEdit();
+
+ // Create the edit to change the imports, only if anything changed
+ TextEdit subEdit = ir.rewriteImports(subMonitor.newChild(1));
+ if (subEdit.hasChildren()) {
+ edit.addChild(subEdit);
+ }
+
+ // Create the edit to change the Java source, only if anything changed
+ subEdit = ar.rewriteAST();
+ if (subEdit.hasChildren()) {
+ edit.addChild(subEdit);
+ }
+
+ // Only create a change set if any edit was collected
+ if (edit.hasChildren()) {
+ change.setEdit(edit);
+ changes.add(change);
+ }
+
+ // TODO to modify another Java source, loop back to the creation of the
+ // TextFileChange and accumulate in changes. Right now only one source is
+ // modified.
+
+ if (changes.size() > 0) {
+ return changes;
+ }
+
+ } catch (CoreException e) {
+ // ImportRewrite.rewriteImports failed.
+ status.addFatalError(e.getMessage());
+ }
+ return null;
+ }
+
+ public class ReplaceStringsVisitor extends ASTVisitor {
+
+ private final AST mAst;
+ private final ASTRewrite mRewriter;
+ private final String mOldString;
+ private final String mRQualifier;
+ private final String mXmlId;
+
+ public ReplaceStringsVisitor(AST ast,
+ ASTRewrite astRewrite,
+ String oldString,
+ String rQualifier,
+ String xmlId) {
+ mAst = ast;
+ mRewriter = astRewrite;
+ mOldString = oldString;
+ mRQualifier = rQualifier;
+ mXmlId = xmlId;
+ }
+
+ @Override
+ public boolean visit(StringLiteral node) {
+ if (node.getLiteralValue().equals(mOldString)) {
+
+ Name qualifierName = mAst.newName(mRQualifier + ".string"); //$NON-NLS-1$
+ SimpleName idName = mAst.newSimpleName(mXmlId);
+ QualifiedName newNode = mAst.newQualifiedName(qualifierName, idName);
+
+ TextEditGroup editGroup = new TextEditGroup(getName());
+ mRewriter.replace(node, newNode, editGroup);
+ }
+ return super.visit(node);
+ }
+ }
+
+ /**
+ * Step 3 of 3 of the refactoring: returns the {@link Change} that will be able to do the
+ * work and creates a descriptor that can be used to replay that refactoring later.
+ *
+ * @see org.eclipse.ltk.core.refactoring.Refactoring#createChange(org.eclipse.core.runtime.IProgressMonitor)
+ *
+ * @throws CoreException
+ */
+ @Override
+ public Change createChange(IProgressMonitor monitor)
+ throws CoreException, OperationCanceledException {
+
+ try {
+ monitor.beginTask("Applying changes...", 1);
+
+ CompositeChange change = new CompositeChange(
+ getName(),
+ mChanges.toArray(new Change[mChanges.size()])) {
+ @Override
+ public ChangeDescriptor getDescriptor() {
+
+ String comment = String.format(
+ "Extracts string '%1$s' into R.string.%2$s",
+ mTokenString,
+ mXmlStringId);
+
+ ExtractStringDescriptor desc = new ExtractStringDescriptor(
+ mUnit.getJavaProject().getElementName(), //project
+ comment, //description
+ comment, //comment
+ createArgumentMap());
+
+ return new RefactoringChangeDescriptor(desc);
+ }
+ };
+
+ monitor.worked(1);
+
+ return change;
+
+ } finally {
+ monitor.done();
+ }
+
+ }
+
+ /**
+ * Utility method used by the wizard to check whether the given string ID is already
+ * defined in the XML file which path is given.
+ *
+ * @param xmlFileWsPath The project path of the XML file, e.g. "/res/values/strings.xml".
+ * The given file may or may not exist.
+ * @param stringId The string ID to find.
+ * @return True if such a string ID is already defined.
+ */
+ public boolean isResIdDuplicate(String xmlFileWsPath, String stringId) {
+ // This is going to be called many times on the same file.
+ // Build a cache of the existing IDs for a given file.
+ if (mResIdCache == null) {
+ mResIdCache = new HashMap>();
+ }
+ HashSet cache = mResIdCache.get(xmlFileWsPath);
+ if (cache == null) {
+ cache = getResIdsForFile(xmlFileWsPath);
+ mResIdCache.put(xmlFileWsPath, cache);
+ }
+
+ return cache.contains(stringId);
+ }
+
+ /**
+ * Extract all the defined string IDs from a given file using XPath.
+ *
+ * @param xmlFileWsPath The project path of the file to parse. It may not exist.
+ * @return The set of all string IDs defined in the file. The returned set is always non
+ * null. It is empty if the file does not exist.
+ */
+ private HashSet getResIdsForFile(String xmlFileWsPath) {
+ HashSet ids = new HashSet();
+
+ if (mXPath == null) {
+ mXPath = AndroidXPathFactory.newXPath();
+ }
+
+ // Access the project that contains the resource that contains the compilation unit
+ IResource resource = getTargetXmlResource(xmlFileWsPath);
+
+ if (resource != null && resource.exists() && resource.getType() == IResource.FILE) {
+ InputSource source;
+ try {
+ source = new InputSource(((IFile) resource).getContents());
+
+ // We want all the IDs in an XML structure like this:
+ //
+ // something
+ //
+
+ String xpathExpr = "/resources/string/@name"; //$NON-NLS-1$
+
+ Object result = mXPath.evaluate(xpathExpr, source, XPathConstants.NODESET);
+ if (result instanceof NodeList) {
+ NodeList list = (NodeList) result;
+ for (int n = list.getLength() - 1; n >= 0; n--) {
+ String id = list.item(n).getNodeValue();
+ ids.add(id);
+ }
+ }
+
+ } catch (CoreException e1) {
+ // IFile.getContents failed. Ignore.
+ } catch (XPathExpressionException e) {
+ // mXPath.evaluate failed. Ignore.
+ }
+ }
+
+ return ids;
+ }
+
+ /**
+ * Given a file project path, returns its resource in the same project than the
+ * compilation unit. The resource may not exist.
+ */
+ private IResource getTargetXmlResource(String xmlFileWsPath) {
+ IProject proj = mUnit.getPrimary().getResource().getProject();
+ Path path = new Path(xmlFileWsPath);
+ IResource resource = proj.findMember(path);
+ return resource;
+ }
+
+ /**
+ * Sets the replacement string ID. Used by the wizard to set the user input.
+ */
+ public void setReplacementStringId(String replacementStringId) {
+ mXmlStringId = replacementStringId;
+ }
+
+ /**
+ * Sets the target file. This is a project path, e.g. "/res/values/strings.xml".
+ * Used by the wizard to set the user input.
+ */
+ public void setTargetFile(String targetXmlFileWsPath) {
+ mTargetXmlFileWsPath = targetXmlFileWsPath;
+ }
+
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java
new file mode 100644
index 000000000..2083a6e5d
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.refactorings.extractstring;
+
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+
+/**
+ * A wizard for ExtractString based on a simple dialog with one page.
+ *
+ * @see ExtractStringInputPage
+ * @see ExtractStringRefactoring
+ */
+class ExtractStringWizard extends RefactoringWizard {
+
+ /**
+ * Create a wizard for ExtractString based on a simple dialog with one page.
+ */
+ public ExtractStringWizard(ExtractStringRefactoring ref, String title) {
+ super(ref, DIALOG_BASED_USER_INTERFACE | PREVIEW_EXPAND_FIRST_NODE);
+ setDefaultPageTitle(title);
+ }
+
+ @Override
+ protected void addUserInputPages() {
+ addPage(new ExtractStringInputPage());
+ }
+
+}
From 4e2e0609bfba03a93afc0e184179106fc8ba09f1 Mon Sep 17 00:00:00 2001
From: Ricky Ng-Adam <>
Date: Tue, 24 Mar 2009 20:25:59 -0700
Subject: [PATCH 011/127] Automated import from
//branches/donutburger/...@141508,141508
---
.../src/com/android/ide/eclipse/adt/AdtPlugin.java | 2 +-
.../src/com/android/ide/eclipse/adt/build/ApkBuilder.java | 2 +-
.../com/android/ide/eclipse/adt/build/PreCompilerBuilder.java | 2 +-
.../android/ide/eclipse/adt/build/ResourceManagerBuilder.java | 2 +-
.../com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
index e0708f32b..42db64a61 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
@@ -1282,7 +1282,7 @@ public class AdtPlugin extends AbstractUIPlugin {
AdtPlugin.PLUGIN_ID,
UNKNOWN_EDITOR);
try {
- file.setPersistentProperty(qname, "1");
+ file.setPersistentProperty(qname, "1"); //$NON-NLS-1$
} catch (CoreException e) {
// pass
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
index f8a969e94..bc5b01c5f 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
@@ -196,7 +196,7 @@ public class ApkBuilder extends BaseBuilder {
}
// build() returns a list of project from which this project depends for future compilation.
- @SuppressWarnings("unchecked") //$NON-NLS-1$
+ @SuppressWarnings("unchecked")
@Override
protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
throws CoreException {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
index 8aa1abad8..6f9c2f16a 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
@@ -197,7 +197,7 @@ public class PreCompilerBuilder extends BaseBuilder {
}
// build() returns a list of project from which this project depends for future compilation.
- @SuppressWarnings("unchecked") //$NON-NLS-1$
+ @SuppressWarnings("unchecked")
@Override
protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
throws CoreException {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java
index 035aa5b73..b1f9ec1d4 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java
@@ -55,7 +55,7 @@ public class ResourceManagerBuilder extends BaseBuilder {
}
// build() returns a list of project from which this project depends for future compilation.
- @SuppressWarnings("unchecked") //$NON-NLS-1$
+ @SuppressWarnings("unchecked")
@Override
protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
throws CoreException {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java
index 30b072389..91bd21cb7 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java
@@ -58,7 +58,7 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
/**
*
*/
- public static final String LAUNCH_TAB_IMAGE = "mainLaunchTab.png";
+ public static final String LAUNCH_TAB_IMAGE = "mainLaunchTab.png"; //$NON-NLS-1$
protected static final String EMPTY_STRING = ""; //$NON-NLS-1$
From f791f113e18eeba32be7d57c134ad1cb8ac8a4e5 Mon Sep 17 00:00:00 2001
From: Ricky Ng-Adam <>
Date: Tue, 24 Mar 2009 20:27:07 -0700
Subject: [PATCH 012/127] Automated import from
//branches/donutburger/...@141516,141516
---
.../adt/launch/AndroidLaunchController.java | 89 +++++++++++++++++++
.../adt/launch/LaunchConfigDelegate.java | 39 +-------
.../eclipse/adt/project/ProjectHelper.java | 71 +++++++++++++++
3 files changed, 162 insertions(+), 37 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
index 5cf966904..3aa0b91f6 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
@@ -34,8 +34,10 @@ import com.android.ide.eclipse.adt.launch.DelayedLaunchInfo.InstallRetryMode;
import com.android.ide.eclipse.adt.launch.DeviceChooserDialog.DeviceChooserResponse;
import com.android.ide.eclipse.adt.project.ProjectHelper;
import com.android.ide.eclipse.adt.sdk.Sdk;
+import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
import com.android.prefs.AndroidLocation.AndroidLocationException;
+import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkManager;
import com.android.sdklib.avd.AvdManager;
@@ -54,6 +56,9 @@ import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.jdt.core.IJavaModel;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IVMConnector;
import org.eclipse.jdt.launching.JavaRuntime;
@@ -66,6 +71,7 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -807,6 +813,14 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
return false;
}
+ // The app is now installed, now try the dependent projects
+ for (DelayedLaunchInfo dependentLaunchInfo : getDependenciesLaunchInfo(launchInfo)) {
+ String msg = String.format("Project dependency found, syncing: %s",
+ dependentLaunchInfo.getProject().getName());
+ AdtPlugin.printToConsole(launchInfo.getProject(), msg);
+ syncApp(dependentLaunchInfo, device);
+ }
+
return installResult;
}
@@ -818,6 +832,81 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
return false;
}
+ /**
+ * For the current launchInfo, create additional DelayedLaunchInfo that should be used to
+ * sync APKs that we are dependent on to the device.
+ *
+ * @param launchInfo the original launch info that we want to find the
+ * @return a list of DelayedLaunchInfo (may be empty if no dependencies were found or error)
+ */
+ public List getDependenciesLaunchInfo(DelayedLaunchInfo launchInfo) {
+ List dependencies = new ArrayList();
+
+ // Convert to equivalent JavaProject
+ IJavaProject javaProject;
+ try {
+ //assuming this is an Android (and Java) project since it is attached to the launchInfo.
+ javaProject = BaseProjectHelper.getJavaProject(launchInfo.getProject());
+ } catch (CoreException e) {
+ // return empty dependencies
+ AdtPlugin.printErrorToConsole(launchInfo.getProject(), e);
+ return dependencies;
+ }
+
+ // Get all projects that this depends on
+ List androidProjectList;
+ try {
+ androidProjectList = ProjectHelper.getAndroidProjectDependencies(javaProject);
+ } catch (JavaModelException e) {
+ // return empty dependencies
+ AdtPlugin.printErrorToConsole(launchInfo.getProject(), e);
+ return dependencies;
+ }
+
+ // for each project, parse manifest and create launch information
+ for (IJavaProject androidProject : androidProjectList) {
+ // Parse the Manifest to get various required information
+ // copied from LaunchConfigDelegate
+ AndroidManifestParser manifestParser;
+ try {
+ manifestParser = AndroidManifestParser.parse(
+ androidProject, null /* errorListener */,
+ true /* gatherData */, false /* markErrors */);
+ } catch (CoreException e) {
+ AdtPlugin.printErrorToConsole(
+ launchInfo.getProject(),
+ String.format("Error parsing manifest of %s",
+ androidProject.getElementName()));
+ continue;
+ }
+
+ // Get the APK location (can return null)
+ IFile apk = ProjectHelper.getApplicationPackage(androidProject.getProject());
+ if (apk == null) {
+ // getApplicationPackage will have logged an error message
+ continue;
+ }
+
+ // Create new launchInfo as an hybrid between parent and dependency information
+ DelayedLaunchInfo delayedLaunchInfo = new DelayedLaunchInfo(
+ androidProject.getProject(),
+ manifestParser.getPackage(),
+ launchInfo.getLaunchAction(),
+ apk,
+ manifestParser.getDebuggable(),
+ manifestParser.getApiLevelRequirement(),
+ launchInfo.getLaunch(),
+ launchInfo.getMonitor());
+
+ // Add to the list
+ dependencies.add(delayedLaunchInfo);
+ }
+
+ return dependencies;
+ }
+
+
+
/**
* Installs the application package that was pushed to a temporary location on the device.
* @param launchInfo The launch information
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
index db9a4aceb..d057ac709 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
@@ -25,9 +25,7 @@ import com.android.ide.eclipse.common.project.AndroidManifestParser;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
@@ -153,7 +151,7 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
AdtPlugin.printToConsole(project, "Android Launch!");
// check if the project is using the proper sdk.
- // if that throws an exception, we simply let it propage to the caller.
+ // if that throws an exception, we simply let it propagate to the caller.
if (checkAndroidProject(project) == false) {
AdtPlugin.printErrorToConsole(project, "Project is not an Android Project. Aborting!");
androidLaunch.stopLaunch();
@@ -216,7 +214,7 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
AndroidLaunchController controller = AndroidLaunchController.getInstance();
// get the application package
- IFile applicationPackage = getApplicationPackage(project);
+ IFile applicationPackage = ProjectHelper.getApplicationPackage(project);
if (applicationPackage == null) {
androidLaunch.stopLaunch();
return;
@@ -388,39 +386,6 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
}
- /**
- * Returns the android package file as an IFile object for the specified
- * project.
- * @param project The project
- * @return The android package as an IFile object or null if not found.
- */
- private IFile getApplicationPackage(IProject project) {
- // get the output folder
- IFolder outputLocation = BaseProjectHelper.getOutputFolder(project);
-
- if (outputLocation == null) {
- AdtPlugin.printErrorToConsole(project,
- "Failed to get the output location of the project. Check build path properties"
- );
- return null;
- }
-
-
- // get the package path
- String packageName = project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE;
- IResource r = outputLocation.findMember(packageName);
-
- // check the package is present
- if (r instanceof IFile && r.exists()) {
- return (IFile)r;
- }
-
- String msg = String.format("Could not find %1$s!", packageName);
- AdtPlugin.printErrorToConsole(project, msg);
-
- return null;
- }
-
/**
* Returns the name of the activity.
*/
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java
index c650b9846..e091b1385 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java
@@ -20,8 +20,10 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerInitializer;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
+import com.android.ide.eclipse.common.project.BaseProjectHelper;
import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
@@ -34,12 +36,14 @@ import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.launching.JavaRuntime;
import java.util.ArrayList;
+import java.util.List;
/**
* Utility class to manipulate Project parameters/properties.
@@ -679,4 +683,71 @@ public final class ProjectHelper {
return project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE;
}
+
+ /**
+ * Find the list of projects on which this JavaProject is dependent on at the compilation level.
+ *
+ * @param javaProject Java project that we are looking for the dependencies.
+ * @return A list of Java projects for which javaProject depend on.
+ * @throws JavaModelException
+ */
+ public static List getAndroidProjectDependencies(IJavaProject javaProject)
+ throws JavaModelException {
+ String[] requiredProjectNames = javaProject.getRequiredProjectNames();
+
+ // Go from java project name to JavaProject name
+ IJavaModel javaModel = javaProject.getJavaModel();
+
+ // loop through all dependent projects and keep only those that are Android projects
+ List projectList = new ArrayList(requiredProjectNames.length);
+ for (String javaProjectName : requiredProjectNames) {
+ IJavaProject androidJavaProject = javaModel.getJavaProject(javaProjectName);
+
+ //Verify that the project has also the Android Nature
+ try {
+ if (!androidJavaProject.getProject().hasNature(AndroidConstants.NATURE)) {
+ continue;
+ }
+ } catch (CoreException e) {
+ continue;
+ }
+
+ projectList.add(androidJavaProject);
+ }
+
+ return projectList;
+ }
+
+ /**
+ * Returns the android package file as an IFile object for the specified
+ * project.
+ * @param project The project
+ * @return The android package as an IFile object or null if not found.
+ */
+ public static IFile getApplicationPackage(IProject project) {
+ // get the output folder
+ IFolder outputLocation = BaseProjectHelper.getOutputFolder(project);
+
+ if (outputLocation == null) {
+ AdtPlugin.printErrorToConsole(project,
+ "Failed to get the output location of the project. Check build path properties"
+ );
+ return null;
+ }
+
+
+ // get the package path
+ String packageName = project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE;
+ IResource r = outputLocation.findMember(packageName);
+
+ // check the package is present
+ if (r instanceof IFile && r.exists()) {
+ return (IFile)r;
+ }
+
+ String msg = String.format("Could not find %1$s!", packageName);
+ AdtPlugin.printErrorToConsole(project, msg);
+
+ return null;
+ }
}
From 7b82e66c0dd17948918782b3d2f353846e6436c5 Mon Sep 17 00:00:00 2001
From: Xavier Ducrohet <>
Date: Tue, 24 Mar 2009 20:32:36 -0700
Subject: [PATCH 013/127] Automated import from
//branches/donutburger/...@141557,141557
---
.../com/android/ide/eclipse/adt/build/ApkBuilder.java | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
index bc5b01c5f..1edcf79fd 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
@@ -979,7 +979,10 @@ public class ApkBuilder extends BaseBuilder {
writeStandardProjectResources(jarBuilder, javaProject, wsRoot, list);
for (IJavaProject referencedJavaProject : referencedJavaProjects) {
- if (referencedJavaProject.getProject().hasNature(AndroidConstants.NATURE)) {
+ // only include output from non android referenced project
+ // (This is to handle the case of reference Android projects in the context of
+ // instrumentation projects that need to reference the projects to be tested).
+ if (referencedJavaProject.getProject().hasNature(AndroidConstants.NATURE) == false) {
writeStandardProjectResources(jarBuilder, referencedJavaProject, wsRoot, list);
}
}
@@ -1084,7 +1087,10 @@ public class ApkBuilder extends BaseBuilder {
IWorkspaceRoot wsRoot = ws.getRoot();
for (IJavaProject javaProject : referencedJavaProjects) {
- if (javaProject.getProject().hasNature(AndroidConstants.NATURE)) {
+ // only include output from non android referenced project
+ // (This is to handle the case of reference Android projects in the context of
+ // instrumentation projects that need to reference the projects to be tested).
+ if (javaProject.getProject().hasNature(AndroidConstants.NATURE) == false) {
// get the output folder
IPath path = null;
try {
From aa3b63abf4f949b6ef98ef72c1305d5b2603e3fd Mon Sep 17 00:00:00 2001
From: Xavier Ducrohet <>
Date: Tue, 24 Mar 2009 20:36:19 -0700
Subject: [PATCH 014/127] Automated import from
//branches/donutburger/...@141594,141594
---
.../src/com/android/ant/AaptExecLoopTask.java | 11 +++++++----
tools/scripts/android_rules.xml | 1 +
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java b/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java
index d2c71624d..6444e4d01 100644
--- a/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java
+++ b/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java
@@ -181,11 +181,14 @@ public final class AaptExecLoopTask extends Task {
task.createArg().setValue("-M");
task.createArg().setValue(mManifest);
- // resources location
- task.createArg().setValue("-S");
- task.createArg().setValue(mResources);
+ // resources location. This may not exists, and aapt doesn't like it, so we check first.
+ File res = new File(mResources);
+ if (res.isDirectory()) {
+ task.createArg().setValue("-S");
+ task.createArg().setValue(mResources);
+ }
- // assets location. this may not exists, and aapt doesn't like it, so we check first.
+ // assets location. This may not exists, and aapt doesn't like it, so we check first.
File assets = new File(mAssets);
if (assets.isDirectory()) {
task.createArg().setValue("-A");
diff --git a/tools/scripts/android_rules.xml b/tools/scripts/android_rules.xml
index aad9dbd6e..003021c35 100644
--- a/tools/scripts/android_rules.xml
+++ b/tools/scripts/android_rules.xml
@@ -72,6 +72,7 @@
Creating output directories if needed...
+
From 3ce38fa6ccaba1bb1d83162c61ee222daa30d312 Mon Sep 17 00:00:00 2001
From: Jeff Hamilton <>
Date: Tue, 24 Mar 2009 20:48:56 -0700
Subject: [PATCH 015/127] Automated import from
//branches/donutburger/...@141737,141737
---
.../android/apis/app/VoiceRecognition.java | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java b/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java
index a784e1587..1a4b5e4a0 100644
--- a/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java
+++ b/samples/ApiDemos/src/com/example/android/apis/app/VoiceRecognition.java
@@ -16,17 +16,18 @@
package com.example.android.apis.app;
+import com.example.android.apis.R;
+
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
+import android.speech.RecognizerIntent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
-import com.example.android.apis.R;
-
import java.util.ArrayList;
/**
@@ -71,9 +72,10 @@ public class VoiceRecognition extends Activity implements OnClickListener {
*/
private void startVoiceRecognitionActivity() {
//TODO Get these values from constants
- Intent intent = new Intent("android.speech.action.RECOGNIZE_SPEECH");
- intent.putExtra("language_model", "free_form");
- intent.putExtra("prompt", "Speech recognition demo");
+ Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+ intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+ RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
+ intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Speech recognition demo");
startActivityForResult(intent, VOICE_RECOGNITION_REQUEST_CODE);
}
@@ -83,8 +85,8 @@ public class VoiceRecognition extends Activity implements OnClickListener {
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == VOICE_RECOGNITION_REQUEST_CODE && resultCode == RESULT_OK) {
- //TODO get the value from a constant
- ArrayListmatches = data.getStringArrayListExtra("results");
+ ArrayList matches = data.getStringArrayListExtra(
+ RecognizerIntent.EXTRA_RESULTS);
mList.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1,
matches));
}
From 6005aabd22d1014b04914f7d02811e7f12101721 Mon Sep 17 00:00:00 2001
From: Brett Chabot <>
Date: Tue, 24 Mar 2009 20:53:14 -0700
Subject: [PATCH 016/127] Automated import from
//branches/donutburger/...@141781,141781
---
.../AndroidJUnitLaunchConfigDelegate.java | 138 ++++++-----------
.../AndroidJUnitLaunchConfigurationTab.java | 42 ++---
.../junit/AndroidJUnitLaunchShortcut.java | 39 ++---
.../junit/InstrumentationRunnerValidator.java | 145 ++++++++++++++++++
4 files changed, 225 insertions(+), 139 deletions(-)
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
index a624b0001..fa8e4b01a 100755
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
@@ -32,23 +32,21 @@ import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
-import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry;
/**
- * Run configuration that can execute JUnit tests on an Android platform
+ * Run configuration that can execute JUnit tests on an Android platform.
*
- * Will deploy apps on target Android platform by reusing functionality from ADT
- * LaunchConfigDelegate, and then run JUnits tests by reusing functionality from JDT
+ * Will deploy apps on target Android platform by reusing functionality from ADT
+ * LaunchConfigDelegate, and then run JUnits tests by reusing functionality from JDT
* JUnitLaunchConfigDelegate.
*/
-@SuppressWarnings("restriction") //$NON-NLS-1$
+@SuppressWarnings("restriction")
public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
- /** Launch config attribute that stores instrumentation runner */
+ /** Launch config attribute that stores instrumentation runner. */
static final String ATTR_INSTR_NAME = AdtPlugin.PLUGIN_ID + ".instrumentation"; //$NON-NLS-1$
- static final String INSTRUMENTATION_OK = null;
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
@Override
@@ -58,7 +56,7 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
IFile applicationPackage, AndroidManifestParser manifestParser) {
String testPackage = manifestParser.getPackage();
- String runner = getRunnerFromConfig(configuration);
+ String runner = getRunner(project, configuration, manifestParser);
if (runner == null) {
AdtPlugin.displayError("Android Launch",
"An instrumention test runner is not specified!");
@@ -72,45 +70,57 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
manifestParser.getDebuggable(), manifestParser.getApiLevelRequirement(),
junitLaunch, config, androidLaunch, monitor);
}
-
- private String getRunnerFromConfig(ILaunchConfiguration configuration) {
- String runner = EMPTY_STRING;
+
+ /**
+ * Gets a instrumentation runner for the launch.
+ *
+ * If a runner is stored in the given configuration, will return that.
+ * Otherwise, will try to find the first valid runner for the project.
+ * If a runner can still not be found, will return null.
+ *
+ * @param project the {@link IProject} for the app
+ * @param configuration the {@link ILaunchConfiguration} for the launch
+ * @param manifestParser the {@link AndroidManifestParser} for the project
+ *
+ * @return null if no instrumentation runner can be found, otherwise return
+ * the fully qualified runner name.
+ */
+ private String getRunner(IProject project, ILaunchConfiguration configuration,
+ AndroidManifestParser manifestParser) {
try {
- runner = configuration.getAttribute(ATTR_INSTR_NAME, EMPTY_STRING);
+ String runner = getRunnerFromConfig(configuration);
+ if (runner != null) {
+ return runner;
+ }
+ final InstrumentationRunnerValidator instrFinder = new InstrumentationRunnerValidator(
+ BaseProjectHelper.getJavaProject(project), manifestParser);
+ runner = instrFinder.getValidInstrumentationTestRunner();
+ if (runner != null) {
+ AdtPlugin.printErrorToConsole(project,
+ String.format("Warning: No instrumentation runner found for the launch, " +
+ "using %1$s", runner));
+ return runner;
+ }
+ AdtPlugin.printErrorToConsole(project,
+ String.format("ERROR: Application does not specify a %1$s instrumentation or does not declare uses-library %2$s",
+ AndroidConstants.CLASS_INSTRUMENTATION_RUNNER,
+ AndroidConstants.LIBRARY_TEST_RUNNER));
+ return null;
} catch (CoreException e) {
- AdtPlugin.log(e, "Error when retrieving instrumentation info from launch config"); //$NON-NLS-1$
+ AdtPlugin.log(e, "Error when retrieving instrumentation info"); //$NON-NLS-1$
}
+ return null;
+
+ }
+
+ private String getRunnerFromConfig(ILaunchConfiguration configuration) throws CoreException {
+ String runner = configuration.getAttribute(ATTR_INSTR_NAME, EMPTY_STRING);
if (runner.length() < 1) {
return null;
}
return runner;
}
- /**
- * Helper method to return the set of instrumentations for the Android project
- *
- * @param project the {@link IProject} to get instrumentations for
- * @return null if error occurred parsing instrumentations, otherwise returns array of
- * instrumentation class names
- */
- static String[] getInstrumentationsForProject(IProject project) {
- if (project != null) {
- try {
- // parse the manifest for the list of instrumentations
- AndroidManifestParser manifestParser = AndroidManifestParser.parse(
- BaseProjectHelper.getJavaProject(project), null /* errorListener */,
- true /* gatherData */, false /* markErrors */);
- if (manifestParser != null) {
- return manifestParser.getInstrumentations();
- }
- } catch (CoreException e) {
- AdtPlugin.log(e, "%s: Error parsing AndroidManifest.xml", //$NON-NLS-1$
- project.getName());
- }
- }
- return null;
- }
-
/**
* Helper method to set JUnit-related attributes expected by JDT JUnit runner
*
@@ -121,56 +131,4 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
config.setAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_RUNNER_KIND,
TestKindRegistry.JUNIT3_TEST_KIND_ID);
}
-
- /**
- * Helper method to determine if specified instrumentation can be used as a test runner
- *
- * @param project the {@link IJavaProject} to validate
- * @param instrumentation the instrumentation class name to validate
- * @return INSTRUMENTATION_OK if valid, otherwise returns error message
- */
- static String validateInstrumentationRunner(IJavaProject project, String instrumentation) {
- AndroidManifestParser manifestParser;
- try {
- manifestParser = AndroidManifestParser.parse(
- project, null /* errorListener */,
- true /* gatherData */, false /* markErrors */);
- // check if this instrumentation is the standard test runner
- if (!instrumentation.equals(AndroidConstants.CLASS_INSTRUMENTATION_RUNNER)) {
- // check if it extends the standard test runner
- String result = BaseProjectHelper.testClassForManifest(project,
- instrumentation, AndroidConstants.CLASS_INSTRUMENTATION_RUNNER, true);
- if (result != BaseProjectHelper.TEST_CLASS_OK) {
- return String.format("The instrumentation runner must be of type %s",
- AndroidConstants.CLASS_INSTRUMENTATION_RUNNER);
- }
- }
- if (!hasTestRunnerLibrary(manifestParser)) {
- return String.format("%s does not not use the %s library",
- project.getProject().getName(), AndroidConstants.LIBRARY_TEST_RUNNER);
- }
- } catch (CoreException e) {
- String err = String.format("Error parsing AndroidManifest for %s",
- project.getProject().getName());
- AdtPlugin.log(e, err);
- return err;
- }
- return INSTRUMENTATION_OK;
- }
-
- /**
- * Helper method to determine if given manifest has a AndroidConstants.LIBRARY_TEST_RUNNER
- * library reference
- *
- * @param manifestParser the {@link AndroidManifestParser} to search
- * @return true if test runner library found, false otherwise
- */
- static boolean hasTestRunnerLibrary(AndroidManifestParser manifestParser) {
- for (String lib : manifestParser.getUsesLibraries()) {
- if (lib.equals(AndroidConstants.LIBRARY_TEST_RUNNER)) {
- return true;
- }
- }
- return false;
- }
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
index aa59a5157..eb5748269 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
@@ -118,7 +118,9 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
private Image mTabIcon = null;
private Combo mInstrumentationCombo;
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
+ private static final String TAG = "AndroidJUnitLaunchConfigurationTab"; //$NON-NLS-1$
private String[] mInstrumentations = null;
+ private InstrumentationRunnerValidator mInstrValidator = null;
private ProjectChooserHelper mProjectChooserHelper;
/* (non-Javadoc)
@@ -349,7 +351,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
break;
}
}
- }
+ }
} catch (CoreException ce) {
// ignore
}
@@ -454,7 +456,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
mapResources(config);
} catch (CoreException e) {
// TODO: does the real error need to be extracted out of CoreException
- AdtPlugin.log(e, "Error occurred saving configuration");
+ AdtPlugin.log(e, "Error occurred saving configuration"); //$NON-NLS-1$
}
AndroidJUnitLaunchConfigDelegate.setJUnitDefaults(config);
@@ -486,7 +488,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
public Image getImage() {
// reuse icon from the Android App Launch config tab
if (mTabIcon == null) {
- mTabIcon= AdtPlugin.getImageLoader().loadImage(MainLaunchConfigTab.LAUNCH_TAB_IMAGE,
+ mTabIcon = AdtPlugin.getImageLoader().loadImage(MainLaunchConfigTab.LAUNCH_TAB_IMAGE,
null);
}
return mTabIcon;
@@ -514,7 +516,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
setErrorMessage(e.getMessage());
return;
} catch (InvocationTargetException e) {
- AdtPlugin.log(e.getTargetException(), "Error finding test types");
+ AdtPlugin.log(e.getTargetException(), "Error finding test types"); //$NON-NLS-1$
return;
} finally {
mTestRadioButton.setSelection(radioSetting[0]);
@@ -675,10 +677,10 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
return;
}
} catch (CoreException e) {
- AdtPlugin.log(e, "validatePage failed");
+ AdtPlugin.log(e, "validatePage failed"); //$NON-NLS-1$
}
- validateInstrumentation(javaProject);
+ validateInstrumentation();
}
private void validateJavaProject(IJavaProject javaProject) {
@@ -688,19 +690,14 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
}
}
- private void validateInstrumentation(IJavaProject javaProject) {
- if (mInstrumentations == null || mInstrumentations.length < 1) {
- setErrorMessage("Specified project has no defined instrumentations");
- return;
- }
+ private void validateInstrumentation() {
String instrumentation = getSelectedInstrumentation();
if (instrumentation == null) {
- setErrorMessage("Instrumentation not specified");
+ setErrorMessage("Instrumentation runner not specified");
return;
}
- String result = AndroidJUnitLaunchConfigDelegate.validateInstrumentationRunner(
- javaProject, instrumentation);
- if (result != AndroidJUnitLaunchConfigDelegate.INSTRUMENTATION_OK) {
+ String result = mInstrValidator.validateInstrumentationRunner(instrumentation);
+ if (result != InstrumentationRunnerValidator.INSTRUMENTATION_OK) {
setErrorMessage(result);
return;
}
@@ -949,14 +946,15 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
/**
* Loads the UI with the instrumentations of the specified project, and stores the
- * activities in mActivities.
- *
- * First activity is selected by default if present.
+ * instrumentations in mInstrumentations.
*
* @param project the {@link IProject} to load the instrumentations from.
*/
private void loadInstrumentations(IProject project) {
- mInstrumentations = AndroidJUnitLaunchConfigDelegate.getInstrumentationsForProject(project);
+ try {
+ mInstrValidator = new InstrumentationRunnerValidator(project);
+ mInstrumentations = (mInstrValidator == null ? null :
+ mInstrValidator.getInstrumentations());
if (mInstrumentations != null) {
mInstrumentationCombo.removeAll();
for (String instrumentation : mInstrumentations) {
@@ -966,9 +964,13 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
// config object.
return;
}
-
+ } catch (CoreException e) {
+ AdtPlugin.logAndPrintError(e, TAG, "ERROR: Failed to get instrumentations for %1$s",
+ project.getName());
+ }
// if we reach this point, either project is null, or we got an exception during
// the parsing. In either case, we empty the instrumentation list.
+ mInstrValidator = null;
mInstrumentations = null;
mInstrumentationCombo.removeAll();
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java
index 30649e2e8..f06f7eb4c 100755
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchShortcut.java
@@ -16,10 +16,6 @@
package com.android.ide.eclipse.adt.launch.junit;
-import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.common.AndroidConstants;
-
-import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.jdt.core.IJavaElement;
@@ -43,33 +39,18 @@ public class AndroidJUnitLaunchShortcut extends JUnitLaunchShortcut {
protected ILaunchConfigurationWorkingCopy createLaunchConfiguration(IJavaElement element)
throws CoreException {
ILaunchConfigurationWorkingCopy config = super.createLaunchConfiguration(element);
- IProject project = element.getResource().getProject();
- String[] instrumentations =
- AndroidJUnitLaunchConfigDelegate.getInstrumentationsForProject(project);
- boolean runnerFound = false;
- if (instrumentations != null) {
- // just pick the first valid runner
- for (String instr : instrumentations) {
- if (AndroidJUnitLaunchConfigDelegate.validateInstrumentationRunner(
- element.getJavaProject(), instr) ==
- AndroidJUnitLaunchConfigDelegate.INSTRUMENTATION_OK) {
+ // just get first valid instrumentation runner
+ String instrumentation = new InstrumentationRunnerValidator(element.getJavaProject()).
+ getValidInstrumentationTestRunner();
+ if (instrumentation != null) {
+ config.setAttribute(AndroidJUnitLaunchConfigDelegate.ATTR_INSTR_NAME,
+ instrumentation);
+ }
+ // if a valid runner is not found, rely on launch delegate to log error.
+ // This method is called without explicit user action to launch Android JUnit, so avoid
+ // logging an error here.
- config.setAttribute(AndroidJUnitLaunchConfigDelegate.ATTR_INSTR_NAME,
- instr);
- runnerFound = true;
- break;
- }
- }
- }
- if (!runnerFound) {
- // TODO: put this in a string properties
- String msg = String.format("ERROR: Application does not specify a %s instrumentation or does not declare uses-library %s",
- AndroidConstants.CLASS_INSTRUMENTATION_RUNNER,
- AndroidConstants.LIBRARY_TEST_RUNNER);
- AdtPlugin.printErrorToConsole(project, msg);
- }
AndroidJUnitLaunchConfigDelegate.setJUnitDefaults(config);
-
return config;
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java
new file mode 100644
index 000000000..f22fc7cda
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.eclipse.adt.launch.junit;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.common.AndroidConstants;
+import com.android.ide.eclipse.common.project.AndroidManifestParser;
+import com.android.ide.eclipse.common.project.BaseProjectHelper;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IJavaProject;
+
+/**
+ * Provides validation for Android instrumentation test runner
+ */
+class InstrumentationRunnerValidator {
+ private final IJavaProject mJavaProject;
+ private String[] mInstrumentations = null;
+ private boolean mHasRunnerLibrary = false;
+
+ static final String INSTRUMENTATION_OK = null;
+
+ /**
+ * Initializes the InstrumentationRunnerValidator.
+ *
+ * @param javaProject the {@link IJavaProject} for the Android project to validate
+ */
+ InstrumentationRunnerValidator(IJavaProject javaProject) {
+ mJavaProject = javaProject;
+ try {
+ AndroidManifestParser manifestParser = AndroidManifestParser.parse(javaProject,
+ null /* errorListener */, true /* gatherData */, false /* markErrors */);
+ init(manifestParser);
+ } catch (CoreException e) {
+ AdtPlugin.printErrorToConsole(javaProject.getProject(), "ERROR: Failed to parse %1$s",
+ AndroidConstants.FN_ANDROID_MANIFEST);
+ }
+ }
+
+ /**
+ * Initializes the InstrumentationRunnerValidator.
+ *
+ * @param project the {@link IProject} for the Android project to validate
+ * @throws CoreException if a fatal error occurred in initialization
+ */
+ InstrumentationRunnerValidator(IProject project) throws CoreException {
+ this(BaseProjectHelper.getJavaProject(project));
+ }
+
+ /**
+ * Initializes the InstrumentationRunnerValidator with an existing {@link AndroidManifestParser}
+ *
+ * @param javaProject the {@link IJavaProject} for the Android project to validate
+ * @param manifestParser the {@link AndroidManifestParser} for the Android project
+ */
+ InstrumentationRunnerValidator(IJavaProject javaProject, AndroidManifestParser manifestParser) {
+ mJavaProject = javaProject;
+ init(manifestParser);
+ }
+
+ private void init(AndroidManifestParser manifestParser) {
+ mInstrumentations = manifestParser.getInstrumentations();
+ mHasRunnerLibrary = hasTestRunnerLibrary(manifestParser);
+ }
+
+ /**
+ * Helper method to determine if given manifest has a AndroidConstants.LIBRARY_TEST_RUNNER
+ * library reference
+ *
+ * @param manifestParser the {@link AndroidManifestParser} to search
+ * @return true if test runner library found, false otherwise
+ */
+ private boolean hasTestRunnerLibrary(AndroidManifestParser manifestParser) {
+ for (String lib : manifestParser.getUsesLibraries()) {
+ if (lib.equals(AndroidConstants.LIBRARY_TEST_RUNNER)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return the set of instrumentations for the Android project.
+ *
+ * @return nullnull if no valid
+ * instrumentation can be found.
+ */
+ String getValidInstrumentationTestRunner() {
+ for (String instrumentation : getInstrumentations()) {
+ if (validateInstrumentationRunner(instrumentation) == INSTRUMENTATION_OK) {
+ return instrumentation;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Helper method to determine if specified instrumentation can be used as a test runner
+ *
+ * @param instrumentation the instrumentation class name to validate. Assumes this
+ * instrumentation is one of {@link #getInstrumentations()}
+ * @return INSTRUMENTATION_OK if valid, otherwise returns error message
+ */
+ String validateInstrumentationRunner(String instrumentation) {
+ if (!mHasRunnerLibrary) {
+ return String.format("The application does not declare uses-library %1$s",
+ AndroidConstants.LIBRARY_TEST_RUNNER);
+ }
+ // check if this instrumentation is the standard test runner
+ if (!instrumentation.equals(AndroidConstants.CLASS_INSTRUMENTATION_RUNNER)) {
+ // check if it extends the standard test runner
+ String result = BaseProjectHelper.testClassForManifest(mJavaProject,
+ instrumentation, AndroidConstants.CLASS_INSTRUMENTATION_RUNNER, true);
+ if (result != BaseProjectHelper.TEST_CLASS_OK) {
+ return String.format("The instrumentation runner must be of type %s",
+ AndroidConstants.CLASS_INSTRUMENTATION_RUNNER);
+ }
+ }
+ return INSTRUMENTATION_OK;
+ }
+}
From c56fb4fdf2419a5140505935bbd389e6e5d709a6 Mon Sep 17 00:00:00 2001
From: Xavier Ducrohet <>
Date: Tue, 24 Mar 2009 20:56:16 -0700
Subject: [PATCH 017/127] Automated import from
//branches/donutburger/...@141809,141809
---
.../icons/androidjunit.png | Bin 0 -> 393 bytes
.../com.android.ide.eclipse.adt/plugin.xml | 2 +-
2 files changed, 1 insertion(+), 1 deletion(-)
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/androidjunit.png
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/androidjunit.png b/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/androidjunit.png
new file mode 100644
index 0000000000000000000000000000000000000000..25826dce773d24bec4b87f86e814bdefef638505
GIT binary patch
literal 393
zcmV;40e1e0P)(bu;eV6wl%QIx`oy#!OC4~{B895d}hT;SoORN4fvJW{^t=8i4FvY;3Rr4r|-~R~}vtcPci8xWt%1QFq|h
zDTemT``~JBynFy=yU6RplP+8mjlq
From 511d47136376a2d9d7e5c690edf8e3fecb543fdd Mon Sep 17 00:00:00 2001
From: Brett Chabot <>
Date: Tue, 24 Mar 2009 20:56:49 -0700
Subject: [PATCH 018/127] Automated import from
//branches/donutburger/...@141815,141815
---
testrunner/coverage_targets.xml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/testrunner/coverage_targets.xml b/testrunner/coverage_targets.xml
index d7700f63a..32a485659 100644
--- a/testrunner/coverage_targets.xml
+++ b/testrunner/coverage_targets.xml
@@ -108,4 +108,8 @@
+
+
+
From b6007ebd5c6f373ab4bc48363b6738b681ef13a3 Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Tue, 24 Mar 2009 20:57:57 -0700
Subject: [PATCH 019/127] Automated import from
//branches/donutburger/...@141823,141823
---
.../extractstring/ExtractStringAction.java | 51 +++--
.../ExtractStringRefactoring.java | 204 +++++++++++-------
2 files changed, 153 insertions(+), 102 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java
index 528701573..dceb1441d 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java
@@ -16,7 +16,11 @@
package com.android.ide.eclipse.adt.refactorings.extractstring;
+import com.android.ide.eclipse.common.AndroidConstants;
+
import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaCore;
@@ -32,6 +36,7 @@ import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.FileEditorInput;
/*
* Quick Reference Link:
@@ -71,7 +76,7 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
/** Keep track of the current workbench window. */
private IWorkbenchWindow mWindow;
private ITextSelection mSelection;
- private ICompilationUnit mUnit;
+ private IFile mFile;
/**
* Keep track of the current workbench window.
@@ -99,30 +104,24 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
// runs since we don't have access to the AST yet.
mSelection = null;
- mUnit = null;
+ mFile = null;
if (selection instanceof ITextSelection) {
mSelection = (ITextSelection) selection;
if (mSelection.getLength() > 0) {
- mUnit = getCompilationUnit();
+ mFile = getSelectedFile();
}
-
- // Keep for debugging purposes
- //System.out.println(String.format("-- Selection: %d + %d = %s",
- // mSelection.getOffset(),
- // mSelection.getLength(),
- // mSelection.getText()));
}
- action.setEnabled(mSelection != null && mUnit != null);
+ action.setEnabled(mSelection != null && mFile != null);
}
/**
* Create a new instance of our refactoring and a wizard to configure it.
*/
public void run(IAction action) {
- if (mSelection != null && mUnit != null) {
- ExtractStringRefactoring ref = new ExtractStringRefactoring(mUnit, mSelection);
+ if (mSelection != null && mFile != null) {
+ ExtractStringRefactoring ref = new ExtractStringRefactoring(mFile, mSelection);
RefactoringWizard wizard = new ExtractStringWizard(ref, "Extract Android String");
RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
try {
@@ -134,9 +133,14 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
}
/**
- * Returns the active {@link ICompilationUnit} or null.
+ * Returns the active {@link IFile} (hopefully matching our selection) or null.
+ * The file is only returned if it's a file from a project with an Android nature.
+ *
+ * At that point we do not try to analyze if the selection nor the file is suitable
+ * for the refactoring. This check is performed when the refactoring is invoked since
+ * it can then produce meaningful error messages as needed.
*/
- private ICompilationUnit getCompilationUnit() {
+ private IFile getSelectedFile() {
IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (wwin != null) {
IWorkbenchPage page = wwin.getActivePage();
@@ -144,12 +148,19 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
IEditorPart editor = page.getActiveEditor();
if (editor != null) {
IEditorInput input = editor.getEditorInput();
- if (input != null) {
- ITypeRoot typeRoot = JavaUI.getEditorInputTypeRoot(input);
- // The type root can be either a .class or a .java (aka compilation unit).
- // We want the compilation unit kind.
- if (typeRoot instanceof ICompilationUnit) {
- return (ICompilationUnit) typeRoot;
+
+ if (input instanceof FileEditorInput) {
+ FileEditorInput fi = (FileEditorInput) input;
+ IFile file = fi.getFile();
+ if (file.exists()) {
+ IProject proj = file.getProject();
+ try {
+ if (proj != null && proj.hasNature(AndroidConstants.NATURE)) {
+ return file;
+ }
+ } catch (CoreException e) {
+ // ignore
+ }
}
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
index 715503c1f..99711bf20 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
@@ -24,7 +24,9 @@ import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourceAttributes;
+import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
@@ -126,46 +128,54 @@ import javax.xml.xpath.XPathExpressionException;
*/
class ExtractStringRefactoring extends Refactoring {
- /** The compilation unit, a.k.a. the Java file model. */
- private final ICompilationUnit mUnit;
+ /** The file model being manipulated. */
+ private final IFile mFile;
+ /** The start of the selection in {@link #mFile}. */
private final int mSelectionStart;
+ /** The end of the selection in {@link #mFile}. */
private final int mSelectionEnd;
+
+ /** The compilation unit, only defined if {@link #mFile} points to a usable Java source file. */
+ private ICompilationUnit mUnit;
/** The actual string selected, after UTF characters have been escaped, good for display. */
private String mTokenString;
- /** Start position of the string token in the source buffer. */
- private int mTokenStart;
- /** End position of the string token in the source buffer. */
- private int mTokenEnd;
+
+ /** The XML string ID selected by the user in the wizard. */
private String mXmlStringId;
+ /** The path of the XML file that will define {@link #mXmlStringId}, selected by the user
+ * in the wizard. */
private String mTargetXmlFileWsPath;
+
+ /** A temporary cache of R.string IDs defined by a given xml file. The key is the
+ * project path of the file, the data is a set of known string Ids for that file. */
private HashMap> mResIdCache;
+ /** An instance of XPath, created lazily on demand. */
private XPath mXPath;
+ /** The list of changes computed by {@link #checkFinalConditions(IProgressMonitor)} and
+ * used by {@link #createChange(IProgressMonitor)}. */
private ArrayList mChanges;
public ExtractStringRefactoring(Map arguments)
throws NullPointerException {
- mUnit = (ICompilationUnit) JavaCore.create(arguments.get("CU")); //$NON-NLS-1$
+
+ IPath path = Path.fromPortableString(arguments.get("file")); //$NON-NLS-1$
+ mFile = (IFile) ResourcesPlugin.getWorkspace().getRoot().findMember(path);
mSelectionStart = Integer.parseInt(arguments.get("sel-start")); //$NON-NLS-1$
mSelectionEnd = Integer.parseInt(arguments.get("sel-end")); //$NON-NLS-1$
- mTokenStart = Integer.parseInt(arguments.get("tok-start")); //$NON-NLS-1$
- mTokenEnd = Integer.parseInt(arguments.get("tok-end")); //$NON-NLS-1$
mTokenString = arguments.get("tok-esc"); //$NON-NLS-1$
}
private Map createArgumentMap() {
HashMap args = new HashMap();
- args.put("CU", mUnit.getHandleIdentifier()); //$NON-NLS-1$
+ args.put("file", mFile.getFullPath().toPortableString()); //$NON-NLS-1$
args.put("sel-start", Integer.toString(mSelectionStart)); //$NON-NLS-1$
args.put("sel-end", Integer.toString(mSelectionEnd)); //$NON-NLS-1$
- args.put("tok-start", Integer.toString(mTokenStart)); //$NON-NLS-1$
- args.put("tok-end", Integer.toString(mTokenEnd)); //$NON-NLS-1$
args.put("tok-esc", mTokenString); //$NON-NLS-1$
return args;
}
- public ExtractStringRefactoring(ICompilationUnit unit, ITextSelection selection) {
- mUnit = unit;
-
+ public ExtractStringRefactoring(IFile file, ITextSelection selection) {
+ mFile = file;
mSelectionStart = selection.getOffset();
mSelectionEnd = mSelectionStart + selection.getLength();
}
@@ -207,75 +217,42 @@ class ExtractStringRefactoring extends Refactoring {
public RefactoringStatus checkInitialConditions(IProgressMonitor monitor)
throws CoreException, OperationCanceledException {
+ mUnit = null;
mTokenString = null;
- mTokenStart = -1;
- mTokenEnd = -1;
RefactoringStatus status = new RefactoringStatus();
try {
- monitor.beginTask("Checking preconditions...", 3);
-
- if (!extraChecks(monitor, status)) {
+ monitor.beginTask("Checking preconditions...", 5);
+
+ if (!checkSourceFile(mFile, status, monitor)) {
return status;
}
-
+
+ // Try to get a compilation unit from this file. If it fails, mUnit is null.
try {
- IBuffer buffer = mUnit.getBuffer();
+ mUnit = JavaCore.createCompilationUnitFrom(mFile);
- IScanner scanner = ToolFactory.createScanner(
- false, //tokenizeComments
- false, //tokenizeWhiteSpace
- false, //assertMode
- false //recordLineSeparator
- );
- scanner.setSource(buffer.getCharacters());
- monitor.worked(1);
-
- for(int token = scanner.getNextToken();
- token != ITerminalSymbols.TokenNameEOF;
- token = scanner.getNextToken()) {
- if (scanner.getCurrentTokenStartPosition() <= mSelectionStart &&
- scanner.getCurrentTokenEndPosition() >= mSelectionEnd) {
- // found the token, but only keep of the right type
- if (token == ITerminalSymbols.TokenNameStringLiteral) {
- mTokenString = new String(scanner.getCurrentTokenSource());
- mTokenStart = scanner.getCurrentTokenStartPosition();
- mTokenEnd = scanner.getCurrentTokenEndPosition();
- }
- break;
- } else if (scanner.getCurrentTokenStartPosition() > mSelectionEnd) {
- // scanner is past the selection, abort.
- break;
- }
+ // Make sure the unit is not read-only, e.g. it's not a class file or inside a Jar
+ if (mUnit.isReadOnly()) {
+ status.addFatalError("The file is read-only, please make it writeable first.");
+ return status;
}
- } catch (JavaModelException e1) {
- // Error in mUnit.getBuffer. Ignore.
- } catch (InvalidInputException e2) {
- // Error in scanner.getNextToken. Ignore.
- } finally {
- monitor.worked(1);
- }
-
- if (mTokenString != null) {
- // As a literal string, the token should have surrounding quotes. Remove them.
- int len = mTokenString.length();
- if (len > 0 &&
- mTokenString.charAt(0) == '"' &&
- mTokenString.charAt(len - 1) == '"') {
- mTokenString = mTokenString.substring(1, len - 1);
- }
- // We need a non-empty string literal
- if (mTokenString.length() == 0) {
- mTokenString = null;
+
+ // This is a Java file. Check if it contains the selection we want.
+ if (!findSelectionInJavaUnit(mUnit, status, monitor)) {
+ return status;
}
+
+ } catch (Exception e) {
+ // That was not a Java file. Ignore.
}
- if (mTokenString == null) {
- status.addFatalError("Please select a Java string literal.");
+ if (mUnit == null) {
+ // Check this an XML file and get the selection and its context.
+ // TODO
+ status.addFatalError("Selection must be inside a Java source file.");
}
-
- monitor.worked(1);
} finally {
monitor.done();
}
@@ -283,31 +260,94 @@ class ExtractStringRefactoring extends Refactoring {
return status;
}
+ /**
+ * Try to find the selected Java element in the compilation unit.
+ *
+ * If selection matches a string literal, capture it, otherwise add a fatal error
+ * to the status.
+ *
+ * On success, advance the monitor by 3.
+ */
+ private boolean findSelectionInJavaUnit(ICompilationUnit unit,
+ RefactoringStatus status, IProgressMonitor monitor) {
+ try {
+ IBuffer buffer = unit.getBuffer();
+
+ IScanner scanner = ToolFactory.createScanner(
+ false, //tokenizeComments
+ false, //tokenizeWhiteSpace
+ false, //assertMode
+ false //recordLineSeparator
+ );
+ scanner.setSource(buffer.getCharacters());
+ monitor.worked(1);
+
+ for(int token = scanner.getNextToken();
+ token != ITerminalSymbols.TokenNameEOF;
+ token = scanner.getNextToken()) {
+ if (scanner.getCurrentTokenStartPosition() <= mSelectionStart &&
+ scanner.getCurrentTokenEndPosition() >= mSelectionEnd) {
+ // found the token, but only keep of the right type
+ if (token == ITerminalSymbols.TokenNameStringLiteral) {
+ mTokenString = new String(scanner.getCurrentTokenSource());
+ }
+ break;
+ } else if (scanner.getCurrentTokenStartPosition() > mSelectionEnd) {
+ // scanner is past the selection, abort.
+ break;
+ }
+ }
+ } catch (JavaModelException e1) {
+ // Error in unit.getBuffer. Ignore.
+ } catch (InvalidInputException e2) {
+ // Error in scanner.getNextToken. Ignore.
+ } finally {
+ monitor.worked(1);
+ }
+
+ if (mTokenString != null) {
+ // As a literal string, the token should have surrounding quotes. Remove them.
+ int len = mTokenString.length();
+ if (len > 0 &&
+ mTokenString.charAt(0) == '"' &&
+ mTokenString.charAt(len - 1) == '"') {
+ mTokenString = mTokenString.substring(1, len - 1);
+ }
+ // We need a non-empty string literal
+ if (mTokenString.length() == 0) {
+ mTokenString = null;
+ }
+ }
+
+ if (mTokenString == null) {
+ status.addFatalError("Please select a Java string literal.");
+ }
+
+ monitor.worked(1);
+ return status.isOK();
+ }
+
/**
* Tests from org.eclipse.jdt.internal.corext.refactoringChecks#validateEdit()
* Might not be useful.
*
+ * On success, advance the monitor by 2.
+ *
* @return False if caller should abort, true if caller should continue.
*/
- private boolean extraChecks(IProgressMonitor monitor, RefactoringStatus status) {
- //
- IResource res = mUnit.getPrimary().getResource();
- if (res == null || res.getType() != IResource.FILE) {
- status.addFatalError("Cannot access resource; only regular files can be used.");
- return false;
- }
- monitor.worked(1);
-
+ private boolean checkSourceFile(IFile file,
+ RefactoringStatus status,
+ IProgressMonitor monitor) {
// check whether the source file is in sync
- if (!res.isSynchronized(IResource.DEPTH_ZERO)) {
+ if (!file.isSynchronized(IResource.DEPTH_ZERO)) {
status.addFatalError("The file is not synchronized. Please save it first.");
return false;
}
monitor.worked(1);
// make sure we can write to it.
- ResourceAttributes resAttr = res.getResourceAttributes();
- if (mUnit.isReadOnly() || resAttr == null || resAttr.isReadOnly()) {
+ ResourceAttributes resAttr = file.getResourceAttributes();
+ if (resAttr == null || resAttr.isReadOnly()) {
status.addFatalError("The file is read-only, please make it writeable first.");
return false;
}
From 71194de29071f45e3f9a45ad32bcf377aeab2c5a Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Tue, 24 Mar 2009 22:20:57 -0700
Subject: [PATCH 020/127] Automated import from
//branches/donutburger/...@142274,142274
---
.../resources/DeclareStyleableInfo.java | 12 ++
.../descriptors/ElementDescriptor.java | 13 +++
.../layout/descriptors/LayoutDescriptors.java | 110 ++++++++++++++++--
3 files changed, 128 insertions(+), 7 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java
index efa5981b5..7aad7c84a 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/resources/DeclareStyleableInfo.java
@@ -70,6 +70,18 @@ public class DeclareStyleableInfo {
mFormats = formats;
}
+ /**
+ * @param name The XML Name of the attribute
+ * @param formats The formats of the attribute. Cannot be null.
+ * Should have at least one format.
+ * @param javadoc Short javadoc (i.e. the first sentence).
+ */
+ public AttributeInfo(String name, Format[] formats, String javadoc) {
+ mName = name;
+ mFormats = formats;
+ mJavaDoc = javadoc;
+ }
+
public AttributeInfo(AttributeInfo info) {
mName = info.mName;
mFormats = info.mFormats;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/ElementDescriptor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/ElementDescriptor.java
index 555015537..6a9b7db29 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/ElementDescriptor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/ElementDescriptor.java
@@ -24,6 +24,7 @@ import com.android.sdklib.SdkConstants;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.Image;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -221,6 +222,18 @@ public class ElementDescriptor {
mChildren = newChildren;
}
+ /** Sets the list of allowed children.
+ *
+ * This is just a convenience method that converts a Collection into an array and
+ * calls {@link #setChildren(ElementDescriptor[])}.
+ *
+ * This means a copy of the collection is made. The collection is not
+ * stored by the recipient and can thus be altered by the caller.
+ */
+ public void setChildren(Collection newChildren) {
+ setChildren(newChildren.toArray(new ElementDescriptor[newChildren.size()]));
+ }
+
/**
* Returns an optional tooltip. Will be null if not present.
*
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java
index 5726d7899..2c0f984db 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java
@@ -43,7 +43,7 @@ public final class LayoutDescriptors implements IDescriptorProvider {
public static final String ID_ATTR = "id"; //$NON-NLS-1$
/** The document descriptor. Contains all layouts and views linked together. */
- private DocumentDescriptor mDescriptor =
+ private DocumentDescriptor mRootDescriptor =
new DocumentDescriptor("layout_doc", null); //$NON-NLS-1$
/** The list of all known ViewLayout descriptors. */
@@ -60,7 +60,7 @@ public final class LayoutDescriptors implements IDescriptorProvider {
/** @return the document descriptor. Contains all layouts and views linked together. */
public DocumentDescriptor getDescriptor() {
- return mDescriptor;
+ return mRootDescriptor;
}
/** @return The read-only list of all known ViewLayout descriptors. */
@@ -74,7 +74,7 @@ public final class LayoutDescriptors implements IDescriptorProvider {
}
public ElementDescriptor[] getRootElementDescriptors() {
- return mDescriptor.getChildren();
+ return mRootDescriptor.getChildren();
}
/**
@@ -98,6 +98,10 @@ public final class LayoutDescriptors implements IDescriptorProvider {
}
}
+ // Create as a synthetic regular view.
+ // Note: ViewStub is already described by attrs.xml
+ insertInclude(newViews);
+
ArrayList newLayouts = new ArrayList();
if (layouts != null) {
for (ViewClassInfo info : layouts) {
@@ -109,17 +113,22 @@ public final class LayoutDescriptors implements IDescriptorProvider {
ArrayList newDescriptors = new ArrayList();
newDescriptors.addAll(newLayouts);
newDescriptors.addAll(newViews);
- ElementDescriptor[] newArray = newDescriptors.toArray(
- new ElementDescriptor[newDescriptors.size()]);
// Link all layouts to everything else here.. recursively
for (ElementDescriptor layoutDesc : newLayouts) {
- layoutDesc.setChildren(newArray);
+ layoutDesc.setChildren(newDescriptors);
}
+ // The tag can only be a root tag, so it is added at the end.
+ // It gets everything else as children but it is not made a child itself.
+ ElementDescriptor mergeTag = createMerge();
+ mergeTag.setChildren(newDescriptors); // mergeTag makes a copy of the list
+ newDescriptors.add(mergeTag);
+ newLayouts.add(mergeTag);
+
mViewDescriptors = newViews;
mLayoutDescriptors = newLayouts;
- mDescriptor.setChildren(newArray);
+ mRootDescriptor.setChildren(newDescriptors);
mROLayoutDescriptors = Collections.unmodifiableList(mLayoutDescriptors);
mROViewDescriptors = Collections.unmodifiableList(mViewDescriptors);
@@ -217,4 +226,91 @@ public final class LayoutDescriptors implements IDescriptorProvider {
false /* mandatory */);
}
+ /**
+ * Creates a new descriptor and adds it to the list of view descriptors.
+ *
+ * @param newViews A list of view descriptors being populated. Also used to find the
+ * View description and extract its layout attributes.
+ */
+ private void insertInclude(ArrayList newViews) {
+ String xml_name = "include"; //$NON-NLS-1$
+
+ // Create the include custom attributes
+ ArrayList attributes = new ArrayList();
+
+ // Note that the "layout" attribute does NOT have the Android namespace
+ DescriptorsUtils.appendAttribute(attributes,
+ null, //elementXmlName
+ null, //nsUri
+ new AttributeInfo(
+ "layout", //$NON-NLS-1$
+ new AttributeInfo.Format[] { AttributeInfo.Format.REFERENCE }
+ ),
+ true, //required
+ null); //overrides
+
+ DescriptorsUtils.appendAttribute(attributes,
+ null, //elementXmlName
+ SdkConstants.NS_RESOURCES, //nsUri
+ new AttributeInfo(
+ "id", //$NON-NLS-1$
+ new AttributeInfo.Format[] { AttributeInfo.Format.REFERENCE }
+ ),
+ true, //required
+ null); //overrides
+
+ // Find View and inherit all its layout attributes
+ AttributeDescriptor[] viewLayoutAttribs = findViewLayoutAttributes(newViews);
+
+ // Create the include descriptor
+ ViewElementDescriptor desc = new ViewElementDescriptor(xml_name, // xml_name
+ xml_name, // ui_name
+ null, // canonical class name, we don't have one
+ "Lets you statically include XML layouts inside other XML layouts.", // tooltip
+ null, // sdk_url
+ attributes.toArray(new AttributeDescriptor[attributes.size()]),
+ viewLayoutAttribs, // layout attributes
+ null, // children
+ false /* mandatory */);
+
+ newViews.add(desc);
+ }
+
+ /**
+ * Finds the View descriptor and retrieves all its layout attributes.
+ */
+ private AttributeDescriptor[] findViewLayoutAttributes(
+ ArrayList newViews) {
+
+ for (ElementDescriptor desc : newViews) {
+ if (desc instanceof ViewElementDescriptor) {
+ ViewElementDescriptor viewDesc = (ViewElementDescriptor) desc;
+ if (AndroidConstants.CLASS_VIEW.equals(viewDesc.getCanonicalClassName())) {
+ return viewDesc.getLayoutAttributes();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates and return a new descriptor.
+ */
+ private ElementDescriptor createMerge() {
+ String xml_name = "merge"; //$NON-NLS-1$
+
+ // Create the include descriptor
+ ViewElementDescriptor desc = new ViewElementDescriptor(xml_name, // xml_name
+ xml_name, // ui_name
+ null, // canonical class name, we don't have one
+ "A root tag useful for XML layouts inflated using a ViewStub.", // tooltip
+ null, // sdk_url
+ null, // attributes
+ null, // layout attributes
+ null, // children
+ false /* mandatory */);
+
+ return desc;
+ }
}
From 0bd19e8b8b709d5ce440139e6f49d2307ead09db Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Tue, 24 Mar 2009 22:21:57 -0700
Subject: [PATCH 021/127] Automated import from
//branches/donutburger/...@142276,142276
---
.../com.android.ide.eclipse.adt/plugin.xml | 2 +-
.../extractstring/ExtractStringAction.java | 4 +-
.../extractstring/ExtractStringInputPage.java | 392 ++++++++++++++++--
.../ExtractStringRefactoring.java | 7 +-
.../extractstring/ExtractStringWizard.java | 14 +-
.../wizards/NewXmlFileCreationPage.java | 26 +-
6 files changed, 375 insertions(+), 70 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index 81bb60634..2c1394cd2 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -501,7 +501,7 @@
class="com.android.ide.eclipse.adt.refactorings.extractstring.ExtractStringAction"
definitionId="com.android.ide.eclipse.adt.refactoring.extract.string"
id="com.android.ide.eclipse.adt.actions.ExtractString"
- label="Extract Android String"
+ label="Extract Android String..."
menubarPath="org.eclipse.jdt.ui.refactoring.menu/codingGroup"
style="push"
tooltip="Extracts a string into Android resource string">
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java
index dceb1441d..4ef1268de 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringAction.java
@@ -22,9 +22,7 @@ import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
-import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaCore;
-import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
@@ -122,7 +120,7 @@ public class ExtractStringAction implements IWorkbenchWindowActionDelegate {
public void run(IAction action) {
if (mSelection != null && mFile != null) {
ExtractStringRefactoring ref = new ExtractStringRefactoring(mFile, mSelection);
- RefactoringWizard wizard = new ExtractStringWizard(ref, "Extract Android String");
+ RefactoringWizard wizard = new ExtractStringWizard(ref, mFile.getProject());
RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
try {
op.run(mWindow.getShell(), wizard.getDefaultPageTitle());
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
index cb449f02e..5ffeeb05f 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
@@ -16,29 +16,74 @@
package com.android.ide.eclipse.adt.refactorings.extractstring;
+
+import com.android.ide.eclipse.common.AndroidConstants;
+import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
+import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
+import com.android.ide.eclipse.editors.wizards.ConfigurationSelector;
+import com.android.sdklib.SdkConstants;
+
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.ltk.ui.refactoring.UserInputWizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
+import java.util.HashMap;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
/**
* @see ExtractStringRefactoring
*/
class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage {
- public ExtractStringInputPage() {
- super("ExtractStringInputPage"); //$NON-NLS-1$
- }
+ /** Last res file path used, shared across the session instances but specific to the
+ * current project. The default for unknown projects is {@link #DEFAULT_RES_FILE_PATH}. */
+ private static HashMap sLastResFilePath = new HashMap();
+ /** The project where the user selection happened. */
+ private final IProject mProject;
+
+ /** Field displaying the user-selected string to be replaced. */
private Label mStringLabel;
+ /** Test field where the user enters the new ID to be generated or replaced with. */
private Text mNewIdTextField;
- private Label mFileLabel;
+ /** The configuration selector, to select the resource path of the XML file. */
+ private ConfigurationSelector mConfigSelector;
+ /** The combo to display the existing XML files or enter a new one. */
+ private Combo mResFileCombo;
+
+ /** Regex pattern to read a valid res XML file path. It checks that the are 2 folders and
+ * a leaf file name ending with .xml */
+ private static final Pattern RES_XML_FILE_REGEX = Pattern.compile(
+ "/res/[a-z][a-zA-Z0-9_-]+/[^.]+\\.xml"); //$NON-NLS-1$
+ /** Absolute destination folder root, e.g. "/res/" */
+ private static final String RES_FOLDER_ABS =
+ AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP;
+ /** Relative destination folder root, e.g. "res/" */
+ private static final String RES_FOLDER_REL =
+ SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
+
+ private static final String DEFAULT_RES_FILE_PATH = "/res/values/strings.xml";
+
+ public ExtractStringInputPage(IProject project) {
+ super("ExtractStringInputPage"); //$NON-NLS-1$
+ mProject = project;
+ }
/**
* Create the UI for the refactoring wizard.
@@ -48,34 +93,57 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
*/
public void createControl(Composite parent) {
- final ExtractStringRefactoring ref = getOurRefactoring();
-
Composite content = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout();
- layout.numColumns = 2;
+ layout.numColumns = 1;
content.setLayout(layout);
-
- // line 1: String found in selection
- Label label = new Label(content, SWT.NONE);
+ createStringReplacementGroup(content);
+ createResFileGroup(content);
+
+ validatePage();
+ setControl(content);
+ }
+
+ /**
+ * Creates the top group with the field to replace which string and by what
+ * and by which options.
+ *
+ * @param content A composite with a 1-column grid layout
+ */
+ private void createStringReplacementGroup(Composite content) {
+
+ final ExtractStringRefactoring ref = getOurRefactoring();
+
+ Group group = new Group(content, SWT.NONE);
+ group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ group.setText("String Replacement");
+
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ group.setLayout(layout);
+
+ // line: String found in selection
+
+ Label label = new Label(group, SWT.NONE);
label.setText("String:");
String selectedString = ref.getTokenString();
- mStringLabel = new Label(content, SWT.NONE);
+ mStringLabel = new Label(group, SWT.NONE);
mStringLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mStringLabel.setText(selectedString != null ? selectedString : "");
// TODO provide an option to replace all occurences of this string instead of
// just the one.
- // line 2 : Textfield for new ID
+ // line : Textfield for new ID
- label = new Label(content, SWT.NONE);
+ label = new Label(group, SWT.NONE);
label.setText("Replace by R.string.");
- mNewIdTextField = new Text(content, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ mNewIdTextField = new Text(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
mNewIdTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mNewIdTextField.setText(guessId(selectedString));
@@ -83,37 +151,65 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
mNewIdTextField.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
- if (validatePage(ref)) {
+ if (validatePage()) {
ref.setReplacementStringId(mNewIdTextField.getText().trim());
}
}
});
-
- // line 3: selection of the output file
- // TODO add a file field/chooser combo to let the user select the file to edit.
-
- label = new Label(content, SWT.NONE);
- label.setText("Resource file:");
-
- mFileLabel = new Label(content, SWT.NONE);
- mFileLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- mFileLabel.setText("/res/values/strings.xml");
- ref.setTargetFile(mFileLabel.getText());
-
- // line 4: selection of the res config
- // TODO add the Configuration Selector to decide with strings.xml to change
-
- label = new Label(content, SWT.NONE);
- label.setText("Configuration:");
-
- label = new Label(content, SWT.NONE);
- label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- label.setText("default");
-
- validatePage(ref);
- setControl(content);
}
+ /**
+ * Creates the lower group with the fields to choose the resource confirmation and
+ * the target XML file.
+ *
+ * @param content A composite with a 1-column grid layout
+ */
+ private void createResFileGroup(Composite content) {
+
+ Group group = new Group(content, SWT.NONE);
+ group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ group.setText("XML resource to edit");
+
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ group.setLayout(layout);
+
+ // line: selection of the res config
+
+ Label label;
+ label = new Label(group, SWT.NONE);
+ label.setText("Configuration:");
+
+ mConfigSelector = new ConfigurationSelector(group);
+ GridData gd = new GridData(2, GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
+ gd.widthHint = ConfigurationSelector.WIDTH_HINT;
+ gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
+ mConfigSelector.setLayoutData(gd);
+ OnConfigSelectorUpdated onConfigSelectorUpdated = new OnConfigSelectorUpdated();
+ mConfigSelector.setOnChangeListener(onConfigSelectorUpdated);
+
+ // line: selection of the output file
+
+ label = new Label(group, SWT.NONE);
+ label.setText("Resource file:");
+
+ mResFileCombo = new Combo(group, SWT.DROP_DOWN);
+ mResFileCombo.select(0);
+ mResFileCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mResFileCombo.addModifyListener(onConfigSelectorUpdated);
+
+ // set output file name to the last one used
+
+ String projPath = mProject.getFullPath().toPortableString();
+ String filePath = sLastResFilePath.get(projPath);
+
+ mResFileCombo.setText(filePath != null ? filePath : DEFAULT_RES_FILE_PATH);
+ onConfigSelectorUpdated.run();
+ }
+
+ /**
+ * Utility method to guess a suitable new XML ID based on the selected string.
+ */
private String guessId(String text) {
// make lower case
text = text.toLowerCase();
@@ -128,16 +224,25 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
return text;
}
+ /**
+ * Returns the {@link ExtractStringRefactoring} instance used by this wizard page.
+ */
private ExtractStringRefactoring getOurRefactoring() {
return (ExtractStringRefactoring) getRefactoring();
}
- private boolean validatePage(ExtractStringRefactoring ref) {
- String text = mNewIdTextField.getText().trim();
+ /**
+ * Validates fields of the wizard input page. Displays errors as appropriate and
+ * enable the "Next" button (or not) by calling {@link #setPageComplete(boolean)}.
+ *
+ * @return True if the page has been positively validated. It may still have warnings.
+ */
+ private boolean validatePage() {
boolean success = true;
// Analyze fatal errors.
+ String text = mNewIdTextField.getText().trim();
if (text == null || text.length() < 1) {
setErrorMessage("Please provide a resource ID to replace with.");
success = false;
@@ -157,21 +262,216 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
}
}
+ String resFile = mResFileCombo.getText();
+ if (success) {
+ if (resFile == null || resFile.length() == 0) {
+ setErrorMessage("A resource file name is required.");
+ success = false;
+ } else if (!RES_XML_FILE_REGEX.matcher(resFile).matches()) {
+ setErrorMessage("The XML file name is not valid.");
+ success = false;
+ }
+ }
+
// Analyze info & warnings.
if (success) {
- if (ref.isResIdDuplicate(mFileLabel.getText(), text)) {
- setErrorMessage(null);
+ setErrorMessage(null);
+
+ ExtractStringRefactoring ref = getOurRefactoring();
+
+ ref.setTargetFile(resFile);
+ sLastResFilePath.put(mProject.getFullPath().toPortableString(), resFile);
+
+ if (ref.isResIdDuplicate(resFile, text)) {
setMessage(
- String.format("Warning: There's already a string item called '%1$s' in %2$s.",
- text, mFileLabel.getText()));
+ String.format("There's already a string item called '%1$s' in %2$s.",
+ text, resFile),
+ WizardPage.WARNING);
+ } else if (mProject.findMember(resFile) == null) {
+ setMessage(
+ String.format("File %2$s does not exist and will be created.",
+ text, resFile),
+ WizardPage.INFORMATION);
} else {
setMessage(null);
- setErrorMessage(null);
}
}
setPageComplete(success);
return success;
}
+
+ public class OnConfigSelectorUpdated implements Runnable, ModifyListener {
+
+ /** Regex pattern to parse a valid res path: it reads (/res/folder-name/)+(filename). */
+ private final Pattern mPathRegex = Pattern.compile(
+ "(/res/[a-z][a-zA-Z0-9_-]+/)(.+)"); //$NON-NLS-1$
+
+ /** Temporary config object used to retrieve the Config Selector value. */
+ private FolderConfiguration mTempConfig = new FolderConfiguration();
+
+ private HashMap> mFolderCache =
+ new HashMap>();
+ private String mLastFolderUsedInCombo = null;
+ private boolean mInternalConfigChange;
+ private boolean mInternalFileComboChange;
+
+ /**
+ * Callback invoked when the {@link ConfigurationSelector} has been changed.
+ *
+ * The callback does the following:
+ *
+ *
Examine the current file name to retrieve the XML filename, if any.
+ *
Recompute the path based on the configuration selector (e.g. /res/values-fr/).
+ *
Examine the path to retrieve all the files in it. Keep those in a local cache.
+ *
If the XML filename from step 1 is not in the file list, it's a custom file name.
+ * Insert it and sort it.
+ *
Re-populate the file combo with all the choices.
+ *
Select the original XML file.
+ */
+ public void run() {
+ if (mInternalConfigChange) {
+ return;
+ }
+
+ // get current leafname, if any
+ String leafName = "";
+ String currPath = mResFileCombo.getText();
+ Matcher m = mPathRegex.matcher(currPath);
+ if (m.matches()) {
+ // Note: groups 1 and 2 cannot be null.
+ leafName = m.group(2);
+ currPath = m.group(1);
+ } else {
+ // There was a path but it was invalid. Ignore it.
+ currPath = "";
+ }
+
+ // recreate the res path from the current configuration
+ mConfigSelector.getConfiguration(mTempConfig);
+ StringBuffer sb = new StringBuffer(RES_FOLDER_ABS);
+ sb.append(mTempConfig.getFolderName(ResourceFolderType.VALUES));
+ sb.append('/');
+
+ String newPath = sb.toString();
+ if (newPath.equals(currPath) && newPath.equals(mLastFolderUsedInCombo)) {
+ // Path has not changed. No need to reload.
+ return;
+ }
+
+ // Get all the files at the new path
+
+ TreeSet filePaths = mFolderCache.get(newPath);
+
+ if (filePaths == null) {
+ filePaths = new TreeSet();
+
+ IFolder folder = mProject.getFolder(newPath);
+ if (folder != null && folder.exists()) {
+ try {
+ for (IResource res : folder.members()) {
+ String name = res.getName();
+ if (res.getType() == IResource.FILE && name.endsWith(".xml")) {
+ filePaths.add(newPath + name);
+ }
+ }
+ } catch (CoreException e) {
+ // Ignore.
+ }
+ }
+
+ mFolderCache.put(newPath, filePaths);
+ }
+
+ currPath = newPath + leafName;
+ if (leafName.length() > 0 && !filePaths.contains(currPath)) {
+ filePaths.add(currPath);
+ }
+
+ // Fill the combo
+ try {
+ mInternalFileComboChange = true;
+
+ mResFileCombo.removeAll();
+
+ for (String filePath : filePaths) {
+ mResFileCombo.add(filePath);
+ }
+
+ int index = -1;
+ if (leafName.length() > 0) {
+ index = mResFileCombo.indexOf(currPath);
+ if (index >= 0) {
+ mResFileCombo.select(index);
+ }
+ }
+
+ if (index == -1) {
+ mResFileCombo.setText(currPath);
+ }
+
+ mLastFolderUsedInCombo = newPath;
+
+ } finally {
+ mInternalFileComboChange = false;
+ }
+
+ // finally validate the whole page
+ validatePage();
+ }
+
+ /**
+ * Callback invoked when {@link ExtractStringInputPage#mResFileCombo} has been
+ * modified.
+ */
+ public void modifyText(ModifyEvent e) {
+ if (mInternalFileComboChange) {
+ return;
+ }
+
+ String wsFolderPath = mResFileCombo.getText();
+
+ // This is a custom path, we need to sanitize it.
+ // First it should start with "/res/". Then we need to make sure there are no
+ // relative paths, things like "../" or "./" or even "//".
+ wsFolderPath = wsFolderPath.replaceAll("/+\\.\\./+|/+\\./+|//+|\\\\+|^/+", "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ wsFolderPath = wsFolderPath.replaceAll("^\\.\\./+|^\\./+", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ wsFolderPath = wsFolderPath.replaceAll("/+\\.\\.$|/+\\.$|/+$", ""); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // We get "res/foo" from selections relative to the project when we want a "/res/foo" path.
+ if (wsFolderPath.startsWith(RES_FOLDER_REL)) {
+ wsFolderPath = RES_FOLDER_ABS + wsFolderPath.substring(RES_FOLDER_REL.length());
+
+ mInternalFileComboChange = true;
+ mResFileCombo.setText(wsFolderPath);
+ mInternalFileComboChange = false;
+ }
+
+ if (wsFolderPath.startsWith(RES_FOLDER_ABS)) {
+ wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length());
+
+ int pos = wsFolderPath.indexOf(AndroidConstants.WS_SEP_CHAR);
+ if (pos >= 0) {
+ wsFolderPath = wsFolderPath.substring(0, pos);
+ }
+
+ String[] folderSegments = wsFolderPath.split(FolderConfiguration.QUALIFIER_SEP);
+
+ if (folderSegments.length > 0) {
+ String folderName = folderSegments[0];
+
+ if (folderName != null && !folderName.equals(wsFolderPath)) {
+ // update config selector
+ mInternalConfigChange = true;
+ mConfigSelector.setConfiguration(folderSegments);
+ mInternalConfigChange = false;
+ }
+ }
+ }
+
+ validatePage();
+ }
+ }
+
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
index 99711bf20..6c88a38c1 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
@@ -177,7 +177,7 @@ class ExtractStringRefactoring extends Refactoring {
public ExtractStringRefactoring(IFile file, ITextSelection selection) {
mFile = file;
mSelectionStart = selection.getOffset();
- mSelectionEnd = mSelectionStart + selection.getLength();
+ mSelectionEnd = mSelectionStart + Math.max(0, selection.getLength() - 1);
}
/**
@@ -906,9 +906,8 @@ class ExtractStringRefactoring extends Refactoring {
* compilation unit. The resource may not exist.
*/
private IResource getTargetXmlResource(String xmlFileWsPath) {
- IProject proj = mUnit.getPrimary().getResource().getProject();
- Path path = new Path(xmlFileWsPath);
- IResource resource = proj.findMember(path);
+ IProject proj = mFile.getProject();
+ IResource resource = proj.getFile(xmlFileWsPath);
return resource;
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java
index 2083a6e5d..c5b0c7d11 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.adt.refactorings.extractstring;
+import org.eclipse.core.resources.IProject;
import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
/**
@@ -26,17 +27,24 @@ import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
*/
class ExtractStringWizard extends RefactoringWizard {
+ private final IProject mProject;
+
/**
* Create a wizard for ExtractString based on a simple dialog with one page.
+ *
+ * @param ref The instance of {@link ExtractStringRefactoring} to associate to the wizard.
+ * @param project The project where the wizard was invoked from (e.g. where the user selection
+ * happened, so that we can retrieve project resources.)
*/
- public ExtractStringWizard(ExtractStringRefactoring ref, String title) {
+ public ExtractStringWizard(ExtractStringRefactoring ref, IProject project) {
super(ref, DIALOG_BASED_USER_INTERFACE | PREVIEW_EXPAND_FIRST_NODE);
- setDefaultPageTitle(title);
+ mProject = project;
+ setDefaultPageTitle(ref.getName());
}
@Override
protected void addUserInputPages() {
- addPage(new ExtractStringInputPage());
+ addPage(new ExtractStringInputPage(mProject));
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
index e84c05123..80cc0f5aa 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
@@ -271,9 +271,9 @@ class NewXmlFileCreationPage extends WizardPage {
final static int NUM_COL = 4;
/** Absolute destination folder root, e.g. "/res/" */
- private static String sResFolderAbs = AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP;
+ private static final String RES_FOLDER_ABS = AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP;
/** Relative destination folder root, e.g. "res/" */
- private static String sResFolderRel = SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
+ private static final String RES_FOLDER_REL = SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
private IProject mProject;
private Text mProjectTextField;
@@ -856,16 +856,16 @@ class NewXmlFileCreationPage extends WizardPage {
ArrayList matches = new ArrayList();
// We get "res/foo" from selections relative to the project when we want a "/res/foo" path.
- if (wsFolderPath.startsWith(sResFolderRel)) {
- wsFolderPath = sResFolderAbs + wsFolderPath.substring(sResFolderRel.length());
+ if (wsFolderPath.startsWith(RES_FOLDER_REL)) {
+ wsFolderPath = RES_FOLDER_ABS + wsFolderPath.substring(RES_FOLDER_REL.length());
mInternalWsFolderPathUpdate = true;
mWsFolderPathTextField.setText(wsFolderPath);
mInternalWsFolderPathUpdate = false;
}
- if (wsFolderPath.startsWith(sResFolderAbs)) {
- wsFolderPath = wsFolderPath.substring(sResFolderAbs.length());
+ if (wsFolderPath.startsWith(RES_FOLDER_ABS)) {
+ wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length());
int pos = wsFolderPath.indexOf(AndroidConstants.WS_SEP_CHAR);
if (pos >= 0) {
@@ -952,16 +952,16 @@ class NewXmlFileCreationPage extends WizardPage {
// The configuration is valid. Reformat the folder path using the canonical
// value from the configuration.
- newPath = sResFolderAbs + mTempConfig.getFolderName(type.getResFolderType());
+ newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType());
} else {
// The configuration is invalid. We still update the path but this time
// do it manually on the string.
- if (wsFolderPath.startsWith(sResFolderAbs)) {
+ if (wsFolderPath.startsWith(RES_FOLDER_ABS)) {
wsFolderPath.replaceFirst(
- "^(" + sResFolderAbs +")[^-]*(.*)", //$NON-NLS-1$ //$NON-NLS-2$
+ "^(" + RES_FOLDER_ABS +")[^-]*(.*)", //$NON-NLS-1$ //$NON-NLS-2$
"\\1" + type.getResFolderName() + "\\2"); //$NON-NLS-1$ //$NON-NLS-2$
} else {
- newPath = sResFolderAbs + mTempConfig.getFolderName(type.getResFolderType());
+ newPath = RES_FOLDER_ABS + mTempConfig.getFolderName(type.getResFolderType());
}
}
@@ -1018,7 +1018,7 @@ class NewXmlFileCreationPage extends WizardPage {
if (type != null) {
mConfigSelector.getConfiguration(mTempConfig);
- StringBuffer sb = new StringBuffer(sResFolderAbs);
+ StringBuffer sb = new StringBuffer(RES_FOLDER_ABS);
sb.append(mTempConfig.getFolderName(type.getResFolderType()));
mInternalWsFolderPathUpdate = true;
@@ -1131,8 +1131,8 @@ class NewXmlFileCreationPage extends WizardPage {
// -- validate generated path
if (error == null) {
String wsFolderPath = getWsFolderPath();
- if (!wsFolderPath.startsWith(sResFolderAbs)) {
- error = String.format("Target folder must start with %1$s.", sResFolderAbs);
+ if (!wsFolderPath.startsWith(RES_FOLDER_ABS)) {
+ error = String.format("Target folder must start with %1$s.", RES_FOLDER_ABS);
}
}
From 02e8d006b0d2bee89023b522db113765fdf3713c Mon Sep 17 00:00:00 2001
From: Romain Guy <>
Date: Tue, 24 Mar 2009 22:45:50 -0700
Subject: [PATCH 022/127] Automated import from
//branches/donutburger/...@142465,142465
---
samples/ApiDemos/AndroidManifest.xml | 7 ++
samples/ApiDemos/res/layout/animation_3.xml | 44 ++++++++
samples/ApiDemos/res/values/strings.xml | 1 +
.../example/android/apis/view/Animation3.java | 101 ++++++++++++++++++
4 files changed, 153 insertions(+)
create mode 100644 samples/ApiDemos/res/layout/animation_3.xml
create mode 100644 samples/ApiDemos/src/com/example/android/apis/view/Animation3.java
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index 0cbba1449..90a5c5de9 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -1106,6 +1106,13 @@
+
+
+
+
+
+
+
diff --git a/samples/ApiDemos/res/layout/animation_3.xml b/samples/ApiDemos/res/layout/animation_3.xml
new file mode 100644
index 000000000..4d1474c3b
--- /dev/null
+++ b/samples/ApiDemos/res/layout/animation_3.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml
index 0cafe1428..29bbcec69 100644
--- a/samples/ApiDemos/res/values/strings.xml
+++ b/samples/ApiDemos/res/values/strings.xml
@@ -476,6 +476,7 @@
a chance to be better.— Albert CamusSelect an animation:
+ InterpolatorsType in the text field for auto-completion.Country:Give me Focus
diff --git a/samples/ApiDemos/src/com/example/android/apis/view/Animation3.java b/samples/ApiDemos/src/com/example/android/apis/view/Animation3.java
new file mode 100644
index 000000000..11fc9ed41
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/view/Animation3.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.view;
+
+// Need the following import to get access to the app resources, since this
+// class is in a sub-package.
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Animation;
+import android.view.animation.TranslateAnimation;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+
+public class Animation3 extends Activity implements AdapterView.OnItemSelectedListener {
+ private static final String[] INTERPOLATORS = {
+ "Accelerate", "Decelerate", "Accelerate/Decelerate",
+ "Anticipate", "Overshoot", "Anticipate/Overshoot",
+ "Bounce"
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.animation_3);
+
+ Spinner s = (Spinner) findViewById(R.id.spinner);
+ ArrayAdapter adapter = new ArrayAdapter(this,
+ android.R.layout.simple_spinner_item, INTERPOLATORS);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ s.setAdapter(adapter);
+ s.setOnItemSelectedListener(this);
+ }
+
+ public void onItemSelected(AdapterView parent, View v, int position, long id) {
+ final View target = findViewById(R.id.target);
+ final View targetParent = (View) target.getParent();
+
+ Animation a = new TranslateAnimation(0.0f,
+ targetParent.getWidth() - target.getWidth() - targetParent.getPaddingLeft() -
+ targetParent.getPaddingRight(), 0.0f, 0.0f);
+ a.setDuration(1000);
+ a.setStartOffset(300);
+ a.setRepeatMode(Animation.RESTART);
+ a.setRepeatCount(Animation.INFINITE);
+
+ switch (position) {
+ case 0:
+ a.setInterpolator(AnimationUtils.loadInterpolator(this,
+ android.R.anim.accelerate_interpolator));
+ break;
+ case 1:
+ a.setInterpolator(AnimationUtils.loadInterpolator(this,
+ android.R.anim.decelerate_interpolator));
+ break;
+ case 2:
+ a.setInterpolator(AnimationUtils.loadInterpolator(this,
+ android.R.anim.accelerate_decelerate_interpolator));
+ break;
+ case 3:
+ a.setInterpolator(AnimationUtils.loadInterpolator(this,
+ android.R.anim.anticipate_interpolator));
+ break;
+ case 4:
+ a.setInterpolator(AnimationUtils.loadInterpolator(this,
+ android.R.anim.overshoot_interpolator));
+ break;
+ case 5:
+ a.setInterpolator(AnimationUtils.loadInterpolator(this,
+ android.R.anim.anticipate_overshoot_interpolator));
+ break;
+ case 6:
+ a.setInterpolator(AnimationUtils.loadInterpolator(this,
+ android.R.anim.bounce_interpolator));
+ break;
+ }
+
+ target.startAnimation(a);
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ }
+}
\ No newline at end of file
From 46a1d902174efe99c6b4048d6dfa78ef7916f937 Mon Sep 17 00:00:00 2001
From: Jack Palevich <>
Date: Tue, 24 Mar 2009 22:49:05 -0700
Subject: [PATCH 023/127] Automated import from
//branches/donutburger/...@142486,142486
---
.../graphics/TranslucentGLSurfaceViewActivity.java | 4 ++++
.../android/apis/graphics/TriangleActivity.java | 1 +
.../android/apis/graphics/TriangleRenderer.java | 10 ----------
.../android/apis/graphics/kube/KubeRenderer.java | 9 ---------
.../com/example/android/compass/CompassActivity.java | 10 ----------
5 files changed, 5 insertions(+), 29 deletions(-)
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/TranslucentGLSurfaceViewActivity.java b/samples/ApiDemos/src/com/example/android/apis/graphics/TranslucentGLSurfaceViewActivity.java
index 750a47ba7..a0bad4bc9 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/TranslucentGLSurfaceViewActivity.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/TranslucentGLSurfaceViewActivity.java
@@ -34,6 +34,10 @@ public class TranslucentGLSurfaceViewActivity extends Activity {
// Create our Preview view and set it as the content of our
// Activity
mGLSurfaceView = new GLSurfaceView(this);
+ // We want an 8888 pixel format because that's required for
+ // a translucent window.
+ // And we want a depth buffer.
+ mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
// Tell the cube renderer that we want to render a translucent version
// of the cube:
mGLSurfaceView.setRenderer(new CubeRenderer(true));
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java b/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java
index 59f3c6cf8..e5b06f426 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleActivity.java
@@ -26,6 +26,7 @@ public class TriangleActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(this);
+ mGLView.setEGLConfigChooser(false);
mGLView.setRenderer(new TriangleRenderer(this));
setContentView(mGLView);
}
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleRenderer.java b/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleRenderer.java
index 451b927a9..e5299b332 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleRenderer.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/TriangleRenderer.java
@@ -44,16 +44,6 @@ public class TriangleRenderer implements GLSurfaceView.Renderer{
mTriangle = new Triangle();
}
- public int[] getConfigSpec() {
- // We don't need a depth buffer, and don't care about our
- // color depth.
- int[] configSpec = {
- EGL10.EGL_DEPTH_SIZE, 0,
- EGL10.EGL_NONE
- };
- return configSpec;
- }
-
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
/*
* By default, OpenGL enables features that improve quality
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/kube/KubeRenderer.java b/samples/ApiDemos/src/com/example/android/apis/graphics/kube/KubeRenderer.java
index 252f566f9..9977041a2 100644
--- a/samples/ApiDemos/src/com/example/android/apis/graphics/kube/KubeRenderer.java
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/kube/KubeRenderer.java
@@ -74,15 +74,6 @@ class KubeRenderer implements GLSurfaceView.Renderer {
mWorld.draw(gl);
}
- public int[] getConfigSpec() {
- // Need a depth buffer, don't care about color depth.
- int[] configSpec = {
- EGL10.EGL_DEPTH_SIZE, 16,
- EGL10.EGL_NONE
- };
- return configSpec;
- }
-
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
diff --git a/samples/Compass/src/com/example/android/compass/CompassActivity.java b/samples/Compass/src/com/example/android/compass/CompassActivity.java
index c4b956675..74f3bc71d 100644
--- a/samples/Compass/src/com/example/android/compass/CompassActivity.java
+++ b/samples/Compass/src/com/example/android/compass/CompassActivity.java
@@ -89,16 +89,6 @@ public class CompassActivity extends Activity implements Renderer, SensorEventLi
mSensorManager.unregisterListener(this);
}
- public int[] getConfigSpec() {
- // We want a depth buffer, don't care about the
- // details of the color buffer.
- int[] configSpec = {
- EGL10.EGL_DEPTH_SIZE, 16,
- EGL10.EGL_NONE
- };
- return configSpec;
- }
-
public void onDrawFrame(GL10 gl) {
/*
* Usually, the first thing one might want to do is to clear
From 26aa2cdeb0a87c61b8bcf22e4f14c7db984fc2b5 Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Wed, 25 Mar 2009 16:23:00 -0700
Subject: [PATCH 024/127] Automated import from
//branches/donutburger/...@142707,142707
---
.../common/project/AndroidManifestParser.java | 56 ++++++++++++++-----
.../editors/uimodel/UiElementNode.java | 18 +++---
2 files changed, 50 insertions(+), 24 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
index fa7e9b9f4..85ba96839 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
@@ -34,6 +34,7 @@ import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
@@ -585,19 +586,31 @@ public class AndroidManifestParser {
// get the result from the handler
return new AndroidManifestParser(manifestHandler.getPackage(),
- manifestHandler.getActivities(), manifestHandler.getLauncherActivity(),
- manifestHandler.getProcesses(), manifestHandler.getDebuggable(),
- manifestHandler.getApiLevelRequirement(), manifestHandler.getInstrumentations(),
+ manifestHandler.getActivities(),
+ manifestHandler.getLauncherActivity(),
+ manifestHandler.getProcesses(),
+ manifestHandler.getDebuggable(),
+ manifestHandler.getApiLevelRequirement(),
+ manifestHandler.getInstrumentations(),
manifestHandler.getUsesLibraries());
} catch (ParserConfigurationException e) {
AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
- "Bad parser configuration for %s", manifestFile.getFullPath());
+ "Bad parser configuration for %s: %s",
+ manifestFile.getFullPath(),
+ e.getMessage());
} catch (SAXException e) {
AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
- "Parser exception for %s", manifestFile.getFullPath());
+ "Parser exception for %s: %s",
+ manifestFile.getFullPath(),
+ e.getMessage());
} catch (IOException e) {
- AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
- "I/O error for %s", manifestFile.getFullPath());
+ // Don't log a console error when failing to read a non-existing file
+ if (!(e instanceof FileNotFoundException)) {
+ AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
+ "I/O error for %s: %s",
+ manifestFile.getFullPath(),
+ e.getMessage());
+ }
}
return null;
@@ -633,20 +646,33 @@ public class AndroidManifestParser {
// get the result from the handler
return new AndroidManifestParser(manifestHandler.getPackage(),
- manifestHandler.getActivities(), manifestHandler.getLauncherActivity(),
- manifestHandler.getProcesses(), manifestHandler.getDebuggable(),
- manifestHandler.getApiLevelRequirement(), manifestHandler.getInstrumentations(),
+ manifestHandler.getActivities(),
+ manifestHandler.getLauncherActivity(),
+ manifestHandler.getProcesses(),
+ manifestHandler.getDebuggable(),
+ manifestHandler.getApiLevelRequirement(),
+ manifestHandler.getInstrumentations(),
manifestHandler.getUsesLibraries());
} catch (ParserConfigurationException e) {
AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
- "Bad parser configuration for %s", manifestFile.getAbsolutePath());
+ "Bad parser configuration for %s: %s",
+ manifestFile.getAbsolutePath(),
+ e.getMessage());
} catch (SAXException e) {
AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
- "Parser exception for %s", manifestFile.getAbsolutePath());
+ "Parser exception for %s: %s",
+ manifestFile.getAbsolutePath(),
+ e.getMessage());
} catch (IOException e) {
- AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
- "I/O error for %s", manifestFile.getAbsolutePath());
- }
+ // Don't log a console error when failing to read a non-existing file
+ if (!(e instanceof FileNotFoundException)) {
+ AdtPlugin.logAndPrintError(e, AndroidManifestParser.class.getCanonicalName(),
+ "I/O error for %s: %s",
+ manifestFile.getAbsolutePath(),
+ e.getMessage());
+ }
+ }
+
return null;
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiElementNode.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiElementNode.java
index 3728886b5..517284c83 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiElementNode.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiElementNode.java
@@ -1233,8 +1233,9 @@ public class UiElementNode implements IPropertySource {
Node attr = attrs.item(n);
if ("xmlns".equals(attr.getPrefix())) { //$NON-NLS-1$
String uri = attr.getNodeValue();
- String nsPrefix = attr.getLocalName();
- if (SdkConstants.NS_RESOURCES.equals(uri)) {
+ String nsPrefix = attr.getLocalName();
+ // Is this the URI we are looking for? If yes, we found its prefix.
+ if (nsUri.equals(uri)) {
return nsPrefix;
}
visited.add(nsPrefix);
@@ -1244,7 +1245,8 @@ public class UiElementNode implements IPropertySource {
// Use a sensible default prefix if we can't find one.
// We need to make sure the prefix is not one that was declared in the scope
- // visited above.
+ // visited above. Use a default namespace prefix "android" for the Android resource
+ // NS and use "ns" for all other custom namespaces.
String prefix = SdkConstants.NS_RESOURCES.equals(nsUri) ? "android" : "ns"; //$NON-NLS-1$ //$NON-NLS-2$
String base = prefix;
for (int i = 1; visited.contains(prefix); i++) {
@@ -1475,18 +1477,18 @@ public class UiElementNode implements IPropertySource {
}
}
}
-
+
if (attribute != null) {
- final UiAttributeNode fAttribute = attribute;
// get the current value and compare it to the new value
- String oldValue = fAttribute.getCurrentValue();
+ String oldValue = attribute.getCurrentValue();
final String newValue = (String)value;
if (oldValue.equals(newValue)) {
return;
}
-
+
+ final UiAttributeNode fAttribute = attribute;
AndroidEditor editor = getEditor();
editor.editXmlModel(new Runnable() {
public void run() {
@@ -1495,6 +1497,4 @@ public class UiElementNode implements IPropertySource {
});
}
}
-
-
}
From 562031f8064a714e51628189a02d0c6afbac62d7 Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Wed, 25 Mar 2009 16:26:44 -0700
Subject: [PATCH 025/127] Automated import from
//branches/donutburger/...@142708,142708
---
.../ExtractStringRefactoring.java | 162 +++++++++++-------
1 file changed, 99 insertions(+), 63 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
index 6c88a38c1..430ff1819 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
@@ -57,6 +57,7 @@ import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.ltk.core.refactoring.TextEditChangeGroup;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MultiTextEdit;
@@ -413,28 +414,23 @@ class ExtractStringRefactoring extends Refactoring {
// Prepare the change for the XML file.
-
+
if (!isResIdDuplicate(mTargetXmlFileWsPath, mXmlStringId)) {
// We actually change it only if the ID doesn't exist yet
- TextFileChange xmlChange = new TextFileChange(getName(), (IFile) targetXml);
- xmlChange.setTextType("xml"); //$NON-NLS-1$
- TextEdit edit = createXmlEdit((IFile) targetXml, mXmlStringId, mTokenString);
- if (edit == null) {
- status.addFatalError(String.format("Failed to modify file %1$s",
- mTargetXmlFileWsPath));
+ Change change = createXmlChange((IFile) targetXml, mXmlStringId, mTokenString,
+ status, SubMonitor.convert(monitor, 1));
+ if (change != null) {
+ mChanges.add(change);
}
- xmlChange.setEdit(edit);
- mChanges.add(xmlChange);
}
- monitor.worked(1);
-
+
if (status.hasError()) {
return status;
}
// Prepare the change to the Java compilation unit
- List changes = computeJavaChanges(mUnit, mXmlStringId, mTokenString, status,
- SubMonitor.convert(monitor, 1));
+ List changes = computeJavaChanges(mUnit, mXmlStringId, mTokenString,
+ status, SubMonitor.convert(monitor, 1));
if (changes != null) {
mChanges.addAll(changes);
}
@@ -448,72 +444,94 @@ class ExtractStringRefactoring extends Refactoring {
}
/**
- * Internal helper that actually prepares the {@link TextEdit} that adds the given
+ * Internal helper that actually prepares the {@link Change} that adds the given
* ID to the given XML File.
*
* This does not actually modify the file.
*
- * @param xmlFile The file resource to modify.
- * @param replacementStringId The new ID to insert.
- * @param oldString The old string, which will be the value in the XML string.
+ * @param targetXml The file resource to modify.
+ * @param xmlStringId The new ID to insert.
+ * @param tokenString The old string, which will be the value in the XML string.
* @return A new {@link TextEdit} that describes how to change the file.
*/
- private TextEdit createXmlEdit(IFile xmlFile, String replacementStringId, String oldString) {
+ private Change createXmlChange(IFile targetXml,
+ String xmlStringId,
+ String tokenString,
+ RefactoringStatus status,
+ SubMonitor subMonitor) {
- if (!xmlFile.exists()) {
+ TextFileChange xmlChange = new TextFileChange(getName(), targetXml);
+ xmlChange.setTextType("xml"); //$NON-NLS-1$
+
+ TextEdit edit = null;
+ TextEditGroup editGroup = null;
+
+ if (!targetXml.exists()) {
// The XML file does not exist. Simply create it.
StringBuilder content = new StringBuilder();
content.append("\n"); //$NON-NLS-1$
content.append("\n"); //$NON-NLS-1$
content.append(" "). //$NON-NLS-1$
- append(oldString).
+ append(tokenString).
append("\n"); //$NON-NLS-1$
content.append("\n"); //$NON-NLS-1$
- return new InsertEdit(0, content.toString());
- }
+ edit = new InsertEdit(0, content.toString());
+ editGroup = new TextEditGroup("Create ID in new XML file", edit);
+ } else {
+ // The file exist. Attempt to parse it as a valid XML document.
+ try {
+ int[] indices = new int[2];
+ if (findXmlOpeningTagPos(targetXml.getContents(), "resources", indices)) { //$NON-NLS-1$
+ // Indices[1] indicates whether we found > or />. It can only be 1 or 2.
+ // Indices[0] is the position of the first character of either > or />.
+ //
+ // Note: we don't even try to adapt our formatting to the existing structure (we
+ // could by capturing whatever whitespace is after the closing bracket and
+ // applying it here before our tag, unless we were dealing with an empty
+ // resource tag.)
+
+ int offset = indices[0];
+ int len = indices[1];
+ StringBuilder content = new StringBuilder();
+ content.append(">\n"); //$NON-NLS-1$
+ content.append(" "). //$NON-NLS-1$
+ append(tokenString).
+ append(""); //$NON-NLS-1$
+ if (len == 2) {
+ content.append("\n"); //$NON-NLS-1$
+ }
- // The file exist. Attempt to parse it as a valid XML document.
- try {
- int[] indices = new int[2];
- if (findXmlOpeningTagPos(xmlFile.getContents(), "resources", indices)) { //$NON-NLS-1$
- // Indices[1] indicates whether we found > or />. It can only be 1 or 2.
- // Indices[0] is the position of the first character of either > or />.
- //
- // Note: we don't even try to adapt our formatting to the existing structure (we
- // could by capturing whatever whitespace is after the closing bracket and
- // applying it here before our tag, unless we were dealing with an empty
- // resource tag.)
-
- int offset = indices[0];
- int len = indices[1];
- StringBuilder content = new StringBuilder();
- content.append(">\n"); //$NON-NLS-1$
- content.append(" "). //$NON-NLS-1$
- append(oldString).
- append(""); //$NON-NLS-1$
- if (len == 2) {
- content.append("\n"); //$NON-NLS-1$
+ edit = new ReplaceEdit(offset, len, content.toString());
+ editGroup = new TextEditGroup("Insert ID in XML file", edit);
}
-
- return new ReplaceEdit(offset, len, content.toString());
+ } catch (CoreException e) {
+ // Failed to read file. Ignore. Will return null below.
}
-
- } catch (CoreException e) {
- // Failed to read file. Ignore. Will return null below.
}
-
- return null;
+
+ if (edit == null) {
+ status.addFatalError(String.format("Failed to modify file %1$s",
+ mTargetXmlFileWsPath));
+ return null;
+ }
+
+ xmlChange.setEdit(edit);
+ // The TextEditChangeGroup let the user toggle this change on and off later.
+ xmlChange.addTextEditChangeGroup(new TextEditChangeGroup(xmlChange, editGroup));
+
+ subMonitor.worked(1);
+ return xmlChange;
}
/**
* Parse an XML input stream, looking for an opening tag.
*
- * If found, returns the character offet in the buffer of the closing bracket of that
+ * If found, returns the character offest in the buffer of the closing bracket of that
* tag, e.g. the position of > in "". The first character is at offset 0.
*
* The implementation here relies on a simple character-based parser. No DOM nor SAX
@@ -622,6 +640,9 @@ class ExtractStringRefactoring extends Refactoring {
return false;
}
+ /**
+ * Computes the changes to be made to Java file(s) and returns a list of {@link Change}.
+ */
private List computeJavaChanges(ICompilationUnit unit,
String xmlStringId,
String tokenString,
@@ -700,29 +721,31 @@ class ExtractStringRefactoring extends Refactoring {
// ImportRewrite will allow us to add the new type to the imports and will resolve
// what the Java source must reference, e.g. the FQCN or just the simple name.
- ImportRewrite ir = ImportRewrite.create((CompilationUnit) node, true);
+ ImportRewrite importRewrite = ImportRewrite.create((CompilationUnit) node, true);
String Rqualifier = packageName + ".R"; //$NON-NLS-1$
- Rqualifier = ir.addImport(Rqualifier);
+ Rqualifier = importRewrite.addImport(Rqualifier);
// Rewrite the AST itself via an ASTVisitor
AST ast = node.getAST();
- ASTRewrite ar = ASTRewrite.create(ast);
- ReplaceStringsVisitor visitor = new ReplaceStringsVisitor(ast, ar,
+ ASTRewrite astRewrite = ASTRewrite.create(ast);
+ ArrayList astEditGroups = new ArrayList();
+ ReplaceStringsVisitor visitor = new ReplaceStringsVisitor(
+ ast, astRewrite, astEditGroups,
tokenString, Rqualifier, xmlStringId);
node.accept(visitor);
-
+
// Finally prepare the change set
try {
MultiTextEdit edit = new MultiTextEdit();
// Create the edit to change the imports, only if anything changed
- TextEdit subEdit = ir.rewriteImports(subMonitor.newChild(1));
+ TextEdit subEdit = importRewrite.rewriteImports(subMonitor.newChild(1));
if (subEdit.hasChildren()) {
edit.addChild(subEdit);
}
// Create the edit to change the Java source, only if anything changed
- subEdit = ar.rewriteAST();
+ subEdit = astRewrite.rewriteAST();
if (subEdit.hasChildren()) {
edit.addChild(subEdit);
}
@@ -730,6 +753,13 @@ class ExtractStringRefactoring extends Refactoring {
// Only create a change set if any edit was collected
if (edit.hasChildren()) {
change.setEdit(edit);
+
+ // Create TextEditChangeGroups which let the user turn changes on or off
+ // individually. This must be done after the change.setEdit() call above.
+ for (TextEditGroup editGroup : astEditGroups) {
+ change.addTextEditChangeGroup(new TextEditChangeGroup(change, editGroup));
+ }
+
changes.add(change);
}
@@ -740,7 +770,9 @@ class ExtractStringRefactoring extends Refactoring {
if (changes.size() > 0) {
return changes;
}
-
+
+ subMonitor.worked(1);
+
} catch (CoreException e) {
// ImportRewrite.rewriteImports failed.
status.addFatalError(e.getMessage());
@@ -755,14 +787,17 @@ class ExtractStringRefactoring extends Refactoring {
private final String mOldString;
private final String mRQualifier;
private final String mXmlId;
+ private final ArrayList mEditGroups;
public ReplaceStringsVisitor(AST ast,
ASTRewrite astRewrite,
+ ArrayList editGroups,
String oldString,
String rQualifier,
String xmlId) {
mAst = ast;
mRewriter = astRewrite;
+ mEditGroups = editGroups;
mOldString = oldString;
mRQualifier = rQualifier;
mXmlId = xmlId;
@@ -776,7 +811,8 @@ class ExtractStringRefactoring extends Refactoring {
SimpleName idName = mAst.newSimpleName(mXmlId);
QualifiedName newNode = mAst.newQualifiedName(qualifierName, idName);
- TextEditGroup editGroup = new TextEditGroup(getName());
+ TextEditGroup editGroup = new TextEditGroup("Replace string by ID");
+ mEditGroups.add(editGroup);
mRewriter.replace(node, newNode, editGroup);
}
return super.visit(node);
From 0105db6763d878836bedc7e9d265bd037ab91a33 Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Wed, 25 Mar 2009 16:30:11 -0700
Subject: [PATCH 026/127] Automated import from
//branches/donutburger/...@142709,142709
---
.../wizards/NewXmlFileCreationPage.java | 33 +++++++++++++++++--
1 file changed, 31 insertions(+), 2 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
index 80cc0f5aa..7a9115050 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
@@ -17,6 +17,7 @@
package com.android.ide.eclipse.editors.wizards;
+import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.AndroidConstants;
@@ -39,6 +40,7 @@ import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jface.viewers.IStructuredSelection;
@@ -745,8 +747,35 @@ class NewXmlFileCreationPage extends WizardPage {
// cleared above.
// get the AndroidTargetData from the project
- IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
- AndroidTargetData data = Sdk.getCurrent().getTargetData(target);
+ IAndroidTarget target = null;
+ AndroidTargetData data = null;
+
+ target = Sdk.getCurrent().getTarget(mProject);
+ if (target == null) {
+ // A project should have a target. The target can be missing if the project
+ // is an old project for which a target hasn't been affected or if the
+ // target no longer exists in this SDK. Simply log the error and dismiss.
+
+ AdtPlugin.log(IStatus.INFO,
+ "NewXmlFile wizard: no platform target for project %s", //$NON-NLS-1$
+ mProject.getName());
+ continue;
+ } else {
+ data = Sdk.getCurrent().getTargetData(target);
+
+ if (data == null) {
+ // We should have both a target and its data.
+ // However if the wizard is invoked whilst the platform is still being
+ // loaded we can end up in a weird case where we have a target but it
+ // doesn't have any data yet.
+ // Lets log a warning and silently ignore this root.
+
+ AdtPlugin.log(IStatus.INFO,
+ "NewXmlFile wizard: no data for target %s, project %s", //$NON-NLS-1$
+ target.getName(), mProject.getName());
+ continue;
+ }
+ }
IDescriptorProvider provider = data.getDescriptorProvider((Integer)rootSeed);
ElementDescriptor descriptor = provider.getDescriptor();
From b1c2adad16ef074d3b80d5fe7f3ec821ed383343 Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Fri, 27 Mar 2009 15:22:09 -0700
Subject: [PATCH 027/127] AI 143156: am: CL 142851 ADT GLE fix #1731389:
widgets dropped in lack their default layout_widht/height. Issue:
did not have layout attributes. When new widgets are dropped in a
layout, GLE only adds layout width/height if the parent supports them.
Fix: Make have layout attributes from FrameLayout. The ideal choice
would have been ViewGroup, but since ViewGroup is abstract we don't have
any ViewDescriptor for it. FrameLayout should provide a sensible
replacement. Note: this requires CL 142780 to compile. Original author:
raphael Merged from: //branches/cupcake/...
Automated import of CL 143156
---
.../eclipse/adt/sdk/LayoutParamsParser.java | 7 +--
.../ide/eclipse/common/AndroidConstants.java | 12 ++--
.../layout/descriptors/LayoutDescriptors.java | 62 +++++++++++--------
.../editors/layout/parts/DropFeedback.java | 2 +
.../layout/uimodel/UiViewElementNode.java | 2 +-
5 files changed, 48 insertions(+), 37 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/LayoutParamsParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/LayoutParamsParser.java
index dc600d7f7..19ef16cfa 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/LayoutParamsParser.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/LayoutParamsParser.java
@@ -157,7 +157,7 @@ public class LayoutParamsParser {
superClasses[2] = paramsClassName;
}
HashMap> found =
- mClassLoader.findClassesDerivingFrom("android.", superClasses);
+ mClassLoader.findClassesDerivingFrom("android.", superClasses); //$NON-NLS-1$
mTopViewClass = mClassLoader.getClass(rootClassName);
mTopGroupClass = mClassLoader.getClass(groupClassName);
if (paramsClassName != null) {
@@ -179,8 +179,7 @@ public class LayoutParamsParser {
addView(mTopViewClass);
// ViewGroup derives from View
- mGroupMap.get(groupClassName).setSuperClass(
- mViewMap.get(rootClassName));
+ mGroupMap.get(groupClassName).setSuperClass(mViewMap.get(rootClassName));
progress.setWorkRemaining(mGroupList.size() + mViewList.size());
@@ -346,7 +345,7 @@ public class LayoutParamsParser {
private IClassDescriptor findLayoutParams(IClassDescriptor groupClass) {
IClassDescriptor[] innerClasses = groupClass.getDeclaredClasses();
for (IClassDescriptor innerClass : innerClasses) {
- if (innerClass.getSimpleName().equals(AndroidConstants.CLASS_LAYOUTPARAMS)) {
+ if (innerClass.getSimpleName().equals(AndroidConstants.CLASS_NAME_LAYOUTPARAMS)) {
return innerClass;
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
index 1da753c7b..226357f7a 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
@@ -189,14 +189,16 @@ public class AndroidConstants {
public final static String CLASS_CONTEXT = "android.content.Context"; //$NON-NLS-1$
public final static String CLASS_VIEW = "android.view.View"; //$NON-NLS-1$
public final static String CLASS_VIEWGROUP = "android.view.ViewGroup"; //$NON-NLS-1$
- public final static String CLASS_LAYOUTPARAMS = "LayoutParams"; //$NON-NLS-1$
+ public final static String CLASS_NAME_LAYOUTPARAMS = "LayoutParams"; //$NON-NLS-1$
public final static String CLASS_VIEWGROUP_LAYOUTPARAMS =
- CLASS_VIEWGROUP + "$" + CLASS_LAYOUTPARAMS; //$NON-NLS-1$
- public final static String CLASS_FRAMELAYOUT = "FrameLayout"; //$NON-NLS-1$
+ CLASS_VIEWGROUP + "$" + CLASS_NAME_LAYOUTPARAMS; //$NON-NLS-1$
+ public final static String CLASS_NAME_FRAMELAYOUT = "FrameLayout"; //$NON-NLS-1$
+ public final static String CLASS_FRAMELAYOUT =
+ "android.widget." + CLASS_NAME_FRAMELAYOUT; //$NON-NLS-1$
public final static String CLASS_PREFERENCE = "android.preference.Preference"; //$NON-NLS-1$
- public final static String CLASS_PREFERENCE_SCREEN = "PreferenceScreen"; //$NON-NLS-1$
+ public final static String CLASS_NAME_PREFERENCE_SCREEN = "PreferenceScreen"; //$NON-NLS-1$
public final static String CLASS_PREFERENCES =
- "android.preference." + CLASS_PREFERENCE_SCREEN; //$NON-NLS-1$
+ "android.preference." + CLASS_NAME_PREFERENCE_SCREEN; //$NON-NLS-1$
public final static String CLASS_PREFERENCEGROUP = "android.preference.PreferenceGroup"; //$NON-NLS-1$
public final static String CLASS_PARCELABLE = "android.os.Parcelable"; //$NON-NLS-1$
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java
index 2c0f984db..a59ad6f19 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java
@@ -121,7 +121,7 @@ public final class LayoutDescriptors implements IDescriptorProvider {
// The tag can only be a root tag, so it is added at the end.
// It gets everything else as children but it is not made a child itself.
- ElementDescriptor mergeTag = createMerge();
+ ElementDescriptor mergeTag = createMerge(newLayouts);
mergeTag.setChildren(newDescriptors); // mergeTag makes a copy of the list
newDescriptors.add(mergeTag);
newLayouts.add(mergeTag);
@@ -195,7 +195,7 @@ public final class LayoutDescriptors implements IDescriptorProvider {
if (need_separator) {
String title;
if (layoutParams.getShortClassName().equals(
- AndroidConstants.CLASS_LAYOUTPARAMS)) {
+ AndroidConstants.CLASS_NAME_LAYOUTPARAMS)) {
title = String.format("Layout Attributes from %1$s",
layoutParams.getViewLayoutClass().getShortClassName());
} else {
@@ -229,10 +229,10 @@ public final class LayoutDescriptors implements IDescriptorProvider {
/**
* Creates a new descriptor and adds it to the list of view descriptors.
*
- * @param newViews A list of view descriptors being populated. Also used to find the
- * View description and extract its layout attributes.
+ * @param knownViews A list of view descriptors being populated. Also used to find the
+ * View descriptor and extract its layout attributes.
*/
- private void insertInclude(ArrayList newViews) {
+ private void insertInclude(ArrayList knownViews) {
String xml_name = "include"; //$NON-NLS-1$
// Create the include custom attributes
@@ -260,7 +260,8 @@ public final class LayoutDescriptors implements IDescriptorProvider {
null); //overrides
// Find View and inherit all its layout attributes
- AttributeDescriptor[] viewLayoutAttribs = findViewLayoutAttributes(newViews);
+ AttributeDescriptor[] viewLayoutAttribs = findViewLayoutAttributes(
+ AndroidConstants.CLASS_VIEW, knownViews);
// Create the include descriptor
ViewElementDescriptor desc = new ViewElementDescriptor(xml_name, // xml_name
@@ -273,33 +274,21 @@ public final class LayoutDescriptors implements IDescriptorProvider {
null, // children
false /* mandatory */);
- newViews.add(desc);
- }
-
- /**
- * Finds the View descriptor and retrieves all its layout attributes.
- */
- private AttributeDescriptor[] findViewLayoutAttributes(
- ArrayList newViews) {
-
- for (ElementDescriptor desc : newViews) {
- if (desc instanceof ViewElementDescriptor) {
- ViewElementDescriptor viewDesc = (ViewElementDescriptor) desc;
- if (AndroidConstants.CLASS_VIEW.equals(viewDesc.getCanonicalClassName())) {
- return viewDesc.getLayoutAttributes();
- }
- }
- }
-
- return null;
+ knownViews.add(desc);
}
/**
* Creates and return a new descriptor.
+ * @param knownLayouts A list of all known layout view descriptors, used to find the
+ * FrameLayout descriptor and extract its layout attributes.
*/
- private ElementDescriptor createMerge() {
+ private ElementDescriptor createMerge(ArrayList knownLayouts) {
String xml_name = "merge"; //$NON-NLS-1$
+ // Find View and inherit all its layout attributes
+ AttributeDescriptor[] viewLayoutAttribs = findViewLayoutAttributes(
+ AndroidConstants.CLASS_FRAMELAYOUT, knownLayouts);
+
// Create the include descriptor
ViewElementDescriptor desc = new ViewElementDescriptor(xml_name, // xml_name
xml_name, // ui_name
@@ -307,10 +296,29 @@ public final class LayoutDescriptors implements IDescriptorProvider {
"A root tag useful for XML layouts inflated using a ViewStub.", // tooltip
null, // sdk_url
null, // attributes
- null, // layout attributes
+ viewLayoutAttribs, // layout attributes
null, // children
false /* mandatory */);
return desc;
}
+
+ /**
+ * Finds the descriptor and retrieves all its layout attributes.
+ */
+ private AttributeDescriptor[] findViewLayoutAttributes(
+ String viewFqcn,
+ ArrayList knownViews) {
+
+ for (ElementDescriptor desc : knownViews) {
+ if (desc instanceof ViewElementDescriptor) {
+ ViewElementDescriptor viewDesc = (ViewElementDescriptor) desc;
+ if (viewFqcn.equals(viewDesc.getCanonicalClassName())) {
+ return viewDesc.getLayoutAttributes();
+ }
+ }
+ }
+
+ return null;
+ }
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/DropFeedback.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/DropFeedback.java
index 6e79d64d4..b7d69af06 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/DropFeedback.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/parts/DropFeedback.java
@@ -96,6 +96,8 @@ class DropFeedback {
RelativeInfo info = null;
UiElementEditPart sibling = null;
+ // TODO consider merge like a vertical layout
+ // TODO consider TableLayout like a linear
if (LayoutConstants.LINEAR_LAYOUT.equals(layoutXmlName)) {
sibling = findLinearTarget(parentPart, where)[1];
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/uimodel/UiViewElementNode.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/uimodel/UiViewElementNode.java
index 1bf5d5abf..738591a29 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/uimodel/UiViewElementNode.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/uimodel/UiViewElementNode.java
@@ -81,7 +81,7 @@ public class UiViewElementNode extends UiElementNode {
if (layoutDescriptors != null) {
for (ElementDescriptor desc : layoutDescriptors) {
if (desc instanceof ViewElementDescriptor &&
- desc.getXmlName().equals(AndroidConstants.CLASS_FRAMELAYOUT)) {
+ desc.getXmlName().equals(AndroidConstants.CLASS_NAME_FRAMELAYOUT)) {
layout_attrs = ((ViewElementDescriptor) desc).getLayoutAttributes();
need_xmlns = true;
break;
From 745fd9097525faa5f4da9a06550b541b7ca9d469 Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Fri, 27 Mar 2009 15:25:11 -0700
Subject: [PATCH 028/127] AI 143157: am: CL 142852 ADT fix #1682502: New XML
File Wizard root combo is sometimes empty. Two issues in fact: 1- On some
selections the resource type is determined before the root values are
computed. 2- Added an sdk target change listener to refresh the project's
roots if the SDK was still loading or if the project changed targets. The
New Project Wizard has been updated with a similar sdk change listener to
refresh the target selector. This is useful when the NPW is used before the
targets have finished loading, e.g. upon lauching Eclipse. Note: this
requires CL 142690 to compile. Original author: raphael Merged from:
//branches/cupcake/...
Automated import of CL 143157
---
.../newproject/NewProjectCreationPage.java | 51 +++++++++++++++----
.../wizards/NewXmlFileCreationPage.java | 46 ++++++++++++++++-
2 files changed, 85 insertions(+), 12 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java
index 7d3cad3a0..e26b31cc3 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java
@@ -22,7 +22,9 @@
package com.android.ide.eclipse.adt.wizards.newproject;
+import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.sdk.Sdk;
+import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
import com.android.sdklib.IAndroidTarget;
@@ -122,6 +124,7 @@ public class NewProjectCreationPage extends WizardPage {
private Button mCreateActivityCheck;
private Text mMinSdkVersionField;
private SdkTargetSelector mSdkTargetSelector;
+ private ITargetChangeListener mSdkTargetChangeListener;
private boolean mInternalLocationPathUpdate;
protected boolean mInternalProjectNameUpdate;
@@ -263,6 +266,17 @@ public class NewProjectCreationPage extends WizardPage {
// Validate. This will complain about the first empty field.
setPageComplete(validatePage());
}
+
+ @Override
+ public void dispose() {
+
+ if (mSdkTargetChangeListener != null) {
+ AdtPlugin.getDefault().removeTargetListener(mSdkTargetChangeListener);
+ mSdkTargetChangeListener = null;
+ }
+
+ super.dispose();
+ }
/**
* Creates the group for the project name:
@@ -389,18 +403,35 @@ public class NewProjectCreationPage extends WizardPage {
group.setFont(parent.getFont());
group.setText("Target");
- // get the targets from the sdk
- IAndroidTarget[] targets = null;
- if (Sdk.getCurrent() != null) {
- targets = Sdk.getCurrent().getTargets();
- }
+ // The selector is created without targets. They are added below in the change listener.
+ mSdkTargetSelector = new SdkTargetSelector(group, null, false /*multi-selection*/);
- mSdkTargetSelector = new SdkTargetSelector(group, targets, false /*multi-selection*/);
+ mSdkTargetChangeListener = new ITargetChangeListener() {
+ public void onProjectTargetChange(IProject changedProject) {
+ // Ignore
+ }
- // If there's only one target, select it
- if (targets != null && targets.length == 1) {
- mSdkTargetSelector.setSelection(targets[0]);
- }
+ public void onTargetsLoaded() {
+ // Update the sdk target selector with the new targets
+
+ // get the targets from the sdk
+ IAndroidTarget[] targets = null;
+ if (Sdk.getCurrent() != null) {
+ targets = Sdk.getCurrent().getTargets();
+ }
+ mSdkTargetSelector.setTargets(targets);
+
+ // If there's only one target, select it
+ if (targets != null && targets.length == 1) {
+ mSdkTargetSelector.setSelection(targets[0]);
+ }
+ }
+ };
+
+ AdtPlugin.getDefault().addTargetListener(mSdkTargetChangeListener);
+
+ // Invoke it once to initialize the targets
+ mSdkTargetChangeListener.onTargetsLoaded();
mSdkTargetSelector.setSelectionListener(new SelectionAdapter() {
@Override
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
index 7a9115050..83ab59be1 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
@@ -20,6 +20,7 @@ package com.android.ide.eclipse.editors.wizards;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.sdk.Sdk;
+import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.ProjectChooserHelper;
import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor;
@@ -237,7 +238,7 @@ class NewXmlFileCreationPage extends WizardPage {
"An XML file that describes preferences.", // tooltip
ResourceFolderType.XML, // folder type
AndroidTargetData.DESCRIPTOR_PREFERENCES, // root seed
- AndroidConstants.CLASS_PREFERENCE_SCREEN, // default root
+ AndroidConstants.CLASS_NAME_PREFERENCE_SCREEN, // default root
SdkConstants.NS_RESOURCES, // xmlns
null, // default attributes
1 // target API level
@@ -290,7 +291,9 @@ class NewXmlFileCreationPage extends WizardPage {
private boolean mInternalTypeUpdate;
private boolean mInternalConfigSelectorUpdate;
private ProjectChooserHelper mProjectChooserHelper;
+ private ITargetChangeListener mSdkTargetChangeListener;
+ private TypeInfo mCurrentTypeInfo;
// --- UI creation ---
@@ -337,8 +340,43 @@ class NewXmlFileCreationPage extends WizardPage {
initializeFromSelection(mInitialSelection);
initializeRootValues();
enableTypesBasedOnApi();
+ if (mCurrentTypeInfo != null) {
+ updateRootCombo(mCurrentTypeInfo);
+ }
+ installTargetChangeListener();
validatePage();
}
+
+ private void installTargetChangeListener() {
+ mSdkTargetChangeListener = new ITargetChangeListener() {
+ public void onProjectTargetChange(IProject changedProject) {
+ // If this is the current project, force it to reload its data
+ if (changedProject != null && changedProject == mProject) {
+ changeProject(mProject);
+ }
+ }
+
+ public void onTargetsLoaded() {
+ // Reload the current project, if any, in case its target has changed.
+ if (mProject != null) {
+ changeProject(mProject);
+ }
+ }
+ };
+
+ AdtPlugin.getDefault().addTargetListener(mSdkTargetChangeListener);
+ }
+
+ @Override
+ public void dispose() {
+
+ if (mSdkTargetChangeListener != null) {
+ AdtPlugin.getDefault().removeTargetListener(mSdkTargetChangeListener);
+ mSdkTargetChangeListener = null;
+ }
+
+ super.dispose();
+ }
/**
* Returns the target project or null.
@@ -652,7 +690,6 @@ class NewXmlFileCreationPage extends WizardPage {
return;
}
-
// Find the best match in the element list. In case there are multiple selected elements
// select the one that provides the most information and assign them a score,
// e.g. project=1 + folder=2 + file=4.
@@ -848,6 +885,10 @@ class NewXmlFileCreationPage extends WizardPage {
/**
* Changes mProject to the given new project and update the UI accordingly.
+ *
+ * Note that this does not check if the new project is the same as the current one
+ * on purpose, which allows a project to be updated when its target has changed or
+ * when targets are loaded in the background.
*/
private void changeProject(IProject newProject) {
mProject = newProject;
@@ -1067,6 +1108,7 @@ class NewXmlFileCreationPage extends WizardPage {
private void selectType(TypeInfo type) {
if (type == null || !type.getWidget().getSelection()) {
mInternalTypeUpdate = true;
+ mCurrentTypeInfo = type;
for (TypeInfo type2 : sTypes) {
type2.getWidget().setSelection(type2 == type);
}
From 62c4d9bfc5eca4a7bd160e8fb67c0bbc066d9ce8 Mon Sep 17 00:00:00 2001
From: Evan Millar <>
Date: Fri, 27 Mar 2009 17:10:47 -0700
Subject: [PATCH 029/127] AI 143190: am: CL 142951 Change the way we handle
monkey throttling. We now will only sleep after complete down/[move]/up
sequences. This avoids unwanted long pressing. We will need to add code to
explicitly long press at some point. Original author: emillar Merged
from: //branches/cupcake/...
Automated import of CL 143190
---
.../com/android/commands/monkey/Monkey.java | 14 ++---
.../android/commands/monkey/MonkeyEvent.java | 1 +
.../commands/monkey/MonkeySourceRandom.java | 14 ++++-
.../commands/monkey/MonkeyThrottleEvent.java | 52 +++++++++++++++++++
4 files changed, 71 insertions(+), 10 deletions(-)
create mode 100644 cmds/monkey/src/com/android/commands/monkey/MonkeyThrottleEvent.java
diff --git a/cmds/monkey/src/com/android/commands/monkey/Monkey.java b/cmds/monkey/src/com/android/commands/monkey/Monkey.java
index f6ab19d73..00fb40cb9 100644
--- a/cmds/monkey/src/com/android/commands/monkey/Monkey.java
+++ b/cmds/monkey/src/com/android/commands/monkey/Monkey.java
@@ -369,7 +369,7 @@ public class Monkey {
if (mVerbose >= 2) { // check seeding performance
System.out.println("// Seeded: " + mSeed);
}
- mEventSource = new MonkeySourceRandom(mSeed, mMainApps);
+ mEventSource = new MonkeySourceRandom(mSeed, mMainApps, mThrottle);
mEventSource.setVerbose(mVerbose);
//set any of the factors that has been set
for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
@@ -709,13 +709,6 @@ public class Monkey {
}
}
- try {
- Thread.sleep(mThrottle);
- } catch (InterruptedException e1) {
- System.out.println("** Monkey interrupted in sleep.");
- return i;
- }
-
// In this debugging mode, we never send any events. This is primarily
// here so you can manually test the package or category limits, while manually
// exercising the system.
@@ -730,7 +723,10 @@ public class Monkey {
MonkeyEvent ev = mEventSource.getNextEvent();
if (ev != null) {
- i++;
+ // We don't want to count throttling as an event.
+ if (!(ev instanceof MonkeyThrottleEvent)) {
+ i++;
+ }
int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
if (injectCode == MonkeyEvent.INJECT_FAIL) {
if (ev instanceof MonkeyKeyEvent) {
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeyEvent.java b/cmds/monkey/src/com/android/commands/monkey/MonkeyEvent.java
index ff99f5f06..7176073b9 100644
--- a/cmds/monkey/src/com/android/commands/monkey/MonkeyEvent.java
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeyEvent.java
@@ -29,6 +29,7 @@ public abstract class MonkeyEvent {
public static final int EVENT_TYPE_TRACKBALL = 2;
public static final int EVENT_TYPE_ACTIVITY = 3;
public static final int EVENT_TYPE_FLIP = 4; // Keyboard flip
+ public static final int EVENT_TYPE_THROTTLE = 5;
public static final int INJECT_SUCCESS = 1;
public static final int INJECT_FAIL = 0;
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java
index 3dbb575aa..902d8e84c 100644
--- a/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java
@@ -171,6 +171,7 @@ public class MonkeySourceRandom implements MonkeyEventSource{
private LinkedList mQ = new LinkedList();
private Random mRandom;
private int mVerbose = 0;
+ private long mThrottle = 0;
private boolean mKeyboardOpen = false;
@@ -185,7 +186,7 @@ public class MonkeySourceRandom implements MonkeyEventSource{
return KEY_NAMES[keycode];
}
- public MonkeySourceRandom(long seed, ArrayList MainApps) {
+ public MonkeySourceRandom(long seed, ArrayList MainApps, long throttle) {
// default values for random distributions
// note, these are straight percentages, to match user input (cmd line args)
// but they will be converted to 0..1 values before the main loop runs.
@@ -202,6 +203,7 @@ public class MonkeySourceRandom implements MonkeyEventSource{
mRandom = new SecureRandom();
mRandom.setSeed((seed == 0) ? -1 : seed);
mMainApps = MainApps;
+ mThrottle = throttle;
}
/**
@@ -334,6 +336,7 @@ public class MonkeySourceRandom implements MonkeyEventSource{
downAt, MotionEvent.ACTION_UP, x, y, 0);
e.setIntermediateNote(false);
mQ.addLast(e);
+ addThrottle();
}
/**
@@ -384,6 +387,7 @@ public class MonkeySourceRandom implements MonkeyEventSource{
e.setIntermediateNote(false);
mQ.addLast(e);
}
+ addThrottle();
}
/**
@@ -416,11 +420,13 @@ public class MonkeySourceRandom implements MonkeyEventSource{
MonkeyActivityEvent e = new MonkeyActivityEvent(mMainApps.get(
mRandom.nextInt(mMainApps.size())));
mQ.addLast(e);
+ addThrottle();
return;
} else if (cls < mFactors[FACTOR_FLIP]) {
MonkeyFlipEvent e = new MonkeyFlipEvent(mKeyboardOpen);
mKeyboardOpen = !mKeyboardOpen;
mQ.addLast(e);
+ addThrottle();
return;
} else {
lastKey = 1 + mRandom.nextInt(KeyEvent.getMaxKeyCode() - 1);
@@ -431,6 +437,8 @@ public class MonkeySourceRandom implements MonkeyEventSource{
e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, lastKey);
mQ.addLast(e);
+
+ addThrottle();
}
public boolean validate() {
@@ -464,4 +472,8 @@ public class MonkeySourceRandom implements MonkeyEventSource{
mQ.removeFirst();
return e;
}
+
+ private void addThrottle() {
+ mQ.addLast(new MonkeyThrottleEvent(MonkeyEvent.EVENT_TYPE_THROTTLE, mThrottle));
+ }
}
diff --git a/cmds/monkey/src/com/android/commands/monkey/MonkeyThrottleEvent.java b/cmds/monkey/src/com/android/commands/monkey/MonkeyThrottleEvent.java
new file mode 100644
index 000000000..3d7d48aa5
--- /dev/null
+++ b/cmds/monkey/src/com/android/commands/monkey/MonkeyThrottleEvent.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.commands.monkey;
+
+import android.app.IActivityManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.view.IWindowManager;
+import android.view.MotionEvent;
+
+
+/**
+ * monkey throttle event
+ */
+public class MonkeyThrottleEvent extends MonkeyEvent {
+ private long mThrottle;
+
+ public MonkeyThrottleEvent(int type, long throttle) {
+ super(type);
+ mThrottle = throttle;
+ }
+
+ @Override
+ public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {
+
+ if (verbose > 1) {
+ System.out.println("Sleeping for " + mThrottle + " milliseconds");
+ }
+ try {
+ Thread.sleep(mThrottle);
+ } catch (InterruptedException e1) {
+ System.out.println("** Monkey interrupted in sleep.");
+ return MonkeyEvent.INJECT_FAIL;
+ }
+
+ return MonkeyEvent.INJECT_SUCCESS;
+ }
+}
From 60070181900b65933d0ca1eebaa56e2456245bb3 Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Fri, 27 Mar 2009 17:36:09 -0700
Subject: [PATCH 030/127] AI 143199: am: CL 142978 ADT #1738393: fix typo in
launch controller. Also fixed some javadoc warnings. Original author:
raphael Merged from: //branches/cupcake/...
Automated import of CL 143199
---
.../launch/AndroidLaunchConfiguration.java | 13 +++++++----
.../adt/launch/AndroidLaunchController.java | 17 +++++++-------
.../junit/AndroidJUnitLaunchAction.java | 23 +++++++++++++++----
3 files changed, 34 insertions(+), 19 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java
index 3e610db08..b5ea7275c 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java
@@ -32,8 +32,14 @@ public class AndroidLaunchConfiguration {
*/
public int mLaunchAction = LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION;
+ /**
+ * Target selection mode for the configuration: either {@link #AUTO} or {@link #MANUAL}.
+ */
public enum TargetMode {
- AUTO(true), MANUAL(false);
+ /** Automatic target selection mode. */
+ AUTO(true),
+ /** Manual target selection mode. */
+ MANUAL(false);
private boolean mValue;
@@ -58,10 +64,7 @@ public class AndroidLaunchConfiguration {
/**
* Target selection mode.
- *
- *
true: automatic mode, see {@link #AUTO_TARGET_MODE}
Once an emulator has connected, {@link DelayedLaunchInfo#mDevice} is set and the
- * DelayedLaunchInfo object is moved to {@link AndroidLaunchController#mWaitingForReadyEmulatorList}.
+ *
Once an emulator has connected, {@link DelayedLaunchInfo#getDevice()} is set and the
+ * DelayedLaunchInfo object is moved to
+ * {@link AndroidLaunchController#mWaitingForReadyEmulatorList}.
* ALL ACCESS MUST BE INSIDE A synchronized (sListLock) block!
*/
private final ArrayList mWaitingForEmulatorLaunches =
@@ -487,7 +486,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
// FIXME: ask the user if he wants to create a AVD.
// we found no compatible AVD.
AdtPlugin.printErrorToConsole(project, String.format(
- "Failed to find a AVD compatible with target '%1$s'. Launch aborted.",
+ "Failed to find an AVD compatible with target '%1$s'. Launch aborted.",
projectTarget.getName()));
stopLaunch(launchInfo);
return;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
index b88026329..747fcfe5c 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
@@ -57,7 +57,7 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
* Launch a instrumentation test run on given Android device.
* Reuses JDT JUnit launch delegate so results can be communicated back to JDT JUnit UI.
*
- * @see com.android.ide.eclipse.adt.launch.IAndroidLaunchAction#doLaunchAction(com.android.ide.eclipse.adt.launch.AndroidLaunchController.DelayedLaunchInfo, com.android.ddmlib.Device)
+ * @see IAndroidLaunchAction#doLaunchAction(DelayedLaunchInfo, IDevice)
*/
public boolean doLaunchAction(DelayedLaunchInfo info, IDevice device) {
String msg = String.format("Launching instrumentation %s on device %s", mRunner,
@@ -108,7 +108,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
super.launch(configuration, mode, launch, monitor);
}
- /* (non-Javadoc)
+ /**
+ * {@inheritDoc}
+ * @throws CoreException
* @see org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate#verifyMainTypeName(org.eclipse.debug.core.ILaunchConfiguration)
*/
@Override
@@ -119,6 +121,7 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
/**
* Overrides parent to return a VM Runner implementation which launches a thread, rather
* than a separate VM process
+ * @throws CoreException
*/
@Override
public IVMRunner getVMRunner(ILaunchConfiguration configuration, String mode)
@@ -127,7 +130,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
mTestPackage, mRunner, mLaunchInfo.isDebugMode(), mDevice));
}
- /* (non-Javadoc)
+ /**
+ * {@inheritDoc}
+ * @throws CoreException
* @see org.eclipse.debug.core.model.LaunchConfigurationDelegate#getLaunch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String)
*/
@Override
@@ -148,6 +153,10 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
mJUnitInfo = info;
}
+ /**
+ * {@inheritDoc}
+ * @throws CoreException
+ */
public void run(final VMRunnerConfiguration config, ILaunch launch,
IProgressMonitor monitor) throws CoreException {
@@ -183,7 +192,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
return null;
}
- /* (non-Javadoc)
+ /**
+ * {@inheritDoc}
+ * @throws DebugException
* @see org.eclipse.debug.core.model.IProcess#getExitValue()
*/
public int getExitValue() throws DebugException {
@@ -241,7 +252,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
return mIsTerminated;
}
- /* (non-Javadoc)
+ /**
+ * {@inheritDoc}
+ * @throws DebugException
* @see org.eclipse.debug.core.model.ITerminate#terminate()
*/
public void terminate() throws DebugException {
From f5171c1aa74f04ac4d47e71cec145af8dab9814e Mon Sep 17 00:00:00 2001
From: Mike Lockwood <>
Date: Fri, 27 Mar 2009 17:53:21 -0700
Subject: [PATCH 031/127] AI 143204: am: CL 142988 Remove emulator support from
user builds. Saves us 52K in /system partition. BUG=1739507 Original
author: lockwood Merged from: //branches/cupcake/...
Automated import of CL 143204
---
emulator/qemud/Android.mk | 1 +
emulator/sensors/Android.mk | 1 +
2 files changed, 2 insertions(+)
diff --git a/emulator/qemud/Android.mk b/emulator/qemud/Android.mk
index 454f32dda..a186c73de 100644
--- a/emulator/qemud/Android.mk
+++ b/emulator/qemud/Android.mk
@@ -11,5 +11,6 @@ LOCAL_SHARED_LIBRARIES := \
libcutils \
LOCAL_MODULE:= qemud
+LOCAL_MODULE_TAGS := debug
include $(BUILD_EXECUTABLE)
diff --git a/emulator/sensors/Android.mk b/emulator/sensors/Android.mk
index 402da823f..74e02adb6 100644
--- a/emulator/sensors/Android.mk
+++ b/emulator/sensors/Android.mk
@@ -24,5 +24,6 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES := liblog libcutils
LOCAL_SRC_FILES := sensors_qemu.c
LOCAL_MODULE := sensors.goldfish
+LOCAL_MODULE_TAGS := debug
include $(BUILD_SHARED_LIBRARY)
endif
From 5d22c85df0e079569aba21ded54fd2fdf6c07dd3 Mon Sep 17 00:00:00 2001
From: Xavier Ducrohet <>
Date: Fri, 27 Mar 2009 18:00:15 -0700
Subject: [PATCH 032/127] AI 143206: am: CL 142994 Clean up official ApiDemos
to not reference Google Maps Original author: xav Merged from:
//branches/cupcake/...
Automated import of CL 143206
---
samples/ApiDemos/AndroidManifest.xml | 16 ----------------
1 file changed, 16 deletions(-)
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index 90a5c5de9..fcb7e7999 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -33,8 +33,6 @@
android:label="@string/activity_sample_code"
android:icon="@drawable/app_sample_code" >
-
-
@@ -1312,20 +1310,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
From 49f4090c7a1335d4ca0906d423bd96ae9f7852f4 Mon Sep 17 00:00:00 2001
From: Dianne Hackborn <>
Date: Fri, 27 Mar 2009 19:03:33 -0700
Subject: [PATCH 033/127] AI 143220: am: CL 143022 New spare parts feature.
Spare parts is only in engineering builds, so I am hoping it is okay to check
in -- it's the same as updating sample code etc. Original author: hackbod
Merged from: //branches/cupcake/...
Automated import of CL 143220
---
apps/SpareParts/res/values/strings.xml | 4 ++++
apps/SpareParts/res/xml/spare_parts.xml | 6 ++++++
.../src/com/android/spare_parts/SpareParts.java | 10 ++++++++++
3 files changed, 20 insertions(+)
diff --git a/apps/SpareParts/res/values/strings.xml b/apps/SpareParts/res/values/strings.xml
index 6aecca826..7bd13e54a 100644
--- a/apps/SpareParts/res/values/strings.xml
+++ b/apps/SpareParts/res/values/strings.xml
@@ -45,6 +45,10 @@
Use fancier animations for input method windowsUse normal animations for input method windows
+ Fancy rotation animations
+ Use fancier animations for screen rotation
+ Use normal animations for screen rotation
+
Haptic feedbackUse haptic feedback with user interactionUse haptic feedback with user interaction
diff --git a/apps/SpareParts/res/xml/spare_parts.xml b/apps/SpareParts/res/xml/spare_parts.xml
index 3e06397a6..3ef1b7961 100644
--- a/apps/SpareParts/res/xml/spare_parts.xml
+++ b/apps/SpareParts/res/xml/spare_parts.xml
@@ -72,6 +72,12 @@
android:summaryOn="@string/summary_on_fancy_ime_animations"
android:summaryOff="@string/summary_off_fancy_ime_animations"/>
+
+
Date: Fri, 27 Mar 2009 19:13:58 -0700
Subject: [PATCH 034/127] AI 143223: am: CL 143034 ADT #1738834: Properly
validate project name, package name and activity name for "create project".
Also validate AVD name for "create avd". Original author: raphael
Merged from: //branches/cupcake/...
Automated import of CL 143223
---
.../app/src/com/android/sdkmanager/Main.java | 51 +++++++++++++++++--
.../sdklib/project/ProjectCreator.java | 33 ++++++++++--
2 files changed, 78 insertions(+), 6 deletions(-)
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
index 154788ee1..adf37ed0b 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
@@ -35,6 +35,7 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.regex.Pattern;
/**
* Main class for the 'android' application.
@@ -50,6 +51,12 @@ class Main {
private final static String[] BOOLEAN_YES_REPLIES = new String[] { "yes", "y" };
private final static String[] BOOLEAN_NO_REPLIES = new String[] { "no", "n" };
+ /** Regex used to validate characters that compose an AVD name. */
+ private final static Pattern RE_AVD_NAME = Pattern.compile("[a-zA-Z0-9._-]+");
+ /** List of valid characters for an AVD name. Used for display purposes. */
+ private final static String CHARS_AVD_NAME = "a-z A-Z 0-9 . _ -";
+
+
/** Path to the SDK folder. This is the parent of {@link #TOOLSDIR}. */
private String mSdkFolder;
/** Logger object. Use this to print normal output, warnings or errors. */
@@ -239,11 +246,41 @@ class Main {
mSdkLog);
String projectDir = getProjectLocation(mSdkCommandLine.getParamLocationPath());
+
+ String projectName = mSdkCommandLine.getParamName();
+ String packageName = mSdkCommandLine.getParamProjectPackage();
+ String activityName = mSdkCommandLine.getParamProjectActivity();
+ if (projectName != null &&
+ !ProjectCreator.RE_PROJECT_NAME.matcher(projectName).matches()) {
+ errorAndExit(
+ "Project name '%1$s' contains invalid characters.\nAllowed characters are: %2$s",
+ projectName, ProjectCreator.CHARS_PROJECT_NAME);
+ return;
+ }
+
+ if (activityName != null &&
+ !ProjectCreator.RE_ACTIVITY_NAME.matcher(activityName).matches()) {
+ errorAndExit(
+ "Activity name '%1$s' contains invalid characters.\nAllowed characters are: %2$s",
+ activityName, ProjectCreator.CHARS_ACTIVITY_NAME);
+ return;
+ }
+
+ if (packageName != null &&
+ !ProjectCreator.RE_PACKAGE_NAME.matcher(packageName).matches()) {
+ errorAndExit(
+ "Package name '%1$s' contains invalid characters.\n" +
+ "A package name must be constitued of two Java identifiers.\n" +
+ "Each identifier allowed characters are: %2$s",
+ packageName, ProjectCreator.CHARS_PACKAGE_NAME);
+ return;
+ }
+
creator.createProject(projectDir,
- mSdkCommandLine.getParamName(),
- mSdkCommandLine.getParamProjectPackage(),
- mSdkCommandLine.getParamProjectActivity(),
+ projectName,
+ activityName,
+ packageName,
target,
false /* isTestProject*/);
}
@@ -447,6 +484,14 @@ class Main {
AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog);
String avdName = mSdkCommandLine.getParamName();
+
+ if (!RE_AVD_NAME.matcher(avdName).matches()) {
+ errorAndExit(
+ "AVD name '%1$s' contains invalid characters.\nAllowed characters are: %2$s",
+ avdName, CHARS_AVD_NAME);
+ return;
+ }
+
AvdInfo info = avdManager.getAvd(avdName);
if (info != null) {
if (mSdkCommandLine.getFlagForce()) {
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java
index 7489b65d6..b84be18da 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java
@@ -62,6 +62,27 @@ public class ProjectCreator {
private final static String FOLDER_TESTS = "tests";
+ /** Pattern for characters accepted in a project name. Since this will be used as a
+ * directory name, we're being a bit conservative on purpose: dot and space cannot be used. */
+ public static final Pattern RE_PROJECT_NAME = Pattern.compile("[a-zA-Z0-9_]+");
+ /** List of valid characters for a project name. Used for display purposes. */
+ public final static String CHARS_PROJECT_NAME = "a-z A-Z 0-9 _";
+
+ /** Pattern for characters accepted in a package name. A package is list of Java identifier
+ * separated by a dot. We need to have at least one dot (e.g. a two-level package name).
+ * A Java identifier cannot start by a digit. */
+ public static final Pattern RE_PACKAGE_NAME =
+ Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)+");
+ /** List of valid characters for a project name. Used for display purposes. */
+ public final static String CHARS_PACKAGE_NAME = "a-z A-Z 0-9 _";
+
+ /** Pattern for characters accepted in an activity name, which is a Java identifier. */
+ public static final Pattern RE_ACTIVITY_NAME =
+ Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*");
+ /** List of valid characters for a project name. Used for display purposes. */
+ public final static String CHARS_ACTIVITY_NAME = "a-z A-Z 0-9 _";
+
+
public enum OutputLevel {
/** Silent mode. Project creation will only display errors. */
SILENT,
@@ -106,11 +127,17 @@ public class ProjectCreator {
/**
* Creates a new project.
+ *
+ * The caller should have already checked and sanitized the parameters.
*
* @param folderPath the folder of the project to create.
- * @param projectName the name of the project.
- * @param packageName the package of the project.
- * @param activityName the activity of the project as it will appear in the manifest.
+ * @param projectName the name of the project. The name must match the
+ * {@link #RE_PROJECT_NAME} regex.
+ * @param packageName the package of the project. The name must match the
+ * {@link #RE_PACKAGE_NAME} regex.
+ * @param activityName the activity of the project as it will appear in the manifest. Can be
+ * null if no activity should be created. The name must match the
+ * {@link #RE_ACTIVITY_NAME} regex.
* @param target the project target.
* @param isTestProject whether the project to create is a test project.
*/
From 1fd79529a96ab5d30612cb99c7dadfd62c2f5292 Mon Sep 17 00:00:00 2001
From: Xavier Ducrohet <>
Date: Fri, 27 Mar 2009 20:34:05 -0700
Subject: [PATCH 035/127] AI 143269: am: CL 143143 Update ADT changes.txt with
JUnit features, and properly restrict ADT package access. Original author:
xav Merged from: //branches/cupcake/...
Automated import of CL 143269
---
tools/eclipse/changes.txt | 1 +
.../com.android.ide.eclipse.adt/META-INF/MANIFEST.MF | 8 ++++----
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/tools/eclipse/changes.txt b/tools/eclipse/changes.txt
index 8cd843e20..02d907585 100644
--- a/tools/eclipse/changes.txt
+++ b/tools/eclipse/changes.txt
@@ -5,6 +5,7 @@
* Project properties (right click project in Package Explorer, then "Properties"), lets you edit project target.
* New Launch configuration option to choose debug deployment target.
- Ability to export multiple apk from one project, using resource filters. See the 'android' property for Android projects.
+- Support for running JUnit tests on a device/emulator from a new "Android JUnit tests" launch configuration.
0.8.1:
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
index c0dfcefd5..0ec97aa38 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
@@ -45,16 +45,16 @@ Require-Bundle: com.android.ide.eclipse.ddms,
org.eclipse.ltk.core.refactoring,
org.eclipse.ltk.ui.refactoring
Eclipse-LazyStart: true
-Export-Package: com.android.ide.eclipse.adt,
+Export-Package: com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.build;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.launch;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.project;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.project.internal;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.sdk;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.wizards.newproject;x-friends:="com.android.ide.eclipse.tests",
- com.android.ide.eclipse.common,
- com.android.ide.eclipse.common.project,
- com.android.ide.eclipse.common.resources,
+ com.android.ide.eclipse.common;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.common.project;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.common.resources;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.descriptors;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.layout;x-friends:="com.android.ide.eclipse.tests",
From 5303d5c523bd4e67821940cbe415bfb1848e13e7 Mon Sep 17 00:00:00 2001
From: Xavier Ducrohet <>
Date: Fri, 27 Mar 2009 20:37:00 -0700
Subject: [PATCH 036/127] AI 143270: am: CL 143149 Make ADT look for javadoc in
docs/reference for the optional libraries (to match the base docs).
Original author: xav Merged from: //branches/cupcake/...
Automated import of CL 143270
---
.../com/android/ide/eclipse/common/AndroidConstants.java | 5 +++--
.../libs/sdklib/src/com/android/sdklib/AddOnTarget.java | 3 ++-
.../libs/sdklib/src/com/android/sdklib/SdkConstants.java | 2 ++
.../sdkuilib/src/com/android/sdkuilib/AvdSelector.java | 8 ++++----
4 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
index 226357f7a..d0d8ae3af 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
@@ -90,7 +90,7 @@ public class AndroidConstants {
/** Name of the android sources directory */
public static final String FD_ANDROID_SOURCES = "sources"; //$NON-NLS-1$
-
+
/** Resource java class filename, i.e. "R.java" */
public final static String FN_RESOURCE_CLASS = "R.java"; //$NON-NLS-1$
/** Resource class file filename, i.e. "R.class" */
@@ -128,7 +128,8 @@ public class AndroidConstants {
public final static String WS_ASSETS = WS_SEP + SdkConstants.FD_ASSETS;
/** Leaf of the javaDoc folder. Does not start with a separator. */
- public final static String WS_JAVADOC_FOLDER_LEAF = SdkConstants.FD_DOCS + "/reference"; //$NON-NLS-1$
+ public final static String WS_JAVADOC_FOLDER_LEAF = SdkConstants.FD_DOCS + "/" +
+ SdkConstants.FD_DOCS_REFERENCE; //$NON-NLS-1$
/** Path of the samples directory relative to the sdk folder.
* This is an OS path, ending with a separator.
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java
index 0a5910734..d1ae343d5 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java
@@ -153,7 +153,8 @@ final class AddOnTarget implements IAndroidTarget {
case SKINS:
return mLocation + SdkConstants.OS_SKINS_FOLDER;
case DOCS:
- return mLocation + SdkConstants.FD_DOCS + File.separator;
+ return mLocation + SdkConstants.FD_DOCS + File.separator
+ + SdkConstants.FD_DOCS_REFERENCE;
default :
return mBasePlatform.getPath(pathId);
}
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
index 00594d12f..9eb6ade6b 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
@@ -143,6 +143,8 @@ public final class SdkConstants {
public final static String FD_LIB = "lib";
/** Name of the SDK docs folder. */
public final static String FD_DOCS = "docs";
+ /** Name of the doc folder containing API reference doc (javadoc) */
+ public static final String FD_DOCS_REFERENCE = "reference";
/** Name of the SDK images folder. */
public final static String FD_IMAGES = "images";
/** Name of the SDK skins folder. */
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java
index 67c70a676..d62c231c1 100644
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java
@@ -89,9 +89,9 @@ public final class AvdSelector {
final TableColumn column1 = new TableColumn(mTable, SWT.NONE);
column1.setText("Target Name");
final TableColumn column2 = new TableColumn(mTable, SWT.NONE);
- column2.setText("API Level");
+ column2.setText("SDK");
final TableColumn column3 = new TableColumn(mTable, SWT.NONE);
- column3.setText("SDK");
+ column3.setText("API Level");
adjustColumnsWidth(mTable, column0, column1, column2, column3);
setupSelectionListener(mTable);
@@ -235,8 +235,8 @@ public final class AvdSelector {
Rectangle r = table.getClientArea();
column0.setWidth(r.width * 30 / 100); // 30%
column1.setWidth(r.width * 45 / 100); // 45%
- column2.setWidth(r.width * 15 / 100); // 15%
- column3.setWidth(r.width * 10 / 100); // 10%
+ column2.setWidth(r.width * 10 / 100); // 10%
+ column3.setWidth(r.width * 15 / 100); // 15%
}
});
}
From 376013596da8b9585b0244fb3db4d0c67a4196e9 Mon Sep 17 00:00:00 2001
From: Jack Palevich <>
Date: Fri, 27 Mar 2009 20:39:49 -0700
Subject: [PATCH 037/127] AI 143272: am: CL 143256 Make the Term emulator work
with the most recent keyboard IME. + Makes the "Enter" key work again. +
Makes the "Delete" key delete just one character each time you press it
instead of two. Original author: jackpal Merged from:
//branches/cupcake/...
Automated import of CL 143272
---
apps/Term/src/com/android/term/Term.java | 36 +++++++++---------------
1 file changed, 13 insertions(+), 23 deletions(-)
diff --git a/apps/Term/src/com/android/term/Term.java b/apps/Term/src/com/android/term/Term.java
index d74159b90..1f43843a3 100644
--- a/apps/Term/src/com/android/term/Term.java
+++ b/apps/Term/src/com/android/term/Term.java
@@ -548,16 +548,6 @@ public class Term extends Activity {
+ controlKey + " 6 ==> Control-^").
show();
}
-
- private void print(String msg) {
- char[] chars = msg.toCharArray();
- int len = chars.length;
- byte[] bytes = new byte[len];
- for (int i = 0; i < len; i++) {
- bytes[i] = (byte) chars[i];
- }
- mEmulatorView.append(bytes, 0, len);
- }
}
@@ -2707,8 +2697,13 @@ class EmulatorView extends View implements GestureDetector.OnGestureListener {
return null;
}
- public boolean hideStatusIcon() {
- return true;
+ public boolean performEditorAction(int actionCode) {
+ if(actionCode == EditorInfo.IME_ACTION_UNSPECIFIED) {
+ // The "return" key has been pressed on the IME.
+ sendText("\n");
+ return true;
+ }
+ return false;
}
public boolean performContextMenuAction(int id) {
@@ -2720,13 +2715,12 @@ class EmulatorView extends View implements GestureDetector.OnGestureListener {
}
public boolean sendKeyEvent(KeyEvent event) {
- switch(event.getKeyCode()) {
- case KeyEvent.KEYCODE_ENTER:
- sendChar('\r');
- break;
- case KeyEvent.KEYCODE_DEL:
- sendChar(127);
- break;
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch(event.getKeyCode()) {
+ case KeyEvent.KEYCODE_DEL:
+ sendChar(127);
+ break;
+ }
}
return true;
}
@@ -2739,10 +2733,6 @@ class EmulatorView extends View implements GestureDetector.OnGestureListener {
return true;
}
- public boolean showStatusIcon(String packageName, int resId) {
- return true;
- }
-
private void sendChar(int c) {
try {
mTermOut.write(c);
From 1e3355dd7d5b1afbb296fefd69514ca6c3b8bf45 Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Fri, 27 Mar 2009 20:42:40 -0700
Subject: [PATCH 038/127] AI 143273: am: CL 143259 ADT #1743364: Refactor all
wizard classes & their actions together. Original author: raphael Merged
from: //branches/cupcake/...
Automated import of CL 143273
---
.../ide/eclipse/adt/wizards/actions/NewXmlFileAction.java | 2 +-
.../{project => wizards/actions}/NewXmlFileWizardAction.java | 4 ++--
.../wizards/newxmlfile}/NewXmlFileCreationPage.java | 3 ++-
.../wizards => adt/wizards/newxmlfile}/NewXmlFileWizard.java | 4 ++--
4 files changed, 7 insertions(+), 6 deletions(-)
rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/{project => wizards/actions}/NewXmlFileWizardAction.java (94%)
rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/{editors/wizards => adt/wizards/newxmlfile}/NewXmlFileCreationPage.java (99%)
rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/{editors/wizards => adt/wizards/newxmlfile}/NewXmlFileWizard.java (98%)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java
index 8c4a115b4..d1530d4ea 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java
@@ -16,7 +16,7 @@
package com.android.ide.eclipse.adt.wizards.actions;
-import com.android.ide.eclipse.editors.wizards.NewXmlFileWizard;
+import com.android.ide.eclipse.adt.wizards.newxmlfile.NewXmlFileWizard;
import org.eclipse.jface.action.IAction;
import org.eclipse.ui.IWorkbenchWizard;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/NewXmlFileWizardAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileWizardAction.java
similarity index 94%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/NewXmlFileWizardAction.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileWizardAction.java
index c117b4e57..20cfc8275 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/NewXmlFileWizardAction.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileWizardAction.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.project;
+package com.android.ide.eclipse.adt.wizards.actions;
-import com.android.ide.eclipse.editors.wizards.NewXmlFileWizard;
+import com.android.ide.eclipse.adt.wizards.newxmlfile.NewXmlFileWizard;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java
similarity index 99%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java
index 83ab59be1..f3cbfa221 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java
@@ -15,7 +15,7 @@
*/
-package com.android.ide.eclipse.editors.wizards;
+package com.android.ide.eclipse.adt.wizards.newxmlfile;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
@@ -31,6 +31,7 @@ import com.android.ide.eclipse.editors.resources.configurations.FolderConfigurat
import com.android.ide.eclipse.editors.resources.configurations.ResourceQualifier;
import com.android.ide.eclipse.editors.resources.descriptors.ResourcesDescriptors;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
+import com.android.ide.eclipse.editors.wizards.ConfigurationSelector;
import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.ConfigurationState;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileWizard.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileWizard.java
similarity index 98%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileWizard.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileWizard.java
index 125102b96..d7e43cfd7 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileWizard.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileWizard.java
@@ -16,11 +16,11 @@
-package com.android.ide.eclipse.editors.wizards;
+package com.android.ide.eclipse.adt.wizards.newxmlfile;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.wizards.newxmlfile.NewXmlFileCreationPage.TypeInfo;
import com.android.ide.eclipse.editors.IconFactory;
-import com.android.ide.eclipse.editors.wizards.NewXmlFileCreationPage.TypeInfo;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
From c33ae50cc30b6a1807fdf098938c06c6a680e1fa Mon Sep 17 00:00:00 2001
From: Xavier Ducrohet <>
Date: Fri, 27 Mar 2009 20:54:01 -0700
Subject: [PATCH 039/127] AI 143408: am: CL 143407 Prevent reinstalling APKs
during launch if they have not been recompiled since the previous launch.
Original author: xav Merged from: //branches/cupcake/...
Automated import of CL 143408
---
.../ide/eclipse/adt/build/ApkBuilder.java | 4 +
.../adt/launch/AndroidLaunchController.java | 46 +++-
.../adt/project/ApkInstallManager.java | 207 ++++++++++++++++++
3 files changed, 250 insertions(+), 7 deletions(-)
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ApkInstallManager.java
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
index 1edcf79fd..47ea3e731 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
@@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.build;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.project.ApkInstallManager;
import com.android.ide.eclipse.adt.project.ProjectHelper;
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.sdk.Sdk;
@@ -551,6 +552,9 @@ public class ApkBuilder extends BaseBuilder {
// and store it
saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage);
+ // reset the installation manager to force new installs of this project
+ ApkInstallManager.getInstance().resetInstallationFor(project);
+
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(),
"Build Success!");
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
index fafc4020b..b6c7640c7 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
@@ -32,6 +32,7 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration.TargetMode;
import com.android.ide.eclipse.adt.launch.DelayedLaunchInfo.InstallRetryMode;
import com.android.ide.eclipse.adt.launch.DeviceChooserDialog.DeviceChooserResponse;
+import com.android.ide.eclipse.adt.project.ApkInstallManager;
import com.android.ide.eclipse.adt.project.ProjectHelper;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
@@ -762,13 +763,46 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
/**
- * Syncs the application on the device/emulator.
+ * If needed, syncs the application and all its dependencies on the device/emulator.
*
* @param launchInfo The Launch information object.
* @param device the device on which to sync the application
* @return true if the install succeeded.
*/
private boolean syncApp(DelayedLaunchInfo launchInfo, IDevice device) {
+ boolean alreadyInstalled = ApkInstallManager.getInstance().isApplicationInstalled(
+ launchInfo.getProject(), device);
+
+ if (alreadyInstalled) {
+ AdtPlugin.printToConsole(launchInfo.getProject(),
+ "Application already deployed. No need to reinstall.");
+ } else {
+ if (doSyncApp(launchInfo, device) == false) {
+ return false;
+ }
+ }
+
+ // The app is now installed, now try the dependent projects
+ for (DelayedLaunchInfo dependentLaunchInfo : getDependenciesLaunchInfo(launchInfo)) {
+ String msg = String.format("Project dependency found, installing: %s",
+ dependentLaunchInfo.getProject().getName());
+ AdtPlugin.printToConsole(launchInfo.getProject(), msg);
+ if (syncApp(dependentLaunchInfo, device) == false) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Syncs the application on the device/emulator.
+ *
+ * @param launchInfo The Launch information object.
+ * @param device the device on which to sync the application
+ * @return true if the install succeeded.
+ */
+ private boolean doSyncApp(DelayedLaunchInfo launchInfo, IDevice device) {
SyncService sync = device.getSyncService();
if (sync != null) {
IPath path = launchInfo.getPackageFile().getLocation();
@@ -812,12 +846,10 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
return false;
}
- // The app is now installed, now try the dependent projects
- for (DelayedLaunchInfo dependentLaunchInfo : getDependenciesLaunchInfo(launchInfo)) {
- String msg = String.format("Project dependency found, syncing: %s",
- dependentLaunchInfo.getProject().getName());
- AdtPlugin.printToConsole(launchInfo.getProject(), msg);
- syncApp(dependentLaunchInfo, device);
+ // if the installation succeeded, we register it.
+ if (installResult) {
+ ApkInstallManager.getInstance().registerInstallation(
+ launchInfo.getProject(), device);
}
return installResult;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ApkInstallManager.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ApkInstallManager.java
new file mode 100644
index 000000000..172c555f4
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ApkInstallManager.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.project;
+
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.Device;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener;
+import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
+import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor;
+import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener;
+
+import org.eclipse.core.resources.IProject;
+
+import java.util.ArrayList;
+
+/**
+ * Registers which apk was installed on which device.
+ *
+ * The goal of this class is to remember the installation of APKs on devices, and provide
+ * information about whether a new APK should be installed on a device prior to running the
+ * application from a launch configuration.
+ *
+ * The manager uses {@link IProject} and {@link IDevice} to identify the target device and the
+ * (project generating the) APK. This ensures that disconnected and reconnected devices will
+ * always receive new APKs (since the APK could be uninstalled manually).
+ *
+ * Manually uninstalling an APK from a connected device will still be a problem, but this should
+ * be a limited use case.
+ *
+ * This is a singleton. To get the instance, use {@link #getInstance()}
+ */
+public class ApkInstallManager implements IDeviceChangeListener, IDebugBridgeChangeListener,
+ IProjectListener {
+
+ private final static ApkInstallManager sThis = new ApkInstallManager();
+
+ /**
+ * Internal struct to associate a project and a device.
+ */
+ private static class ApkInstall {
+ public ApkInstall(IProject project, IDevice device) {
+ this.project = project;
+ this.device = device;
+ }
+ IProject project;
+ IDevice device;
+ }
+
+ private final ArrayList mInstallList = new ArrayList();
+
+ public static ApkInstallManager getInstance() {
+ return sThis;
+ }
+
+ /**
+ * Registers an installation of project onto device
+ * @param project The project that was installed.
+ * @param device The device that received the installation.
+ */
+ public void registerInstallation(IProject project, IDevice device) {
+ synchronized (mInstallList) {
+ mInstallList.add(new ApkInstall(project, device));
+ }
+ }
+
+ /**
+ * Returns whether a project was installed on the device.
+ * @param project the project that may have been installed.
+ * @param device the device that may have received the installation.
+ * @return
+ */
+ public boolean isApplicationInstalled(IProject project, IDevice device) {
+ synchronized (mInstallList) {
+ for (ApkInstall install : mInstallList) {
+ if (project == install.project && device == install.device) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Resets registered installations for a specific {@link IProject}.
+ * This ensures that {@link #isApplicationInstalled(IProject, IDevice)} will always return
+ * null for this specified project, for any device.
+ * @param project the project for which to reset all installations.
+ */
+ public void resetInstallationFor(IProject project) {
+ synchronized (mInstallList) {
+ for (int i = 0 ; i < mInstallList.size() ;) {
+ ApkInstall install = mInstallList.get(i);
+ if (install.project == project) {
+ mInstallList.remove(i);
+ } else {
+ i++;
+ }
+ }
+ }
+ }
+
+ private ApkInstallManager() {
+ AndroidDebugBridge.addDeviceChangeListener(this);
+ AndroidDebugBridge.addDebugBridgeChangeListener(this);
+ ResourceMonitor.getMonitor().addProjectListener(this);
+ }
+
+ /*
+ * Responds to a bridge change by clearing the full installation list.
+ * (non-Javadoc)
+ * @see com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener#bridgeChanged(com.android.ddmlib.AndroidDebugBridge)
+ */
+ public void bridgeChanged(AndroidDebugBridge bridge) {
+ // the bridge changed, there is no way to know which IDevice will be which.
+ // We reset everything
+ synchronized (mInstallList) {
+ mInstallList.clear();
+ }
+ }
+
+ /*
+ * Responds to a device being disconnected by removing all installations related to this device.
+ * (non-Javadoc)
+ * @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceDisconnected(com.android.ddmlib.Device)
+ */
+ public void deviceDisconnected(Device device) {
+ synchronized (mInstallList) {
+ for (int i = 0 ; i < mInstallList.size() ;) {
+ ApkInstall install = mInstallList.get(i);
+ if (install.device == device) {
+ mInstallList.remove(i);
+ } else {
+ i++;
+ }
+ }
+ }
+ }
+
+ /*
+ * Responds to a close project by resetting all its installation.
+ * (non-Javadoc)
+ * @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectClosed(org.eclipse.core.resources.IProject)
+ */
+ public void projectClosed(IProject project) {
+ resetInstallationFor(project);
+ }
+
+ /*
+ * Responds to a close project by resetting all its installation.
+ * (non-Javadoc)
+ * @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectDeleted(org.eclipse.core.resources.IProject)
+ */
+ public void projectDeleted(IProject project) {
+ resetInstallationFor(project);
+ }
+
+ /*
+ * Does nothing
+ * (non-Javadoc)
+ * @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceChanged(com.android.ddmlib.Device, int)
+ */
+ public void deviceChanged(Device device, int changeMask) {
+ // nothing to do.
+ }
+
+ /*
+ * Does nothing
+ * (non-Javadoc)
+ * @see com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener#deviceConnected(com.android.ddmlib.Device)
+ */
+ public void deviceConnected(Device device) {
+ // nothing to do.
+ }
+
+ /*
+ * Does nothing
+ * (non-Javadoc)
+ * @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectOpened(org.eclipse.core.resources.IProject)
+ */
+ public void projectOpened(IProject project) {
+ // nothing to do.
+ }
+
+ /*
+ * Does nothing
+ * (non-Javadoc)
+ * @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener#projectOpenedWithWorkspace(org.eclipse.core.resources.IProject)
+ */
+ public void projectOpenedWithWorkspace(IProject project) {
+ // nothing to do.
+ }
+}
From 69811e352661a8d545470f785c77dc5c7edd6d24 Mon Sep 17 00:00:00 2001
From: Amith Yamasani <>
Date: Tue, 31 Mar 2009 10:51:16 -0700
Subject: [PATCH 040/127] AI 143659: am: CL 143472 Reduce dictionary size.
Changed the tree structure to have variable length nodes to save an average
of 21% on the dictionary size. Created a shortened English dictionary for
Dream - 50K words. Added a shortened Spanish dictionary for Dream - 32K
words. Original author: yamasani Merged from: //branches/cupcake/...
Automated import of CL 143659
---
.../tools/dict/MakeBinaryDictionary.java | 100 ++++++++++++------
1 file changed, 67 insertions(+), 33 deletions(-)
diff --git a/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java b/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java
index cbe702872..8a8a677da 100755
--- a/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java
+++ b/tools/makedict/src/com/android/tools/dict/MakeBinaryDictionary.java
@@ -45,6 +45,10 @@ public class MakeBinaryDictionary {
public static final String TAG_WORD = "w";
public static final String ATTR_FREQ = "f";
+ private static final int FLAG_ADDRESS_MASK = 0x400000;
+ private static final int FLAG_TERMINAL_MASK = 0x800000;
+ private static final int ADDRESS_MASK = 0x3FFFFF;
+
public static final CharNode EMPTY_NODE = new CharNode();
List roots;
@@ -179,7 +183,7 @@ public class MakeBinaryDictionary {
parent.children.add(child);
}
child.data = data;
- child.freq += occur;
+ if (child.freq == 0) child.freq = occur;
if (word.length() > charAt + 1) {
addWordRec(child, word, charAt + 1, occur);
} else {
@@ -195,56 +199,76 @@ public class MakeBinaryDictionary {
static final int ADDR_WIDTH = 23; // Offset to children
static final int FREQ_WIDTH_BYTES = 1;
static final int COUNT_WIDTH_BYTES = 1;
- static final int NODE_SIZE_BYTES =
- (CHAR_WIDTH + FLAGS_WIDTH + ADDR_WIDTH) / 8 + FREQ_WIDTH_BYTES;
private void addCount(int count) {
dict[dictSize++] = (byte) (0xFF & count);
}
- /* TODO: Allow characters to be beyond the 0-255 range. This is required for some latin
- language not currently supported */
private void addNode(CharNode node) {
int charData = 0xFFFF & node.data;
if (charData > 254) {
- System.out.println("WARNING: Non-ASCII character encountered : " + node.data +
- ", value = " + charData);
- dict[dictSize++] = '@';
+ dict[dictSize++] = (byte) 255;
+ dict[dictSize++] = (byte) ((node.data >> 8) & 0xFF);
+ dict[dictSize++] = (byte) (node.data & 0xFF);
} else {
dict[dictSize++] = (byte) (0xFF & node.data);
}
- dictSize += 3; // Space for children address
- if ((0xFFFFFF & node.freq) > 255) {
- node.freq = (byte) 255;
+ if (node.children != null) {
+ dictSize += 3; // Space for children address
+ } else {
+ dictSize += 1; // Space for just the terminal/address flags
+ }
+ if ((0xFFFFFF & node.freq) > 255) {
+ node.freq = 255;
+ }
+ if (node.terminal) {
+ byte freq = (byte) (0xFF & node.freq);
+ dict[dictSize++] = freq;
}
- dict[dictSize++] = (byte) (0xFF & node.freq);
}
+ int nullChildrenCount = 0;
+ int notTerminalCount = 0;
+
private void updateNodeAddress(int nodeAddress, CharNode node,
int childrenAddress) {
- childrenAddress = 0x7FFFFF & childrenAddress;
+ if ((dict[nodeAddress] & 0xFF) == 0xFF) { // 3 byte character
+ nodeAddress += 2;
+ }
+ childrenAddress = ADDRESS_MASK & childrenAddress;
+ if (childrenAddress == 0) {
+ nullChildrenCount++;
+ } else {
+ childrenAddress |= FLAG_ADDRESS_MASK;
+ }
if (node.terminal) {
- childrenAddress |= 0x800000;
+ childrenAddress |= FLAG_TERMINAL_MASK;
+ } else {
+ notTerminalCount++;
}
dict[nodeAddress + 1] = (byte) (childrenAddress >> 16);
- dict[nodeAddress + 2] = (byte) ((childrenAddress & 0xFF00) >> 8);
- dict[nodeAddress + 3] = (byte) ((childrenAddress & 0xFF));
+ if ((childrenAddress & FLAG_ADDRESS_MASK) != 0) {
+ dict[nodeAddress + 2] = (byte) ((childrenAddress & 0xFF00) >> 8);
+ dict[nodeAddress + 3] = (byte) ((childrenAddress & 0xFF));
+ }
}
void writeWordsRec(List children) {
if (children == null || children.size() == 0) {
return;
}
- addCount(children.size());
- int childrenStart = dictSize;
- for (int j = 0; j < children.size(); j++) {
+ final int childCount = children.size();
+ addCount(childCount);
+ //int childrenStart = dictSize;
+ int[] childrenAddresses = new int[childCount];
+ for (int j = 0; j < childCount; j++) {
CharNode node = children.get(j);
+ childrenAddresses[j] = dictSize;
addNode(node);
}
- for (int j = 0; j < children.size(); j++) {
+ for (int j = 0; j < childCount; j++) {
CharNode node = children.get(j);
- // TODO: Fix this when child length becomes variable
- int nodeAddress = childrenStart + NODE_SIZE_BYTES * j;
+ int nodeAddress = childrenAddresses[j];
int cacheDictSize = dictSize;
writeWordsRec(node.children);
updateNodeAddress(nodeAddress, node, node.children != null
@@ -253,8 +277,8 @@ public class MakeBinaryDictionary {
}
void writeToDict(String dictFilename) {
- // 2MB max
- dict = new byte[2 * 1024 * 1024]; // 2MB upper limit. Actual is probably
+ // 4MB max, 22-bit offsets
+ dict = new byte[4 * 1024 * 1024]; // 4MB upper limit. Actual is probably
// < 1MB in most cases, as there is a limit in the
// resource size in apks.
dictSize = 0;
@@ -272,19 +296,29 @@ public class MakeBinaryDictionary {
void traverseDict(int pos, char[] word, int depth) {
int count = dict[pos++] & 0xFF;
for (int i = 0; i < count; i++) {
- char c = (char) (dict[pos] & 0xFF);
- word[depth] = c;
- if ((dict[pos + 1] & 0x80) > 0) {
- showWord(word, depth + 1, dict[pos + 4] & 0xFF);
+ char c = (char) (dict[pos++] & 0xFF);
+ if (c == 0xFF) {
+ c = (char) (((dict[pos] & 0xFF) << 8) | (dict[pos+1] & 0xFF));
+ pos += 2;
+ }
+ word[depth] = c;
+ boolean terminal = (dict[pos] & 0x80) > 0;
+ int address = 0;
+ if ((dict[pos] & (FLAG_ADDRESS_MASK >> 16)) > 0) {
+ address =
+ ((dict[pos + 0] & (FLAG_ADDRESS_MASK >> 16)) << 16)
+ | ((dict[pos + 1] & 0xFF) << 8)
+ | ((dict[pos + 2] & 0xFF));
+ pos += 2;
+ }
+ pos++;
+ if (terminal) {
+ showWord(word, depth + 1, dict[pos] & 0xFF);
+ pos++;
}
- int address =
- ((dict[pos + 1] & 0x7F) << 16)
- | ((dict[pos + 2] & 0xFF) << 8)
- | ((dict[pos + 3] & 0xFF));
if (address != 0) {
traverseDict(address, word, depth + 1);
}
- pos += NODE_SIZE_BYTES;
}
}
From 2be623a30071e4b1eec5641ee24c76f523f4904f Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Tue, 31 Mar 2009 10:56:49 -0700
Subject: [PATCH 041/127] AI 143665: am: CL 143491 ADT #1742875: Document the
SDK build process and new cupcake SDK changes. Original author: raphael
Merged from: //branches/cupcake/...
Automated import of CL 143665
---
docs/howto_build_SDK.txt | 280 +++++++++++++++++++++++++
docs/howto_use_cupcake_sdk.txt | 371 +++++++++++++++++++++++++++++++++
2 files changed, 651 insertions(+)
create mode 100644 docs/howto_build_SDK.txt
create mode 100644 docs/howto_use_cupcake_sdk.txt
diff --git a/docs/howto_build_SDK.txt b/docs/howto_build_SDK.txt
new file mode 100644
index 000000000..4b6507d4a
--- /dev/null
+++ b/docs/howto_build_SDK.txt
@@ -0,0 +1,280 @@
+Subject: How to build an Android SDK & ADT Eclipse plugin.
+Date: 2009/03/27
+
+
+Table of content:
+ 0- License
+ 1- Foreword
+ 2- Building an SDK for MacOS and Linux
+ 3- Building an SDK for Windows
+ 4- Building an ADT plugin for Eclipse
+ 5- Conclusion
+
+
+
+----------
+0- License
+----------
+
+ Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
+-----------
+1- Foreword
+-----------
+
+This document explains how to build the Android SDK and the ADT Eclipse plugin.
+
+It is designed for advanced users which are proficient with command-line
+operations and know how to setup the pre-required software.
+
+Basically it's not trivial yet when done right it's not that complicated.
+
+
+
+--------------------------------------
+2- Building an SDK for MacOS and Linux
+--------------------------------------
+
+First, setup your development environment and get the Android source code from
+git as explained here:
+
+ http://source.android.com/download
+
+For example for the cupcake branch:
+
+ $ mkdir ~/my-android-git
+ $ cd ~/my-android-git
+ $ repo init -u git://android.git.kernel.org/platform/manifest.git -b cupcake
+ $ repo sync
+
+Then once you have all the source, simply build the SDK using:
+
+ $ cd ~/my-android-git
+ $ . build/envsetup.sh
+ $ make sdk
+
+This will take a while, maybe between 20 minutes and several hours depending on
+your machine. After a while you'll see this in the output:
+
+ Package SDK: out/host/darwin-x86/sdk/android-sdk_eng._mac-x86.zip
+
+Some options:
+
+- Depending on your machine you can tell 'make' to build more things in
+ parallel, e.g. if you have a dual core, use "make -j4 sdk" to build faster.
+
+- You can define "BUILD_NUMBER" to control the build identifier that gets
+ incorporated in the resulting archive. The default is to use your username.
+ One suggestion is to include the date, e.g.:
+
+ $ export BUILD_NUMBER=${USER}-`date +%Y%m%d-%H%M%S`
+
+ There are certain characters you should avoid in the build number, typically
+ everything that might confuse 'make' or your shell. So for example avoid
+ punctuation and characters like $ & : / \ < > , and .
+
+
+
+------------------------------
+3- Building an SDK for Windows
+------------------------------
+
+A- SDK pre-requisite
+--------------------
+
+First you need to build an SDK for MacOS and Linux. The Windows build works by
+updating an existing MacOS or Linux SDK zip file and replacing the unix
+binaries by Windows binaries.
+
+
+
+B- Cygwin pre-requisite & code checkout
+---------------------------------------
+
+Second you need to install Cygwin and configure it:
+- Get the installer at http://sources.redhat.com/cygwin/
+- When installing Cygwin, set Default Text File Type to Unix/binary, not DOS/text.
+ This is really important, otherwise you will get errors when trying to
+ checkout code using git.
+- Packages that you must install or not:
+ - Required packages: autoconf, bison, curl, flex, gcc, g++, git, gnupg, make,
+ mingw-zlib, python, zip, unzip.
+ - Suggested extra packages: diffutils, emacs, openssh, rsync, vim, wget.
+ - Packages that must not be installed: readline.
+
+Once you installed Cygwin properly, checkout the code from git as you did
+for MacOS or Linux. Make sure to get the same branch, and if possible keep
+it as close to the other one as possible:
+
+ $ mkdir ~/my-android-git
+ $ cd ~/my-android-git
+ $ repo init -u git://android.git.kernel.org/platform/manifest.git -b cupcake
+ $ repo sync
+
+
+
+C- Building the Windows SDK
+---------------------------
+
+Now it's time to build that Windows SDK. You need:
+- The path to the MacOS or Linux SDK zip.
+- A directory where to place the final SDK. It will also hold some temporary
+ files.
+- The build number will be extracted from the SDK zip filename, but this will
+ only work if that build number has no underscores in it. It is suggested you
+ just define SDK_NUMBER (and not BUILD_NUMBER!) on the command line before
+ invoking the script.
+
+ Note that the "SDK number" is really a free identifier of your choice. It
+ doesn't need to be strictly a number. As always it is suggested you avoid
+ too much punctuation and special shell/make characters. Underscores cannot
+ be used.
+
+
+To summarize, the steps on the command line would be something like this:
+
+ $ mkdir ~/mysdk
+ $ export SDK_NUMBER=${USER}-`date +%Y%m%d-%H%M%S`
+ $ cd ~/my-android-git
+ $ development/build/tools/make_windows_sdk.sh /path/to/macos/or/linux/sdk.zip ~/mysdk
+
+This will take a while to build some Windows-specific binaries, including the
+emulator, unzip the previous zip, rename & replace things and rezip the final
+Windows SDK zip file. A typical build time should be around 5-10 minutes.
+
+
+
+-------------------------------------
+4- Building an ADT plugin for Eclipse
+-------------------------------------
+
+Requirements:
+- You can currently only build an ADT plugin for Eclipse under Linux.
+- You must have a working version of Eclipse 3.4 "ganymede" RCP installed.
+- You need X11 to run Eclipse at least once.
+- You need a lot of patience. The trick is to do the initial setup correctly
+ once, after it's a piece of cake.
+
+
+
+A- Pre-requisites
+-----------------
+
+Note for Ubuntu or Debian users: your apt repository probably only has Eclipse
+3.2 available and it's probably not suitable to build plugins in the first
+place. Forget that and install a working 3.4 manually as described below.
+
+- Visit http://www.eclipse.org/downloads/ to grab the
+ "Eclipse for RCP/Plug-in Developers (176 MB)" download for Linux.
+ 32-bit and 64-bit versions are available, depending on your Linux installation.
+
+ Note: we've always used a 32-bit one, so use the 64-bit one at your own risk.
+
+ Note: Eclipse comes in various editions. Do yourself a favor and just stick
+ to the RCP for building this plugin. For example the J2EE contains too many
+ useless features that will get in the way, and the "Java" version lacks some
+ plugins you need to build other plugins. Please just use the RCP one.
+
+- Unpack "eclipse-rcp-ganymede-SR2-linux-gtk.tar.gz" in the directory of
+ your choice, e.g.:
+
+ $ mkdir ~/eclipse-3.4
+ $ cd ~/eclipse-3.4
+ $ tar xvzf eclipse-rcp-ganymede-SR2-linux-gtk.tar.gz
+
+ This will create an "eclipse" directory in the current directory.
+
+- Set ECLIPSE_HOME to that "eclipse" directory:
+
+ $ export ECLIPSE_HOME=~/eclipse-3.4/eclipse
+
+ Note: it is important you set ECLIPSE_HOME before starting the build.
+ Otherwise the build process will try to download and install its own Eclipse
+ installation in /buildroot, which is probably limited to root.
+
+- Now, before you can build anything, it is important that you start Eclipse
+ *manually* once using the same user that you will use to build later. That's
+ because your Eclipse installation is not finished: Eclipse must be run at
+ least once to create some files in ~/.eclipse/. So run Eclipse now:
+
+ $ ~/eclipse-3.4/eclipse/eclipse &
+
+ Wait for it load, create a workspace when requested and then simply quit
+ using the File > Quit menu. That's it. You won't need to run it manually
+ again.
+
+
+
+B- Building ADT
+---------------
+
+Finally, you have Eclipse, it's installed and it created its own config files,
+so now you can build your ADT plugin. To do that you'll change directories to
+your git repository and invoke the build script by giving it a destination
+directory and an optional build number:
+
+ $ mkdir ~/mysdk
+ $ cd ~/my-android-git # <-- this is where you did your "repo sync"
+ $ development/tools/eclipse/scripts/build_server.sh ~/mysdk $USER
+
+The first argument is the destination directory. It must be absolute. Do not
+give a relative destination directory such as "../mysdk". This will make the
+Eclipse build fail with a cryptic message:
+
+ BUILD SUCCESSFUL
+ Total time: 1 minute 5 seconds
+ **** Package in ../mysdk
+ Error: Build failed to produce ../mysdk/android-eclipse
+ Aborting
+
+The second argument is the build "number". The example used "$USER" but it
+really is a free identifier of your choice. It cannot contain spaces nor
+periods (dashes are ok.) If the build number is missing, a build timestamp will
+be used instead in the filename.
+
+The build should take something like 5-10 minutes.
+
+
+When the build succeeds, you'll see something like this at the end of the
+output:
+
+ ZIP of Update site available at ~/mysdk/android-eclipse-v200903272328.zip
+or
+ ZIP of Update site available at ~/mysdk/android-eclipse-.zip
+
+When you load the plugin in Eclipse, its feature and plugin name will look like
+"com.android.ide.eclipse.adt_0.9.0.v200903272328-.jar". The
+internal plugin ID is always composed of the package, the build timestamp and
+then your own build identifier (a.k.a. the "build number"), if provided. This
+means successive builds with the same build identifier are incremental and
+Eclipse will know how to update to more recent ones.
+
+
+
+-------------
+5- Conclusion
+-------------
+
+This completes the howto guide on building your own SDK and ADT plugin.
+Feedback is welcome on the public Android Open Source forums:
+ http://source.android.com/discuss
+
+If you are upgrading from a pre-cupcake to a cupcake or later SDK please read
+the accompanying document "howto_use_cupcake_sdk.txt".
+
+-end-
+
diff --git a/docs/howto_use_cupcake_sdk.txt b/docs/howto_use_cupcake_sdk.txt
new file mode 100644
index 000000000..b13123043
--- /dev/null
+++ b/docs/howto_use_cupcake_sdk.txt
@@ -0,0 +1,371 @@
+Subject: How to build use a Cupcake Android SDK & ADT Eclipse plugin.
+Date: 2009/03/27
+
+
+Table of content:
+ 0- License
+ 1- Foreword
+ 2- Installation steps
+ 3- For Eclipse users
+ 4- For Ant users
+ 5- Targets, AVDs, Emulator changes
+ 6- Conclusion
+
+
+
+----------
+0- License
+----------
+
+ Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
+-----------
+1- Foreword
+-----------
+
+This explains how to use the "new" SDK provided starting with cupcake.
+The new SDK has as a different structure than the pre-cupcake ones.
+
+This means:
+- The new SDK does not work with older Eclipse plugins (ADT 0.8)
+- The old SDKs (1.0 and 1.1) do NOT work with this Eclipse plugin (ADT 0.9)
+
+
+
+----------------------
+2- Installation steps
+----------------------
+
+First you will need to grab the zip of the SDK for your platform or build it
+yourself. Please refer to the accompanying document "howto_build_SDK.txt" if
+needed.
+
+Unzip the SDK somewhere. We'll call that directory "SDK" in command-line
+examples.
+
+Grab the new ADT Eclipse plugin zip file or build it yourself. Keep it
+somewhere (no need to unzip).
+
+
+
+--------------------
+3- For Eclipse users
+--------------------
+
+
+Below we'll explain how you can upgrade your Eclipse install to the new plugin.
+If you already have a working Eclipse installation with a pre-0.9 ADT,
+another suggestion is to simply install a new copy of Eclipse and create a
+new empty workspace. This is just a precaution. The update process should
+be otherwise harmless.
+
+
+
+A- Setting up Eclipse
+---------------------
+
+- You must have Eclipse 3.3 or 3.4. Eclipse 3.2 is not longer supported.
+
+ There are many flavors, or "editions", of Eclipse. To develop, we'd recommend
+ the "Java" edition. The "RCP" one is totally suitable too. The J2EE one is
+ probably overkill.
+
+
+- If updating an existing Eclipse, use Help > Software Update and please
+ uninstall the two features of the previous ADT: the "editors" feature and the
+ ADT feature itself.
+
+ => If you don't you will get a conflict on editors when installing
+ the new one.
+
+- Using Help > Software Update, add a new "archived site", point it to the new
+ adt.zip (e.g. android-eclipse-.zip), select the "Install" button at
+ the top right and restart eclipse as needed.
+
+- After it restarts, please use Window > Preferences > Android and select
+ the new SDK folder that you unzipped in paragraph 2.
+
+
+
+B- Updating older projects
+--------------------------
+
+If you have pre-0.9 projects in your Eclipse workspace, or if you import them
+from your code repository, these projects will fail to build at first.
+
+First right-click on the project and select "Properties":
+
+- In the properties, open the Android panel and select the platform to use.
+ The SDK comes with a 1.5 platform. Select it and close the properties panel.
+- Do a clean build.
+
+
+The new plugin creates a "gen" folder in your project where it puts the R.java
+and all automatically generated AIDL java files. If you get an error such as:
+
+ "The type R is already defined"
+
+that means you must check to see if your old R.java or your old auto-generated
+AIDL Java files are still present in the "src" folder. If yes, remove them.
+
+Note: this does not apply to your own hand-crafted parcelable AIDL java files.
+
+Note: if you want to reuse the project with an older Eclipse ADT install,
+ simply remove the "gen" folder from the build path of the project.
+
+
+C- New Wizards
+--------------
+
+The "New Android Project" wizard has been expanded to use the multi-platform
+capabilities of the new SDK.
+
+There is now a "New XML File" wizard that lets you create skeleton XML resource
+files for your Android projects. This makes it easier to create a new layout, a
+new strings file, etc.
+
+Both wizard are available via File > New... as well as new icons in the main
+icon bar. If you do not see the new icons, you may need to use Window > Reset
+Perspective on your Java perspective.
+
+
+Please see step 5 "Emulator changes" below for important details on how to run
+the emulator.
+
+
+
+----------------
+4- For Ant users
+----------------
+
+
+A- build.xml has changed
+------------------------
+
+You must re-create your build.xml file.
+
+First if you had customized your build.xml, make a copy of it:
+
+ $ cd my-project
+ $ cp build.xml build.xml.old
+
+
+Then use the new "android" tool to create a new build.xml:
+
+ $ SDK/tools/android update project --path /path/to/my-project
+
+or
+
+ $ cd my-project
+ $ SDK/tools/android update project --path .
+
+
+A "gen" folder will be created the first time you build and your R.java and
+your AIDL Java files will be generated in this "gen" folder. You MUST remove
+the old R.java and old auto-generated AIDL java files manually. (Note: this
+does not apply to your own hand-crafted parcelabe AIDL java files.)
+
+
+B- Where is activitycreator?
+----------------------------
+
+Note that the "activitycreator" tool has been replaced by the new "android"
+tool too. Example of how to create a new Ant project:
+
+ $ SDK/tools/android create project --path /path/to/my/project --name ProjectName
+ --package com.mycompany.myapp --activity MyActivityClass
+ --target 1 --mode activity
+
+
+Please see paragraph 5 below for important details on how to run the emulator
+and the meaning of that "--target 1" parameter.
+
+
+
+----------------------------------
+5- Targets, AVDs, Emulator changes
+----------------------------------
+
+This applies to BOTH Eclipse and Ant users.
+
+One major change with the emulator is that now you must pre-create an "Android
+Virtual Device" (a.k.a "AVD") before you run the emulator.
+
+
+
+A- What is an AVD and why do I need one?
+----------------------------------------
+
+What is an "AVD"? If you forget, just run:
+
+ $ SDK/tools/emulator -help-virtual-device
+
+ An Android Virtual Device (AVD) models a single virtual device running the
+ Android platform that has, at least, its own kernel, system image and data
+ partition.
+
+There is a lot more explanation given by the emulator. Please run the help
+command given above to read the rest.
+
+The bottom line is that you can create many emulator configurations, or "AVDs",
+each with their own system image and most important each with their own user
+data and SD card data. Then you tell Eclipse or the emulator which one to use
+to debug or run your applications.
+
+
+Note for Eclipse users: eventually there will be a user interface to do all of
+these operations. For right now, please use the command line interface.
+
+
+B- Listing targets and AVDs
+---------------------------
+
+There is a new tool called "android" in the SDK that lets you know which
+"target" and AVDs you can use.
+
+A target is a specific version of Android that you can use. By default the SDK
+comes with an "Android 1.5" target, codenamed "cupcake". In the future there
+will be more versions of Android to use, e.g. "Android 2.0" or specific add-ons
+provided by hardware manufacturers. When you want to run an emulator, you need
+to specify a given flavor of Android: this is the "target".
+
+
+To learn about available targets in your SDK, use this command:
+
+ $ SDK/tools/android list targets
+
+This will give you an output such as:
+
+ Available Android targets:
+ [1] Android 1.5
+ API level: 3
+ Skins: HVGA (default), HVGA-L, HVGA-P, QVGA-L, QVGA-P
+
+Note the "[1]". Later you will need to reference this as "--target 1" on the
+command line.
+
+
+Similarly you can list the available AVDs:
+
+ $ SDK/tools/android list avds
+
+Which might output something as:
+
+ Available Android Virtual Devices:
+ Name: my_avd
+ Path: C:\Users\\.android\avd\my_avd.avd
+ Target: Android 1.5 (API level 3)
+ Skin: 320x480
+ Sdcard: 16M
+
+
+
+C- Creating an AVD
+------------------
+
+To create a configuration:
+
+ $ SDK/tools/android create avd --name my_avd_name --target 1
+
+
+where "target 1" is the index of a target listed by "android list targets".
+
+The AVD name is purely an identifier used to refer to the AVD later.
+Since it is used as directory name, please avoid using shell or path specific
+characters.
+
+To learn the various options available when creating an AVD, simply type:
+
+ $ SDK/tools/android create avd
+
+The android tool will automatically print an explanation of required arguments.
+
+
+
+D- Invoking an AVD from the command-line
+----------------------------------------
+
+To use this AVD in the emulator from the command-line, type:
+
+ $ SDK/tools/emulator @my_avd_name
+
+
+For more options, please consult the emulator help:
+
+ $ SDK/tools/emulator -help-virtual-device
+
+
+
+E- Invoking an AVD from Eclipse
+-------------------------------
+
+By default Android projects in Eclipse have an "automatic target" mode.
+In this mode, when a project is deployed in debug or run, it checks:
+- If there's one running device or emulator, this is used for deployment.
+- If there's more than one running device or emulator, a "device chooser" is
+ shown to let the user select which one to use.
+- If there are no running devices or emulators, ADT looks at available AVDs.
+ If one matches the project configuration (e.g. same API level), it is
+ automatically used.
+
+Alternatively you can edit the "launch configuration" on your Android project
+in Eclipse by selecting the menu Run > Run Configurations. In the "target" tab
+of the configuration, you can choose:
+
+- Manual or automatic targetting mode.
+
+ - Manual means to always present the device chooser.
+ - Automatic is the behavior explained above.
+
+- In automatic mode, which AVD is preferred. If none is selected, the first
+ suitable is used.
+
+
+F- AVD concurrency
+------------------
+
+You can no longer run several emulators at the same time on the same
+configuration.
+
+Before this used to put the second or more emulators in a transient read-only
+mode that would not save user data.
+
+Now you just need to create as many AVDs as you want to run emulators.
+
+For example if you are working on a client/server application for Android, you
+could create a "client" AVD and a "server" AVD then run them both at once. The
+emulator window will show you the AVD name so that you know which one is which.
+
+Example:
+
+ $ SDK/tools/android create avd --name client --target 1 --sdcard 16M --skin HVGA
+ $ SDK/tools/android create avd --name server --target 1 --sdcard 32M --skin HVGA-P
+ $ SDK/tools/emulator @server &
+ $ SDK/tools/emulator @client &
+
+
+
+-------------
+6- Conclusion
+-------------
+
+This completes the howto guide on how to use the new Cupcake SDK.
+Feedback is welcome on the public Android Open Source forums:
+ http://source.android.com/discuss
+
+-end-
+
From db258ddbc797d4c8065480b8501c62fa0f0d590e Mon Sep 17 00:00:00 2001
From: Xavier Ducrohet <>
Date: Tue, 31 Mar 2009 11:02:33 -0700
Subject: [PATCH 042/127] AI 143672: am: CL 143499 Fix the icon for the JUnit
launch shortcut Original author: xav Merged from: //branches/cupcake/...
Automated import of CL 143672
---
tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index 2c1394cd2..a75b8b915 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -551,7 +551,7 @@
point="org.eclipse.debug.ui.launchShortcuts">
From 72731f3420eea590b107c7a03e9c269e609df3f5 Mon Sep 17 00:00:00 2001
From: Brett Chabot <>
Date: Tue, 31 Mar 2009 11:14:05 -0700
Subject: [PATCH 043/127] AI 143688: am: CL 143562 Usability fixes for
runtest.py Original author: brettchabot Merged from:
//branches/cupcake/...
Automated import of CL 143688
---
testrunner/adb_interface.py | 23 +++++++++++++++++------
testrunner/coverage.py | 21 +++++++++++++++++++++
testrunner/logger.py | 17 ++++++++++++++---
testrunner/runtest.py | 16 ++++++++++++++--
4 files changed, 66 insertions(+), 11 deletions(-)
diff --git a/testrunner/adb_interface.py b/testrunner/adb_interface.py
index fb304df9d..ad1b2c94f 100755
--- a/testrunner/adb_interface.py
+++ b/testrunner/adb_interface.py
@@ -297,8 +297,7 @@ class AdbInterface:
WaitForResponseTimedOutError if wait_time elapses and pm still does not
respond.
"""
- logger.Log("Waiting for device package manager for %s seconds..."
- % wait_time)
+ logger.Log("Waiting for device package manager...")
self.SendCommand("wait-for-device")
# Now the device is there, but may not be running.
# Query the package manager with a basic command
@@ -315,7 +314,8 @@ class AdbInterface:
time.sleep(wait_period)
attempts += 1
if not pm_found:
- raise errors.WaitForResponseTimedOutError
+ raise errors.WaitForResponseTimedOutError(
+ "Package manager did not respond after %s seconds" % wait_time)
def Sync(self, retry_count=3):
"""Perform a adb sync.
@@ -331,13 +331,12 @@ class AdbInterface:
output = self.SendCommand("sync", retry_count=retry_count)
if "Read-only file system" in output:
logger.SilentLog(output)
- logger.Log("adb sync failed due to read only fs, retrying")
+ logger.Log("Remounting read-only filesystem")
self.SendCommand("remount")
output = self.SendCommand("sync", retry_count=retry_count)
if "No space left on device" in output:
logger.SilentLog(output)
- logger.Log("adb sync failed due to no space on device, trying shell" +
- " start/stop")
+ logger.Log("Restarting device runtime")
self.SendShellCommand("stop", retry_count=retry_count)
output = self.SendCommand("sync", retry_count=retry_count)
self.SendShellCommand("start", retry_count=retry_count)
@@ -345,3 +344,15 @@ class AdbInterface:
logger.SilentLog(output)
self.WaitForDevicePm()
return output
+
+ def IsDevicePresent(self):
+ """Check if targeted device is present.
+
+ Returns:
+ True if device is present, False otherwise.
+ """
+ output = self.SendShellCommand("ls", retry_count=0)
+ if output.startswith("error:"):
+ return False
+ else:
+ return True
diff --git a/testrunner/coverage.py b/testrunner/coverage.py
index 507c5c79d..39a2ceb21 100755
--- a/testrunner/coverage.py
+++ b/testrunner/coverage.py
@@ -70,6 +70,27 @@ class CoverageGenerator(object):
def EnableCoverageBuild(self):
"""Enable building an Android target with code coverage instrumentation."""
os.environ[self._EMMA_BUILD_FLAG] = "true"
+ #TODO: can emma.jar automagically be added to bootclasspath here?
+
+ def TestDeviceCoverageSupport(self):
+ """Check if device has support for generating code coverage metrics.
+
+ Currently this will check if the emma.jar file is on the device's boot
+ classpath.
+
+ Returns:
+ True if device can support code coverage. False otherwise.
+ """
+ output = self._adb.SendShellCommand("cat init.rc | grep BOOTCLASSPATH | "
+ "grep emma.jar")
+ if len(output) > 0:
+ return True
+ else:
+ logger.Log("Error: Targeted device does not have emma.jar on its "
+ "BOOTCLASSPATH.")
+ logger.Log("Modify the BOOTCLASSPATH entry in system/core/rootdir/init.rc"
+ " to add emma.jar")
+ return False
def ExtractReport(self, test_suite,
device_coverage_path=_DEVICE_COVERAGE_PATH,
diff --git a/testrunner/logger.py b/testrunner/logger.py
index 762c89311..61463a198 100755
--- a/testrunner/logger.py
+++ b/testrunner/logger.py
@@ -25,6 +25,7 @@ import datetime
_LOG_FILE = None
_verbose = False
+_log_time = True
def Init(log_file_path):
"""Set the path to the log file"""
@@ -57,8 +58,13 @@ def _WriteLog(msg):
def _PrependTimeStamp(log_string):
"""Returns the log_string prepended with current timestamp """
- return "# %s: %s" % (datetime.datetime.now().strftime("%m/%d/%y %H:%M:%S"),
- log_string)
+ global _log_time
+ if _log_time:
+ return "# %s: %s" % (datetime.datetime.now().strftime("%m/%d/%y %H:%M:%S"),
+ log_string)
+ else:
+ # timestamp logging disabled
+ return log_string
def SilentLog(new_str):
"""Silently log new_str. Unless verbose mode is enabled, will log new_str
@@ -77,7 +83,12 @@ def SetVerbose(new_verbose=True):
""" Enable or disable verbose logging"""
global _verbose
_verbose = new_verbose
-
+
+def SetTimestampLogging(new_timestamp=True):
+ """ Enable or disable outputting a timestamp with each log entry"""
+ global _log_time
+ _log_time = new_timestamp
+
def main():
pass
diff --git a/testrunner/runtest.py b/testrunner/runtest.py
index bf5bb2232..a4df95034 100755
--- a/testrunner/runtest.py
+++ b/testrunner/runtest.py
@@ -53,6 +53,10 @@ class TestRunner(object):
"The runtest script works in two ways. You can query it "
"for a list of tests, or you can launch one or more tests.")
+ def __init__(self):
+ # disable logging of timestamp
+ logger.SetTimestampLogging(False)
+
def _ProcessOptions(self):
"""Processes command-line options."""
# TODO error messages on once-only or mutually-exclusive options.
@@ -178,10 +182,14 @@ class TestRunner(object):
self._coverage_gen.EnableCoverageBuild()
self._AddBuildTarget(self._coverage_gen.GetEmmaBuildPath(), target_set)
target_build_string = " ".join(list(target_set))
- logger.Log("Building %s" % target_build_string)
+ logger.Log("mmm %s" % target_build_string)
cmd = 'ONE_SHOT_MAKEFILE="%s" make -C "%s" files' % (target_build_string,
self._root_path)
- if not self._options.preview:
+ if self._options.preview:
+ # in preview mode, just display to the user what command would have been
+ # run
+ logger.Log("adb sync")
+ else:
run_command.RunCommand(cmd, return_output=False)
logger.Log("Syncing to device...")
self._adb.Sync()
@@ -259,6 +267,10 @@ class TestRunner(object):
self._DumpTests()
return
+ if not self._adb.IsDevicePresent():
+ logger.Log("Error: specified device cannot be found")
+ return
+
if not self._options.skip_build:
self._DoBuild()
From a256470ec4c04d8c162f1dc3e36968391b9d8a32 Mon Sep 17 00:00:00 2001
From: Yu Shan Emily Lau <>
Date: Tue, 31 Mar 2009 11:25:02 -0700
Subject: [PATCH 044/127] AI 143698: am: CL 143596 Removed all the obsoleted
media related functional test suite. (incl, very old meidaProvider,
RingToneSettings and the flaky Music Player test. Original author: yslau
Merged from: //branches/cupcake/...
Automated import of CL 143698
---
testrunner/tests.xml | 24 ------------------------
1 file changed, 24 deletions(-)
diff --git a/testrunner/tests.xml b/testrunner/tests.xml
index d186af4c7..7e13a35db 100644
--- a/testrunner/tests.xml
+++ b/testrunner/tests.xml
@@ -186,21 +186,6 @@ These attributes map to the following commands:
package="com.android.mediaframeworktest"
runner=".MediaFrameworkUnitTestRunner"
coverage_target="framework" />
-
-
-
-
-
-
-
From 361030c1d85b210c859357d672e10a2f2dcc1e73 Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Tue, 31 Mar 2009 15:10:16 -0700
Subject: [PATCH 045/127] AI 143808: am: CL 143754 SdkManager: list unknown
AVDs and why they didn't load. Original author: raphael Merged from:
//branches/cupcake/...
Automated import of CL 143808
---
.../app/src/com/android/sdkmanager/Main.java | 21 +-
.../com/android/sdklib/avd/AvdManager.java | 205 ++++++++++++++----
2 files changed, 181 insertions(+), 45 deletions(-)
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
index adf37ed0b..191aa9eda 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
@@ -459,11 +459,30 @@ class Main {
mSdkLog.printf(" Sdcard: %s\n", sdcard);
}
}
+
+ // Are there some unused AVDs?
+ List badAvds = avdManager.getUnavailableAvdList();
+
+ if (badAvds == null || badAvds.size() == 0) {
+ return;
+ }
+
+ mSdkLog.printf("\nThe following Android Virtual Devices are no longer available:\n");
+ boolean needSeparator = false;
+ for (AvdInfo info : badAvds) {
+ if (needSeparator) {
+ mSdkLog.printf("---------\n");
+ }
+ mSdkLog.printf(" Name: %s\n", info.getName() == null ? "--" : info.getName());
+ mSdkLog.printf(" Path: %s\n", info.getPath() == null ? "--" : info.getPath());
+ mSdkLog.printf(" Error: %s\n", info.getError() == null ? "--" : info.getError());
+ needSeparator = true;
+ }
} catch (AndroidLocationException e) {
errorAndExit(e.getMessage());
}
}
-
+
/**
* Creates a new AVD. This is a text based creation with command line prompt.
*/
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
index 93577e42b..4342551ec 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
@@ -32,8 +32,11 @@ import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.TreeSet;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -118,15 +121,43 @@ public final class AvdManager {
private final String mPath;
private final IAndroidTarget mTarget;
private final Map mProperties;
-
- /** Creates a new AVD info. Values are immutable.
- * @param properties */
+ private final String mError;
+
+ /**
+ * Creates a new valid AVD info. Values are immutable.
+ *
+ * Such an AVD is available and can be used.
+ * The error string is set to null.
+ *
+ * @param name The name of the AVD (for display or reference)
+ * @param path The path to the config.ini file
+ * @param target The target. Cannot be null.
+ * @param properties The property map. Cannot be null.
+ */
public AvdInfo(String name, String path, IAndroidTarget target,
Map properties) {
+ this(name, path, target, properties, null /*error*/);
+ }
+
+ /**
+ * Creates a new invalid AVD info. Values are immutable.
+ *
+ * Such an AVD is not complete and cannot be used.
+ * The error string must be non-null.
+ *
+ * @param name The name of the AVD (for display or reference)
+ * @param path The path to the config.ini file
+ * @param target The target. Can be null.
+ * @param properties The property map. Can be null.
+ * @param error The error describing why this AVD is invalid. Cannot be null.
+ */
+ public AvdInfo(String name, String path, IAndroidTarget target,
+ Map properties, String error) {
mName = name;
mPath = path;
mTarget = target;
mProperties = properties;
+ mError = error;
}
/** Returns the name of the AVD. */
@@ -144,6 +175,11 @@ public final class AvdManager {
return mTarget;
}
+ /** Returns the error describing why an AVD failed to load. Always null for valid AVDs. */
+ public String getError() {
+ return mError;
+ }
+
/**
* Helper method that returns the .ini {@link File} for a given AVD name.
* @throws AndroidLocationException if there's a problem getting android root directory.
@@ -634,29 +670,27 @@ public final class AvdManager {
}
}
- private void buildAvdList(ArrayList list) throws AndroidLocationException {
+ /**
+ * Returns a list of files that are potential AVD ini files.
+ *
+ * This lists the $HOME/.android/avd/.ini files.
+ * Such files are properties file than then indicate where the AVD folder is located.
+ *
+ * @return A new {@link File} array or null. The array might be empty.
+ * @throws AndroidLocationException if there's a problem getting android root directory.
+ */
+ private File[] buildAvdFilesList() throws AndroidLocationException {
// get the Android prefs location.
String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;
- final boolean avdListDebug = System.getenv("AVD_LIST_DEBUG") != null;
- if (avdListDebug) {
- mSdkLog.printf("[AVD LIST DEBUG] AVD root: '%s'\n", avdRoot);
- }
-
// ensure folder validity.
File folder = new File(avdRoot);
if (folder.isFile()) {
- if (avdListDebug) {
- mSdkLog.printf("[AVD LIST DEBUG] AVD root is a file.\n");
- }
throw new AndroidLocationException(String.format("%s is not a valid folder.", avdRoot));
} else if (folder.exists() == false) {
- if (avdListDebug) {
- mSdkLog.printf("[AVD LIST DEBUG] AVD root folder doesn't exist, creating.\n");
- }
// folder is not there, we create it and return
folder.mkdirs();
- return;
+ return null;
}
File[] avds = folder.listFiles(new FilenameFilter() {
@@ -664,10 +698,6 @@ public final class AvdManager {
if (INI_NAME_PATTERN.matcher(name).matches()) {
// check it's a file and not a folder
boolean isFile = new File(parent, name).isFile();
- if (avdListDebug) {
- mSdkLog.printf("[AVD LIST DEBUG] Item '%s': %s\n",
- name, isFile ? "accepted file" : "rejected");
- }
return isFile;
}
@@ -675,52 +705,130 @@ public final class AvdManager {
}
});
+ return avds;
+ }
+
+ /**
+ * Computes the internal list of available AVDs.
+ * This only contains AVDs that reference the target currently available.
+ *
+ * @param list An array list that will contain the list of AVDs.
+ * @throws AndroidLocationException if there's a problem getting android root directory.
+ */
+ private void buildAvdList(ArrayList list) throws AndroidLocationException {
+
+ File[] avds = buildAvdFilesList();
+
for (File avd : avds) {
- AvdInfo info = parseAvdInfo(avd);
+ AvdInfo info = parseAvdInfo(avd, false /*acceptError*/);
if (info != null) {
list.add(info);
- if (avdListDebug) {
- mSdkLog.printf("[AVD LIST DEBUG] Added AVD '%s'\n", info.getPath());
- }
- } else if (avdListDebug) {
- mSdkLog.printf("[AVD LIST DEBUG] Failed to parse AVD '%s'\n", avd.getPath());
}
}
}
-
- private AvdInfo parseAvdInfo(File path) {
- Map map = SdkManager.parsePropertyFile(path, mSdkLog);
+
+ public List getUnavailableAvdList() throws AndroidLocationException {
+ AvdInfo[] avds = getAvds();
+ File[] allAvds = buildAvdFilesList();
+ if (allAvds == null || allAvds.length == 0) {
+ return null;
+ }
+
+ TreeSet list = new TreeSet(Arrays.asList(allAvds));
+
+ for (AvdInfo info : avds) {
+ if (list.remove(info.getIniFile())) {
+ if (list.size() == 0) {
+ return null;
+ }
+ }
+ }
+ ArrayList errorAvds = new ArrayList(list.size());
+ for (File file : list) {
+ errorAvds.add(parseAvdInfo(file, true /*acceptError*/));
+ }
+
+ return errorAvds;
+ }
+
+ /**
+ * Parses an AVD config.ini to create an {@link AvdInfo}.
+ *
+ * @param path The path to the AVD config.ini
+ * @param acceptError When false, an AVD that fails to load will be discarded and null will be
+ * returned. When true, such an AVD will be returned with an error description.
+ * @return A new {@link AvdInfo} or null if the file is not valid or null if the AVD is not
+ * valid and acceptError is false.
+ */
+ private AvdInfo parseAvdInfo(File path, boolean acceptError) {
+ String error = null;
+ Map map = SdkManager.parsePropertyFile(path, mSdkLog);
+
String avdPath = map.get(AVD_INFO_PATH);
- if (avdPath == null) {
- return null;
- }
-
String targetHash = map.get(AVD_INFO_TARGET);
- if (targetHash == null) {
- return null;
+
+ IAndroidTarget target = null;
+ File configIniFile = null;
+ Map properties = null;
+
+ if (targetHash != null) {
+ target = mSdk.getTargetFromHashString(targetHash);
}
- IAndroidTarget target = mSdk.getTargetFromHashString(targetHash);
- if (target == null) {
- return null;
+ // load the avd properties.
+ if (avdPath != null) {
+ configIniFile = new File(avdPath, CONFIG_INI);
}
- // load the avd properties.
- File configIniFile = new File(avdPath, CONFIG_INI);
- Map properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog);
+ if (configIniFile != null) {
+ properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog);
+ }
+ // get name
+ String name = path.getName();
Matcher matcher = INI_NAME_PATTERN.matcher(path.getName());
-
+ if (matcher.matches()) {
+ name = matcher.group(1);
+ }
+
+ if (!acceptError) {
+ if (avdPath == null ||
+ targetHash == null ||
+ target == null ||
+ configIniFile == null ||
+ properties == null) {
+ return null;
+ }
+ } else {
+ if (avdPath == null || configIniFile == null) {
+ error = String.format("Missing AVD 'path' property in %1$s", name);
+ } else if (targetHash == null) {
+ error = String.format("Missing 'target' property in %1$s", name);
+ } else if (target == null) {
+ error = String.format("Unknown 'target=%2$s' property in %1$s", name, targetHash);
+ } else if (properties == null) {
+ error = String.format("Failed to parse properties from %1$s", avdPath);
+ }
+ }
+
AvdInfo info = new AvdInfo(
- matcher.matches() ? matcher.group(1) : path.getName(), // should not happen
+ name,
avdPath,
target,
- properties);
+ properties,
+ error);
return info;
}
+ /**
+ * Writes a new AVD config.ini file from a set of properties.
+ *
+ * @param iniFile The file to generate.
+ * @param values THe properties to place in the ini file.
+ * @throws IOException if {@link FileWriter} fails to open, write or close the file.
+ */
private static void createConfigIni(File iniFile, Map values)
throws IOException {
FileWriter writer = new FileWriter(iniFile);
@@ -732,6 +840,15 @@ public final class AvdManager {
}
+ /**
+ * Invokes the tool to create a new SD card image file.
+ *
+ * @param toolLocation The path to the mksdcard tool.
+ * @param size The size of the new SD Card, compatible with {@link #SDCARD_SIZE_PATTERN}.
+ * @param location The path of the new sdcard image file to generate.
+ * @param log The logger object, to report errors.
+ * @return True if the sdcard could be created.
+ */
private boolean createSdCard(String toolLocation, String size, String location, ISdkLog log) {
try {
String[] command = new String[3];
From 2d85c0eb069c9bb5cab75987bc288e2e0d97e23f Mon Sep 17 00:00:00 2001
From: Brett Chabot <>
Date: Tue, 31 Mar 2009 15:19:13 -0700
Subject: [PATCH 046/127] AI 143819: am: CL 143765 Rename tests.xml to
test_defs.xml Original author: brettchabot Merged from:
//branches/cupcake/...
Automated import of CL 143819
---
testrunner/Android.mk | 2 +-
testrunner/runtest.py | 8 ++++----
testrunner/{tests.xml => test_defs.xml} | 24 ++++++++++++++++++++++++
3 files changed, 29 insertions(+), 5 deletions(-)
rename testrunner/{tests.xml => test_defs.xml} (91%)
diff --git a/testrunner/Android.mk b/testrunner/Android.mk
index 93c092841..b6bde553b 100644
--- a/testrunner/Android.mk
+++ b/testrunner/Android.mk
@@ -10,7 +10,7 @@ LOCAL_PATH := $(call my-dir)
########################
include $(CLEAR_VARS)
-LOCAL_MODULE := tests.xml
+LOCAL_MODULE := test_defs.xml
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(local_target_dir)
diff --git a/testrunner/runtest.py b/testrunner/runtest.py
index a4df95034..8d3659130 100755
--- a/testrunner/runtest.py
+++ b/testrunner/runtest.py
@@ -41,12 +41,12 @@ class TestRunner(object):
# file path to android core platform tests, relative to android build root
# TODO move these test data files to another directory
- _CORE_TEST_PATH = os.path.join("development", "testrunner", "tests.xml")
+ _CORE_TEST_PATH = os.path.join("development", "testrunner", "test_defs.xml")
# vendor glob file path patterns to tests, relative to android
# build root
_VENDOR_TEST_PATH = os.path.join("vendor", "*", "tests", "testinfo",
- "tests.xml")
+ "test_defs.xml")
_RUNTEST_USAGE = (
"usage: runtest.py [options] short-test-name[s]\n\n"
@@ -61,7 +61,7 @@ class TestRunner(object):
"""Processes command-line options."""
# TODO error messages on once-only or mutually-exclusive options.
user_test_default = os.path.join(os.environ.get("HOME"), ".android",
- "tests.xml")
+ "test_defs.xml")
parser = optparse.OptionParser(usage=self._RUNTEST_USAGE)
@@ -153,7 +153,7 @@ class TestRunner(object):
try:
known_tests = test_defs.TestDefinitions()
known_tests.Parse(core_test_path)
- # read all /vendor/*/tests/testinfo/tests.xml paths
+ # read all /vendor/*/tests/testinfo/test_defs.xml paths
vendor_tests_pattern = os.path.join(self._root_path,
self._VENDOR_TEST_PATH)
test_file_paths = glob.glob(vendor_tests_pattern)
diff --git a/testrunner/tests.xml b/testrunner/test_defs.xml
similarity index 91%
rename from testrunner/tests.xml
rename to testrunner/test_defs.xml
index 7e13a35db..d186af4c7 100644
--- a/testrunner/tests.xml
+++ b/testrunner/test_defs.xml
@@ -186,6 +186,21 @@ These attributes map to the following commands:
package="com.android.mediaframeworktest"
runner=".MediaFrameworkUnitTestRunner"
coverage_target="framework" />
+
+
+
+
+
+
+
From a0d0f0ce62bfb4cd6bfed842c172591d38016d8a Mon Sep 17 00:00:00 2001
From: Jeffrey Sharkey <>
Date: Tue, 31 Mar 2009 16:58:03 -0700
Subject: [PATCH 047/127] AI 143865: am: CL 143864 Fix SDK example to correctly
pass back newly-configured appWidgetId. Original author: jsharkey Merged
from: //branches/cupcake/...
Automated import of CL 143865
---
.../android/apis/appwidget/ExampleAppWidgetConfigure.java | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java b/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java
index e0a4c76b0..d7ef9d632 100644
--- a/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java
+++ b/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java
@@ -89,7 +89,10 @@ public class ExampleAppWidgetConfigure extends Activity {
saveTitlePref(ExampleAppWidgetConfigure.this, mAppWidgetId,
mAppWidgetPrefix.getText().toString());
- setResult(RESULT_OK);
+ // Make sure we pass back the original appWidgetId
+ Intent resultValue = new Intent();
+ resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
+ setResult(RESULT_OK, resultValue);
finish();
}
};
From 3ae1236bc99e54d7584b13f3f657b28f9618dbbf Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Tue, 31 Mar 2009 17:19:15 -0700
Subject: [PATCH 048/127] AI 143879: am: CL 143876 Include hprof-conv in SDK
(bug #1640225) Original author: raphael Merged from:
//branches/cupcake/...
Automated import of CL 143879
---
build/sdk.atree | 1 +
build/tools/make_windows_sdk.sh | 5 +++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/build/sdk.atree b/build/sdk.atree
index 0f62ea0bf..438ab77d8 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -24,6 +24,7 @@ bin/aidl platforms/${PLATFORM_NAME}/tools/aidl
bin/adb tools/adb
bin/sqlite3 tools/sqlite3
bin/dmtracedump tools/dmtracedump
+bin/hprof-conv tools/hprof-conv
bin/mksdcard tools/mksdcard
# other tools
diff --git a/build/tools/make_windows_sdk.sh b/build/tools/make_windows_sdk.sh
index dedf3a785..2abe7b1f2 100755
--- a/build/tools/make_windows_sdk.sh
+++ b/build/tools/make_windows_sdk.sh
@@ -64,7 +64,8 @@ function build() {
make -j 4 emulator || die "Build failed"
# Disable parallel build: it generates "permission denied" issues when
# multiple "ar.exe" are running in parallel.
- make prebuilt adb fastboot aidl aapt dexdump dmtracedump mksdcard sqlite3 || die "Build failed"
+ make prebuilt adb fastboot aidl aapt dexdump dmtracedump hprof-conv mksdcard sqlite3 \
+ || die "Build failed"
}
function package() {
@@ -104,7 +105,7 @@ function package() {
# Remove obsolete stuff from tools & platform
TOOLS="$DEST/tools"
LIB="$DEST/tools/lib"
- rm -v "$TOOLS"/{adb,emulator,traceview,draw9patch,hierarchyviewer,apkbuilder,ddms,dmtracedump,mksdcard,sqlite3,android}
+ rm -v "$TOOLS"/{adb,emulator,traceview,draw9patch,hierarchyviewer,apkbuilder,ddms,dmtracedump,hprof-conv,mksdcard,sqlite3,android}
rm -v --force "$LIB"/*.so "$LIB"/*.jnilib
rm -v "$PLATFORM_TOOLS"/{aapt,aidl,dx,dexdump}
From 1edcab473890abd0859154fd7b65ad9482878697 Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Tue, 31 Mar 2009 17:23:46 -0700
Subject: [PATCH 049/127] AI 143883: am: CL 143881 AVD #1703143: delete AVDs
not loaded correctly. This covers the case where an AVD has an invalid
target or is missing its AVD folder or the config.ini in it. Made some
cosmetic cleanup too. Original author: raphael Merged from:
//branches/cupcake/...
Automated import of CL 143883
---
.../com/android/ide/eclipse/adt/sdk/Sdk.java | 4 +-
.../app/src/com/android/sdkmanager/Main.java | 22 +++-
.../src/com/android/sdklib/ISdkLog.java | 8 +-
.../com/android/sdklib/avd/AvdManager.java | 103 ++++++++++++------
4 files changed, 96 insertions(+), 41 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
index ba0b56803..40b3f76ec 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
@@ -100,7 +100,7 @@ public class Sdk implements IProjectListener {
ISdkLog log = new ISdkLog() {
public void error(Throwable throwable, String errorFormat, Object... arg) {
if (errorFormat != null) {
- logMessages.add(String.format(errorFormat, arg));
+ logMessages.add(String.format("Error: " + errorFormat, arg));
}
if (throwable != null) {
@@ -109,7 +109,7 @@ public class Sdk implements IProjectListener {
}
public void warning(String warningFormat, Object... arg) {
- logMessages.add(String.format(warningFormat, arg));
+ logMessages.add(String.format("Warning: " + warningFormat, arg));
}
public void printf(String msgFormat, Object... arg) {
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
index 191aa9eda..738640214 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
@@ -461,13 +461,13 @@ class Main {
}
// Are there some unused AVDs?
- List badAvds = avdManager.getUnavailableAvdList();
+ List badAvds = avdManager.getUnavailableAvds();
if (badAvds == null || badAvds.size() == 0) {
return;
}
- mSdkLog.printf("\nThe following Android Virtual Devices are no longer available:\n");
+ mSdkLog.printf("\nThe following Android Virtual Devices could not be loaded:\n");
boolean needSeparator = false;
for (AvdInfo info : badAvds) {
if (needSeparator) {
@@ -592,7 +592,7 @@ class Main {
File dir = new File(oldAvdInfo.getPath());
avdManager.recursiveDelete(dir);
dir.delete();
- // Remove old avd info from manager
+ // Remove old AVD info from manager
avdManager.removeAvd(oldAvdInfo);
}
@@ -602,13 +602,27 @@ class Main {
}
/**
- * Delete an AVD.
+ * Delete an AVD. If the AVD name is not part of the available ones look for an
+ * invalid AVD (one not loaded due to some error) to remove it too.
*/
private void deleteAvd() {
try {
String avdName = mSdkCommandLine.getParamName();
AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog);
AvdInfo info = avdManager.getAvd(avdName);
+
+ if (info == null) {
+ // Look in unavailable AVDs
+ List badAvds = avdManager.getUnavailableAvds();
+ if (badAvds != null) {
+ for (AvdInfo i : badAvds) {
+ if (i.getName().equals(avdName)) {
+ info = i;
+ break;
+ }
+ }
+ }
+ }
if (info == null) {
errorAndExit("There is no Android Virtual Device named '%s'.", avdName);
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java
index 489451746..163f7a950 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/ISdkLog.java
@@ -26,8 +26,10 @@ public interface ISdkLog {
/**
* Prints a warning message on stdout.
*
+ * The message will be tagged with "Warning" on the output so the caller does not
+ * need to put such a prefix in the format string.
+ *
* Implementations should only display warnings in verbose mode.
- * The message should be prefixed with "Warning:".
*
* @param warningFormat is an optional error format. If non-null, it will be printed
* using a {@link Formatter} with the provided arguments.
@@ -38,8 +40,10 @@ public interface ISdkLog {
/**
* Prints an error message on stderr.
*
+ * The message will be tagged with "Error" on the output so the caller does not
+ * need to put such a prefix in the format string.
+ *
* Implementation should always display errors, independent of verbose mode.
- * The message should be prefixed with "Error:".
*
* @param t is an optional {@link Throwable} or {@link Exception}. If non-null, it's
* message will be printed out.
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
index 4342551ec..a6326630e 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
@@ -279,7 +279,7 @@ public final class AvdManager {
// AVD shouldn't already exist if removePrevious is false.
if (log != null) {
log.error(null,
- "Folder %s is in the way. Use --force if you want to overwrite.",
+ "Folder %1$s is in the way. Use --force if you want to overwrite.",
avdFolder.getAbsolutePath());
}
return null;
@@ -429,9 +429,9 @@ public final class AvdManager {
if (log != null) {
if (target.isPlatform()) {
- log.printf("Created AVD '%s' based on %s\n", name, target.getName());
+ log.printf("Created AVD '%1$s' based on %2$s\n", name, target.getName());
} else {
- log.printf("Created AVD '%s' based on %s (%s)\n", name, target.getName(),
+ log.printf("Created AVD '%1$s' based on %2$s (%3$s)\n", name, target.getName(),
target.getVendor());
}
}
@@ -563,29 +563,49 @@ public final class AvdManager {
*
* This also remove it from the manager's list, The caller does not need to
* call {@link #removeAvd(AvdInfo)} afterwards.
+ *
+ * This method is designed to somehow work with an unavailable AVD, that is an AVD that
+ * could not be loaded due to some error. That means this method still tries to remove
+ * the AVD ini file or its folder if it can be found. An error will be output if any of
+ * these operations fail.
*
* @param avdInfo the information on the AVD to delete
*/
public void deleteAvd(AvdInfo avdInfo, ISdkLog log) {
try {
+ boolean error = false;
+
File f = avdInfo.getIniFile();
- if (f.exists()) {
- log.warning("Deleting file %s", f.getCanonicalPath());
+ if (f != null && f.exists()) {
+ log.warning("Deleting file %1$s", f.getCanonicalPath());
if (!f.delete()) {
- log.error(null, "Failed to delete %s", f.getCanonicalPath());
+ log.error(null, "Failed to delete %1$s", f.getCanonicalPath());
+ error = true;
}
}
-
- f = new File(avdInfo.getPath());
- if (f.exists()) {
- log.warning("Deleting folder %s", f.getCanonicalPath());
- recursiveDelete(f);
- if (!f.delete()) {
- log.error(null, "Failed to delete %s", f.getCanonicalPath());
+
+ String path = avdInfo.getPath();
+ if (path != null) {
+ f = new File(path);
+ if (f.exists()) {
+ log.warning("Deleting folder %1$s", f.getCanonicalPath());
+ recursiveDelete(f);
+ if (!f.delete()) {
+ log.error(null, "Failed to delete %1$s", f.getCanonicalPath());
+ error = true;
+ }
}
}
removeAvd(avdInfo);
+
+ if (error) {
+ log.printf("AVD '%1$s' deleted with errors. See warnings above.",
+ avdInfo.getName());
+ } else {
+ log.printf("AVD '%1$s' deleted.", avdInfo.getName());
+ }
+
} catch (AndroidLocationException e) {
log.error(e, null);
} catch (IOException e) {
@@ -611,14 +631,14 @@ public final class AvdManager {
try {
if (paramFolderPath != null) {
File f = new File(avdInfo.getPath());
- log.warning("Moving '%s' to '%s'.", avdInfo.getPath(), paramFolderPath);
+ log.warning("Moving '%1$s' to '%2$s'.", avdInfo.getPath(), paramFolderPath);
if (!f.renameTo(new File(paramFolderPath))) {
- log.error(null, "Failed to move '%s' to '%s'.",
+ log.error(null, "Failed to move '%1$s' to '%2$s'.",
avdInfo.getPath(), paramFolderPath);
return false;
}
- // update avd info
+ // update AVD info
AvdInfo info = new AvdInfo(avdInfo.getName(), paramFolderPath, avdInfo.getTarget(),
avdInfo.getProperties());
mAvdList.remove(avdInfo);
@@ -633,19 +653,22 @@ public final class AvdManager {
File oldIniFile = avdInfo.getIniFile();
File newIniFile = AvdInfo.getIniFile(newName);
- log.warning("Moving '%s' to '%s'.", oldIniFile.getPath(), newIniFile.getPath());
+ log.warning("Moving '%1$s' to '%2$s'.", oldIniFile.getPath(), newIniFile.getPath());
if (!oldIniFile.renameTo(newIniFile)) {
- log.error(null, "Failed to move '%s' to '%s'.",
+ log.error(null, "Failed to move '%1$s' to '%2$s'.",
oldIniFile.getPath(), newIniFile.getPath());
return false;
}
- // update avd info
+ // update AVD info
AvdInfo info = new AvdInfo(newName, avdInfo.getPath(), avdInfo.getTarget(),
avdInfo.getProperties());
mAvdList.remove(avdInfo);
mAvdList.add(info);
}
+
+ log.printf("AVD '%1$s' moved.", avdInfo.getName());
+
} catch (AndroidLocationException e) {
log.error(e, null);
} catch (IOException e) {
@@ -686,7 +709,8 @@ public final class AvdManager {
// ensure folder validity.
File folder = new File(avdRoot);
if (folder.isFile()) {
- throw new AndroidLocationException(String.format("%s is not a valid folder.", avdRoot));
+ throw new AndroidLocationException(
+ String.format("%1$s is not a valid folder.", avdRoot));
} else if (folder.exists() == false) {
// folder is not there, we create it and return
folder.mkdirs();
@@ -727,7 +751,21 @@ public final class AvdManager {
}
}
- public List getUnavailableAvdList() throws AndroidLocationException {
+ /**
+ * Computes the internal list of not available AVDs.
+ *
+ * These are the AVDs that failed to load for some reason or another.
+ * You can retrieve the load error using {@link AvdInfo#getError()}.
+ *
+ * These {@link AvdInfo} must not be used for usual operations (e.g. instanciating
+ * an emulator) or trying to use them for anything else but {@link #deleteAvd(AvdInfo, ISdkLog)}
+ * will have unpredictable results -- that is most likely the operation will fail.
+ *
+ * @return A list of unavailable AVDs, all with errors. The list can be null or empty if there
+ * are no AVDs to return.
+ * @throws AndroidLocationException if there's a problem getting android root directory.
+ */
+ public List getUnavailableAvds() throws AndroidLocationException {
AvdInfo[] avds = getAvds();
File[] allAvds = buildAvdFilesList();
if (allAvds == null || allAvds.length == 0) {
@@ -776,7 +814,7 @@ public final class AvdManager {
target = mSdk.getTargetFromHashString(targetHash);
}
- // load the avd properties.
+ // load the AVD properties.
if (avdPath != null) {
configIniFile = new File(avdPath, CONFIG_INI);
}
@@ -806,7 +844,7 @@ public final class AvdManager {
} else if (targetHash == null) {
error = String.format("Missing 'target' property in %1$s", name);
} else if (target == null) {
- error = String.format("Unknown 'target=%2$s' property in %1$s", name, targetHash);
+ error = String.format("Unknown target '%2$s' in %1$s", name, targetHash);
} else if (properties == null) {
error = String.format("Failed to parse properties from %1$s", avdPath);
}
@@ -834,7 +872,7 @@ public final class AvdManager {
FileWriter writer = new FileWriter(iniFile);
for (Entry entry : values.entrySet()) {
- writer.write(String.format("%s=%s\n", entry.getKey(), entry.getValue()));
+ writer.write(String.format("%1$s=%2$s\n", entry.getKey(), entry.getValue()));
}
writer.close();
@@ -861,26 +899,25 @@ public final class AvdManager {
ArrayList stdOutput = new ArrayList();
int status = grabProcessOutput(process, errorOutput, stdOutput,
true /* waitForReaders */);
-
- if (status != 0) {
- log.error(null, "Failed to create the SD card.");
+
+ if (status == 0) {
+ return true;
+ } else {
for (String error : errorOutput) {
log.error(null, error);
}
-
- return false;
}
- return true;
} catch (InterruptedException e) {
- log.error(null, "Failed to create the SD card.");
+ // pass, print error below
} catch (IOException e) {
- log.error(null, "Failed to create the SD card.");
+ // pass, print error below
}
+ log.error(null, "Failed to create the SD card.");
return false;
}
-
+
/**
* Gets the stderr/stdout outputs of a process and returns when the process is done.
* Both must be read or the process will block on windows.
From 37747492adef6682c9ce01680ee2222be2031fe4 Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Tue, 31 Mar 2009 17:27:19 -0700
Subject: [PATCH 050/127] AI 143886: am: CL 143882 ADT #1743364: Refactor misc
UI widgets together in package adt.ui. Original author: raphael Merged
from: //branches/cupcake/...
Automated import of CL 143886
---
.../com.android.ide.eclipse.adt/META-INF/MANIFEST.MF | 2 +-
.../src/com/android/ide/eclipse/adt/AdtPlugin.java | 2 +-
.../extractstring/ExtractStringInputPage.java | 2 +-
.../wizards => adt/ui}/ConfigurationSelector.java | 2 +-
.../ide/eclipse/{common => adt/ui}/EclipseUiHelper.java | 2 +-
.../wizards => adt/ui}/ReferenceChooserDialog.java | 2 +-
.../{editors/wizards => adt/ui}/ResourceChooser.java | 2 +-
.../wizards => adt/ui}/ResourceContentProvider.java | 2 +-
.../wizards => adt/ui}/ResourceLabelProvider.java | 2 +-
.../adt/wizards/newxmlfile/NewXmlFileCreationPage.java | 4 ++--
.../ide/eclipse/editors/layout/GraphicalLayoutEditor.java | 8 ++++----
.../ide/eclipse/editors/layout/LayoutCreatorDialog.java | 4 ++--
.../android/ide/eclipse/editors/layout/LayoutEditor.java | 2 +-
.../ide/eclipse/editors/layout/UiContentOutlinePage.java | 2 +-
.../editors/resources/explorer/ResourceExplorerView.java | 4 ++--
.../eclipse/editors/uimodel/UiResourceAttributeNode.java | 4 ++--
16 files changed, 23 insertions(+), 23 deletions(-)
rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/{editors/wizards => adt/ui}/ConfigurationSelector.java (99%)
rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/{common => adt/ui}/EclipseUiHelper.java (98%)
rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/{editors/wizards => adt/ui}/ReferenceChooserDialog.java (99%)
rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/{editors/wizards => adt/ui}/ResourceChooser.java (99%)
rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/{editors/wizards => adt/ui}/ResourceContentProvider.java (98%)
rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/{editors/wizards => adt/ui}/ResourceLabelProvider.java (99%)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
index 0ec97aa38..4b9d3a007 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
@@ -52,6 +52,7 @@ Export-Package: com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.
com.android.ide.eclipse.adt.project.internal;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.sdk;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.wizards.newproject;x-friends:="com.android.ide.eclipse.tests",
+ com.android.ide.eclipse.adt.ui;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.common;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.common.project;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.common.resources;x-friends:="com.android.ide.eclipse.tests",
@@ -77,7 +78,6 @@ Export-Package: com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.
com.android.ide.eclipse.editors.ui;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.ui.tree;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.uimodel;x-friends:="com.android.ide.eclipse.tests",
- com.android.ide.eclipse.editors.wizards;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.xml;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.editors.xml.descriptors;x-friends:="com.android.ide.eclipse.tests"
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
index 42db64a61..4a7a002e2 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
@@ -29,8 +29,8 @@ import com.android.ide.eclipse.adt.sdk.AndroidTargetParser;
import com.android.ide.eclipse.adt.sdk.LoadStatus;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
+import com.android.ide.eclipse.adt.ui.EclipseUiHelper;
import com.android.ide.eclipse.common.AndroidConstants;
-import com.android.ide.eclipse.common.EclipseUiHelper;
import com.android.ide.eclipse.common.SdkStatsHelper;
import com.android.ide.eclipse.common.StreamHelper;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
index 5ffeeb05f..9822b32e7 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
@@ -17,10 +17,10 @@
package com.android.ide.eclipse.adt.refactorings.extractstring;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector;
import com.android.sdklib.SdkConstants;
import org.eclipse.core.resources.IFolder;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ConfigurationSelector.java
similarity index 99%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ConfigurationSelector.java
index 4a05b1ee5..651d1e016 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ConfigurationSelector.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ConfigurationSelector.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.editors.wizards;
+package com.android.ide.eclipse.adt.ui;
import com.android.ide.eclipse.editors.resources.configurations.CountryCodeQualifier;
import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/EclipseUiHelper.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/EclipseUiHelper.java
similarity index 98%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/EclipseUiHelper.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/EclipseUiHelper.java
index 6dc856233..55878bf28 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/EclipseUiHelper.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/EclipseUiHelper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.common;
+package com.android.ide.eclipse.adt.ui;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java
similarity index 99%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java
index 6913ce07d..e141396c5 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ReferenceChooserDialog.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.editors.wizards;
+package com.android.ide.eclipse.adt.ui;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.common.resources.IResourceRepository;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceChooser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceChooser.java
similarity index 99%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceChooser.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceChooser.java
index 60a627b52..4290f6b79 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceChooser.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceChooser.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.editors.wizards;
+package com.android.ide.eclipse.adt.ui;
import com.android.ide.eclipse.common.resources.IResourceRepository;
import com.android.ide.eclipse.common.resources.ResourceItem;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceContentProvider.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceContentProvider.java
similarity index 98%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceContentProvider.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceContentProvider.java
index 7c6a539ca..3792fe312 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceContentProvider.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceContentProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.editors.wizards;
+package com.android.ide.eclipse.adt.ui;
import com.android.ide.eclipse.common.resources.IResourceRepository;
import com.android.ide.eclipse.common.resources.ResourceItem;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceLabelProvider.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceLabelProvider.java
similarity index 99%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceLabelProvider.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceLabelProvider.java
index 024d08433..f7c26346a 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/ResourceLabelProvider.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceLabelProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.editors.wizards;
+package com.android.ide.eclipse.adt.ui;
import com.android.ide.eclipse.common.resources.IIdResourceItem;
import com.android.ide.eclipse.common.resources.ResourceItem;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java
index f3cbfa221..f85050444 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newxmlfile/NewXmlFileCreationPage.java
@@ -21,6 +21,8 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector.ConfigurationState;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.ProjectChooserHelper;
import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor;
@@ -31,8 +33,6 @@ import com.android.ide.eclipse.editors.resources.configurations.FolderConfigurat
import com.android.ide.eclipse.editors.resources.configurations.ResourceQualifier;
import com.android.ide.eclipse.editors.resources.descriptors.ResourcesDescriptors;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.ConfigurationState;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java
index 12d49fe27..e22382034 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java
@@ -22,6 +22,10 @@ import com.android.ide.eclipse.adt.sdk.LoadStatus;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.adt.sdk.AndroidTargetData.LayoutBridge;
import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector.DensityVerifier;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector.DimensionVerifier;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector.LanguageRegionVerifier;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector.MobileCodeVerifier;
import com.android.ide.eclipse.common.resources.ResourceType;
import com.android.ide.eclipse.editors.IconFactory;
import com.android.ide.eclipse.editors.layout.LayoutEditor.UiEditorActions;
@@ -55,10 +59,6 @@ import com.android.ide.eclipse.editors.ui.tree.CopyCutAction;
import com.android.ide.eclipse.editors.ui.tree.PasteAction;
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.DensityVerifier;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.DimensionVerifier;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.LanguageRegionVerifier;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.MobileCodeVerifier;
import com.android.layoutlib.api.ILayoutLog;
import com.android.layoutlib.api.ILayoutResult;
import com.android.layoutlib.api.IProjectCallback;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java
index c4a8f5cc1..69402381a 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutCreatorDialog.java
@@ -16,12 +16,12 @@
package com.android.ide.eclipse.editors.layout;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector.ConfigurationState;
import com.android.ide.eclipse.editors.IconFactory;
import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
import com.android.ide.eclipse.editors.resources.configurations.ResourceQualifier;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector;
-import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.ConfigurationState;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TrayDialog;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java
index f3a5113a9..7bed7ce6a 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java
@@ -18,8 +18,8 @@ package com.android.ide.eclipse.editors.layout;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
+import com.android.ide.eclipse.adt.ui.EclipseUiHelper;
import com.android.ide.eclipse.common.AndroidConstants;
-import com.android.ide.eclipse.common.EclipseUiHelper;
import com.android.ide.eclipse.editors.AndroidEditor;
import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor;
import com.android.ide.eclipse.editors.resources.manager.ResourceFolder;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java
index 536e9020b..4e0e35f06 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java
@@ -17,7 +17,7 @@
package com.android.ide.eclipse.editors.layout;
-import com.android.ide.eclipse.common.EclipseUiHelper;
+import com.android.ide.eclipse.adt.ui.EclipseUiHelper;
import com.android.ide.eclipse.editors.IconFactory;
import com.android.ide.eclipse.editors.layout.parts.UiDocumentTreeEditPart;
import com.android.ide.eclipse.editors.layout.parts.UiElementTreeEditPart;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java
index d1d88917f..b61eddc4d 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/explorer/ResourceExplorerView.java
@@ -17,14 +17,14 @@
package com.android.ide.eclipse.editors.resources.explorer;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.ui.ResourceContentProvider;
+import com.android.ide.eclipse.adt.ui.ResourceLabelProvider;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.editors.resources.manager.ProjectResourceItem;
import com.android.ide.eclipse.editors.resources.manager.ProjectResources;
import com.android.ide.eclipse.editors.resources.manager.ResourceFile;
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IResourceEventListener;
-import com.android.ide.eclipse.editors.wizards.ResourceContentProvider;
-import com.android.ide.eclipse.editors.wizards.ResourceLabelProvider;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java
index 32cac9f2b..654e792cc 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java
@@ -17,6 +17,8 @@
package com.android.ide.eclipse.editors.uimodel;
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
+import com.android.ide.eclipse.adt.ui.ReferenceChooserDialog;
+import com.android.ide.eclipse.adt.ui.ResourceChooser;
import com.android.ide.eclipse.common.resources.IResourceRepository;
import com.android.ide.eclipse.common.resources.ResourceItem;
import com.android.ide.eclipse.common.resources.ResourceType;
@@ -26,8 +28,6 @@ import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
import com.android.ide.eclipse.editors.ui.SectionHelper;
-import com.android.ide.eclipse.editors.wizards.ReferenceChooserDialog;
-import com.android.ide.eclipse.editors.wizards.ResourceChooser;
import org.eclipse.core.resources.IProject;
import org.eclipse.jface.window.Window;
From 47eccd8c05c7ef66b1d7892011ae4bea56fcdf98 Mon Sep 17 00:00:00 2001
From: Brett Chabot <>
Date: Tue, 31 Mar 2009 19:15:42 -0700
Subject: [PATCH 051/127] AI 143918: am: CL 143917 ADT Android JUnit: Change
logic to provide an explicit project or package to run to the device
InstrumentationTestRunner, instead of providing the potentially huge list of
test classes. Discontinue support for running all tests in a source folder.
Original author: brettchabot Merged from: //branches/cupcake/...
Automated import of CL 143918
---
.../testrunner/RemoteAndroidTestRunner.java | 11 ++
.../RemoteAndroidTestRunnerTest.java | 16 ++-
.../META-INF/MANIFEST.MF | 3 +-
.../com.android.ide.eclipse.adt/plugin.xml | 12 +-
.../junit/AndroidJUnitLaunchAction.java | 68 ++++-----
.../AndroidJUnitLaunchConfigDelegate.java | 83 ++++++++++-
.../AndroidJUnitLaunchConfigurationTab.java | 34 ++++-
.../junit/AndroidJUnitPropertyTester.java | 130 ++++++++++++++++++
.../junit/runtime/AndroidJUnitLaunchInfo.java | 103 ++++++++++++--
.../junit/runtime/RemoteAdtTestRunner.java | 24 ++--
10 files changed, 404 insertions(+), 80 deletions(-)
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
index 999542634..9dd1d1640 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
@@ -49,6 +49,7 @@ public class RemoteAndroidTestRunner {
private static final String LOG_ARG_NAME = "log";
private static final String DEBUG_ARG_NAME = "debug";
private static final String COVERAGE_ARG_NAME = "coverage";
+ private static final String PACKAGE_ARG_NAME = "package";
/**
* Creates a remote Android test runner.
@@ -145,6 +146,16 @@ public class RemoteAndroidTestRunner {
setClassName(className + METHOD_SEPARATOR + testName);
}
+ /**
+ * Sets to run all tests in specified package
+ * Must be called before 'run'.
+ *
+ * @param packageName fully qualified package name (eg x.y.z)
+ */
+ public void setTestPackageName(String packageName) {
+ addInstrumentationArg(PACKAGE_ARG_NAME, packageName);
+ }
+
/**
* Adds a argument to include in instrumentation command.
*
diff --git a/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java b/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
index 6a653ad05..864e219fc 100644
--- a/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
+++ b/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
@@ -17,16 +17,17 @@
package com.android.ddmlib.testrunner;
import com.android.ddmlib.Client;
-import com.android.ddmlib.Device.DeviceState;
import com.android.ddmlib.FileListingService;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.IShellOutputReceiver;
-import com.android.ddmlib.log.LogReceiver;
import com.android.ddmlib.RawImage;
import com.android.ddmlib.SyncService;
+import com.android.ddmlib.Device.DeviceState;
+import com.android.ddmlib.log.LogReceiver;
import java.io.IOException;
import java.util.Map;
+
import junit.framework.TestCase;
/**
@@ -80,6 +81,17 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
testName, TEST_PACKAGE, TEST_RUNNER), mMockDevice.getLastShellCommand());
}
+ /**
+ * Test the building of the instrumentation runner command with test package set.
+ */
+ public void testRunWithPackage() {
+ final String packageName = "foo.test";
+ mRunner.setTestPackageName(packageName);
+ mRunner.run(new EmptyListener());
+ assertStringsEquals(String.format("am instrument -w -r -e package %s %s/%s", packageName,
+ TEST_PACKAGE, TEST_RUNNER), mMockDevice.getLastShellCommand());
+ }
+
/**
* Test the building of the instrumentation runner command with extra argument added.
*/
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
index 4b9d3a007..8092f3a5e 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF
@@ -43,7 +43,8 @@ Require-Bundle: com.android.ide.eclipse.ddms,
org.eclipse.jdt.junit,
org.eclipse.jdt.junit.runtime,
org.eclipse.ltk.core.refactoring,
- org.eclipse.ltk.ui.refactoring
+ org.eclipse.ltk.ui.refactoring,
+ org.eclipse.core.expressions
Eclipse-LazyStart: true
Export-Package: com.android.ide.eclipse.adt;x-friends:="com.android.ide.eclipse.tests",
com.android.ide.eclipse.adt.build;x-friends:="com.android.ide.eclipse.tests",
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index a75b8b915..35ceba76e 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -563,7 +563,7 @@
-
+
@@ -595,4 +595,14 @@
id="com.android.ide.eclipse.adt.refactoring.extract.string">
+
+
+
+
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
index 747fcfe5c..9bcc63d06 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
@@ -24,7 +24,6 @@ import com.android.ide.eclipse.adt.launch.junit.runtime.RemoteAdtTestRunner;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchManager;
@@ -39,18 +38,15 @@ import org.eclipse.jdt.launching.VMRunnerConfiguration;
*/
class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
- private String mTestPackage;
- private String mRunner;
+ private final AndroidJUnitLaunchInfo mLaunchInfo;
/**
* Creates a AndroidJUnitLaunchAction.
*
- * @param testPackage the Android application package that contains the tests to run
- * @param runner the InstrumentationTestRunner that will execute the tests
+ * @param launchInfo the {@link AndroidJUnitLaunchInfo} for the JUnit run
*/
- public AndroidJUnitLaunchAction(String testPackage, String runner) {
- mTestPackage = testPackage;
- mRunner = runner;
+ public AndroidJUnitLaunchAction(AndroidJUnitLaunchInfo launchInfo) {
+ mLaunchInfo = launchInfo;
}
/**
@@ -60,17 +56,21 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
* @see IAndroidLaunchAction#doLaunchAction(DelayedLaunchInfo, IDevice)
*/
public boolean doLaunchAction(DelayedLaunchInfo info, IDevice device) {
- String msg = String.format("Launching instrumentation %s on device %s", mRunner,
- device.getSerialNumber());
+ String msg = String.format("Launching instrumentation %s on device %s",
+ mLaunchInfo.getRunner(), device.getSerialNumber());
AdtPlugin.printToConsole(info.getProject(), msg);
try {
- JUnitLaunchDelegate junitDelegate = new JUnitLaunchDelegate(info, device);
+ mLaunchInfo.setDebugMode(info.isDebugMode());
+ mLaunchInfo.setDevice(info.getDevice());
+ mLaunchInfo.setLaunch(info.getLaunch());
+ JUnitLaunchDelegate junitDelegate = new JUnitLaunchDelegate(mLaunchInfo);
final String mode = info.isDebugMode() ? ILaunchManager.DEBUG_MODE :
ILaunchManager.RUN_MODE;
+
junitDelegate.launch(info.getLaunch().getLaunchConfiguration(), mode, info.getLaunch(),
info.getMonitor());
-
+
// TODO: need to add AMReceiver-type functionality somewhere
} catch (CoreException e) {
AdtPlugin.printErrorToConsole(info.getProject(), "Failed to launch test");
@@ -82,20 +82,18 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
* {@inheritDoc}
*/
public String getLaunchDescription() {
- return String.format("%s JUnit launch", mRunner);
+ return String.format("%s JUnit launch", mLaunchInfo.getRunner());
}
/**
* Extends the JDT JUnit launch delegate to allow for JUnit UI reuse.
*/
- private class JUnitLaunchDelegate extends JUnitLaunchConfigurationDelegate {
+ private static class JUnitLaunchDelegate extends JUnitLaunchConfigurationDelegate {
- private IDevice mDevice;
- private DelayedLaunchInfo mLaunchInfo;
+ private AndroidJUnitLaunchInfo mLaunchInfo;
- public JUnitLaunchDelegate(DelayedLaunchInfo info, IDevice device) {
- mLaunchInfo = info;
- mDevice = device;
+ public JUnitLaunchDelegate(AndroidJUnitLaunchInfo launchInfo) {
+ mLaunchInfo = launchInfo;
}
/* (non-Javadoc)
@@ -110,34 +108,28 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
/**
* {@inheritDoc}
- * @throws CoreException
* @see org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate#verifyMainTypeName(org.eclipse.debug.core.ILaunchConfiguration)
*/
@Override
- public String verifyMainTypeName(ILaunchConfiguration configuration) throws CoreException {
+ public String verifyMainTypeName(ILaunchConfiguration configuration) {
return "com.android.ide.eclipse.adt.junit.internal.runner.RemoteAndroidTestRunner"; //$NON-NLS-1$
}
/**
* Overrides parent to return a VM Runner implementation which launches a thread, rather
* than a separate VM process
- * @throws CoreException
*/
@Override
- public IVMRunner getVMRunner(ILaunchConfiguration configuration, String mode)
- throws CoreException {
- return new VMTestRunner(new AndroidJUnitLaunchInfo(mLaunchInfo.getProject(),
- mTestPackage, mRunner, mLaunchInfo.isDebugMode(), mDevice));
+ public IVMRunner getVMRunner(ILaunchConfiguration configuration, String mode) {
+ return new VMTestRunner(mLaunchInfo);
}
/**
* {@inheritDoc}
- * @throws CoreException
* @see org.eclipse.debug.core.model.LaunchConfigurationDelegate#getLaunch(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String)
*/
@Override
- public ILaunch getLaunch(ILaunchConfiguration configuration, String mode)
- throws CoreException {
+ public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) {
return mLaunchInfo.getLaunch();
}
}
@@ -161,7 +153,7 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
IProgressMonitor monitor) throws CoreException {
TestRunnerProcess runnerProcess =
- new TestRunnerProcess(config, launch, mJUnitInfo);
+ new TestRunnerProcess(config, mJUnitInfo);
runnerProcess.start();
launch.addProcess(runnerProcess);
}
@@ -173,15 +165,12 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
private static class TestRunnerProcess extends Thread implements IProcess {
private final VMRunnerConfiguration mRunConfig;
- private final ILaunch mLaunch;
private final AndroidJUnitLaunchInfo mJUnitInfo;
private RemoteAdtTestRunner mTestRunner = null;
private boolean mIsTerminated = false;
- TestRunnerProcess(VMRunnerConfiguration runConfig, ILaunch launch,
- AndroidJUnitLaunchInfo info) {
+ TestRunnerProcess(VMRunnerConfiguration runConfig, AndroidJUnitLaunchInfo info) {
mRunConfig = runConfig;
- mLaunch = launch;
mJUnitInfo = info;
}
@@ -194,10 +183,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
/**
* {@inheritDoc}
- * @throws DebugException
* @see org.eclipse.debug.core.model.IProcess#getExitValue()
*/
- public int getExitValue() throws DebugException {
+ public int getExitValue() {
return 0;
}
@@ -205,14 +193,14 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
* @see org.eclipse.debug.core.model.IProcess#getLabel()
*/
public String getLabel() {
- return mLaunch.getLaunchMode();
+ return mJUnitInfo.getLaunch().getLaunchMode();
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IProcess#getLaunch()
*/
public ILaunch getLaunch() {
- return mLaunch;
+ return mJUnitInfo.getLaunch();
}
/* (non-Javadoc)
@@ -254,10 +242,9 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
/**
* {@inheritDoc}
- * @throws DebugException
* @see org.eclipse.debug.core.model.ITerminate#terminate()
*/
- public void terminate() throws DebugException {
+ public void terminate() {
if (mTestRunner != null) {
mTestRunner.terminate();
}
@@ -274,3 +261,4 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
}
}
}
+
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
index fa8e4b01a..543daf06f 100755
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
@@ -22,6 +22,7 @@ import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration;
import com.android.ide.eclipse.adt.launch.AndroidLaunchController;
import com.android.ide.eclipse.adt.launch.IAndroidLaunchAction;
import com.android.ide.eclipse.adt.launch.LaunchConfigDelegate;
+import com.android.ide.eclipse.adt.launch.junit.runtime.AndroidJUnitLaunchInfo;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
@@ -32,8 +33,11 @@ import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants;
import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry;
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
/**
* Run configuration that can execute JUnit tests on an Android platform.
@@ -47,6 +51,7 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
/** Launch config attribute that stores instrumentation runner. */
static final String ATTR_INSTR_NAME = AdtPlugin.PLUGIN_ID + ".instrumentation"; //$NON-NLS-1$
+
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
@Override
@@ -55,7 +60,7 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
AndroidLaunchConfiguration config, AndroidLaunchController controller,
IFile applicationPackage, AndroidManifestParser manifestParser) {
- String testPackage = manifestParser.getPackage();
+ String appPackage = manifestParser.getPackage();
String runner = getRunner(project, configuration, manifestParser);
if (runner == null) {
AdtPlugin.displayError("Android Launch",
@@ -63,14 +68,62 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
androidLaunch.stopLaunch();
return;
}
+ AndroidJUnitLaunchInfo junitLaunchInfo = new AndroidJUnitLaunchInfo(project, appPackage,
+ runner);
+ junitLaunchInfo.setTestClass(getTestClass(configuration));
+ junitLaunchInfo.setTestPackage(getTestPackage(configuration));
+ junitLaunchInfo.setTestMethod(getTestMethod(configuration));
- IAndroidLaunchAction junitLaunch = new AndroidJUnitLaunchAction(testPackage, runner);
+ IAndroidLaunchAction junitLaunch = new AndroidJUnitLaunchAction(junitLaunchInfo);
controller.launch(project, mode, applicationPackage, manifestParser.getPackage(),
manifestParser.getDebuggable(), manifestParser.getApiLevelRequirement(),
junitLaunch, config, androidLaunch, monitor);
}
+ /**
+ * Returns the test package stored in the launch configuration, or null if not
+ * specified.
+ *
+ * @param configuration the {@link ILaunchConfiguration} to retrieve the test package info from
+ * @return the test package or null.
+ */
+ private String getTestPackage(ILaunchConfiguration configuration) {
+ // try to retrieve a package name from the JUnit container attribute
+ String containerHandle = getStringLaunchAttribute(
+ JUnitLaunchConfigurationConstants.ATTR_TEST_CONTAINER, configuration);
+ if (containerHandle != null && containerHandle.length() > 0) {
+ IJavaElement element = JavaCore.create(containerHandle);
+ // containerHandle could be a IProject, check if its a java package
+ if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
+ return element.getElementName();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the test class stored in the launch configuration.
+ *
+ * @param configuration the {@link ILaunchConfiguration} to retrieve the test class info from
+ * @return the test class. null if not specified.
+ */
+ private String getTestClass(ILaunchConfiguration configuration) {
+ return getStringLaunchAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME,
+ configuration);
+ }
+
+ /**
+ * Returns the test method stored in the launch configuration.
+ *
+ * @param configuration the {@link ILaunchConfiguration} to retrieve the test method info from
+ * @return the test method. null if not specified.
+ */
+ private String getTestMethod(ILaunchConfiguration configuration) {
+ return getStringLaunchAttribute(JUnitLaunchConfigurationConstants.ATTR_TEST_METHOD_NAME,
+ configuration);
+ }
+
/**
* Gets a instrumentation runner for the launch.
*
@@ -114,11 +167,29 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
}
private String getRunnerFromConfig(ILaunchConfiguration configuration) throws CoreException {
- String runner = configuration.getAttribute(ATTR_INSTR_NAME, EMPTY_STRING);
- if (runner.length() < 1) {
- return null;
+ return getStringLaunchAttribute(ATTR_INSTR_NAME, configuration);
+ }
+
+ /**
+ * Helper method to retrieve a string attribute from the launch configuration
+ *
+ * @param attributeName name of the launch attribute
+ * @param configuration the {@link ILaunchConfiguration} to retrieve the attribute from
+ * @return the attribute's value. null if not found.
+ */
+ private String getStringLaunchAttribute(String attributeName,
+ ILaunchConfiguration configuration) {
+ try {
+ String attrValue = configuration.getAttribute(attributeName, EMPTY_STRING);
+ if (attrValue.length() < 1) {
+ return null;
+ }
+ return attrValue;
+ } catch (CoreException e) {
+ AdtPlugin.log(e, String.format("Error when retrieving launch info %1$s", //$NON-NLS-1$
+ attributeName));
}
- return runner;
+ return null;
}
/**
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
index eb5748269..584d45eb1 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
@@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.launch.junit;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.launch.MainLaunchConfigTab;
import com.android.ide.eclipse.common.AndroidConstants;
+import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.common.project.ProjectChooserHelper;
import org.eclipse.core.resources.IProject;
@@ -241,7 +242,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
private void createTestContainerSelectionGroup(Composite comp) {
mTestContainerRadioButton = new Button(comp, SWT.RADIO);
mTestContainerRadioButton.setText(
- JUnitMessages.JUnitLaunchConfigurationTab_label_containerTest);
+ "Run all tests in the selected project, or package");
GridData gd = new GridData();
gd.horizontalSpan = 3;
mTestContainerRadioButton.setLayoutData(gd);
@@ -249,12 +250,12 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
public void widgetSelected(SelectionEvent e) {
if (mTestContainerRadioButton.getSelection()) {
testModeChanged();
- }
+ }
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
-
+
mContainerText = new Text(comp, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY);
gd = new GridData(GridData.FILL_HORIZONTAL);
gd.horizontalIndent = 25;
@@ -265,7 +266,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
updateLaunchConfigurationDialog();
}
});
-
+
mContainerSearchButton = new Button(comp, SWT.PUSH);
mContainerSearchButton.setText(JUnitMessages.JUnitLaunchConfigurationTab_label_search);
mContainerSearchButton.addSelectionListener(new SelectionAdapter() {
@@ -821,7 +822,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
@SuppressWarnings("unchecked")
private IJavaElement chooseContainer(IJavaElement initElement) {
- Class[] acceptedClasses = new Class[] { IPackageFragmentRoot.class, IJavaProject.class,
+ Class[] acceptedClasses = new Class[] { IJavaProject.class,
IPackageFragment.class };
TypedElementSelectionValidator validator = new TypedElementSelectionValidator(
acceptedClasses, false) {
@@ -839,7 +840,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
if (element instanceof IPackageFragmentRoot &&
((IPackageFragmentRoot) element).isArchive()) {
return false;
- }
+ }
try {
if (element instanceof IPackageFragment &&
!((IPackageFragment) element).hasChildren()) {
@@ -852,7 +853,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
}
};
- StandardJavaElementContentProvider provider = new StandardJavaElementContentProvider();
+ AndroidJavaElementContentProvider provider = new AndroidJavaElementContentProvider();
ILabelProvider labelProvider = new JavaElementLabelProvider(
JavaElementLabelProvider.SHOW_DEFAULT);
ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(getShell(),
@@ -974,4 +975,23 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
mInstrumentations = null;
mInstrumentationCombo.removeAll();
}
+
+ /**
+ * Overrides the {@link StandardJavaElementContentProvider} to only display Android projects
+ */
+ private static class AndroidJavaElementContentProvider
+ extends StandardJavaElementContentProvider {
+
+ /**
+ * Override parent to return only Android projects if at the root. Otherwise, use parent
+ * functionality.
+ */
+ @Override
+ public Object[] getChildren(Object element) {
+ if (element instanceof IJavaModel) {
+ return BaseProjectHelper.getAndroidProjects((IJavaModel) element);
+ }
+ return super.getChildren(element);
+ }
+ }
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java
new file mode 100644
index 000000000..eadafee77
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitPropertyTester.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.eclipse.adt.launch.junit;
+
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jdt.core.IClassFile;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IMember;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.internal.junit.util.TestSearchEngine;
+
+/**
+ * A {@link PropertyTester} that checks if selected elements can be run as Android
+ * JUnit tests.
+ *
+ * Based on org.eclipse.jdt.internal.junit.JUnitPropertyTester. The only substantial difference in
+ * this implementation is source folders cannot be run as Android JUnit.
+ */
+@SuppressWarnings("restriction")
+public class AndroidJUnitPropertyTester extends PropertyTester {
+ private static final String PROPERTY_IS_TEST = "isTest"; //$NON-NLS-1$
+
+ private static final String PROPERTY_CAN_LAUNCH_AS_JUNIT_TEST = "canLaunchAsJUnit"; //$NON-NLS-1$
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.internal.corext.refactoring.participants.properties.IPropertyEvaluator#test(java.lang.Object, java.lang.String, java.lang.String)
+ */
+ public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+ if (!(receiver instanceof IAdaptable)) {
+ final String elementName = (receiver == null ? "null" : //$NON-NLS-1$
+ receiver.getClass().getName());
+ throw new IllegalArgumentException(
+ String.format("Element must be of type IAdaptable, is %s", //$NON-NLS-1$
+ elementName));
+ }
+
+ IJavaElement element;
+ if (receiver instanceof IJavaElement) {
+ element = (IJavaElement) receiver;
+ } else if (receiver instanceof IResource) {
+ element = JavaCore.create((IResource) receiver);
+ if (element == null) {
+ return false;
+ }
+ } else { // is IAdaptable
+ element= (IJavaElement) ((IAdaptable) receiver).getAdapter(IJavaElement.class);
+ if (element == null) {
+ IResource resource = (IResource) ((IAdaptable) receiver).getAdapter(
+ IResource.class);
+ element = JavaCore.create(resource);
+ if (element == null) {
+ return false;
+ }
+ }
+ }
+ if (PROPERTY_IS_TEST.equals(property)) {
+ return isJUnitTest(element);
+ } else if (PROPERTY_CAN_LAUNCH_AS_JUNIT_TEST.equals(property)) {
+ return canLaunchAsJUnitTest(element);
+ }
+ throw new IllegalArgumentException(
+ String.format("Unknown test property '%s'", property)); //$NON-NLS-1$
+ }
+
+ private boolean canLaunchAsJUnitTest(IJavaElement element) {
+ try {
+ switch (element.getElementType()) {
+ case IJavaElement.JAVA_PROJECT:
+ return true; // can run, let JDT detect if there are tests
+ case IJavaElement.PACKAGE_FRAGMENT_ROOT:
+ return false; // not supported by Android test runner
+ case IJavaElement.PACKAGE_FRAGMENT:
+ return ((IPackageFragment) element).hasChildren();
+ case IJavaElement.COMPILATION_UNIT:
+ case IJavaElement.CLASS_FILE:
+ case IJavaElement.TYPE:
+ case IJavaElement.METHOD:
+ return isJUnitTest(element);
+ default:
+ return false;
+ }
+ } catch (JavaModelException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Return whether the target resource is a JUnit test.
+ */
+ private boolean isJUnitTest(IJavaElement element) {
+ try {
+ IType testType = null;
+ if (element instanceof ICompilationUnit) {
+ testType = (((ICompilationUnit) element)).findPrimaryType();
+ } else if (element instanceof IClassFile) {
+ testType = (((IClassFile) element)).getType();
+ } else if (element instanceof IType) {
+ testType = (IType) element;
+ } else if (element instanceof IMember) {
+ testType = ((IMember) element).getDeclaringType();
+ }
+ if (testType != null && testType.exists()) {
+ return TestSearchEngine.isTestOrTestSuite(testType);
+ }
+ } catch (CoreException e) {
+ // ignore, return false
+ }
+ return false;
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java
index 89cad97ae..8ac80cab5 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/AndroidJUnitLaunchInfo.java
@@ -15,35 +15,38 @@
*/
package com.android.ide.eclipse.adt.launch.junit.runtime;
-import org.eclipse.core.resources.IProject;
-
import com.android.ddmlib.IDevice;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.debug.core.ILaunch;
+
/**
* Contains info about Android JUnit launch
*/
public class AndroidJUnitLaunchInfo {
private final IProject mProject;
- private final String mTestPackage;
+ private final String mAppPackage;
private final String mRunner;
- private final boolean mDebugMode;
- private final IDevice mDevice;
-
- public AndroidJUnitLaunchInfo(IProject project, String testPackage, String runner,
- boolean debugMode, IDevice device) {
+
+ private boolean mDebugMode = false;
+ private IDevice mDevice = null;
+ private String mTestPackage = null;
+ private String mTestClass = null;
+ private String mTestMethod = null;
+ private ILaunch mLaunch = null;
+
+ public AndroidJUnitLaunchInfo(IProject project, String appPackage, String runner) {
mProject = project;
- mTestPackage = testPackage;
+ mAppPackage = appPackage;
mRunner = runner;
- mDebugMode = debugMode;
- mDevice = device;
}
-
+
public IProject getProject() {
return mProject;
}
- public String getTestPackage() {
- return mTestPackage;
+ public String getAppPackage() {
+ return mAppPackage;
}
public String getRunner() {
@@ -53,8 +56,80 @@ public class AndroidJUnitLaunchInfo {
public boolean isDebugMode() {
return mDebugMode;
}
+
+ public void setDebugMode(boolean debugMode) {
+ mDebugMode = debugMode;
+ }
public IDevice getDevice() {
return mDevice;
}
+
+ public void setDevice(IDevice device) {
+ mDevice = device;
+ }
+
+ /**
+ * Specify to run all tests within given package.
+ *
+ * @param testPackage fully qualified java package
+ */
+ public void setTestPackage(String testPackage) {
+ mTestPackage = testPackage;
+ }
+
+ /**
+ * Return the package of tests to run.
+ *
+ * @return fully qualified java package. null if not specified.
+ */
+ public String getTestPackage() {
+ return mTestPackage;
+ }
+
+ /**
+ * Sets the test class to run.
+ *
+ * @param testClass fully qualfied test class to run
+ * Expected format: x.y.x.testclass
+ */
+ public void setTestClass(String testClass) {
+ mTestClass = testClass;
+ }
+
+ /**
+ * Returns the test class to run.
+ *
+ * @return fully qualfied test class to run.
+ * null if not specified.
+ */
+ public String getTestClass() {
+ return mTestClass;
+ }
+
+ /**
+ * Sets the test method to run. testClass must also be set.
+ *
+ * @param testMethod test method to run
+ */
+ public void setTestMethod(String testMethod) {
+ mTestMethod = testMethod;
+ }
+
+ /**
+ * Returns the test method to run.
+ *
+ * @return test method to run. null if not specified.
+ */
+ public String getTestMethod() {
+ return mTestMethod;
+ }
+
+ public ILaunch getLaunch() {
+ return mLaunch;
+ }
+
+ public void setLaunch(ILaunch launch) {
+ mLaunch = launch;
+ }
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java
index 0a6a3daee..962d76133 100755
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/runtime/RemoteAdtTestRunner.java
@@ -69,8 +69,9 @@ public class RemoteAdtTestRunner extends RemoteTestRunner {
* executing the tests, and send it back to JDT JUnit. The second is the actual test execution,
* whose results will be communicated back in real-time to JDT JUnit.
*
- * @param testClassNames array of fully qualified test class names to execute. Cannot be empty.
- * @param testName test to execute. If null, will be ignored.
+ * @param testClassNames ignored - the AndroidJUnitLaunchInfo will be used to determine which
+ * tests to run.
+ * @param testName ignored
* @param execution used to report test progress
*/
@Override
@@ -78,16 +79,21 @@ public class RemoteAdtTestRunner extends RemoteTestRunner {
// hold onto this execution reference so it can be used to report test progress
mExecution = execution;
- RemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(mLaunchInfo.getTestPackage(),
+ RemoteAndroidTestRunner runner = new RemoteAndroidTestRunner(mLaunchInfo.getAppPackage(),
mLaunchInfo.getRunner(), mLaunchInfo.getDevice());
- if (testClassNames != null && testClassNames.length > 0) {
- if (testName != null) {
- runner.setMethodName(testClassNames[0], testName);
- } else {
- runner.setClassNames(testClassNames);
- }
+ if (mLaunchInfo.getTestClass() != null) {
+ if (mLaunchInfo.getTestMethod() != null) {
+ runner.setMethodName(mLaunchInfo.getTestClass(), mLaunchInfo.getTestMethod());
+ } else {
+ runner.setClassName(mLaunchInfo.getTestClass());
+ }
}
+
+ if (mLaunchInfo.getTestPackage() != null) {
+ runner.setTestPackageName(mLaunchInfo.getTestPackage());
+ }
+
// set log only to first collect test case info, so Eclipse has correct test case count/
// tree info
runner.setLogOnly(true);
From 9ae634078663a04f0f8e7aeaf6dce390d030d0d5 Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Wed, 1 Apr 2009 12:30:50 -0700
Subject: [PATCH 052/127] AI 144049: am: CL 144048 ADT #1743364: Uncomment the
system resource chooser code. Rationale: we want to keep that code around,
so we need to compile it to make sure it doesn't use obsoleted APIs. That
does it. Original author: raphael Merged from: //branches/cupcake/...
Automated import of CL 144049
---
.../ide/eclipse/adt/ui/ResourceChooser.java | 63 ++++++++++---------
1 file changed, 34 insertions(+), 29 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceChooser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceChooser.java
index 4290f6b79..26396116c 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceChooser.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ResourceChooser.java
@@ -20,6 +20,10 @@ import com.android.ide.eclipse.common.resources.IResourceRepository;
import com.android.ide.eclipse.common.resources.ResourceItem;
import com.android.ide.eclipse.common.resources.ResourceType;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
@@ -39,11 +43,11 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
private IResourceRepository mProjectResources;
- // TODO: enable when we can display the system resources.
- // private Pattern mSystemResourcePattern;
- // private IResourceRepository mSystemResources;
- // private Button mProjectButton;
- // private Button mSystemButton;
+ private final static boolean SHOW_SYSTEM_RESOURCE = false; // TODO re-enable at some point
+ private Pattern mSystemResourcePattern;
+ private IResourceRepository mSystemResources;
+ private Button mProjectButton;
+ private Button mSystemButton;
private String mCurrentResource;
@@ -60,14 +64,15 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
mResourceType = type;
mProjectResources = project;
- // TODO: enable when we can display the system resources.
- // mSystemResources = system;
mProjectResourcePattern = Pattern.compile(
"@" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
- // TODO: enable when we can display the system resources.
- // mSystemResourcePattern = Pattern.compile(
- // "@android:" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ if (SHOW_SYSTEM_RESOURCE) {
+ mSystemResources = system;
+ mSystemResourcePattern = Pattern.compile(
+ "@android:" + mResourceType.getName() + "/(.+)"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
setTitle("Resource Chooser");
setMessage(String.format("Choose a %1$s resource",
@@ -89,8 +94,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
ResourceItem item = (ResourceItem)elements[0];
mCurrentResource = mResourceType.getXmlString(item,
- // TODO: enable when we can display the system resources.
- false /*mSystemButton.getSelection()*/);
+ SHOW_SYSTEM_RESOURCE && mSystemButton.getSelection());
}
}
@@ -100,9 +104,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
createMessageArea(top);
- // TODO: enable when we can display the system resources.
- // createButtons(top);
-
+ createButtons(top);
createFilterText(top);
createFilteredList(top);
@@ -115,8 +117,10 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
* Creates the radio button to switch between project and system resources.
* @param top the parent composite
*/
- /* TODO: enable when we can display the system resources.
private void createButtons(Composite top) {
+ if (!SHOW_SYSTEM_RESOURCE) {
+ return;
+ }
mProjectButton = new Button(top, SWT.RADIO);
mProjectButton.setText("Project Resources");
mProjectButton.addSelectionListener(new SelectionAdapter() {
@@ -136,7 +140,6 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
}
});
}
- */
/**
* Setups the current list based on the current resource.
@@ -147,20 +150,22 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
ResourceItem[] items = mProjectResources.getResources(mResourceType);
setListElements(items);
}
- /*
- * TODO: enable when we can display the system resources.
- if (setupInitialSelection(mProjectResourcePattern, mProjectResources) == false) {
- if (setupInitialSelection(mSystemResourcePattern, mSystemResources) == false) {
- // if we couldn't understand the current value, we default to the project resources
- IResourceItem[] items = mProjectResources.getResources(mResourceType);
- setListElements(items);
- mProjectButton.setSelection(true);
+
+ if (SHOW_SYSTEM_RESOURCE) {
+ if (setupInitialSelection(mProjectResourcePattern, mProjectResources) == false) {
+ if (setupInitialSelection(mSystemResourcePattern, mSystemResources) == false) {
+ // if we couldn't understand the current value,
+ // we default to the project resources
+ ResourceItem[] items = mProjectResources.getResources(mResourceType);
+ setListElements(items);
+ mProjectButton.setSelection(true);
+ } else {
+ mSystemButton.setSelection(true);
+ }
} else {
- mSystemButton.setSelection(true);
+ mProjectButton.setSelection(true);
}
- } else {
- mProjectButton.setSelection(true);
- }*/
+ }
}
/**
From c073ab28714d9467133be510fa93048e13aabb74 Mon Sep 17 00:00:00 2001
From: Mike Ritter <>
Date: Thu, 2 Apr 2009 01:26:29 -0700
Subject: [PATCH 053/127] AI 144166: am: CL 144164 Adding droiddocs/javadocs
usage to Pdk-docs to get correct style. Original author: mritter Merged
from: //branches/cupcake/...
Automated import of CL 144166
---
pdk/Pdk.mk | 72 +++-
pdk/README | 106 +++--
pdk/docs/audio_sub_system.html | 261 ------------
pdk/docs/audio_sub_system.jd | 56 +++
pdk/docs/bluetooth.html | 276 -------------
pdk/docs/bluetooth.jd | 82 ++++
pdk/docs/{bring_up.html => bring_up.jd} | 207 +---------
pdk/docs/build_new_device.html | 333 ---------------
pdk/docs/build_new_device.jd | 130 ++++++
.../{build_system.html => build_system.jd} | 210 +---------
pdk/docs/camera.html | 280 -------------
pdk/docs/camera.jd | 87 ++++
...isplay_drivers.html => display_drivers.jd} | 210 +---------
pdk/docs/getting_source_code.html | 335 ----------------
pdk/docs/getting_source_code.jd | 126 ++++++
pdk/docs/gps.html | 259 ------------
pdk/docs/gps.jd | 49 +++
.../{group__memory.html => group__memory.jd} | 25 +-
...__networking.html => group__networking.jd} | 26 +-
pdk/docs/index.html | 248 ------------
pdk/docs/index.jd | 39 ++
...work.html => instrumentation_framework.jd} | 181 +--------
...esting.html => instrumentation_testing.jd} | 211 +---------
pdk/docs/intro_source_code.html | 378 ------------------
pdk/docs/intro_source_code.jd | 169 ++++++++
...d_input.html => keymaps_keyboard_input.jd} | 210 +---------
pdk/docs/power_management.html | 311 --------------
pdk/docs/power_management.jd | 108 +++++
pdk/docs/source_setup_guide.html | 323 ---------------
pdk/docs/source_setup_guide.jd | 116 ++++++
pdk/docs/system_requirements.html | 262 ------------
pdk/docs/system_requirements.jd | 63 +++
pdk/docs/{telephony.html => telephony.jd} | 211 +---------
pdk/docs/wifi.html | 250 ------------
pdk/docs/wifi.jd | 49 +++
pdk/hosting/app.yaml | 4 +-
pdk/hosting/pdk.py | 18 +-
37 files changed, 1238 insertions(+), 5043 deletions(-)
delete mode 100755 pdk/docs/audio_sub_system.html
create mode 100755 pdk/docs/audio_sub_system.jd
delete mode 100755 pdk/docs/bluetooth.html
create mode 100755 pdk/docs/bluetooth.jd
rename pdk/docs/{bring_up.html => bring_up.jd} (66%)
delete mode 100755 pdk/docs/build_new_device.html
create mode 100755 pdk/docs/build_new_device.jd
rename pdk/docs/{build_system.html => build_system.jd} (62%)
delete mode 100755 pdk/docs/camera.html
create mode 100755 pdk/docs/camera.jd
rename pdk/docs/{display_drivers.html => display_drivers.jd} (61%)
delete mode 100755 pdk/docs/getting_source_code.html
create mode 100755 pdk/docs/getting_source_code.jd
delete mode 100755 pdk/docs/gps.html
create mode 100755 pdk/docs/gps.jd
rename pdk/docs/{group__memory.html => group__memory.jd} (54%)
rename pdk/docs/{group__networking.html => group__networking.jd} (57%)
delete mode 100755 pdk/docs/index.html
create mode 100644 pdk/docs/index.jd
rename pdk/docs/{instrumentation_framework.html => instrumentation_framework.jd} (50%)
rename pdk/docs/{instrumentation_testing.html => instrumentation_testing.jd} (77%)
delete mode 100755 pdk/docs/intro_source_code.html
create mode 100755 pdk/docs/intro_source_code.jd
rename pdk/docs/{keymaps_keyboard_input.html => keymaps_keyboard_input.jd} (71%)
delete mode 100755 pdk/docs/power_management.html
create mode 100755 pdk/docs/power_management.jd
delete mode 100755 pdk/docs/source_setup_guide.html
create mode 100755 pdk/docs/source_setup_guide.jd
delete mode 100755 pdk/docs/system_requirements.html
create mode 100755 pdk/docs/system_requirements.jd
rename pdk/docs/{telephony.html => telephony.jd} (61%)
delete mode 100755 pdk/docs/wifi.html
create mode 100755 pdk/docs/wifi.jd
diff --git a/pdk/Pdk.mk b/pdk/Pdk.mk
index abe9491d2..fbe8b5c90 100644
--- a/pdk/Pdk.mk
+++ b/pdk/Pdk.mk
@@ -16,7 +16,7 @@
# Assemble the Platform Development Kit (PDK)
# (TODO) Figure out why $(ACP) builds with target ndk but not pdk_docs
-# (TODO) Build doxygen (depend on latest version)
+# (TODO) Build doxygen (depend on latest version) -> line 25 error
pdk:
@echo "Package: $@ has targets ndk, pdk_docs and pdk_all"
@@ -38,6 +38,8 @@ include $(LOCAL_PATH)/ndk/Ndk.mk
# Doxygenize the header files to create html docs in the generatedDocs dir.
# Copy the appengine files, the template files and the generated html
# to the docs dir and zip everything up to the distribution directory.
+# Run javadocs/droiddocs/clearsilver on the generatedDocs dir to get the right
+# styles added to the html.
# Workspace directory
@@ -149,13 +151,27 @@ $(pdk_docs_intermediates)/pdk.py: $(pdk_hosting_dir)/pdk.py
@echo "PDK: $@"
$(copy-file-to-target-with-cp)
+# Copy appengine server files for new system
+$(OUT_DOCS)/app.yaml: $(pdk_hosting_dir)/app.yaml
+ @echo "PDK: $@"
+ $(copy-file-to-target-with-cp)
+
+$(OUT_DOCS)/pdk.py: $(pdk_hosting_dir)/pdk.py
+ @echo "PDK: $@"
+ $(copy-file-to-target-with-cp)
+
+# All the files that we depend upon
+all_pdk_docs_files := $(pdk_doxygen_config_override_file) \
+ $(pdk_doxygen_config_file) $(pdk_docs_intermediates)/header.html \
+ $(pdk_docs_intermediates)/footer.html $(pdk_doxy_docsfiles_dir)/groups.dox \
+ $(pdk_doxy_docsfiles_dir)/main.dox all_copied_pdk_templates
# Run doxygen and copy all output and templates to the final destination
# We replace index.html with a template file so don't use the generated one
pdk_doxygen: all_copied_pdk_headers $(pdk_doxygen_config_override_file) \
$(pdk_doxygen_config_file) $(pdk_docs_intermediates)/header.html \
$(pdk_docs_intermediates)/footer.html $(pdk_doxy_docsfiles_dir)/groups.dox \
- $(pdk_doxy_docsfiles_dir)/main.dox
+ $(pdk_doxy_docsfiles_dir)/main.dox
@echo "Files for Doxygination: $^"
@mkdir -p $(pdk_generated_source_dir)
@rm -f $(pdk_generated_source_dir)/*
@@ -164,7 +180,40 @@ pdk_doxygen: all_copied_pdk_headers $(pdk_doxygen_config_override_file) \
@cd $(pdk_generated_source_dir) && chmod ug+rx *
@rm -f $(pdk_generated_source_dir)/index.html
@cp -fp $(pdk_generated_source_dir)/* $(pdk_docs_dest_dir)
-
+
+
+# ==== docs for the web (on the google app engine server) =======================
+# Run javadoc/droiddoc/clearsilver to get the formatting right
+
+# make droiddocs run after we make our doxygen docs
+$(pdk_docs_intermediates)/pdk-timestamp: pdk_doxygen
+ @touch $(pdk_docs_intermediates)/pdk-timestamp
+
+$(LOCAL_PATH)/pdk-timestamp: $(pdk_docs_intermediates)/pdk-timestamp
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := pdk-timestamp samples/samplejni/src/com/example/jniexample/JNIExample.java
+LOCAL_MODULE_CLASS := development/pdk/ndk/samples/samplejni/src/com/example/jniexample
+LOCAL_DROIDDOC_SOURCE_PATH := $(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
+LOCAL_DROIDDOC_HTML_DIR := ../../../$(pdk_docs_dest_dir)
+
+LOCAL_MODULE := online-pdk
+
+LOCAL_DROIDDOC_OPTIONS := \
+ $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
+ $(web_docs_sample_code_flags) \
+ -toroot /online-pdk/ \
+ -hdf android.whichdoc online-pdk
+
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := build/tools/droiddoc/templates-pdk
+LOCAL_DROIDDOC_CUSTOM_ASSET_DIR := assets-pdk
+
+include $(BUILD_DROIDDOC)
+
+# The docs output dir is: out/target/common/docs/online-pdk
+DOCS_OUT_DIR := $(OUT_DOCS)/$(LOCAL_MODULE)
+
# Name the tar files
name := android_pdk_docs-$(REQUESTED_PRODUCT)
ifeq ($(TARGET_BUILD_TYPE),debug)
@@ -173,21 +222,25 @@ endif
name := $(name)-$(BUILD_NUMBER)
pdk_docs_tarfile := $(pdk_docs_intermediates)/$(name).tar
pdk_docs_tarfile_zipped := $(pdk_docs_tarfile).gz
+new_pdk_docs_tarfile := $(pdk_docs_intermediates)/new-$(name).tar
+new_pdk_docs_tarfile_zipped := $(new_pdk_docs_tarfile).gz
-.PHONY: pdk pdk_docs pdk_doxygen all_copied_pdk_headers all_copied_pdk_templates
+.PHONY: pdk pdk_docs pdk_doxygen all_copied_pdk_headers all_copied_pdk_templates pdk-timestamp
-pdk_docs: $(pdk_docs_tarfile_zipped)
+pdk_docs: $(pdk_docs_tarfile_zipped) $(new_pdk_docs_tarfile)
@echo "PDK: Docs tarred and zipped"
# Put the pdk_docs zip files in the distribution directory
$(call dist-for-goals,pdk_docs,$(pdk_docs_tarfile_zipped))
+$(call dist-for-goals,pdk_docs,$(new_pdk_docs_tarfile_zipped))
# zip up tar files
%.tar.gz: %.tar
- @echo "PDK: zipped $<"
+ @echo "PDK docs: zipped $<"
$(hide) gzip -cf $< > $@
# tar up all the files to make the pdk docs.
+# old version
$(pdk_docs_tarfile): pdk_doxygen all_copied_pdk_templates \
$(pdk_docs_intermediates)/pdk.py $(pdk_docs_intermediates)/app.yaml
@echo "PDK: $@"
@@ -195,6 +248,13 @@ $(pdk_docs_tarfile): pdk_doxygen all_copied_pdk_templates \
@rm -f $@
$(hide) tar rf $@ -C $(pdk_docs_intermediates) docs pdk.py app.yaml
+# new version
+$(new_pdk_docs_tarfile): $(DOCS_OUT_DIR)-timestamp
+ @echo "PDK docs: $@"
+ @mkdir -p $(dir $@)
+ @rm -f $@
+ $(hide) tar rf $@ -C $(OUT_DOCS) $(LOCAL_MODULE)
+
# Debugging reporting can go here, add it as a target to get output.
pdk_debug:
@echo "You are here: $@"
diff --git a/pdk/README b/pdk/README
index 86621552e..18ff5d120 100644
--- a/pdk/README
+++ b/pdk/README
@@ -4,78 +4,72 @@ Building the pdk (platform development kit)
(We currently support version 1.4.6)
sudo apt-get install doxygen
+
+Make sure that you are using the right version of java
+
+ sudo update-java-alternatives -s java-1.5.0-sun
+
+If that doesn't work, go through the instructions on
+
+ http://source.android.com/download again.
+
2) from the root
. build/envsetup.sh
-3) run choosecombo
- Build for the simulator or the device?
- 1. Device
- 2. Simulator
-
- Which would you like? [1] 1
-
-
- Build type choices are:
- 1. release
- 2. debug
-
- Which would you like? [1] 1
-
-
- Product choices are:
- 0. emulator
- 1. generic
- 2. sim
- 3. surf
- You can also type the name of a product if you know it.
- Which would you like? [generic] 1
-
-
- Variant choices are:
- 1. user
- 2. userdebug
- 3. eng
- Which would you like? [eng] 3
-
- ============================================
- TARGET_PRODUCT=generic
- TARGET_BUILD_VARIANT=eng
- TARGET_SIMULATOR=false
- TARGET_BUILD_TYPE=release
- TARGET_ARCH=arm
- HOST_ARCH=x86
- HOST_OS=linux
- HOST_BUILD_TYPE=release
- BUILD_ID=
- ============================================
4) mkdir dist
mkdir logs
- mkpdkcupcake.sh
+
+then build everything:
-(which contains:
+ time make -j4 pdk pdk_all dist DIST_DIR=dist 2>&1 | tee logs/`date +%y%m%d-%H%M%S`
-DT=`date +%y%m%d-%H%M%S`
-time make -j4 pdk pdk_all dist DIST_DIR=dist 2>&1 | tee logs/$DT
+so you can have a record of the build commands in the logs directory.
-so you can see the results of the build in the logs directory.)
5) the pdk tar file is put in the dist directory.
+6) the pdk-docs are in
+
+ out/target
+
The build target 'pdk' brings in the pdk/ndk make files into the build system.
- Then there are three targets:
- pdk_docs - which builds the pdk documentation
- ndk - which builds the native development kit (native compiler, linker, etc.)
- pdk_all - which builds the above two targets
+ Then there are three targets:
+ pdk_docs - which builds just the pdk documentation
+ ndk - which builds the native development kit (native compiler, linker, etc.)
+ pdk_all - which builds the above two targets
-for doxygen version changing you can pass in the variable:
-doxygen_version=''
+To chnage which version of doxygen runs you can pass in the variable:
+ doxygen_version=''
on the make line.
--------------------------------------------------------------------------------
-To host the pdk docs on appengine run:
-/home/build/static/projects/apphosting/devtools/appcfg.py update pdk/
-where the pdk directory contains: pdk.py, app.yaml, and the docs directory,
-all of which are tarred up by the Pdk.mk file when using the targer pdk_docs.
+# Testing
+You must install google appengine. See: http://code.google.com/appengine/downloads.html
+
+Here's the command to run the pdk-docs server locally:
+ python /dev_appserver.py --address 0.0.0.0 \
+ /android/out/target/common/docs/online-pdk
+
+To verify it is working you can access it with a browser loacally on port 8080:
+
+http://localhost:8080/index.html
+TODO: index.html needs correct links.
+TODO: app.yaml not working for redirecting, getting extra '.' in html names...
+
+ --------------------------------------------------------------------------------
+# Deployment
+To host the pdk docs on the interanl appengine run:
+/home/build/static/projects/apphosting/devtools/appcfg.py update /out/common/docs
+where the docs directory contains: pdk.py, app.yaml, and the online-pdk directory,
+all of which are tarred up by the Pdk.mk file when using the target pdk_docs.
+
+# Deployment
+To host the pdk docs on the external appengine run:
+/home/build/static/projects/apphosting/devtools/appcfg.py -s pdk-docs.appspot.com update /out/common/docs
+where the docs directory contains: pdk.py, app.yaml, and the online-pdk directory,
+all of which are tarred up by the Pdk.mk file when using the target pdk_docs.
+
+
diff --git a/pdk/docs/audio_sub_system.html b/pdk/docs/audio_sub_system.html
deleted file mode 100755
index 8639cf0b1..000000000
--- a/pdk/docs/audio_sub_system.html
+++ /dev/null
@@ -1,261 +0,0 @@
-
-
-
-
-Android - Porting Guide
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
AudioHardwareInterface serves as the glue between proprietary audio drivers and the Android AudioFlinger service, the core audio service that handles all audio-related requests from applications.
-
-
-Solid elements represent Android blocks and dashed elements represent partner-specific blocks.
-
-
-
-
Building an Audio Library
-
-
To implement an audio driver, create a shared library that implements the interface defined in AudioHardwareInterface.h. You must name your shared library libaudio.so so that it will get loaded from /system/lib at runtime. Place libaudio sources and Android.mk in partner/acme/chipset_or_board/libaudio/.
-
The following stub Android.mk file ensures that libaudio compiles and links to the appropriate libraries:
Note: This document relies on some Doxygen-generated content that appears in an iFrame below. To return to the Doxygen default content for this page, click here.
AudioHardwareInterface serves as the glue between proprietary audio drivers and the Android AudioFlinger service, the core audio service that handles all audio-related requests from applications.
+
+
+Solid elements represent Android blocks and dashed elements represent partner-specific blocks.
+
+
+
+
Building an Audio Library
+
+
To implement an audio driver, create a shared library that implements the interface defined in AudioHardwareInterface.h. You must name your shared library libaudio.so so that it will get loaded from /system/lib at runtime. Place libaudio sources and Android.mk in partner/acme/chipset_or_board/libaudio/.
+
The following stub Android.mk file ensures that libaudio compiles and links to the appropriate libraries:
Note: This document relies on some Doxygen-generated content that appears in an iFrame below. To return to the Doxygen default content for this page, click here.
Android's Bluetooth stack uses BlueZ version 3.36 for GAP, SDP, and RFCOMM profiles, and is a SIG-qualified Bluetooth 2.0 host stack.
-
-
Bluez is GPL licensed, so the Android framework interacts with userspace bluez code through D-BUS IPC to avoid proprietary code.
-
-
Headset and Handsfree (v1.5) profiles are implemented in the Android framework and are both tightly coupled with the Phone App. These profiles are also SIG qualified.
-
-
The diagram below offers a library-oriented view of the Bluetooth stack. Click Bluetooth Process Diagram for a process-oriented view.
-
-
-
-Solid elements represent Android blocks and dashed elements represent partner-specific blocks.
-
-
-
-
Porting
-
-
BlueZ is Bluetooth 2.0 compatible and should work with any 2.0 chipset. There are two integration points:
-
-
UART driver
-
Bluetooth Power On / Off
-
-
-
-
-
UART Driver
-
-
The BlueZ kernel sub-system attaches to your hardware-specific UART driver using the hciattach daemon.
-
For example, for MSM7201A, this is drivers/serial/msm_serial.c. You may also need to edit command line options to hciattach via init.rc.
-
-
-
Bluetooth Power On / Off
-
-
The method for powering on and off your bluetooth chip varies from Android V 1.0 to post 1.0.
-
-
-
1.0: Android framework writes a 0 or 1 to /sys/modules/board_[PLATFORM]/parameters/bluetooth_power_on.
-
-
Post 1.0: Android framework uses the linux rfkill API. See arch/arm/mach-msm/board-trout-rfkill.c for an example.
-
-
-
-
-
Tools
-
-
BlueZ provides a rich set of command line tools for debugging and interacting with the Bluetooth sub-system, including:
Android's Bluetooth stack uses BlueZ version 3.36 for GAP, SDP, and RFCOMM profiles, and is a SIG-qualified Bluetooth 2.0 host stack.
+
+
Bluez is GPL licensed, so the Android framework interacts with userspace bluez code through D-BUS IPC to avoid proprietary code.
+
+
Headset and Handsfree (v1.5) profiles are implemented in the Android framework and are both tightly coupled with the Phone App. These profiles are also SIG qualified.
+
+
The diagram below offers a library-oriented view of the Bluetooth stack. Click Bluetooth Process Diagram for a process-oriented view.
+
+
+
+Solid elements represent Android blocks and dashed elements represent partner-specific blocks.
+
+
+
+
Porting
+
+
BlueZ is Bluetooth 2.0 compatible and should work with any 2.0 chipset. There are two integration points:
+
+
UART driver
+
Bluetooth Power On / Off
+
+
+
+
+
UART Driver
+
+
The BlueZ kernel sub-system attaches to your hardware-specific UART driver using the hciattach daemon.
+
For example, for MSM7201A, this is drivers/serial/msm_serial.c. You may also need to edit command line options to hciattach via init.rc.
+
+
+
Bluetooth Power On / Off
+
+
The method for powering on and off your bluetooth chip varies from Android V 1.0 to post 1.0.
+
+
+
1.0: Android framework writes a 0 or 1 to /sys/modules/board_[PLATFORM]/parameters/bluetooth_power_on.
+
+
Post 1.0: Android framework uses the linux rfkill API. See arch/arm/mach-msm/board-trout-rfkill.c for an example.
+
+
+
+
+
Tools
+
+
BlueZ provides a rich set of command line tools for debugging and interacting with the Bluetooth sub-system, including:
Once your code is built and you have verified that all necessary directories exist, power on and test your device with basic bring up, as described below. Bring up tests are typically designed to stress certain aspects of your system and allow you to characterize the device's behavior.
1. Confirm a Clean Installation of a Basic Linux Kernel
Before considering Android-specific modifications to the Linux kernel, verify that you can build, deploy, and boot a core Linux kernel on your target hardware.
+
2. Modify Your Kernel Configuration to Accommodate Android Drivers
Your kernel configuration file should include the following:
In the products directory, create an AndroidProducts.mk file that point to (and is responsible for finding) the individual product make files.
-
- #
- # This file should set PRODUCT_MAKEFILES to a list of product makefiles
- # to expose to the build system. LOCAL_DIR will already be set to
- # the directory containing this file.
- #
- # This file may not rely on the value of any variable other than
- # LOCAL_DIR; do not use any conditionals, and do not look up the
- # value of any variable that isn't set in this file or in a file that
- # it includes.
- #
-
- PRODUCT_MAKEFILES := \
- $(LOCAL_DIR)/first_product_name.mk \
-
Create a board-specific directory beneath your company directory that matches the PRODUCT_DEVICE variable <board_name> referenced in the product-specific make file above. This will include a make file that gets accessed by any product using this board.
-
Create a product_config.mk file in the directory created in the previous step (device/partner/<company_name>/<board_name>). If this directory does not include a product_config.mk file, the build will fail.
-
- # These definitions override the defaults in config/config.make for <board_name>
- #
- # TARGET_NO_BOOTLOADER := false
- # TARGET_HARDWARE_3D := false
- #
- TARGET_USE_GENERIC_AUDIO := true
-
If you wish to modify system properties, create a system.prop file in your <board_name> directory(device/partner/<company_name>/<board_name>).
-
- # system.prop for
- # This overrides settings in the products/generic/system.prop file
- #
- # rild.libpath=/system/lib/libreference-ril.so
- # rild.libargs=-d /dev/ttyS0
-
Add a pointer to <second_product_name>.mk within products/AndroidProducts.mk.
-
device/partner/<company_name>/<board_name> must include an Android.mk file with at least the following code:
-
- # make file for new hardware from
- #
- LOCAL_PATH := $(call my-dir)
- #
- # this is here to use the pre-built kernel
- ifeq ($(TARGET_PREBUILT_KERNEL),)
- TARGET_PREBUILT_KERNEL := $(LOCAL_PATH)/kernel
- endif
- #
- file := $(INSTALLED_KERNEL_TARGET)
- ALL_PREBUILT += $(file)
- $(file): $(TARGET_PREBUILT_KERNEL) | $(ACP)
- $(transform-prebuilt-to-target)
- #
- # no boot loader, so we don't need any of that stuff..
- #
- LOCAL_PATH := partner/<company_name>/<board_name>
- #
- include $(CLEAR_VARS)
- #
- # include more board specific stuff here? Such as Audio parameters.
- #
-
To create a second product for the same board, create a second product-specific make file called device/partner/company_name/products/<second_product_name>.mk that includes:
-
By now, you should have two new products, called <first_product_name> and <second_product_name> associated with <company_name>. To verify that a product is properly configured (<first_product_name>, for example), execute the following:
-
- cd device
- . ./envsetup.sh
- partner_setup <first_product_name>
- make PRODUCT-<first_product_name>-user
-
-
You should find new build binaries located in device/out/target/product/<board_name>.
-
-
-
New Product File Tree
-
-
The file tree below illustrates what your own system should look like after completing the steps above.
-
-
-
<company_name>
-
-
<board_name>
-
-
Android.mk
-
product_config.mk
-
system.prop
-
-
products
-
-
AndroidProducts.mk
-
<first_product_name>.mk
-
<second_product_name>.mk
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
v0.6 - 25 November 2008
-
-
-
diff --git a/pdk/docs/build_new_device.jd b/pdk/docs/build_new_device.jd
new file mode 100755
index 000000000..f0a816f54
--- /dev/null
+++ b/pdk/docs/build_new_device.jd
@@ -0,0 +1,130 @@
+page.title=Building Android for a new Mobile Device
+pdk.version=1.0
+@jd:body
+
+
+
+
+
In the products directory, create an AndroidProducts.mk file that point to (and is responsible for finding) the individual product make files.
+
+ #
+ # This file should set PRODUCT_MAKEFILES to a list of product makefiles
+ # to expose to the build system. LOCAL_DIR will already be set to
+ # the directory containing this file.
+ #
+ # This file may not rely on the value of any variable other than
+ # LOCAL_DIR; do not use any conditionals, and do not look up the
+ # value of any variable that isn't set in this file or in a file that
+ # it includes.
+ #
+
+ PRODUCT_MAKEFILES := \
+ $(LOCAL_DIR)/first_product_name.mk \
+
Create a board-specific directory beneath your company directory that matches the PRODUCT_DEVICE variable <board_name> referenced in the product-specific make file above. This will include a make file that gets accessed by any product using this board.
+
Create a product_config.mk file in the directory created in the previous step (device/partner/<company_name>/<board_name>). If this directory does not include a product_config.mk file, the build will fail.
+
+ # These definitions override the defaults in config/config.make for <board_name>
+ #
+ # TARGET_NO_BOOTLOADER := false
+ # TARGET_HARDWARE_3D := false
+ #
+ TARGET_USE_GENERIC_AUDIO := true
+
If you wish to modify system properties, create a system.prop file in your <board_name> directory(device/partner/<company_name>/<board_name>).
+
+ # system.prop for
+ # This overrides settings in the products/generic/system.prop file
+ #
+ # rild.libpath=/system/lib/libreference-ril.so
+ # rild.libargs=-d /dev/ttyS0
+
Add a pointer to <second_product_name>.mk within products/AndroidProducts.mk.
+
device/partner/<company_name>/<board_name> must include an Android.mk file with at least the following code:
+
+ # make file for new hardware from
+ #
+ LOCAL_PATH := $(call my-dir)
+ #
+ # this is here to use the pre-built kernel
+ ifeq ($(TARGET_PREBUILT_KERNEL),)
+ TARGET_PREBUILT_KERNEL := $(LOCAL_PATH)/kernel
+ endif
+ #
+ file := $(INSTALLED_KERNEL_TARGET)
+ ALL_PREBUILT += $(file)
+ $(file): $(TARGET_PREBUILT_KERNEL) | $(ACP)
+ $(transform-prebuilt-to-target)
+ #
+ # no boot loader, so we don't need any of that stuff..
+ #
+ LOCAL_PATH := partner/<company_name>/<board_name>
+ #
+ include $(CLEAR_VARS)
+ #
+ # include more board specific stuff here? Such as Audio parameters.
+ #
+
To create a second product for the same board, create a second product-specific make file called device/partner/company_name/products/<second_product_name>.mk that includes:
+
By now, you should have two new products, called <first_product_name> and <second_product_name> associated with <company_name>. To verify that a product is properly configured (<first_product_name>, for example), execute the following:
+
+ cd device
+ . ./envsetup.sh
+ partner_setup <first_product_name>
+ make PRODUCT-<first_product_name>-user
+
+
You should find new build binaries located in device/out/target/product/<board_name>.
+
+
+
New Product File Tree
+
+
The file tree below illustrates what your own system should look like after completing the steps above.
Android's camera subsystem connects the camera application to the application framework and user space libraries, which in turn communicate with the camera hardware layer that operates the physical camera.
-
The diagram below illustrates the structure of the camera subsystem.
-
-
-
-
Building a Camera Library
-
-
To implement a camera driver, create a shared library that implements the interface defined in CameraHardwareInterface.h. You must name your shared library libcamera.so so that it will get loaded from /system/lib at runtime. Place libcamera sources and Android.mk in partner/acme/chipset_or_board/libcamera/.
-
The following stub Android.mk file ensures that libcamera compiles and links to the appropriate libraries:
The following diagram illustrates the sequence of function calls and actions necessary for your camera to preview.
-
-
-
-
Taking a Picture
-
-
The following diagram illustrates the sequence of function calls and actions necessary for your camera to take a picture.
-
-
-
-
Interface
-
-
-
-
Note: This document relies on some Doxygen-generated content that appears in an iFrame below. To return to the Doxygen default content for this page, click here.
Android's camera subsystem connects the camera application to the application framework and user space libraries, which in turn communicate with the camera hardware layer that operates the physical camera.
+
The diagram below illustrates the structure of the camera subsystem.
+
+
+
+
Building a Camera Library
+
+
To implement a camera driver, create a shared library that implements the interface defined in CameraHardwareInterface.h. You must name your shared library libcamera.so so that it will get loaded from /system/lib at runtime. Place libcamera sources and Android.mk in partner/acme/chipset_or_board/libcamera/.
+
The following stub Android.mk file ensures that libcamera compiles and links to the appropriate libraries:
The following diagram illustrates the sequence of function calls and actions necessary for your camera to preview.
+
+
+
+
Taking a Picture
+
+
The following diagram illustrates the sequence of function calls and actions necessary for your camera to take a picture.
+
+
+
+
Interface
+
+
+
+
Note: This document relies on some Doxygen-generated content that appears in an iFrame below. To return to the Doxygen default content for this page, click here.
Android relies on Git, a version control system, to install the Android platform. You will need to install Git 1.5.2 or greater in order to access the source tree. Please visit http://git.or.cz/ for more information regarding Git.
-
Git permits you to control access to working directories, and we recommend that you use it to limit Android repository access to only a few people within your organization (please refer to your Google NDA for potential contractual restraints on sharing Android source access).
-
You may clone Google's repository to a local copy for sharing internally (see Git documentation for details).
-
-
-
Installing and Configuring Git
-
-
To install the Git package, execute:
-
-% sudo apt-get install git-core
-
-
-
-
Establishing Server Access
-
-
Once Git is cleanly installed, you need to establish a connection with Google's Git server, a connection that requires an RSA key in order to authenticate requests.
-
-
-
Generating RSA Keys
-
-
Each developer must have a unique RSA key in order to access Android source code. To generate an RSA key:
-
-
-
Type:
-
% ssh-keygen -t rsa -C email@domain.com
-You must use a valid email address to create your key.
-
When prompted, indicate the file to which you wish to write your key (id_rsa in this example).
-
When prompted, associate a passphrase with your key.
-
Upon success, you should have two files saved to the designated directory:
-
-
id_rsa: This file contains the private half of your RSA key. You shouldn't share this file with anyone.
-
id_rsa.pub: This is the public half or your RSA key and you should send it to your Google technical account manager.
-
-
-
-
Send your Google Account Manager your public key file in order to establish Git server access.
-
-
-
Verifying a Connection to the Git Server
-
-
Once you have generated an RSA key and shared the public file with Google, you can test your connection with the Git server with the following command:
-
-% ssh android-git.ext.google.com
-
-
-
You should receive one of the following results:
-
-
-
-
Result
-
Cause
-
Action
-
-
-
-fatal: What do you think I am? A shell?
-Connection to android-git closed.
-
-
Success
-
None. You successfully connected to the Git server. (You should not have shell access and it's expected to receive this error.)
-
-
-
ssh hangs and eventually times out.
-
Your setup is failing to locate and establish a basic connection.
-
Google needs to debug network settings.
-
-
-
Error: Permission denied <public key>
-
Either you are not using the matching username or the RSA private key does not match the public key.
Android source code is maintained in two repositories: device and kernel. The device repository includes the Android framework (things like the Activity Manager, Window Manager, Telephony Manager, View System, etc.). The kernel repository includes the core code necessary to run the operating system (things like the Display Driver, Camera Driver, Keypad Driver, Power Management, etc.). (Please see What is Android? for details.)
-
-
Save device and kernel code at the same directory level, for example:
-
-
/home/joe/android/device
-
/home/joe/android/kernel
-
-
Device Code
-
To download device code, you need your username and a unique <path> string supplied by Google to execute the following:
You likely already have Linux running on your platform and only need to integrate Android-specific changes. The following directions describe how to extract an Android patch.
-
-
Download a generic version of the Linux kernel that matches the Linux version downloaded with the Android Kernel code.
-
Run diff on the two kernel packages to get Android-specific changes.
Android relies on Git, a version control system, to install the Android platform. You will need to install Git 1.5.2 or greater in order to access the source tree. Please visit http://git.or.cz/ for more information regarding Git.
+
Git permits you to control access to working directories, and we recommend that you use it to limit Android repository access to only a few people within your organization (please refer to your Google NDA for potential contractual restraints on sharing Android source access).
+
You may clone Google's repository to a local copy for sharing internally (see Git documentation for details).
+
+
+
Installing and Configuring Git
+
+
To install the Git package, execute:
+
+% sudo apt-get install git-core
+
+
+
+
Establishing Server Access
+
+
Once Git is cleanly installed, you need to establish a connection with Google's Git server, a connection that requires an RSA key in order to authenticate requests.
+
+
+
Generating RSA Keys
+
+
Each developer must have a unique RSA key in order to access Android source code. To generate an RSA key:
+
+
+
Type:
+
% ssh-keygen -t rsa -C email@domain.com
+You must use a valid email address to create your key.
+
When prompted, indicate the file to which you wish to write your key (id_rsa in this example).
+
When prompted, associate a passphrase with your key.
+
Upon success, you should have two files saved to the designated directory:
+
+
id_rsa: This file contains the private half of your RSA key. You shouldn't share this file with anyone.
+
id_rsa.pub: This is the public half or your RSA key and you should send it to your Google technical account manager.
+
+
+
+
Send your Google Account Manager your public key file in order to establish Git server access.
+
+
+
Verifying a Connection to the Git Server
+
+
Once you have generated an RSA key and shared the public file with Google, you can test your connection with the Git server with the following command:
+
+% ssh android-git.ext.google.com
+
+
+
You should receive one of the following results:
+
+
+
+
Result
+
Cause
+
Action
+
+
+
+fatal: What do you think I am? A shell?
+Connection to android-git closed.
+
+
Success
+
None. You successfully connected to the Git server. (You should not have shell access and it's expected to receive this error.)
+
+
+
ssh hangs and eventually times out.
+
Your setup is failing to locate and establish a basic connection.
+
Google needs to debug network settings.
+
+
+
Error: Permission denied <public key>
+
Either you are not using the matching username or the RSA private key does not match the public key.
Android source code is maintained in two repositories: device and kernel. The device repository includes the Android framework (things like the Activity Manager, Window Manager, Telephony Manager, View System, etc.). The kernel repository includes the core code necessary to run the operating system (things like the Display Driver, Camera Driver, Keypad Driver, Power Management, etc.). (Please see What is Android? for details.)
+
+
Save device and kernel code at the same directory level, for example:
+
+
/home/joe/android/device
+
/home/joe/android/kernel
+
+
Device Code
+
To download device code, you need your username and a unique <path> string supplied by Google to execute the following:
You likely already have Linux running on your platform and only need to integrate Android-specific changes. The following directions describe how to extract an Android patch.
+
+
Download a generic version of the Linux kernel that matches the Linux version downloaded with the Android Kernel code.
+
Run diff on the two kernel packages to get Android-specific changes.
Android defines a user space C abstraction interface for GPS hardware. The interface header is defined in include/hardware/gps.h. In order to integate GPS with Android, you need to build a shared library that implements this interface.
-
-
-
Building a GPS Library
-
-
To implement a GPS driver, create a shared library that implements the interface defined in gps.h. You must name your shared library libgps.so so that it will get loaded from /system/lib at runtime. Place GPS sources and Android.mk in partner/acme/chipset_or_board/gps/ (where "acme" is your product name and "chipset_or_board" is your hardware target).
-
-
The following stub Android.mk file ensures that libgps compiles and links to the appropriate libraries:
-
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libgps
-
-LOCAL_STATIC_LIBRARIES:= \
-# include any static library dependencies
-
-LOCAL_SHARED_LIBRARIES := \
-# include any shared library dependencies
-
-LOCAL_SRC_FILES += \
-# include your source files. eg. MyGpsLibrary.cpp
-
-LOCAL_CFLAGS += \
-# include any needed compile flags
-
-LOCAL_C_INCLUDES:= \
-# include any needed local header files
-
-include $(BUILD_SHARED_LIBRARY)
-
-
-
-
Interface
-
-
-
-
-
-
Note: This document relies on some Doxygen-generated content that appears in an iFrame below. To return to the Doxygen default content for this page, click here.
Android defines a user space C abstraction interface for GPS hardware. The interface header is defined in include/hardware/gps.h. In order to integate GPS with Android, you need to build a shared library that implements this interface.
+
+
+
Building a GPS Library
+
+
To implement a GPS driver, create a shared library that implements the interface defined in gps.h. You must name your shared library libgps.so so that it will get loaded from /system/lib at runtime. Place GPS sources and Android.mk in partner/acme/chipset_or_board/gps/ (where "acme" is your product name and "chipset_or_board" is your hardware target).
+
+
The following stub Android.mk file ensures that libgps compiles and links to the appropriate libraries:
+
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libgps
+
+LOCAL_STATIC_LIBRARIES:= \
+# include any static library dependencies
+
+LOCAL_SHARED_LIBRARIES := \
+# include any shared library dependencies
+
+LOCAL_SRC_FILES += \
+# include your source files. eg. MyGpsLibrary.cpp
+
+LOCAL_CFLAGS += \
+# include any needed compile flags
+
+LOCAL_C_INCLUDES:= \
+# include any needed local header files
+
+include $(BUILD_SHARED_LIBRARY)
+
The Open Handset Distribution (OHD) is a software distribution for mobile devices, often referred to as Android, developed by members of the Open Handset Alliance. Android includes an operating system, middleware, and key applications typically required for a mobile device.
-
-
This porting guide describes the steps necessary to port Android to a new mobile device. Android is designed as a highly-portable, hardware-independent platform based on Linux, and porting the platform to new devices requires little more than porting the Linux kernel and developing the Linux drivers necessary for your device.
-
-
The current version of this guide describes bringing Android up to "PDA-level" functionality; functionality sufficient to support non-multimedia apps that run on unconnected mobile devices through the standard user interface devices such as keypad and display. Future versions of this guide will cover complete telephony, multi-media and peripheral integration to create a complete mobile device.
-
-
-
Intended Audience
-
-
This porting guide is intended for engineers proficient with running (and writing drivers for) Linux on embedded devices.
-
The guide also assumes you have a target hardware that matches Device Requirements and that you
-can boot and run a recent (2.6.x) version of the Linux kernel
-with at least keypad and display drivers properly installed.
Install necessary packages and retrieve source code through a Git server. Build System offers a conceptual overview of Android's build system and instructions to affect a simple build.
Establish core components necessary to your device, such as keymaps / keyboard input and display drivers.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
v0.6 - 25 November 2008
-
-
-
diff --git a/pdk/docs/index.jd b/pdk/docs/index.jd
new file mode 100644
index 000000000..b7529d3a7
--- /dev/null
+++ b/pdk/docs/index.jd
@@ -0,0 +1,39 @@
+home=true
+page.title=Welcome to the Android Porting Guide
+pdk.version=1.0
+@jd:body
+
+
+
The Open Handset Distribution (OHD) is a software distribution for mobile devices, often referred to as Android, developed by members of the Open Handset Alliance. Android includes an operating system, middleware, and key applications typically required for a mobile device.
+
+
This porting guide describes the steps necessary to port Android to a new mobile device. Android is designed as a highly-portable, hardware-independent platform based on Linux, and porting the platform to new devices requires little more than porting the Linux kernel and developing the Linux drivers necessary for your device.
+
+
The current version of this guide describes bringing Android up to "PDA-level" functionality; functionality sufficient to support non-multimedia apps that run on unconnected mobile devices through the standard user interface devices such as keypad and display. Future versions of this guide will cover complete telephony, multi-media and peripheral integration to create a complete mobile device.
+
+
+
Intended Audience
+
+
This porting guide is intended for engineers proficient with running (and writing drivers for) Linux on embedded devices.
+
The guide also assumes you have a target hardware that matches Device Requirements and that you
+can boot and run a recent (2.6.x) version of the Linux kernel
+with at least keypad and display drivers properly installed.
Install necessary packages and retrieve source code through a Git server. Build System offers a conceptual overview of Android's build system and instructions to affect a simple build.
Android source code is maintained in two code bases: the Android Linux kernel (kernel directory) and Android platform and applications (device directory). This document provides a high-level introduction to the source code organization and an overview of the major components of each primary directory.
-
-
Android Source
-
-
-
Linux Kernel
-
-
The Android Linux kernel includes enhancements to the Linux 2.6 kernel that provide additional drivers to support the Android platform, including:
-
-
Binder: an OpenBinder-based driver to facilitate inter-process communication (IPC) in the Android platform.
-
Android Power Management: a light weight power management driver built on top of standard Linux power management but optimized for embedded systems.
-
Low Memory Killer: Based on hints from the userspace, the low memory killer can kill off processes to free up memory as necessary. It is designed to provide more flexibility than the Out Of Memory (OOM) killer in the standard kernel.
-
Logger: A light weight logging device used to capture system, radio, logdata, etc.
-
USB Gadget: Uses the USB function framework.
-
Android/PMEM: The PMEM (physical memory) driver is used to provide contiguous physical memory regions to userspace libraries that interact with the digital signal processor (DSP) and other hardware that cannot cope with scatter-gather.
-
Android Alarm: A driver which provides timers that can wake the device up from sleep and a monotonic timebase that runs while the device is asleep.
-
-
Look for Android-specific enhancements in the following directories:
-
-
/drivers/android
-
/drivers/misc
-
/include/linux
-
-
-
-
-
Android Platform and Applications
-
-
The following list outlines the directory structure found within the device branch of Android source code:
-
-
-
-
-
-
-
-
apps
-Core Android applications such as Phone, Camera, and Calendar.
-
-
-
-
boot
-Reference Android bootloader and other boot-related source code.
-
-
-
-
commands
-Common Android commands, the most important of which is the runtime command, which does much of the initialization of the system.
-
-
-
-
config
-System-wide makefiles and linker scripts.
-
data
-Fonts, keymaps, sounds, timezone information, etc.
-
-
-
-
docs
-Full set of Android documentation.
-
-
-
-
extlibs
-Non-Android libraries. This directory is intended to host unmodified external code. None of the libraries included within this directory rely on Android headers or libraries.
-
-
-
-
ide
-Tools for support of the IDE's used to write Android applications.
-
-
-
-
include
-Android system headers for inclusion.
-
-
-
-
java
-Android core APIs, as well as some external libraries.
-
-
-
-
libs
-Android-specific C++ based libraries.
-
-
-
-
partner
-Project-specific source code for various proprietary components.
-
-
-
-
prebuilt
-Prebuilt tools, like the toolchains and emulator binary.
-
-
-
-
product
-Device-specific configuration files. This directory will include a subdirectory for each new device.
-
-
-
-
samples
-Sample applications.
-
-
-
-
servers
-C++ based system servers.
-
-
-
-
system
-Core of the embedded Linux platform at the heart of Android. These essential bits are required for basic booting, operation, and debugging.
-
-
-
-
tests
-Platform and application test cases.
-
-
-
-
tools
-Tools for building and debugging Android (of particular interest for porting are "adb" and "emulator").
-
-
-
-
-
-
-
-
-
-
Adding Source Code
-
-
You can develop Android applications with the same standard tools you use to develop any Java application. The Android core libraries provide the functionality needed to build rich mobile applications and the Android development tools are designed to simplify running, debugging, and testing your applications.
-
-
Add project-specific source code to the Android source tree under the partner directory in a directory specific to the application or service you are building. For example, all Google-specific applications would be placed under device/partner/google/. A Google search application would be placed under device/partner/google/apps/Search.
-
Android source code is maintained in two code bases: the Android Linux kernel (kernel directory) and Android platform and applications (device directory). This document provides a high-level introduction to the source code organization and an overview of the major components of each primary directory.
+
+
Android Source
+
+
+
Linux Kernel
+
+
The Android Linux kernel includes enhancements to the Linux 2.6 kernel that provide additional drivers to support the Android platform, including:
+
+
Binder: an OpenBinder-based driver to facilitate inter-process communication (IPC) in the Android platform.
+
Android Power Management: a light weight power management driver built on top of standard Linux power management but optimized for embedded systems.
+
Low Memory Killer: Based on hints from the userspace, the low memory killer can kill off processes to free up memory as necessary. It is designed to provide more flexibility than the Out Of Memory (OOM) killer in the standard kernel.
+
Logger: A light weight logging device used to capture system, radio, logdata, etc.
+
USB Gadget: Uses the USB function framework.
+
Android/PMEM: The PMEM (physical memory) driver is used to provide contiguous physical memory regions to userspace libraries that interact with the digital signal processor (DSP) and other hardware that cannot cope with scatter-gather.
+
Android Alarm: A driver which provides timers that can wake the device up from sleep and a monotonic timebase that runs while the device is asleep.
+
+
Look for Android-specific enhancements in the following directories:
+
+
/drivers/android
+
/drivers/misc
+
/include/linux
+
+
+
+
+
Android Platform and Applications
+
+
The following list outlines the directory structure found within the device branch of Android source code:
+
+
+
+
+
+
+
+
apps
+Core Android applications such as Phone, Camera, and Calendar.
+
+
+
+
boot
+Reference Android bootloader and other boot-related source code.
+
+
+
+
commands
+Common Android commands, the most important of which is the runtime command, which does much of the initialization of the system.
+
+
+
+
config
+System-wide makefiles and linker scripts.
+
data
+Fonts, keymaps, sounds, timezone information, etc.
+
+
+
+
docs
+Full set of Android documentation.
+
+
+
+
extlibs
+Non-Android libraries. This directory is intended to host unmodified external code. None of the libraries included within this directory rely on Android headers or libraries.
+
+
+
+
ide
+Tools for support of the IDE's used to write Android applications.
+
+
+
+
include
+Android system headers for inclusion.
+
+
+
+
java
+Android core APIs, as well as some external libraries.
+
+
+
+
libs
+Android-specific C++ based libraries.
+
+
+
+
partner
+Project-specific source code for various proprietary components.
+
+
+
+
prebuilt
+Prebuilt tools, like the toolchains and emulator binary.
+
+
+
+
product
+Device-specific configuration files. This directory will include a subdirectory for each new device.
+
+
+
+
samples
+Sample applications.
+
+
+
+
servers
+C++ based system servers.
+
+
+
+
system
+Core of the embedded Linux platform at the heart of Android. These essential bits are required for basic booting, operation, and debugging.
+
+
+
+
tests
+Platform and application test cases.
+
+
+
+
tools
+Tools for building and debugging Android (of particular interest for porting are "adb" and "emulator").
+
+
+
+
+
+
+
+
+
+
Adding Source Code
+
+
You can develop Android applications with the same standard tools you use to develop any Java application. The Android core libraries provide the functionality needed to build rich mobile applications and the Android development tools are designed to simplify running, debugging, and testing your applications.
+
+
Add project-specific source code to the Android source tree under the partner directory in a directory specific to the application or service you are building. For example, all Google-specific applications would be placed under device/partner/google/. A Google search application would be placed under device/partner/google/apps/Search.
+
Android supports its own Power Management (on top of the standard Linux Power Management) designed with the premise that the CPU shouldn't consume power if no applications or services require power. For more information regarding standard Linux power management, please see Linux Power Management Support at http://kernel.org.
-
Android requires that applications and services request CPU resources with "wake locks" through the Android application framework and native Linux libraries. If there are no active wake locks, Android will shut down the CPU.
-
The image below illustrates the Android power management architecture.
-
-
-Solid elements represent Android blocks and dashed elements represent partner-specific blocks.
-
-
-
-
Wake Locks
-
-
Wake locks are used by applications and services to request CPU resources.
-
-
-
Types of Wake Locks
-
-
-
-
Wake Lock
-
Description
-
-
-
ACQUIRE_CAUSES_WAKEUP
-
Normally wake locks don't actually wake the device, they just cause it to remain on once it's already on. Think of the video player app as the normal behavior. Notifications that pop up and want the device to be on are the exception; use this flag to be like them.
-
-
-
FULL_WAKE_LOCK
-
Wake lock that ensures that the screen and keyboard are on at full brightness.
-
-
-
ON_AFTER_RELEASE
-
When this wake lock is released, poke the user activity timer so the screen stays on for a little longer.
-
-
-
PARTIAL_WAKE_LOCK
-
Wake lock that ensures that the CPU is running. The screen might not be on.
-
-
-
SCREEN_BRIGHT_WAKE_LOCK
-
Wake lock that ensures that the screen is on at full brightness; the keyboard backlight will be allowed to go off.
-
-
-
SCREEN_DIM_WAKE_LOCK
-
Wake lock that ensures that the screen is on, but the keyboard backlight will be allowed to go off, and the screen backlight will be allowed to go dim.
-
-
-
-
-
Exploring a Wake Lock Example
-
-
All power management calls follow the same basic format:
-
Acquire handle to the PowerManager service.
-
Create a wake lock and specify the power management flags for screen, timeout, etc.
-
Acquire wake lock.
-
Perform operation (play MP3, open HTML page, etc.).
The Android Framework exposes power management to services and applications through the PowerManager class.
-
User space native libraries (any hardware function in //device/lib/hardware/ meant to serve as supporting libraries for Android runtime) should never call into Android Power Management directly (see the image above). Bypassing the power management policy in the Android runtime will destabilize the system.
-
All calls into Power Management should go through the Android runtime PowerManager APIs.
You can register Kernel-level drivers with the Android Power Manager driver so that they're notified immediately before power down or after power up. For example, you might set a display driver to completely power down when a request comes in to power down from the user space (see the Android MSM MDDI display driver for a sample implementation).
-
To register drivers with the Android PM driver, implement call-back handlers and register them with the Android PM, as illustrated in the snippet below:
Android supports its own Power Management (on top of the standard Linux Power Management) designed with the premise that the CPU shouldn't consume power if no applications or services require power. For more information regarding standard Linux power management, please see Linux Power Management Support at http://kernel.org.
+
Android requires that applications and services request CPU resources with "wake locks" through the Android application framework and native Linux libraries. If there are no active wake locks, Android will shut down the CPU.
+
The image below illustrates the Android power management architecture.
+
+
+
Solid elements represent Android blocks and dashed elements represent partner-specific blocks.
+
+
+
+
Wake Locks
+
+
Wake locks are used by applications and services to request CPU resources.
+
+
+
Types of Wake Locks
+
+
+
+
Wake Lock
+
Description
+
+
+
ACQUIRE_CAUSES_WAKEUP
+
Normally wake locks don't actually wake the device, they just cause it to remain on once it's already on. Think of the video player app as the normal behavior. Notifications that pop up and want the device to be on are the exception; use this flag to be like them.
+
+
+
FULL_WAKE_LOCK
+
Wake lock that ensures that the screen and keyboard are on at full brightness.
+
+
+
ON_AFTER_RELEASE
+
When this wake lock is released, poke the user activity timer so the screen stays on for a little longer.
+
+
+
PARTIAL_WAKE_LOCK
+
Wake lock that ensures that the CPU is running. The screen might not be on.
+
+
+
SCREEN_BRIGHT_WAKE_LOCK
+
Wake lock that ensures that the screen is on at full brightness; the keyboard backlight will be allowed to go off.
+
+
+
SCREEN_DIM_WAKE_LOCK
+
Wake lock that ensures that the screen is on, but the keyboard backlight will be allowed to go off, and the screen backlight will be allowed to go dim.
+
+
+
+
+
Exploring a Wake Lock Example
+
+
All power management calls follow the same basic format:
+
Acquire handle to the PowerManager service.
+
Create a wake lock and specify the power management flags for screen, timeout, etc.
+
Acquire wake lock.
+
Perform operation (play MP3, open HTML page, etc.).
The Android Framework exposes power management to services and applications through the PowerManager class.
+
User space native libraries (any hardware function in //device/lib/hardware/ meant to serve as supporting libraries for Android runtime) should never call into Android Power Management directly (see the image above). Bypassing the power management policy in the Android runtime will destabilize the system.
+
All calls into Power Management should go through the Android runtime PowerManager APIs.
You can register Kernel-level drivers with the Android Power Manager driver so that they're notified immediately before power down or after power up. For example, you might set a display driver to completely power down when a request comes in to power down from the user space (see the Android MSM MDDI display driver for a sample implementation).
+
To register drivers with the Android PM driver, implement call-back handlers and register them with the Android PM, as illustrated in the snippet below:
This section provides instructions on how to configure your host system to build Android for mobile devices. While Android is designed as host-environment agnostic, it has been tested and is known to work on the following Linux operating system; Ubuntu 6.06 (Dapper), 7.10 (Gutsy), and 8.04. Cygwin is not recommended.
-
-
-
Installing Packages
-
-
-
-
Required Packages
-
-
Android requires the following system packages:
-
-
flex: This lexical analyzer generator is used to read a given input file for a description of a scanner to generate.
-
bison: This is a general-purpose parser generator.
-
gperf: This is a perfect hash function generator.
-
libesd0-dev: This enlightened sound daemon (dev files) is used to mix digitized audio streams for playback by a single device.
-
libwxgtk2.6-dev: This package provides GUI components and other facilities for many different platforms.
-
build-essential: This package contains a list of packages considered fundamental to building Debian packages.
This snippet includes an artificial line break to maintain a print-friendly document.
-
-
-
Ubuntu 7.10
-
-
The libwxgtk2.6-dev package will only work if the following code is included in your /etc/apt/source file.
-
-## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
-## team, and may not be under a free license. Please satisfy yourself as to
-## your rights to use the software. Also, please note that software in
-## universe WILL NOT receive any review or updates from the Ubuntu security
-## team.
-# Line commented out by installer because it failed to verify:
-deb http://us.archive.ubuntu.com/ubuntu/ gutsy universe
-# Line commented out by installer because it failed to verify:
-deb-src http://us.archive.ubuntu.com/ubuntu/ gutsy universe
-# Line commented out by installer because it failed to verify:
-deb http://us.archive.ubuntu.com/ubuntu/ gutsy-updates universe
-# Line commented out by installer because it failed to verify:
-deb-src http://us.archive.ubuntu.com/ubuntu/ gutsy-updates universe
-
-
Install required packages with the following command:
-
Android source code includes a hard dependency on the Java Developer Kit (JDK) 5.0 Update 12 or greater. The specific file name of the Update 12 package is jdk-1_5_0_12-linux-i586.bin. To download this version of the Java JDK:
Select '5.0 Update 12' from the 'Java 2 Platform Standard Edition (J2SE)' -> 'JDK/JRE - 5.0' field and click 'Go.'
-
Click 'Download JDK.'
-
In the 'Linux Platform' section, click 'Linux self-extracting file' associated with the jdk-1_5_0_12-linux-i586.bin package.
-
Follow the installation instructions.
-
-
-
-
Once you have cleanly installed the JDK, modify your PATH environment variable to include <jdk-install-dir>/jdk1.5.0_12/bin at its beginning so that Dapper will use the correct installation.
-
Ubuntu 7.10
-
An alternative method to quickly install Java is to enable multiverse repo in /etc/apt/sources.list and then execute:
-
-% sudo apt-get install sun-java5-jdk
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
v0.5 - 25 September 2008
-
-
-
diff --git a/pdk/docs/source_setup_guide.jd b/pdk/docs/source_setup_guide.jd
new file mode 100755
index 000000000..5c0bed5e1
--- /dev/null
+++ b/pdk/docs/source_setup_guide.jd
@@ -0,0 +1,116 @@
+page.title=Host System Setup
+@jd:body
+
+
+
This section provides instructions on how to configure your host system to build Android for mobile devices. While Android is designed as host-environment agnostic, it has been tested and is known to work on the following Linux operating system; Ubuntu 6.06 (Dapper), 7.10 (Gutsy), and 8.04. Cygwin is not recommended.
+
+
+
Installing Packages
+
+
+
+
Required Packages
+
+
Android requires the following system packages:
+
+
flex: This lexical analyzer generator is used to read a given input file for a description of a scanner to generate.
+
bison: This is a general-purpose parser generator.
+
gperf: This is a perfect hash function generator.
+
libesd0-dev: This enlightened sound daemon (dev files) is used to mix digitized audio streams for playback by a single device.
+
libwxgtk2.6-dev: This package provides GUI components and other facilities for many different platforms.
+
build-essential: This package contains a list of packages considered fundamental to building Debian packages.
This snippet includes an artificial line break to maintain a print-friendly document.
+
+
+
Ubuntu 7.10
+
+
The libwxgtk2.6-dev package will only work if the following code is included in your /etc/apt/source file.
+
+## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
+## team, and may not be under a free license. Please satisfy yourself as to
+## your rights to use the software. Also, please note that software in
+## universe WILL NOT receive any review or updates from the Ubuntu security
+## team.
+# Line commented out by installer because it failed to verify:
+deb http://us.archive.ubuntu.com/ubuntu/ gutsy universe
+# Line commented out by installer because it failed to verify:
+deb-src http://us.archive.ubuntu.com/ubuntu/ gutsy universe
+# Line commented out by installer because it failed to verify:
+deb http://us.archive.ubuntu.com/ubuntu/ gutsy-updates universe
+# Line commented out by installer because it failed to verify:
+deb-src http://us.archive.ubuntu.com/ubuntu/ gutsy-updates universe
+
+
Install required packages with the following command:
+
Android source code includes a hard dependency on the Java Developer Kit (JDK) 5.0 Update 12 or greater. The specific file name of the Update 12 package is jdk-1_5_0_12-linux-i586.bin. To download this version of the Java JDK:
Select '5.0 Update 12' from the 'Java 2 Platform Standard Edition (J2SE)' -> 'JDK/JRE - 5.0' field and click 'Go.'
+
Click 'Download JDK.'
+
In the 'Linux Platform' section, click 'Linux self-extracting file' associated with the jdk-1_5_0_12-linux-i586.bin package.
+
Follow the installation instructions.
+
+
+
+
Once you have cleanly installed the JDK, modify your PATH environment variable to include <jdk-install-dir>/jdk1.5.0_12/bin at its beginning so that Dapper will use the correct installation.
+
Ubuntu 7.10
+
An alternative method to quickly install Java is to enable multiverse repo in /etc/apt/sources.list and then execute:
While Android is designed to support a wide variety of hardware platforms and configurations, this section provides recommended minimum device requirements.
-
-
-
Feature
-
Minimum Requirement
-
Notes
-
-
-
Chipset
-
ARM-based
-
For the first release, Android is primarily targeted towards mobile handsets and portions of the platform, such as Dalvik VM graphics processing, currently assume an ARM architecture.
-
-
-
Memory
-
128 MB RAM; 256 MB Flash External
-
Android can boot and run in configurations with less memory, but it isn't recommended.
-
-
-
Storage
-
Mini or Micro SD
-
Not necessary for basic bring up, but recommended.
-
-
-
Primary Display
-
HVGA required
-
The current Android interface targets a touch-based HVGA resolution display with a touch-interface no smaller than 2.8 inches in size. However, smaller displays will suffice for initial porting.
-
-
-
Navigation Keys
-
5-way navigation with 5 application keys, power, camera and volume controls
-
-
-
-
Camera
-
2MP CMOS
-
Not required for basic bring up.
-
-
-
USB
-
Standard mini-B USB interface
-
Android uses the USB interface for flashing the device system images and debugging a running device.
-
-
-
Bluetooth
-
1.2 or 2.0
-
Not required for initial bring up.
-
-
-
-
If available, your Android device can also benefit from the following optional device characteristics:
While Android is designed to support a wide variety of hardware platforms and configurations, this section provides recommended minimum device requirements.
+
+
+
+
Feature
+
Minimum Requirement
+
Notes
+
+
+
Chipset
+
ARM-based
+
For the first release, Android is primarily targeted towards mobile handsets and portions of the platform, such as Dalvik VM graphics processing, currently assume an ARM architecture.
+
+
+
Memory
+
128 MB RAM; 256 MB Flash External
+
Android can boot and run in configurations with less memory, but it isn't recommended.
+
+
+
Storage
+
Mini or Micro SD
+
Not necessary for basic bring up, but recommended.
+
+
+
Primary Display
+
HVGA required
+
The current Android interface targets a touch-based HVGA resolution display with a touch-interface no smaller than 2.8 inches in size. However, smaller displays will suffice for initial porting.
+
+
+
Navigation Keys
+
5-way navigation with 5 application keys, power, camera and volume controls
+
+
+
+
Camera
+
2MP CMOS
+
Not required for basic bring up.
+
+
+
USB
+
Standard mini-B USB interface
+
Android uses the USB interface for flashing the device system images and debugging a running device.
+
+
+
Bluetooth
+
1.2 or 2.0
+
Not required for initial bring up.
+
+
+
+
If available, your Android device can also benefit from the following optional device characteristics:
Android uses wpa_supplicant as the platform interface to the Wi-Fi device. Your Wi-Fi driver must be compatible with the standard wpa_supplicant in addition to extensions added to the supplicant (specifically, the "DRIVER" commands described in wifi.h/wifi_command()).
-
-
-
Building a Wi-Fi Library
-
-
To create a Wi-Fi driver for Android:
-
-
create a shared library that implements the interface defined in include/hardware/wifi.h, which also defines the Wi-Fi supplicant.
Test your driver using the command line wpa_cli utilities.
-
-
-
You can find the default implementation in libs/hardware/wifi/wifi.c. If you need to make changes, create a new source file similar to wifi.c, for example, wifi_mywifi.c.
-
-
Update the default Android.mk file (libs/hardware/wifi/Android.mk) as shown below.
Note: This document relies on some Doxygen-generated content that appears in an iFrame below. To return to the Doxygen default content for this page, click here.
Android uses wpa_supplicant as the platform interface to the Wi-Fi device. Your Wi-Fi driver must be compatible with the standard wpa_supplicant in addition to extensions added to the supplicant (specifically, the "DRIVER" commands described in wifi.h/wifi_command()).
+
+
+
Building a Wi-Fi Library
+
+
To create a Wi-Fi driver for Android:
+
+
create a shared library that implements the interface defined in include/hardware/wifi.h, which also defines the Wi-Fi supplicant.
Test your driver using the command line wpa_cli utilities.
+
+
+
You can find the default implementation in libs/hardware/wifi/wifi.c. If you need to make changes, create a new source file similar to wifi.c, for example, wifi_mywifi.c.
+
+
Update the default Android.mk file (libs/hardware/wifi/Android.mk) as shown below.
Note: This document relies on some Doxygen-generated content that appears in an iFrame below. To return to the Doxygen default content for this page, click here.
+
+
+
+
+
+
diff --git a/pdk/hosting/app.yaml b/pdk/hosting/app.yaml
index 407cbb08b..6e08141bc 100644
--- a/pdk/hosting/app.yaml
+++ b/pdk/hosting/app.yaml
@@ -4,8 +4,8 @@ runtime: python
api_version: 1
handlers:
-- url: /docs
- static_dir: docs
+- url: /online-pdk
+ static_dir: online-pdk
- url: /
script: pdk.py
diff --git a/pdk/hosting/pdk.py b/pdk/hosting/pdk.py
index e88f826f8..421f19550 100644
--- a/pdk/hosting/pdk.py
+++ b/pdk/hosting/pdk.py
@@ -26,11 +26,9 @@ from google.appengine.ext.webapp.util import run_wsgi_app
class MainPage(webapp.RequestHandler):
def get(self):
- self.redirect('docs/index.html')
+ self.redirect('online-pdk/index.html')
-application = webapp.WSGIApplication(
- [('/', MainPage)],
- debug=True)
+application = webapp.WSGIApplication([('/', MainPage)], debug=True)
def main():
run_wsgi_app(application)
@@ -38,6 +36,18 @@ def main():
if __name__ == "__main__":
main()
+# Testing
+# You must install google appengine. See: http://code.google.com/appengine/downloads.html
+#
+# Here's the command to run the pdk-docs server locally:
+# python /dev_appserver.py --address 0.0.0.0 \
+# /android/out/target/common/docs
+
+# To verify it is working you can access it with a browser loacally on port 8080:
+
+# http://localhost:8080/index.html
+
+
# To upload this application:
# /home/build/static/projects/apphosting/devtools/appcfg.py update pdk/
# where the pdk directory contains: pdk.py, app.yaml, and the docs directory.
From 7e020ebebdaf251e8758c7b7e9a0130b144341e2 Mon Sep 17 00:00:00 2001
From: Xavier Ducrohet <>
Date: Thu, 2 Apr 2009 11:36:53 -0700
Subject: [PATCH 054/127] AI 144254: am: CL 144252 Reload AVDs when displaying
the run/debug launch dialog (bug #1683892) Original author: xav Merged
from: //branches/cupcake/...
Automated import of CL 144254
---
.../ide/eclipse/adt/launch/EmulatorConfigTab.java | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java
index 3789153e4..24380eb62 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java
@@ -21,6 +21,7 @@ import com.android.ide.eclipse.adt.launch.AndroidLaunchConfiguration.TargetMode;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.avd.AvdManager;
import com.android.sdklib.avd.AvdManager.AvdInfo;
@@ -126,6 +127,14 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
*/
public void createControl(Composite parent) {
Font font = parent.getFont();
+
+ // reload the AVDs to make sure we are up to date
+ try {
+ Sdk.getCurrent().getAvdManager().reloadAvds();
+ } catch (AndroidLocationException e1) {
+ // this happens if the AVD Manager failed to find the folder in which the AVDs are
+ // stored. There isn't much we can do at this point.
+ }
Composite topComp = new Composite(parent, SWT.NONE);
setControl(topComp);
From f61f43e16bd71a4f2e3cf4ba4f31316ed3fe5d4f Mon Sep 17 00:00:00 2001
From: Brett Chabot <>
Date: Thu, 2 Apr 2009 11:52:48 -0700
Subject: [PATCH 055/127] AI 144262: am: CL 144260 Add constant for test file
name in runtest. Original author: brettchabot Merged from:
//branches/cupcake/...
Automated import of CL 144262
---
testrunner/runtest.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/testrunner/runtest.py b/testrunner/runtest.py
index 8d3659130..243da77b5 100755
--- a/testrunner/runtest.py
+++ b/testrunner/runtest.py
@@ -39,14 +39,17 @@ import test_defs
class TestRunner(object):
"""Command line utility class for running pre-defined Android test(s)."""
+ _TEST_FILE_NAME = "test_defs.xml"
+
# file path to android core platform tests, relative to android build root
# TODO move these test data files to another directory
- _CORE_TEST_PATH = os.path.join("development", "testrunner", "test_defs.xml")
+ _CORE_TEST_PATH = os.path.join("development", "testrunner",
+ _TEST_FILE_NAME)
# vendor glob file path patterns to tests, relative to android
# build root
_VENDOR_TEST_PATH = os.path.join("vendor", "*", "tests", "testinfo",
- "test_defs.xml")
+ _TEST_FILE_NAME)
_RUNTEST_USAGE = (
"usage: runtest.py [options] short-test-name[s]\n\n"
@@ -61,7 +64,7 @@ class TestRunner(object):
"""Processes command-line options."""
# TODO error messages on once-only or mutually-exclusive options.
user_test_default = os.path.join(os.environ.get("HOME"), ".android",
- "test_defs.xml")
+ self._TEST_FILE_NAME)
parser = optparse.OptionParser(usage=self._RUNTEST_USAGE)
From d63db799eafc837c23781ca830188dd2e3549c9b Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Thu, 2 Apr 2009 13:42:45 -0700
Subject: [PATCH 056/127] AI 144284: am: CL 144283 ADT: Enhance Resource
Chooser with ability to create new XML strings. That's a first pass.
There's a fair bit of refactoring involved, so it's split in two CLs. Next
CL will add more functionality. Original author: raphael Merged from:
//branches/cupcake/...
Automated import of CL 144284
---
.../extractstring/ExtractStringInputPage.java | 393 ++--------------
.../ExtractStringRefactoring.java | 210 ++++-----
.../extractstring/ExtractStringWizard.java | 2 +-
.../adt/ui/ReferenceChooserDialog.java | 95 +++-
.../wizards/newstring/NewStringBaseImpl.java | 439 ++++++++++++++++++
.../wizards/newstring/NewStringHelper.java | 122 +++++
.../wizards/newstring/NewStringWizard.java | 66 +++
.../newstring/NewStringWizardPage.java | 127 +++++
.../uimodel/UiResourceAttributeNode.java | 4 +-
9 files changed, 985 insertions(+), 473 deletions(-)
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringBaseImpl.java
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringHelper.java
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringWizard.java
create mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringWizardPage.java
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
index 9822b32e7..1f50c07b6 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
@@ -17,72 +17,34 @@
package com.android.ide.eclipse.adt.refactorings.extractstring;
-import com.android.ide.eclipse.adt.ui.ConfigurationSelector;
-import com.android.ide.eclipse.common.AndroidConstants;
-import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
-import com.android.sdklib.SdkConstants;
+import com.android.ide.eclipse.adt.wizards.newstring.NewStringBaseImpl;
+import com.android.ide.eclipse.adt.wizards.newstring.NewStringBaseImpl.INewStringPageCallback;
+import com.android.ide.eclipse.adt.wizards.newstring.NewStringBaseImpl.ValidationStatus;
-import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.wizard.IWizardPage;
-import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.ltk.ui.refactoring.UserInputWizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
-import java.util.HashMap;
-import java.util.TreeSet;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
/**
* @see ExtractStringRefactoring
*/
-class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage {
+class ExtractStringInputPage extends UserInputWizardPage
+ implements IWizardPage, INewStringPageCallback {
- /** Last res file path used, shared across the session instances but specific to the
- * current project. The default for unknown projects is {@link #DEFAULT_RES_FILE_PATH}. */
- private static HashMap sLastResFilePath = new HashMap();
-
- /** The project where the user selection happened. */
- private final IProject mProject;
-
- /** Field displaying the user-selected string to be replaced. */
- private Label mStringLabel;
- /** Test field where the user enters the new ID to be generated or replaced with. */
- private Text mNewIdTextField;
- /** The configuration selector, to select the resource path of the XML file. */
- private ConfigurationSelector mConfigSelector;
- /** The combo to display the existing XML files or enter a new one. */
- private Combo mResFileCombo;
-
- /** Regex pattern to read a valid res XML file path. It checks that the are 2 folders and
- * a leaf file name ending with .xml */
- private static final Pattern RES_XML_FILE_REGEX = Pattern.compile(
- "/res/[a-z][a-zA-Z0-9_-]+/[^.]+\\.xml"); //$NON-NLS-1$
- /** Absolute destination folder root, e.g. "/res/" */
- private static final String RES_FOLDER_ABS =
- AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP;
- /** Relative destination folder root, e.g. "res/" */
- private static final String RES_FOLDER_REL =
- SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
-
- private static final String DEFAULT_RES_FILE_PATH = "/res/values/strings.xml";
+ private NewStringBaseImpl mImpl;
public ExtractStringInputPage(IProject project) {
super("ExtractStringInputPage"); //$NON-NLS-1$
- mProject = project;
+ mImpl = new NewStringBaseImpl(project, this);
}
/**
@@ -92,17 +54,12 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
* {@link ExtractStringRefactoring}.
*/
public void createControl(Composite parent) {
-
Composite content = new Composite(parent, SWT.NONE);
-
GridLayout layout = new GridLayout();
layout.numColumns = 1;
content.setLayout(layout);
-
- createStringReplacementGroup(content);
- createResFileGroup(content);
- validatePage();
+ mImpl.createControl(content);
setControl(content);
}
@@ -111,8 +68,9 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
* and by which options.
*
* @param content A composite with a 1-column grid layout
+ * @return The {@link Text} field for the new String ID name.
*/
- private void createStringReplacementGroup(Composite content) {
+ public Text createStringGroup(Composite content) {
final ExtractStringRefactoring ref = getOurRefactoring();
@@ -124,16 +82,27 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
layout.numColumns = 2;
group.setLayout(layout);
- // line: String found in selection
+ // line: Textfield for string value (based on selection, if any)
Label label = new Label(group, SWT.NONE);
label.setText("String:");
String selectedString = ref.getTokenString();
- mStringLabel = new Label(group, SWT.NONE);
- mStringLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- mStringLabel.setText(selectedString != null ? selectedString : "");
+ final Text stringValueField = new Text(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ stringValueField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ stringValueField.setText(selectedString != null ? selectedString : ""); //$NON-NLS-1$
+
+ ref.setNewStringValue(stringValueField.getText());
+
+ stringValueField.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ if (mImpl.validatePage()) {
+ ref.setNewStringValue(stringValueField.getText());
+ }
+ }
+ });
+
// TODO provide an option to replace all occurences of this string instead of
// just the one.
@@ -143,74 +112,31 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
label = new Label(group, SWT.NONE);
label.setText("Replace by R.string.");
- mNewIdTextField = new Text(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
- mNewIdTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- mNewIdTextField.setText(guessId(selectedString));
+ final Text stringIdField = new Text(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ stringIdField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ stringIdField.setText(guessId(selectedString));
- ref.setReplacementStringId(mNewIdTextField.getText().trim());
+ ref.setNewStringId(stringIdField.getText().trim());
- mNewIdTextField.addModifyListener(new ModifyListener() {
+ stringIdField.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
- if (validatePage()) {
- ref.setReplacementStringId(mNewIdTextField.getText().trim());
+ if (mImpl.validatePage()) {
+ ref.setNewStringId(stringIdField.getText().trim());
}
}
});
- }
-
- /**
- * Creates the lower group with the fields to choose the resource confirmation and
- * the target XML file.
- *
- * @param content A composite with a 1-column grid layout
- */
- private void createResFileGroup(Composite content) {
-
- Group group = new Group(content, SWT.NONE);
- group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- group.setText("XML resource to edit");
-
- GridLayout layout = new GridLayout();
- layout.numColumns = 2;
- group.setLayout(layout);
- // line: selection of the res config
-
- Label label;
- label = new Label(group, SWT.NONE);
- label.setText("Configuration:");
-
- mConfigSelector = new ConfigurationSelector(group);
- GridData gd = new GridData(2, GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
- gd.widthHint = ConfigurationSelector.WIDTH_HINT;
- gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
- mConfigSelector.setLayoutData(gd);
- OnConfigSelectorUpdated onConfigSelectorUpdated = new OnConfigSelectorUpdated();
- mConfigSelector.setOnChangeListener(onConfigSelectorUpdated);
-
- // line: selection of the output file
-
- label = new Label(group, SWT.NONE);
- label.setText("Resource file:");
-
- mResFileCombo = new Combo(group, SWT.DROP_DOWN);
- mResFileCombo.select(0);
- mResFileCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- mResFileCombo.addModifyListener(onConfigSelectorUpdated);
-
- // set output file name to the last one used
-
- String projPath = mProject.getFullPath().toPortableString();
- String filePath = sLastResFilePath.get(projPath);
-
- mResFileCombo.setText(filePath != null ? filePath : DEFAULT_RES_FILE_PATH);
- onConfigSelectorUpdated.run();
+ return stringIdField;
}
/**
* Utility method to guess a suitable new XML ID based on the selected string.
*/
private String guessId(String text) {
+ if (text == null) {
+ return ""; //$NON-NLS-1$
+ }
+
// make lower case
text = text.toLowerCase();
@@ -231,247 +157,8 @@ class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage
return (ExtractStringRefactoring) getRefactoring();
}
- /**
- * Validates fields of the wizard input page. Displays errors as appropriate and
- * enable the "Next" button (or not) by calling {@link #setPageComplete(boolean)}.
- *
- * @return True if the page has been positively validated. It may still have warnings.
- */
- private boolean validatePage() {
- boolean success = true;
-
- // Analyze fatal errors.
-
- String text = mNewIdTextField.getText().trim();
- if (text == null || text.length() < 1) {
- setErrorMessage("Please provide a resource ID to replace with.");
- success = false;
- } else {
- for (int i = 0; i < text.length(); i++) {
- char c = text.charAt(i);
- boolean ok = i == 0 ?
- Character.isJavaIdentifierStart(c) :
- Character.isJavaIdentifierPart(c);
- if (!ok) {
- setErrorMessage(String.format(
- "The resource ID must be a valid Java identifier. The character %1$c at position %2$d is not acceptable.",
- c, i+1));
- success = false;
- break;
- }
- }
- }
-
- String resFile = mResFileCombo.getText();
- if (success) {
- if (resFile == null || resFile.length() == 0) {
- setErrorMessage("A resource file name is required.");
- success = false;
- } else if (!RES_XML_FILE_REGEX.matcher(resFile).matches()) {
- setErrorMessage("The XML file name is not valid.");
- success = false;
- }
- }
-
- // Analyze info & warnings.
-
- if (success) {
- setErrorMessage(null);
-
- ExtractStringRefactoring ref = getOurRefactoring();
-
- ref.setTargetFile(resFile);
- sLastResFilePath.put(mProject.getFullPath().toPortableString(), resFile);
-
- if (ref.isResIdDuplicate(resFile, text)) {
- setMessage(
- String.format("There's already a string item called '%1$s' in %2$s.",
- text, resFile),
- WizardPage.WARNING);
- } else if (mProject.findMember(resFile) == null) {
- setMessage(
- String.format("File %2$s does not exist and will be created.",
- text, resFile),
- WizardPage.INFORMATION);
- } else {
- setMessage(null);
- }
- }
-
- setPageComplete(success);
- return success;
+ public void postValidatePage(ValidationStatus status) {
+ ExtractStringRefactoring ref = getOurRefactoring();
+ ref.setTargetFile(mImpl.getResFileProjPath());
}
-
- public class OnConfigSelectorUpdated implements Runnable, ModifyListener {
-
- /** Regex pattern to parse a valid res path: it reads (/res/folder-name/)+(filename). */
- private final Pattern mPathRegex = Pattern.compile(
- "(/res/[a-z][a-zA-Z0-9_-]+/)(.+)"); //$NON-NLS-1$
-
- /** Temporary config object used to retrieve the Config Selector value. */
- private FolderConfiguration mTempConfig = new FolderConfiguration();
-
- private HashMap> mFolderCache =
- new HashMap>();
- private String mLastFolderUsedInCombo = null;
- private boolean mInternalConfigChange;
- private boolean mInternalFileComboChange;
-
- /**
- * Callback invoked when the {@link ConfigurationSelector} has been changed.
- *
- * The callback does the following:
- *
- *
Examine the current file name to retrieve the XML filename, if any.
- *
Recompute the path based on the configuration selector (e.g. /res/values-fr/).
- *
Examine the path to retrieve all the files in it. Keep those in a local cache.
- *
If the XML filename from step 1 is not in the file list, it's a custom file name.
- * Insert it and sort it.
- *
Re-populate the file combo with all the choices.
- *
Select the original XML file.
- */
- public void run() {
- if (mInternalConfigChange) {
- return;
- }
-
- // get current leafname, if any
- String leafName = "";
- String currPath = mResFileCombo.getText();
- Matcher m = mPathRegex.matcher(currPath);
- if (m.matches()) {
- // Note: groups 1 and 2 cannot be null.
- leafName = m.group(2);
- currPath = m.group(1);
- } else {
- // There was a path but it was invalid. Ignore it.
- currPath = "";
- }
-
- // recreate the res path from the current configuration
- mConfigSelector.getConfiguration(mTempConfig);
- StringBuffer sb = new StringBuffer(RES_FOLDER_ABS);
- sb.append(mTempConfig.getFolderName(ResourceFolderType.VALUES));
- sb.append('/');
-
- String newPath = sb.toString();
- if (newPath.equals(currPath) && newPath.equals(mLastFolderUsedInCombo)) {
- // Path has not changed. No need to reload.
- return;
- }
-
- // Get all the files at the new path
-
- TreeSet filePaths = mFolderCache.get(newPath);
-
- if (filePaths == null) {
- filePaths = new TreeSet();
-
- IFolder folder = mProject.getFolder(newPath);
- if (folder != null && folder.exists()) {
- try {
- for (IResource res : folder.members()) {
- String name = res.getName();
- if (res.getType() == IResource.FILE && name.endsWith(".xml")) {
- filePaths.add(newPath + name);
- }
- }
- } catch (CoreException e) {
- // Ignore.
- }
- }
-
- mFolderCache.put(newPath, filePaths);
- }
-
- currPath = newPath + leafName;
- if (leafName.length() > 0 && !filePaths.contains(currPath)) {
- filePaths.add(currPath);
- }
-
- // Fill the combo
- try {
- mInternalFileComboChange = true;
-
- mResFileCombo.removeAll();
-
- for (String filePath : filePaths) {
- mResFileCombo.add(filePath);
- }
-
- int index = -1;
- if (leafName.length() > 0) {
- index = mResFileCombo.indexOf(currPath);
- if (index >= 0) {
- mResFileCombo.select(index);
- }
- }
-
- if (index == -1) {
- mResFileCombo.setText(currPath);
- }
-
- mLastFolderUsedInCombo = newPath;
-
- } finally {
- mInternalFileComboChange = false;
- }
-
- // finally validate the whole page
- validatePage();
- }
-
- /**
- * Callback invoked when {@link ExtractStringInputPage#mResFileCombo} has been
- * modified.
- */
- public void modifyText(ModifyEvent e) {
- if (mInternalFileComboChange) {
- return;
- }
-
- String wsFolderPath = mResFileCombo.getText();
-
- // This is a custom path, we need to sanitize it.
- // First it should start with "/res/". Then we need to make sure there are no
- // relative paths, things like "../" or "./" or even "//".
- wsFolderPath = wsFolderPath.replaceAll("/+\\.\\./+|/+\\./+|//+|\\\\+|^/+", "/"); //$NON-NLS-1$ //$NON-NLS-2$
- wsFolderPath = wsFolderPath.replaceAll("^\\.\\./+|^\\./+", ""); //$NON-NLS-1$ //$NON-NLS-2$
- wsFolderPath = wsFolderPath.replaceAll("/+\\.\\.$|/+\\.$|/+$", ""); //$NON-NLS-1$ //$NON-NLS-2$
-
- // We get "res/foo" from selections relative to the project when we want a "/res/foo" path.
- if (wsFolderPath.startsWith(RES_FOLDER_REL)) {
- wsFolderPath = RES_FOLDER_ABS + wsFolderPath.substring(RES_FOLDER_REL.length());
-
- mInternalFileComboChange = true;
- mResFileCombo.setText(wsFolderPath);
- mInternalFileComboChange = false;
- }
-
- if (wsFolderPath.startsWith(RES_FOLDER_ABS)) {
- wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length());
-
- int pos = wsFolderPath.indexOf(AndroidConstants.WS_SEP_CHAR);
- if (pos >= 0) {
- wsFolderPath = wsFolderPath.substring(0, pos);
- }
-
- String[] folderSegments = wsFolderPath.split(FolderConfiguration.QUALIFIER_SEP);
-
- if (folderSegments.length > 0) {
- String folderName = folderSegments[0];
-
- if (folderName != null && !folderName.equals(wsFolderPath)) {
- // update config selector
- mInternalConfigChange = true;
- mConfigSelector.setConfiguration(folderSegments);
- mInternalConfigChange = false;
- }
- }
- }
-
- validatePage();
- }
- }
-
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
index 430ff1819..a17d81790 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
@@ -16,9 +16,9 @@
package com.android.ide.eclipse.adt.refactorings.extractstring;
+import com.android.ide.eclipse.adt.wizards.newstring.NewStringHelper;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
-import com.android.ide.eclipse.common.project.AndroidXPathFactory;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
@@ -64,8 +64,6 @@ import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.text.edits.TextEditGroup;
-import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
import java.io.BufferedReader;
import java.io.IOException;
@@ -73,14 +71,9 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpressionException;
-
/**
* This refactoring extracts a string from a file and replaces it by an Android resource ID
* such as R.string.foo.
@@ -105,8 +98,8 @@ import javax.xml.xpath.XPathExpressionException;
*
On success, the wizard is shown, which let the user input the new ID to use.
*
The wizard sets the user input values into this refactoring instance, e.g. the new string
* ID, the XML file to update, etc. The wizard does use the utility method
- * {@link #isResIdDuplicate(String, String)} to check whether the new ID is already defined
- * in the target XML file.
+ * {@link NewStringHelper#isResIdDuplicate(IProject, String, String)} to check whether the new
+ * ID is already defined in the target XML file.
*
Once Preview or Finish is selected in the wizard, the
* {@link #checkFinalConditions(IProgressMonitor)} is called to double-check the user input
* and compute the actual changes.
@@ -127,60 +120,106 @@ import javax.xml.xpath.XPathExpressionException;
*
TODO: Have a pref in the wizard: [x] Change other Java Files
*
*/
-class ExtractStringRefactoring extends Refactoring {
+public class ExtractStringRefactoring extends Refactoring {
- /** The file model being manipulated. */
+ private enum Mode {
+ EDIT_SOURCE,
+ MAKE_ID,
+ MAKE_NEW_ID
+ }
+
+ /** The {@link Mode} of operation of the refactoring. */
+ private final Mode mMode;
+ /** The file model being manipulated.
+ * Value is null when not on {@link Mode#EDIT_SOURCE} mode. */
private final IFile mFile;
- /** The start of the selection in {@link #mFile}. */
+ /** The start of the selection in {@link #mFile}.
+ * Value is -1 when not on {@link Mode#EDIT_SOURCE} mode. */
private final int mSelectionStart;
- /** The end of the selection in {@link #mFile}. */
+ /** The end of the selection in {@link #mFile}.
+ * Value is -1 when not on {@link Mode#EDIT_SOURCE} mode. */
private final int mSelectionEnd;
/** The compilation unit, only defined if {@link #mFile} points to a usable Java source file. */
private ICompilationUnit mUnit;
- /** The actual string selected, after UTF characters have been escaped, good for display. */
+ /** The actual string selected, after UTF characters have been escaped, good for display.
+ * Value is null when not on {@link Mode#EDIT_SOURCE} mode. */
private String mTokenString;
/** The XML string ID selected by the user in the wizard. */
private String mXmlStringId;
+ /** The XML string value. Might be different than the initial selected string. */
+ private String mXmlStringValue;
/** The path of the XML file that will define {@link #mXmlStringId}, selected by the user
* in the wizard. */
private String mTargetXmlFileWsPath;
- /** A temporary cache of R.string IDs defined by a given xml file. The key is the
- * project path of the file, the data is a set of known string Ids for that file. */
- private HashMap> mResIdCache;
- /** An instance of XPath, created lazily on demand. */
- private XPath mXPath;
/** The list of changes computed by {@link #checkFinalConditions(IProgressMonitor)} and
* used by {@link #createChange(IProgressMonitor)}. */
private ArrayList mChanges;
+
+ private NewStringHelper mHelper = new NewStringHelper();
public ExtractStringRefactoring(Map arguments)
throws NullPointerException {
-
- IPath path = Path.fromPortableString(arguments.get("file")); //$NON-NLS-1$
- mFile = (IFile) ResourcesPlugin.getWorkspace().getRoot().findMember(path);
- mSelectionStart = Integer.parseInt(arguments.get("sel-start")); //$NON-NLS-1$
- mSelectionEnd = Integer.parseInt(arguments.get("sel-end")); //$NON-NLS-1$
- mTokenString = arguments.get("tok-esc"); //$NON-NLS-1$
+ mMode = Mode.valueOf(arguments.get("mode")); //$NON-NLS-1$
+
+ if (mMode == Mode.EDIT_SOURCE) {
+ IPath path = Path.fromPortableString(arguments.get("file")); //$NON-NLS-1$
+ mFile = (IFile) ResourcesPlugin.getWorkspace().getRoot().findMember(path);
+ mSelectionStart = Integer.parseInt(arguments.get("sel-start")); //$NON-NLS-1$
+ mSelectionEnd = Integer.parseInt(arguments.get("sel-end")); //$NON-NLS-1$
+ mTokenString = arguments.get("tok-esc"); //$NON-NLS-1$
+ } else {
+ mFile = null;
+ mSelectionStart = mSelectionEnd = -1;
+ mTokenString = null;
+ }
}
private Map createArgumentMap() {
HashMap args = new HashMap();
- args.put("file", mFile.getFullPath().toPortableString()); //$NON-NLS-1$
- args.put("sel-start", Integer.toString(mSelectionStart)); //$NON-NLS-1$
- args.put("sel-end", Integer.toString(mSelectionEnd)); //$NON-NLS-1$
- args.put("tok-esc", mTokenString); //$NON-NLS-1$
+ args.put("mode", mMode.name()); //$NON-NLS-1$
+ if (mMode == Mode.EDIT_SOURCE) {
+ args.put("file", mFile.getFullPath().toPortableString()); //$NON-NLS-1$
+ args.put("sel-start", Integer.toString(mSelectionStart)); //$NON-NLS-1$
+ args.put("sel-end", Integer.toString(mSelectionEnd)); //$NON-NLS-1$
+ args.put("tok-esc", mTokenString); //$NON-NLS-1$
+ }
return args;
}
+ /**
+ * Constructor to use when the Extract String refactoring is called on an
+ * *existing* source file. Its purpose is then to get the selected string of
+ * the source and propose to change it by an XML id. The XML id may be a new one
+ * or an existing one.
+ *
+ * @param file The source file to process. Cannot be null. File must exist in workspace.
+ * @param selection The selection in the source file. Cannot be null or empty.
+ */
public ExtractStringRefactoring(IFile file, ITextSelection selection) {
+ mMode = Mode.EDIT_SOURCE;
mFile = file;
mSelectionStart = selection.getOffset();
mSelectionEnd = mSelectionStart + Math.max(0, selection.getLength() - 1);
}
+ /**
+ * Constructor to use when the Extract String refactoring is called without
+ * any source file. Its purpose is then to create a new XML string ID.
+ *
+ * @param enforceNew If true the XML ID must be a new one. If false, an existing ID can be
+ * used.
+ */
+ public ExtractStringRefactoring(boolean enforceNew) {
+ mMode = enforceNew ? Mode.MAKE_NEW_ID : Mode.MAKE_ID;
+ mFile = null;
+ mSelectionStart = mSelectionEnd = -1;
+ }
+
+
+
/**
* @see org.eclipse.ltk.core.refactoring.Refactoring#getName()
*/
@@ -225,6 +264,11 @@ class ExtractStringRefactoring extends Refactoring {
try {
monitor.beginTask("Checking preconditions...", 5);
+
+ if (mMode != Mode.EDIT_SOURCE) {
+ monitor.worked(5);
+ return status;
+ }
if (!checkSourceFile(mFile, status, monitor)) {
return status;
@@ -388,7 +432,7 @@ class ExtractStringRefactoring extends Refactoring {
status.addFatalError("Missing target xml file path");
}
monitor.worked(1);
-
+
// Either that resource must not exist or it must be a writeable file.
IResource targetXml = getTargetXmlResource(mTargetXmlFileWsPath);
if (targetXml != null) {
@@ -415,9 +459,9 @@ class ExtractStringRefactoring extends Refactoring {
// Prepare the change for the XML file.
- if (!isResIdDuplicate(mTargetXmlFileWsPath, mXmlStringId)) {
+ if (!mHelper.isResIdDuplicate(mFile.getProject(), mTargetXmlFileWsPath, mXmlStringId)) {
// We actually change it only if the ID doesn't exist yet
- Change change = createXmlChange((IFile) targetXml, mXmlStringId, mTokenString,
+ Change change = createXmlChange((IFile) targetXml, mXmlStringId, mXmlStringValue,
status, SubMonitor.convert(monitor, 1));
if (change != null) {
mChanges.add(change);
@@ -427,12 +471,14 @@ class ExtractStringRefactoring extends Refactoring {
if (status.hasError()) {
return status;
}
-
- // Prepare the change to the Java compilation unit
- List changes = computeJavaChanges(mUnit, mXmlStringId, mTokenString,
- status, SubMonitor.convert(monitor, 1));
- if (changes != null) {
- mChanges.addAll(changes);
+
+ if (mMode == Mode.EDIT_SOURCE) {
+ // Prepare the change to the Java compilation unit
+ List changes = computeJavaChanges(mUnit, mXmlStringId, mTokenString,
+ status, SubMonitor.convert(monitor, 1));
+ if (changes != null) {
+ mChanges.addAll(changes);
+ }
}
monitor.worked(1);
@@ -484,6 +530,9 @@ class ExtractStringRefactoring extends Refactoring {
// The file exist. Attempt to parse it as a valid XML document.
try {
int[] indices = new int[2];
+
+ // TODO case where we replace the value of an existing XML String ID
+
if (findXmlOpeningTagPos(targetXml.getContents(), "resources", indices)) { //$NON-NLS-1$
// Indices[1] indicates whether we found > or />. It can only be 1 or 2.
// Indices[0] is the position of the first character of either > or />.
@@ -865,78 +914,6 @@ class ExtractStringRefactoring extends Refactoring {
}
- /**
- * Utility method used by the wizard to check whether the given string ID is already
- * defined in the XML file which path is given.
- *
- * @param xmlFileWsPath The project path of the XML file, e.g. "/res/values/strings.xml".
- * The given file may or may not exist.
- * @param stringId The string ID to find.
- * @return True if such a string ID is already defined.
- */
- public boolean isResIdDuplicate(String xmlFileWsPath, String stringId) {
- // This is going to be called many times on the same file.
- // Build a cache of the existing IDs for a given file.
- if (mResIdCache == null) {
- mResIdCache = new HashMap>();
- }
- HashSet cache = mResIdCache.get(xmlFileWsPath);
- if (cache == null) {
- cache = getResIdsForFile(xmlFileWsPath);
- mResIdCache.put(xmlFileWsPath, cache);
- }
-
- return cache.contains(stringId);
- }
-
- /**
- * Extract all the defined string IDs from a given file using XPath.
- *
- * @param xmlFileWsPath The project path of the file to parse. It may not exist.
- * @return The set of all string IDs defined in the file. The returned set is always non
- * null. It is empty if the file does not exist.
- */
- private HashSet getResIdsForFile(String xmlFileWsPath) {
- HashSet ids = new HashSet();
-
- if (mXPath == null) {
- mXPath = AndroidXPathFactory.newXPath();
- }
-
- // Access the project that contains the resource that contains the compilation unit
- IResource resource = getTargetXmlResource(xmlFileWsPath);
-
- if (resource != null && resource.exists() && resource.getType() == IResource.FILE) {
- InputSource source;
- try {
- source = new InputSource(((IFile) resource).getContents());
-
- // We want all the IDs in an XML structure like this:
- //
- // something
- //
-
- String xpathExpr = "/resources/string/@name"; //$NON-NLS-1$
-
- Object result = mXPath.evaluate(xpathExpr, source, XPathConstants.NODESET);
- if (result instanceof NodeList) {
- NodeList list = (NodeList) result;
- for (int n = list.getLength() - 1; n >= 0; n--) {
- String id = list.item(n).getNodeValue();
- ids.add(id);
- }
- }
-
- } catch (CoreException e1) {
- // IFile.getContents failed. Ignore.
- } catch (XPathExpressionException e) {
- // mXPath.evaluate failed. Ignore.
- }
- }
-
- return ids;
- }
-
/**
* Given a file project path, returns its resource in the same project than the
* compilation unit. The resource may not exist.
@@ -950,8 +927,15 @@ class ExtractStringRefactoring extends Refactoring {
/**
* Sets the replacement string ID. Used by the wizard to set the user input.
*/
- public void setReplacementStringId(String replacementStringId) {
- mXmlStringId = replacementStringId;
+ public void setNewStringId(String newStringId) {
+ mXmlStringId = newStringId;
+ }
+
+ /**
+ * Sets the replacement string ID. Used by the wizard to set the user input.
+ */
+ public void setNewStringValue(String newStringValue) {
+ mXmlStringValue = newStringValue;
}
/**
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java
index c5b0c7d11..cfcc54621 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringWizard.java
@@ -25,7 +25,7 @@ import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
* @see ExtractStringInputPage
* @see ExtractStringRefactoring
*/
-class ExtractStringWizard extends RefactoringWizard {
+public class ExtractStringWizard extends RefactoringWizard {
private final IProject mProject;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java
index e141396c5..031b30382 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java
@@ -17,10 +17,13 @@
package com.android.ide.eclipse.adt.ui;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.refactorings.extractstring.ExtractStringRefactoring;
+import com.android.ide.eclipse.adt.refactorings.extractstring.ExtractStringWizard;
import com.android.ide.eclipse.common.resources.IResourceRepository;
import com.android.ide.eclipse.common.resources.ResourceItem;
import com.android.ide.eclipse.common.resources.ResourceType;
+import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.DialogSettings;
@@ -30,14 +33,20 @@ import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
+import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.ui.dialogs.SelectionStatusDialog;
@@ -58,21 +67,24 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
private IResourceRepository mResources;
private String mCurrentResource;
-
private FilteredTree mFilteredTree;
+ private Button mNewResButton;
+ private final IProject mProject;
/**
+ * @param project
* @param parent
*/
- public ReferenceChooserDialog(IResourceRepository resources, Shell parent) {
+ public ReferenceChooserDialog(IProject project, IResourceRepository resources, Shell parent) {
super(parent);
+ mProject = project;
+ mResources = resources;
int shellStyle = getShellStyle();
setShellStyle(shellStyle | SWT.MAX | SWT.RESIZE);
- setTitle("Reference Dialog");
+ setTitle("Reference Chooser");
setMessage(String.format("Choose a resource"));
- mResources = resources;
setDialogBoundsSettings(sDialogSettings, getDialogBoundsStrategy());
}
@@ -113,13 +125,26 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
// create the filtered tree
createFilteredTree(top);
-
+
// setup the initial selection
setupInitialSelection();
+ // create the "New Resource" button
+ createNewResButtons(top);
+
return top;
}
+ /**
+ * Creates the "New Resource" button.
+ * @param top the parent composite
+ */
+ private void createNewResButtons(Composite top) {
+ mNewResButton = new Button(top, SWT.NONE);
+ mNewResButton.addSelectionListener(new OnNewResButtonSelected());
+ updateNewResButton();
+ }
+
private void createFilteredTree(Composite parent) {
mFilteredTree = new FilteredTree(parent, SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION,
new PatternFilter());
@@ -154,6 +179,7 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
protected void handleSelection() {
validateCurrentSelection();
+ updateNewResButton();
}
protected void handleDoubleClick() {
@@ -205,6 +231,65 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
return status.isOK();
}
+
+ /**
+ * Updates the new res button when the list selection changes.
+ * The name of the button changes depending on the resource.
+ */
+ private void updateNewResButton() {
+ ResourceType type = getSelectedResourceType();
+
+ // We only support adding new strings right now
+ mNewResButton.setEnabled(type == ResourceType.STRING);
+
+ String title = String.format("New %1$s", type == null ? "Resource" : type.getDisplayName());
+ mNewResButton.setText(title);
+ }
+
+ /**
+ * Callback invoked when the mNewResButton is selected by the user.
+ */
+ private class OnNewResButtonSelected extends SelectionAdapter {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+
+ ResourceType type = getSelectedResourceType();
+
+ // We currently only support strings
+ if (type == ResourceType.STRING) {
+
+ ExtractStringRefactoring ref = new ExtractStringRefactoring(true /*enforceNew*/);
+ RefactoringWizard wizard = new ExtractStringWizard(ref, mProject);
+ RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
+ try {
+ IWorkbench w = PlatformUI.getWorkbench();
+ op.run(w.getDisplay().getActiveShell(), wizard.getDefaultPageTitle());
+
+ // TODO Select string
+ } catch (InterruptedException ex) {
+ // Interrupted. Pass.
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the {@link ResourceType} of the selected element, if any.
+ * Returns null if nothing suitable is selected.
+ */
+ private ResourceType getSelectedResourceType() {
+ ResourceType type = null;
+
+ TreePath selection = getSelection();
+ if (selection != null && selection.getSegmentCount() > 0) {
+ Object first = selection.getFirstSegment();
+ if (first instanceof ResourceType) {
+ type = (ResourceType) first;
+ }
+ }
+ return type;
+ }
/**
* Sets up the initial selection.
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringBaseImpl.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringBaseImpl.java
new file mode 100644
index 000000000..334b13387
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringBaseImpl.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.wizards.newstring;
+
+
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector;
+import com.android.ide.eclipse.common.AndroidConstants;
+import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
+import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
+import com.android.sdklib.SdkConstants;
+
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+import java.util.HashMap;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class NewStringBaseImpl {
+
+ public interface INewStringPageCallback {
+ /**
+ * Creates the top group with the field to replace which string and by what
+ * and by which options.
+ *
+ * @param content A composite with a 1-column grid layout
+ * @return The {@link Text} field for the new String ID name.
+ */
+ public Text createStringGroup(Composite content);
+
+ /** Implements {@link WizardPage#setErrorMessage(String)} */
+ public void setErrorMessage(String newMessage);
+ /** Implements {@link WizardPage#setMessage(String, int)} */
+ public void setMessage(String msg, int type);
+ /** Implements {@link WizardPage#setPageComplete(boolean)} */
+ public void setPageComplete(boolean success);
+
+ public void postValidatePage(ValidationStatus status);
+ }
+
+ public class ValidationStatus {
+ private String mError = null;
+ private String mMessage = null;
+ public int mMessageType = WizardPage.NONE;
+
+ public boolean success() {
+ return getError() != null;
+ }
+
+ public void setError(String error) {
+ mError = error;
+ mMessageType = WizardPage.ERROR;
+ }
+
+ public String getError() {
+ return mError;
+ }
+
+ public void setMessage(String msg, int type) {
+ mMessage = msg;
+ mMessageType = type;
+ }
+
+ public String getMessage() {
+ return mMessage;
+ }
+
+ public int getMessageType() {
+ return mMessageType;
+ }
+ }
+
+ /** Last res file path used, shared across the session instances but specific to the
+ * current project. The default for unknown projects is {@link #DEFAULT_RES_FILE_PATH}. */
+ private static HashMap sLastResFilePath = new HashMap();
+
+ /** The project where the user selection happened. */
+ private final IProject mProject;
+ /** Text field where the user enters the new ID. */
+ private Text mStringIdField;
+ /** The configuration selector, to select the resource path of the XML file. */
+ private ConfigurationSelector mConfigSelector;
+ /** The combo to display the existing XML files or enter a new one. */
+ private Combo mResFileCombo;
+
+ private NewStringHelper mHelper = new NewStringHelper();
+
+ /** Regex pattern to read a valid res XML file path. It checks that the are 2 folders and
+ * a leaf file name ending with .xml */
+ private static final Pattern RES_XML_FILE_REGEX = Pattern.compile(
+ "/res/[a-z][a-zA-Z0-9_-]+/[^.]+\\.xml"); //$NON-NLS-1$
+ /** Absolute destination folder root, e.g. "/res/" */
+ private static final String RES_FOLDER_ABS =
+ AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP;
+ /** Relative destination folder root, e.g. "res/" */
+ private static final String RES_FOLDER_REL =
+ SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
+
+ private static final String DEFAULT_RES_FILE_PATH = "/res/values/strings.xml"; //$NON-NLS-1$
+
+ private final INewStringPageCallback mWizardPage;
+
+ public NewStringBaseImpl(IProject project, INewStringPageCallback wizardPage) {
+ mProject = project;
+ mWizardPage = wizardPage;
+ }
+
+ /**
+ * Create the UI for the new string wizard.
+ */
+ public void createControl(Composite parent) {
+ mStringIdField = mWizardPage.createStringGroup(parent);
+ createResFileGroup(parent);
+ }
+
+ /**
+ * Creates the lower group with the fields to choose the resource confirmation and
+ * the target XML file.
+ *
+ * @param content A composite with a 1-column grid layout
+ */
+ private void createResFileGroup(Composite content) {
+
+ Group group = new Group(content, SWT.NONE);
+ group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ group.setText("XML resource to edit");
+
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ group.setLayout(layout);
+
+ // line: selection of the res config
+
+ Label label;
+ label = new Label(group, SWT.NONE);
+ label.setText("Configuration:");
+
+ mConfigSelector = new ConfigurationSelector(group);
+ GridData gd = new GridData(2, GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
+ gd.widthHint = ConfigurationSelector.WIDTH_HINT;
+ gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
+ mConfigSelector.setLayoutData(gd);
+ OnConfigSelectorUpdated onConfigSelectorUpdated = new OnConfigSelectorUpdated();
+ mConfigSelector.setOnChangeListener(onConfigSelectorUpdated);
+
+ // line: selection of the output file
+
+ label = new Label(group, SWT.NONE);
+ label.setText("Resource file:");
+
+ mResFileCombo = new Combo(group, SWT.DROP_DOWN);
+ mResFileCombo.select(0);
+ mResFileCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mResFileCombo.addModifyListener(onConfigSelectorUpdated);
+
+ // set output file name to the last one used
+
+ String projPath = mProject.getFullPath().toPortableString();
+ String filePath = sLastResFilePath.get(projPath);
+
+ mResFileCombo.setText(filePath != null ? filePath : DEFAULT_RES_FILE_PATH);
+ onConfigSelectorUpdated.run();
+ }
+
+ /**
+ * Validates fields of the wizard input page. Displays errors as appropriate and
+ * enable the "Next" button (or not) by calling
+ * {@link INewStringPageCallback#setPageComplete(boolean)}.
+ *
+ * @return True if the page has been positively validated. It may still have warnings.
+ */
+ public boolean validatePage() {
+ ValidationStatus status = new ValidationStatus();
+
+ validateStringFields(status);
+ if (status.success()) {
+ validatePathFields(status);
+ }
+
+ mWizardPage.postValidatePage(status);
+
+ mWizardPage.setErrorMessage(status.getError());
+ mWizardPage.setMessage(status.getMessage(), status.getMessageType());
+ mWizardPage.setPageComplete(status.success());
+ return status.success();
+ }
+
+ public void validateStringFields(ValidationStatus status) {
+
+ String text = mStringIdField.getText().trim();
+ if (text == null || text.length() < 1) {
+ status.setError("Please provide a resource ID to replace with.");
+ } else {
+ for (int i = 0; i < text.length(); i++) {
+ char c = text.charAt(i);
+ boolean ok = i == 0 ?
+ Character.isJavaIdentifierStart(c) :
+ Character.isJavaIdentifierPart(c);
+ if (!ok) {
+ status.setError(String.format(
+ "The resource ID must be a valid Java identifier. The character %1$c at position %2$d is not acceptable.",
+ c, i+1));
+ break;
+ }
+ }
+ }
+ }
+
+ public ValidationStatus validatePathFields(ValidationStatus status) {
+ String resFile = getResFileProjPath();
+ if (resFile == null || resFile.length() == 0) {
+ status.setError("A resource file name is required.");
+ } else if (!RES_XML_FILE_REGEX.matcher(resFile).matches()) {
+ status.setError("The XML file name is not valid.");
+ }
+
+ if (status.success()) {
+ sLastResFilePath.put(mProject.getFullPath().toPortableString(), resFile);
+
+ String text = mStringIdField.getText().trim();
+
+ if (mHelper.isResIdDuplicate(mProject, resFile, text)) {
+ status.setMessage(
+ String.format("There's already a string item called '%1$s' in %2$s.",
+ text, resFile), WizardPage.WARNING);
+ } else if (mProject.findMember(resFile) == null) {
+ status.setMessage(
+ String.format("File %2$s does not exist and will be created.",
+ text, resFile), WizardPage.INFORMATION);
+ }
+ }
+
+ return status;
+ }
+
+ public String getResFileProjPath() {
+ return mResFileCombo.getText().trim();
+ }
+
+ public class OnConfigSelectorUpdated implements Runnable, ModifyListener {
+
+ /** Regex pattern to parse a valid res path: it reads (/res/folder-name/)+(filename). */
+ private final Pattern mPathRegex = Pattern.compile(
+ "(/res/[a-z][a-zA-Z0-9_-]+/)(.+)"); //$NON-NLS-1$
+
+ /** Temporary config object used to retrieve the Config Selector value. */
+ private FolderConfiguration mTempConfig = new FolderConfiguration();
+
+ private HashMap> mFolderCache =
+ new HashMap>();
+ private String mLastFolderUsedInCombo = null;
+ private boolean mInternalConfigChange;
+ private boolean mInternalFileComboChange;
+
+ /**
+ * Callback invoked when the {@link ConfigurationSelector} has been changed.
+ *
+ * The callback does the following:
+ *
+ *
Examine the current file name to retrieve the XML filename, if any.
+ *
Recompute the path based on the configuration selector (e.g. /res/values-fr/).
+ *
Examine the path to retrieve all the files in it. Keep those in a local cache.
+ *
If the XML filename from step 1 is not in the file list, it's a custom file name.
+ * Insert it and sort it.
+ *
Re-populate the file combo with all the choices.
+ *
Select the original XML file.
+ */
+ public void run() {
+ if (mInternalConfigChange) {
+ return;
+ }
+
+ // get current leafname, if any
+ String leafName = ""; //$NON-NLS-1$
+ String currPath = mResFileCombo.getText();
+ Matcher m = mPathRegex.matcher(currPath);
+ if (m.matches()) {
+ // Note: groups 1 and 2 cannot be null.
+ leafName = m.group(2);
+ currPath = m.group(1);
+ } else {
+ // There was a path but it was invalid. Ignore it.
+ currPath = ""; //$NON-NLS-1$
+ }
+
+ // recreate the res path from the current configuration
+ mConfigSelector.getConfiguration(mTempConfig);
+ StringBuffer sb = new StringBuffer(RES_FOLDER_ABS);
+ sb.append(mTempConfig.getFolderName(ResourceFolderType.VALUES));
+ sb.append('/');
+
+ String newPath = sb.toString();
+ if (newPath.equals(currPath) && newPath.equals(mLastFolderUsedInCombo)) {
+ // Path has not changed. No need to reload.
+ return;
+ }
+
+ // Get all the files at the new path
+
+ TreeSet filePaths = mFolderCache.get(newPath);
+
+ if (filePaths == null) {
+ filePaths = new TreeSet();
+
+ IFolder folder = mProject.getFolder(newPath);
+ if (folder != null && folder.exists()) {
+ try {
+ for (IResource res : folder.members()) {
+ String name = res.getName();
+ if (res.getType() == IResource.FILE && name.endsWith(".xml")) { //$NON-NLS-1$
+ filePaths.add(newPath + name);
+ }
+ }
+ } catch (CoreException e) {
+ // Ignore.
+ }
+ }
+
+ mFolderCache.put(newPath, filePaths);
+ }
+
+ currPath = newPath + leafName;
+ if (leafName.length() > 0 && !filePaths.contains(currPath)) {
+ filePaths.add(currPath);
+ }
+
+ // Fill the combo
+ try {
+ mInternalFileComboChange = true;
+
+ mResFileCombo.removeAll();
+
+ for (String filePath : filePaths) {
+ mResFileCombo.add(filePath);
+ }
+
+ int index = -1;
+ if (leafName.length() > 0) {
+ index = mResFileCombo.indexOf(currPath);
+ if (index >= 0) {
+ mResFileCombo.select(index);
+ }
+ }
+
+ if (index == -1) {
+ mResFileCombo.setText(currPath);
+ }
+
+ mLastFolderUsedInCombo = newPath;
+
+ } finally {
+ mInternalFileComboChange = false;
+ }
+
+ // finally validate the whole page
+ validatePage();
+ }
+
+ /**
+ * Callback invoked when {@link NewStringBaseImpl#mResFileCombo} has been
+ * modified.
+ */
+ public void modifyText(ModifyEvent e) {
+ if (mInternalFileComboChange) {
+ return;
+ }
+
+ String wsFolderPath = mResFileCombo.getText();
+
+ // This is a custom path, we need to sanitize it.
+ // First it should start with "/res/". Then we need to make sure there are no
+ // relative paths, things like "../" or "./" or even "//".
+ wsFolderPath = wsFolderPath.replaceAll("/+\\.\\./+|/+\\./+|//+|\\\\+|^/+", "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ wsFolderPath = wsFolderPath.replaceAll("^\\.\\./+|^\\./+", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ wsFolderPath = wsFolderPath.replaceAll("/+\\.\\.$|/+\\.$|/+$", ""); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // We get "res/foo" from selections relative to the project when we want a "/res/foo" path.
+ if (wsFolderPath.startsWith(RES_FOLDER_REL)) {
+ wsFolderPath = RES_FOLDER_ABS + wsFolderPath.substring(RES_FOLDER_REL.length());
+
+ mInternalFileComboChange = true;
+ mResFileCombo.setText(wsFolderPath);
+ mInternalFileComboChange = false;
+ }
+
+ if (wsFolderPath.startsWith(RES_FOLDER_ABS)) {
+ wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length());
+
+ int pos = wsFolderPath.indexOf(AndroidConstants.WS_SEP_CHAR);
+ if (pos >= 0) {
+ wsFolderPath = wsFolderPath.substring(0, pos);
+ }
+
+ String[] folderSegments = wsFolderPath.split(FolderConfiguration.QUALIFIER_SEP);
+
+ if (folderSegments.length > 0) {
+ String folderName = folderSegments[0];
+
+ if (folderName != null && !folderName.equals(wsFolderPath)) {
+ // update config selector
+ mInternalConfigChange = true;
+ mConfigSelector.setConfiguration(folderSegments);
+ mInternalConfigChange = false;
+ }
+ }
+ }
+
+ validatePage();
+ }
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringHelper.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringHelper.java
new file mode 100644
index 000000000..3b8392718
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringHelper.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.wizards.newstring;
+
+import com.android.ide.eclipse.common.project.AndroidXPathFactory;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+
+/**
+ *
+ */
+public class NewStringHelper {
+
+ /** A temporary cache of R.string IDs defined by a given xml file. The key is the
+ * project path of the file, the data is a set of known string Ids for that file. */
+ private HashMap> mResIdCache;
+ /** An instance of XPath, created lazily on demand. */
+ private XPath mXPath;
+
+ public NewStringHelper() {
+ }
+
+ /**
+ * Utility method used by the wizard to check whether the given string ID is already
+ * defined in the XML file which path is given.
+ *
+ * @param project The project contain the XML file.
+ * @param xmlFileWsPath The project path of the XML file, e.g. "/res/values/strings.xml".
+ * The given file may or may not exist.
+ * @param stringId The string ID to find.
+ * @return True if such a string ID is already defined.
+ */
+ public boolean isResIdDuplicate(IProject project, String xmlFileWsPath, String stringId) {
+ // This is going to be called many times on the same file.
+ // Build a cache of the existing IDs for a given file.
+ if (mResIdCache == null) {
+ mResIdCache = new HashMap>();
+ }
+ HashSet cache = mResIdCache.get(xmlFileWsPath);
+ if (cache == null) {
+ cache = getResIdsForFile(project, xmlFileWsPath);
+ mResIdCache.put(xmlFileWsPath, cache);
+ }
+
+ return cache.contains(stringId);
+ }
+
+ /**
+ * Extract all the defined string IDs from a given file using XPath.
+ * @param project The project contain the XML file.
+ * @param xmlFileWsPath The project path of the file to parse. It may not exist.
+ * @return The set of all string IDs defined in the file. The returned set is always non
+ * null. It is empty if the file does not exist.
+ */
+ private HashSet getResIdsForFile(IProject project, String xmlFileWsPath) {
+ HashSet ids = new HashSet();
+
+ if (mXPath == null) {
+ mXPath = AndroidXPathFactory.newXPath();
+ }
+
+ // Access the project that contains the resource that contains the compilation unit
+ IResource resource = project.getFile(xmlFileWsPath);
+
+ if (resource != null && resource.exists() && resource.getType() == IResource.FILE) {
+ InputSource source;
+ try {
+ source = new InputSource(((IFile) resource).getContents());
+
+ // We want all the IDs in an XML structure like this:
+ //
+ // something
+ //
+
+ String xpathExpr = "/resources/string/@name"; //$NON-NLS-1$
+
+ Object result = mXPath.evaluate(xpathExpr, source, XPathConstants.NODESET);
+ if (result instanceof NodeList) {
+ NodeList list = (NodeList) result;
+ for (int n = list.getLength() - 1; n >= 0; n--) {
+ String id = list.item(n).getNodeValue();
+ ids.add(id);
+ }
+ }
+
+ } catch (CoreException e1) {
+ // IFile.getContents failed. Ignore.
+ } catch (XPathExpressionException e) {
+ // mXPath.evaluate failed. Ignore.
+ }
+ }
+
+ return ids;
+ }
+
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringWizard.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringWizard.java
new file mode 100644
index 000000000..f7d8fe863
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringWizard.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.wizards.newstring;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.wizard.Wizard;
+
+/**
+ *
+ */
+public class NewStringWizard extends Wizard {
+
+ protected static final String MAIN_PAGE_NAME = "newXmlStringPage"; //$NON-NLS-1$
+
+ private NewStringWizardPage mMainPage;
+
+ public NewStringWizard(IProject project) {
+ super();
+
+ mMainPage = createMainPage(project);
+ }
+
+ /**
+ * Creates the wizard page.
+ *
+ * Please do NOT override this method.
+ *
+ * This is protected so that it can be overridden by unit tests.
+ * However the contract of this class is private and NO ATTEMPT will be made
+ * to maintain compatibility between different versions of the plugin.
+ * @param project
+ */
+ protected NewStringWizardPage createMainPage(IProject project) {
+ return new NewStringWizardPage(project, MAIN_PAGE_NAME);
+ }
+
+ @Override
+ public void addPages() {
+ addPage(mMainPage);
+ super.addPages();
+ }
+
+ /**
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish() {
+ // pass
+ return false;
+ }
+
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringWizardPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringWizardPage.java
new file mode 100644
index 000000000..1e2d27277
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringWizardPage.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.wizards.newstring;
+
+import com.android.ide.eclipse.adt.wizards.newstring.NewStringBaseImpl.INewStringPageCallback;
+import com.android.ide.eclipse.adt.wizards.newstring.NewStringBaseImpl.ValidationStatus;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ *
+ */
+class NewStringWizardPage extends WizardPage implements INewStringPageCallback {
+
+ private NewStringBaseImpl mImpl;
+
+ /** Field displaying the user-selected string to be replaced. */
+ private Label mStringValueField;
+
+ private String mNewStringId;
+
+ public NewStringWizardPage(IProject project, String pageName) {
+ super(pageName);
+ mImpl = new NewStringBaseImpl(project, this);
+ }
+
+ public String getNewStringValue() {
+ return mStringValueField.getText();
+ }
+
+ public String getNewStringId() {
+ return mNewStringId;
+ }
+
+ public String getResFilePathProjPath() {
+ return mImpl.getResFileProjPath();
+ }
+
+ /**
+ * Create the UI for the new string wizard.
+ */
+ public void createControl(Composite parent) {
+ Composite content = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 1;
+ content.setLayout(layout);
+
+ mImpl.createControl(content);
+ setControl(content);
+ }
+
+ /**
+ * Creates the top group with the field to replace which string and by what
+ * and by which options.
+ *
+ * @param content A composite with a 1-column grid layout
+ * @return The {@link Text} field for the new String ID name.
+ */
+ public Text createStringGroup(Composite content) {
+
+ Group group = new Group(content, SWT.NONE);
+ group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ group.setText("New String");
+
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ group.setLayout(layout);
+
+ Label label = new Label(group, SWT.NONE);
+ label.setText("String:");
+
+ mStringValueField = new Label(group, SWT.NONE);
+ mStringValueField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mStringValueField.setText(""); //$NON-NLS-1$
+
+ // TODO provide an option to refactor all known occurences of this string.
+
+ // line : Textfield for new ID
+
+ label = new Label(group, SWT.NONE);
+ label.setText("Replace by R.string.");
+
+ final Text stringIdField = new Text(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ stringIdField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ stringIdField.setText("");
+
+ mNewStringId = stringIdField.getText().trim();
+
+ stringIdField.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ if (mImpl.validatePage()) {
+ mNewStringId = stringIdField.getText().trim();
+ }
+ }
+ });
+
+ return stringIdField;
+ }
+
+ public void postValidatePage(ValidationStatus status) {
+ // pass
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java
index 654e792cc..56bb8f492 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java
@@ -144,7 +144,9 @@ public class UiResourceAttributeNode extends UiTextAttributeNode {
return dlg.getCurrentResource();
}
} else {
- ReferenceChooserDialog dlg = new ReferenceChooserDialog(projectRepository,
+ ReferenceChooserDialog dlg = new ReferenceChooserDialog(
+ project,
+ projectRepository,
shell);
dlg.setCurrentResource(currentValue);
From 6bc84913965137c435bab1cde6b5cad388de3b7f Mon Sep 17 00:00:00 2001
From: Brett Chabot <>
Date: Thu, 2 Apr 2009 15:37:39 -0700
Subject: [PATCH 057/127] AI 144322: am: CL 144320 ADT: Fix debugger launch
connection when debugging Android JUnit Original author: brettchabot
Merged from: //branches/cupcake/...
Automated import of CL 144322
---
.../adt/launch/AndroidLaunchController.java | 12 +++--
.../eclipse/adt/launch/DelayedLaunchInfo.java | 20 +++++++-
.../adt/launch/LaunchConfigDelegate.java | 7 +--
.../junit/AndroidJUnitLaunchAction.java | 1 -
.../AndroidJUnitLaunchConfigDelegate.java | 41 +++++++++++++---
.../AndroidJUnitLaunchConfigurationTab.java | 2 +-
.../junit/InstrumentationRunnerValidator.java | 19 ++++---
.../common/project/AndroidManifestParser.java | 49 +++++++++++++++----
.../project/AndroidManifestParserTest.java | 16 +++---
9 files changed, 124 insertions(+), 43 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
index b6c7640c7..655c038e1 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
@@ -314,6 +314,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* defined by ILaunchManager - RUN_MODE or
* DEBUG_MODE.
* @param apk the resource to the apk to launch.
+ * @param packageName the Android package name of the app
+ * @param debugPackageName the Android package name to debug
* @param debuggable the debuggable value of the app, or null if not set.
* @param requiredApiVersionNumber the api version required by the app, or
* {@link AndroidManifestParser#INVALID_MIN_SDK} if none.
@@ -322,7 +324,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* @param launch the launch object
*/
public void launch(final IProject project, String mode, IFile apk,
- String packageName, Boolean debuggable, int requiredApiVersionNumber,
+ String packageName, String debugPackageName, Boolean debuggable, int requiredApiVersionNumber,
final IAndroidLaunchAction launchAction, final AndroidLaunchConfiguration config,
final AndroidLaunch launch, IProgressMonitor monitor) {
@@ -331,7 +333,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
// create the launch info
final DelayedLaunchInfo launchInfo = new DelayedLaunchInfo(project, packageName,
- launchAction, apk, debuggable, requiredApiVersionNumber, launch, monitor);
+ debugPackageName, launchAction, apk, debuggable, requiredApiVersionNumber, launch,
+ monitor);
// set the debug mode
launchInfo.setDebugMode(mode.equals(ILaunchManager.DEBUG_MODE));
@@ -922,6 +925,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
DelayedLaunchInfo delayedLaunchInfo = new DelayedLaunchInfo(
androidProject.getProject(),
manifestParser.getPackage(),
+ manifestParser.getPackage(),
launchInfo.getLaunchAction(),
apk,
manifestParser.getDebuggable(),
@@ -1524,14 +1528,14 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
for (int i = 0; i < mWaitingForDebuggerApplications.size(); ) {
final DelayedLaunchInfo launchInfo = mWaitingForDebuggerApplications.get(i);
if (client.getDevice() == launchInfo.getDevice() &&
- applicationName.equals(launchInfo.getPackageName())) {
+ applicationName.equals(launchInfo.getDebugPackageName())) {
// this is a match. We remove the launch info from the list
mWaitingForDebuggerApplications.remove(i);
// and connect the debugger.
String msg = String.format(
"Attempting to connect debugger to '%1$s' on port %2$d",
- launchInfo.getPackageName(), client.getDebuggerListenPort());
+ launchInfo.getDebugPackageName(), client.getDebuggerListenPort());
AdtPlugin.printToConsole(launchInfo.getProject(), msg);
new Thread("Debugger Connection") { //$NON-NLS-1$
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DelayedLaunchInfo.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DelayedLaunchInfo.java
index 7dae56d00..f3bd28a07 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DelayedLaunchInfo.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DelayedLaunchInfo.java
@@ -44,6 +44,9 @@ public final class DelayedLaunchInfo {
/** Package name */
private final String mPackageName;
+
+ /** Debug package name */
+ private final String mDebugPackageName;
/** IFile to the package (.apk) file */
private final IFile mPackageFile;
@@ -79,7 +82,8 @@ public final class DelayedLaunchInfo {
* Basic constructor with activity and package info.
*
* @param project the eclipse project that corresponds to Android app
- * @param packageName package name of Android app
+ * @param packageName package name of Android app
+ * @param debugPackageName the package name of the Andriod app to debug
* @param launchAction action to perform after app install
* @param pack IFile to the package (.apk) file
* @param debuggable debuggable attribute of the app's manifest file.
@@ -88,11 +92,12 @@ public final class DelayedLaunchInfo {
* @param launch the launch object
* @param monitor progress monitor for launch
*/
- public DelayedLaunchInfo(IProject project, String packageName,
+ public DelayedLaunchInfo(IProject project, String packageName, String debugPackageName,
IAndroidLaunchAction launchAction, IFile pack, Boolean debuggable,
int requiredApiVersionNumber, AndroidLaunch launch, IProgressMonitor monitor) {
mProject = project;
mPackageName = packageName;
+ mDebugPackageName = debugPackageName;
mPackageFile = pack;
mLaunchAction = launchAction;
mLaunch = launch;
@@ -129,6 +134,17 @@ public final class DelayedLaunchInfo {
return mPackageName;
}
+ /**
+ * Returns the Android app process name that the debugger should connect to. Typically this is
+ * the same value as {@link getPackageName}
+ */
+ public String getDebugPackageName() {
+ if (mDebugPackageName == null) {
+ return getPackageName();
+ }
+ return mDebugPackageName;
+ }
+
/**
* @return the application package file
*/
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
index d057ac709..9f12b16e4 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
@@ -306,9 +306,10 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
// everything seems fine, we ask the launch controller to handle
// the rest
- controller.launch(project, mode, applicationPackage, manifestParser.getPackage(),
- manifestParser.getDebuggable(), manifestParser.getApiLevelRequirement(),
- launchAction, config, androidLaunch, monitor);
+ controller.launch(project, mode, applicationPackage,manifestParser.getPackage(),
+ manifestParser.getPackage(), manifestParser.getDebuggable(),
+ manifestParser.getApiLevelRequirement(), launchAction, config, androidLaunch,
+ monitor);
}
@Override
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
index 9bcc63d06..24ebe21c0 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchAction.java
@@ -63,7 +63,6 @@ class AndroidJUnitLaunchAction implements IAndroidLaunchAction {
try {
mLaunchInfo.setDebugMode(info.isDebugMode());
mLaunchInfo.setDevice(info.getDevice());
- mLaunchInfo.setLaunch(info.getLaunch());
JUnitLaunchDelegate junitDelegate = new JUnitLaunchDelegate(mLaunchInfo);
final String mode = info.isDebugMode() ? ILaunchManager.DEBUG_MODE :
ILaunchManager.RUN_MODE;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
index 543daf06f..19067921b 100755
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigDelegate.java
@@ -25,6 +25,7 @@ import com.android.ide.eclipse.adt.launch.LaunchConfigDelegate;
import com.android.ide.eclipse.adt.launch.junit.runtime.AndroidJUnitLaunchInfo;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
+import com.android.ide.eclipse.common.project.AndroidManifestParser.Instrumentation;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import org.eclipse.core.resources.IFile;
@@ -60,7 +61,6 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
AndroidLaunchConfiguration config, AndroidLaunchController controller,
IFile applicationPackage, AndroidManifestParser manifestParser) {
- String appPackage = manifestParser.getPackage();
String runner = getRunner(project, configuration, manifestParser);
if (runner == null) {
AdtPlugin.displayError("Android Launch",
@@ -68,19 +68,46 @@ public class AndroidJUnitLaunchConfigDelegate extends LaunchConfigDelegate {
androidLaunch.stopLaunch();
return;
}
- AndroidJUnitLaunchInfo junitLaunchInfo = new AndroidJUnitLaunchInfo(project, appPackage,
- runner);
+ // get the target app's package
+ String targetAppPackage = getTargetPackage(manifestParser, runner);
+ if (targetAppPackage == null) {
+ AdtPlugin.displayError("Android Launch",
+ String.format("A target package for instrumention test runner %1$s could not be found!",
+ runner));
+ androidLaunch.stopLaunch();
+ return;
+ }
+ String testAppPackage = manifestParser.getPackage();
+ AndroidJUnitLaunchInfo junitLaunchInfo = new AndroidJUnitLaunchInfo(project,
+ testAppPackage, runner);
junitLaunchInfo.setTestClass(getTestClass(configuration));
junitLaunchInfo.setTestPackage(getTestPackage(configuration));
junitLaunchInfo.setTestMethod(getTestMethod(configuration));
-
+ junitLaunchInfo.setLaunch(androidLaunch);
IAndroidLaunchAction junitLaunch = new AndroidJUnitLaunchAction(junitLaunchInfo);
-
- controller.launch(project, mode, applicationPackage, manifestParser.getPackage(),
+
+ controller.launch(project, mode, applicationPackage, testAppPackage, targetAppPackage,
manifestParser.getDebuggable(), manifestParser.getApiLevelRequirement(),
junitLaunch, config, androidLaunch, monitor);
}
-
+
+ /**
+ * Get the target Android application's package for the given instrumentation runner, or
+ * null if it could not be found.
+ *
+ * @param manifestParser the {@link AndroidManifestParser} for the test project
+ * @param runner the instrumentation runner class name
+ * @return the target package or null
+ */
+ private String getTargetPackage(AndroidManifestParser manifestParser, String runner) {
+ for (Instrumentation instr : manifestParser.getInstrumentations()) {
+ if (instr.getName().equals(runner)) {
+ return instr.getTargetPackage();
+ }
+ }
+ return null;
+ }
+
/**
* Returns the test package stored in the launch configuration, or null if not
* specified.
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
index 584d45eb1..34e64e327 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/AndroidJUnitLaunchConfigurationTab.java
@@ -955,7 +955,7 @@ public class AndroidJUnitLaunchConfigurationTab extends AbstractLaunchConfigurat
try {
mInstrValidator = new InstrumentationRunnerValidator(project);
mInstrumentations = (mInstrValidator == null ? null :
- mInstrValidator.getInstrumentations());
+ mInstrValidator.getInstrumentationNames());
if (mInstrumentations != null) {
mInstrumentationCombo.removeAll();
for (String instrumentation : mInstrumentations) {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java
index f22fc7cda..7ebbb0997 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/junit/InstrumentationRunnerValidator.java
@@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.launch.junit;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
+import com.android.ide.eclipse.common.project.AndroidManifestParser.Instrumentation;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import org.eclipse.core.resources.IProject;
@@ -29,7 +30,7 @@ import org.eclipse.jdt.core.IJavaProject;
*/
class InstrumentationRunnerValidator {
private final IJavaProject mJavaProject;
- private String[] mInstrumentations = null;
+ private String[] mInstrumentationNames = null;
private boolean mHasRunnerLibrary = false;
static final String INSTRUMENTATION_OK = null;
@@ -73,7 +74,11 @@ class InstrumentationRunnerValidator {
}
private void init(AndroidManifestParser manifestParser) {
- mInstrumentations = manifestParser.getInstrumentations();
+ Instrumentation[] instrumentations = manifestParser.getInstrumentations();
+ mInstrumentationNames = new String[instrumentations.length];
+ for (int i = 0; i < instrumentations.length; i++) {
+ mInstrumentationNames[i] = instrumentations[i].getName();
+ }
mHasRunnerLibrary = hasTestRunnerLibrary(manifestParser);
}
@@ -94,13 +99,13 @@ class InstrumentationRunnerValidator {
}
/**
- * Return the set of instrumentations for the Android project.
+ * Return the set of instrumentation names for the Android project.
*
* @return nullINSTRUMENTATION_OK if valid, otherwise returns error message
*/
String validateInstrumentationRunner(String instrumentation) {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
index 85ba96839..f853adacb 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
@@ -52,6 +52,7 @@ public class AndroidManifestParser {
private final static String ATTRIBUTE_PROCESS = "process"; //$NON-NLS-$
private final static String ATTRIBUTE_DEBUGGABLE = "debuggable"; //$NON-NLS-$
private final static String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion"; //$NON-NLS-$
+ private final static String ATTRIBUTE_TARGET_PACKAGE = "targetPackage"; //$NON-NLS-1$
private final static String NODE_MANIFEST = "manifest"; //$NON-NLS-1$
private final static String NODE_APPLICATION = "application"; //$NON-NLS-1$
private final static String NODE_ACTIVITY = "activity"; //$NON-NLS-1$
@@ -76,6 +77,33 @@ public class AndroidManifestParser {
public final static int INVALID_MIN_SDK = -1;
+ /**
+ * Instrumentation info obtained from manifest
+ */
+ public static class Instrumentation {
+ private final String mName;
+ private final String mTargetPackage;
+
+ public Instrumentation(String name, String targetPackage) {
+ mName = name;
+ mTargetPackage = targetPackage;
+ }
+
+ /**
+ * Returns the fully qualified instrumentation class name
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns the Android app package that is the target of this instrumentation
+ */
+ public String getTargetPackage() {
+ return mTargetPackage;
+ }
+ }
+
/**
* XML error & data handler used when parsing the AndroidManifest.xml file.
*
@@ -100,7 +128,8 @@ public class AndroidManifestParser {
* the attribute was not present. */
private int mApiLevelRequirement = INVALID_MIN_SDK;
/** List of all instrumentations declared by the manifest */
- private final ArrayList mInstrumentations = new ArrayList();
+ private final ArrayList mInstrumentations =
+ new ArrayList();
/** List of all libraries in use declared by the manifest */
private final ArrayList mLibraries = new ArrayList();
@@ -185,11 +214,11 @@ public class AndroidManifestParser {
/**
* Returns the list of instrumentations found in the manifest.
- * @return An array of instrumentation names, or empty if no instrumentations were
+ * @return An array of {@link Instrumentation}, or empty if no instrumentations were
* found.
*/
- String[] getInstrumentations() {
- return mInstrumentations.toArray(new String[mInstrumentations.size()]);
+ Instrumentation[] getInstrumentations() {
+ return mInstrumentations.toArray(new Instrumentation[mInstrumentations.size()]);
}
/**
@@ -459,7 +488,9 @@ public class AndroidManifestParser {
true /* hasNamespace */);
if (instrumentationName != null) {
String instrClassName = combinePackageAndClassName(mPackage, instrumentationName);
- mInstrumentations.add(instrClassName);
+ String targetPackage = getAttributeValue(attributes, ATTRIBUTE_TARGET_PACKAGE,
+ true /* hasNamespace */);
+ mInstrumentations.add(new Instrumentation(instrClassName, targetPackage));
if (mMarkErrors) {
checkClass(instrClassName, AndroidConstants.CLASS_INSTRUMENTATION,
true /* testVisibility */);
@@ -544,7 +575,7 @@ public class AndroidManifestParser {
private final String[] mProcesses;
private final Boolean mDebuggable;
private final int mApiLevelRequirement;
- private final String[] mInstrumentations;
+ private final Instrumentation[] mInstrumentations;
private final String[] mLibraries;
static {
@@ -819,9 +850,9 @@ public class AndroidManifestParser {
/**
* Returns the list of instrumentations found in the manifest.
- * @return An array of fully qualified class names, or empty if no instrumentations were found.
+ * @return An array of {@link Instrumentation}, or empty if no instrumentations were found.
*/
- public String[] getInstrumentations() {
+ public Instrumentation[] getInstrumentations() {
return mInstrumentations;
}
@@ -851,7 +882,7 @@ public class AndroidManifestParser {
*/
private AndroidManifestParser(String javaPackage, String[] activities,
String launcherActivity, String[] processes, Boolean debuggable,
- int apiLevelRequirement, String[] instrumentations, String[] libraries) {
+ int apiLevelRequirement, Instrumentation[] instrumentations, String[] libraries) {
mJavaPackage = javaPackage;
mActivities = activities;
mLauncherActivity = launcherActivity;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java
index 7e8b0af10..2f93e5179 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java
@@ -33,7 +33,7 @@ public class AndroidManifestParserTest extends TestCase {
private static final String ACTIVITY_NAME = "com.android.testapp.MainActivity"; //$NON-NLS-1$
private static final String LIBRARY_NAME = "android.test.runner"; //$NON-NLS-1$
private static final String INSTRUMENTATION_NAME = "android.test.InstrumentationTestRunner"; //$NON-NLS-1$
-
+ private static final String INSTRUMENTATION_TARGET = "com.android.AndroidProject"; //$NON-NLS-1$
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -51,7 +51,10 @@ public class AndroidManifestParserTest extends TestCase {
public void testGetInstrumentationInformation() {
assertEquals(1, mManifestInstrumentation.getInstrumentations().length);
- assertEquals(INSTRUMENTATION_NAME, mManifestTestApp.getInstrumentations()[0]);
+ assertEquals(INSTRUMENTATION_NAME,
+ mManifestInstrumentation.getInstrumentations()[0].getName());
+ assertEquals(INSTRUMENTATION_TARGET,
+ mManifestInstrumentation.getInstrumentations()[0].getTargetPackage());
}
public void testGetPackage() {
@@ -66,17 +69,12 @@ public class AndroidManifestParserTest extends TestCase {
public void testGetLauncherActivity() {
assertEquals(ACTIVITY_NAME, mManifestTestApp.getLauncherActivity());
}
-
+
public void testGetUsesLibraries() {
assertEquals(1, mManifestTestApp.getUsesLibraries().length);
assertEquals(LIBRARY_NAME, mManifestTestApp.getUsesLibraries()[0]);
}
-
- public void testGetInstrumentations() {
- assertEquals(1, mManifestTestApp.getInstrumentations().length);
- assertEquals(INSTRUMENTATION_NAME, mManifestTestApp.getInstrumentations()[0]);
- }
-
+
public void testGetPackageName() {
assertEquals(PACKAGE_NAME, mManifestTestApp.getPackage());
}
From f8e7fcf2afb3a03e899ebc2f772b49601b69a933 Mon Sep 17 00:00:00 2001
From: Eric Fischer <>
Date: Thu, 2 Apr 2009 16:42:12 -0700
Subject: [PATCH 058/127] AI 144336: am: CL 144335 Import US Spanish
translations, and a few Japanese changes. Original author: enf Merged
from: //branches/cupcake/...
Automated import of CL 144336
---
apps/Fallback/res/values-es-rUS/strings.xml | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 apps/Fallback/res/values-es-rUS/strings.xml
diff --git a/apps/Fallback/res/values-es-rUS/strings.xml b/apps/Fallback/res/values-es-rUS/strings.xml
new file mode 100644
index 000000000..0ce57511e
--- /dev/null
+++ b/apps/Fallback/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+
+
+
+ "Fallback"
+ "Acción no admitida"
+ "Esa acción no se admite actualmente."
+
From 2e990b97d3b256791dcc3e3580dc0bf948bc0f83 Mon Sep 17 00:00:00 2001
From: Niko Catania <>
Date: Thu, 2 Apr 2009 16:52:26 -0700
Subject: [PATCH 059/127] AI 144340: Added support to run native tests on the
device. The tests name must start with 'test_'. Tests should return 0 on
success, 1 on failure. * development/testrunner/test_defs.xml: Added new
element to represent native tests. * development/testrunner/test_defs.py:
Added handling of the new element. The testsuite has new
IsNative method. TestDefinition's iterator is ordered by test names.
Added GetDescription() method to access the optional description. *
development/testrunner/runtest.py: Print the description next to the test
name if applicable (runtest_py -l) Added a _RunNativeTest method to run a
test on the target, report the status and clean up the test after the run.
Added BUG=1601432
Automated import of CL 144340
---
testrunner/runtest.py | 42 ++++++++++++++++++--
testrunner/test_defs.py | 63 +++++++++++++++++++++++++++---
testrunner/test_defs.xml | 83 +++++++++++++++++++++++++++++-----------
3 files changed, 157 insertions(+), 31 deletions(-)
diff --git a/testrunner/runtest.py b/testrunner/runtest.py
index 243da77b5..b72727064 100755
--- a/testrunner/runtest.py
+++ b/testrunner/runtest.py
@@ -23,6 +23,7 @@ Based on previous /development/tools/runtest shell script.
import glob
import optparse
import os
+import re
from sets import Set
import sys
@@ -58,6 +59,7 @@ class TestRunner(object):
def __init__(self):
# disable logging of timestamp
+ self._root_path = android_build.GetTop()
logger.SetTimestampLogging(False)
def _ProcessOptions(self):
@@ -137,8 +139,6 @@ class TestRunner(object):
if self._options.verbose:
logger.SetVerbose(True)
- self._root_path = android_build.GetTop()
-
self._known_tests = self._ReadTests()
self._coverage_gen = coverage.CoverageGenerator(
@@ -172,7 +172,7 @@ class TestRunner(object):
"""Prints out set of defined tests."""
print "The following tests are currently defined:"
for test in self._known_tests:
- print test.GetName()
+ print "%-15s %s" % (test.GetName(), test.GetDescription())
def _DoBuild(self):
logger.SilentLog("Building tests...")
@@ -261,6 +261,37 @@ class TestRunner(object):
if coverage_file is not None:
logger.Log("Coverage report generated at %s" % coverage_file)
+ def _RunNativeTest(self, test_suite):
+ """Run the provided *native* test suite.
+
+ The test_suite must contain a build path where the native test files are.
+ Each test's name must start with 'test_' and have a .cc or .cpp extension.
+ A successful test must return 0. Any other value will be considered
+ as an error.
+
+ Args:
+ test_suite: TestSuite to run
+ """
+ # find all test files, convert unicode names to ascii, take the basename
+ # and drop the .cc/.cpp extension.
+ file_pattern = os.path.join(test_suite.GetBuildPath(), "test_*")
+ file_list = []
+ for f in map(str, glob.glob(file_pattern)):
+ f = os.path.basename(f)
+ f = re.split(".[cp]+$", f)[0]
+ file_list.append(f)
+
+ for f in file_list:
+ full_path = "/system/bin/%s" % f
+
+ # Run
+ status = self._adb.SendShellCommand("%s >/dev/null 2>&1;echo -n $?" %
+ full_path)
+ logger.Log("%s... %s" % (f, status == "0" and "ok" or "failed"))
+
+ # Cleanup
+ self._adb.SendShellCommand("rm %s" % full_path)
+
def RunTests(self):
"""Main entry method - executes the tests according to command line args."""
try:
@@ -278,7 +309,10 @@ class TestRunner(object):
self._DoBuild()
for test_suite in self._GetTestsToRun():
- self._RunTest(test_suite)
+ if test_suite.IsNative():
+ self._RunNativeTest(test_suite)
+ else:
+ self._RunTest(test_suite)
except KeyboardInterrupt:
logger.Log("Exiting...")
except errors.AbortError:
diff --git a/testrunner/test_defs.py b/testrunner/test_defs.py
index 949ad6ef8..f7a435e95 100644
--- a/testrunner/test_defs.py
+++ b/testrunner/test_defs.py
@@ -38,7 +38,14 @@ class TestDefinitions(object):
[class=""]
[coverage_target=""]
[build_path=""]
- [continuous]
+ [continuous=false]
+ [description=""]
+ />
+
@@ -48,13 +55,17 @@ class TestDefinitions(object):
# tag/attribute constants
_TEST_TAG_NAME = "test"
+ _TEST_NATIVE_TAG_NAME = "test-native"
def __init__(self):
# dictionary of test name to tests
self._testname_map = {}
def __iter__(self):
- return iter(self._testname_map.values())
+ ordered_list = []
+ for k in sorted(self._testname_map):
+ ordered_list.append(self._testname_map[k])
+ return iter(ordered_list)
def Parse(self, file_path):
"""Parse the test suite data from from given file path.
@@ -87,6 +98,12 @@ class TestDefinitions(object):
test = self._ParseTestSuite(suite_element)
self._AddTest(test)
+ suite_elements = doc.getElementsByTagName(self._TEST_NATIVE_TAG_NAME)
+
+ for suite_element in suite_elements:
+ test = self._ParseNativeTestSuite(suite_element)
+ self._AddTest(test)
+
def _ParseTestSuite(self, suite_element):
"""Parse the suite element.
@@ -96,6 +113,17 @@ class TestDefinitions(object):
test = TestSuite(suite_element)
return test
+ def _ParseNativeTestSuite(self, suite_element):
+ """Parse the native test element.
+
+ Returns:
+ a TestSuite object, populated with parsed data
+ Raises:
+ ParseError if some required attribute is missing.
+ """
+ test = TestSuite(suite_element, native=True)
+ return test
+
def _AddTest(self, test):
"""Adds a test to this TestManifest.
@@ -129,13 +157,27 @@ class TestSuite(object):
_TARGET_ATTR = "coverage_target"
_BUILD_ATTR = "build_path"
_CONTINUOUS_ATTR = "continuous"
+ _DESCRIPTION_ATTR = "description"
_DEFAULT_RUNNER = "android.test.InstrumentationTestRunner"
- def __init__(self, suite_element):
- """Populates this instance's data from given suite xml element."""
+ def __init__(self, suite_element, native=False):
+ """Populates this instance's data from given suite xml element.
+ Raises:
+ ParseError if some required attribute is missing.
+ """
+ self._native = native
self._name = suite_element.getAttribute(self._NAME_ATTR)
- self._package = suite_element.getAttribute(self._PKG_ATTR)
+
+ if self._native:
+ # For native runs, _BUILD_ATTR is required
+ if not suite_element.hasAttribute(self._BUILD_ATTR):
+ logger.Log("Error: %s is missing required build_path attribute" %
+ self._name)
+ raise errors.ParseError
+ else:
+ self._package = suite_element.getAttribute(self._PKG_ATTR)
+
if suite_element.hasAttribute(self._RUNNER_ATTR):
self._runner = suite_element.getAttribute(self._RUNNER_ATTR)
else:
@@ -156,6 +198,10 @@ class TestSuite(object):
self._continuous = suite_element.getAttribute(self._CONTINUOUS_ATTR)
else:
self._continuous = False
+ if suite_element.hasAttribute(self._DESCRIPTION_ATTR):
+ self._description = suite_element.getAttribute(self._DESCRIPTION_ATTR)
+ else:
+ self._description = ""
def GetName(self):
return self._name
@@ -184,6 +230,13 @@ class TestSuite(object):
"""Returns true if test is flagged as being part of the continuous tests"""
return self._continuous
+ def IsNative(self):
+ """Returns true if test is a native one."""
+ return self._native
+
+ def GetDescription(self):
+ return self._description
+
def Parse(file_path):
"""Parses out a TestDefinitions from given path to xml file.
diff --git a/testrunner/test_defs.xml b/testrunner/test_defs.xml
index d186af4c7..9007a11bf 100644
--- a/testrunner/test_defs.xml
+++ b/testrunner/test_defs.xml
@@ -17,32 +17,65 @@
@@ -223,4 +256,10 @@ These attributes map to the following commands:
coverage_target="Settings" />
-->
+
+
+
+
From a98128aff502c23c14b8c816ffd31bfea299fc47 Mon Sep 17 00:00:00 2001
From: Mike Lockwood <>
Date: Thu, 2 Apr 2009 21:41:55 -0700
Subject: [PATCH 060/127] AI 144372: Cleanup Settings support for enabling and
disabling location providers: LocationManagerService now listens for
changes to settings, making LocationManager.updateProviders() unnecessary.
Removed LocationManager.updateProviders() Added
Settings.Secure.setLocationProviderEnabled(), which is a thread-safe way of
enabling or disabling a single location provider. This is safer than
reading, modifying and writing the LOCATION_PROVIDERS_ALLOWED directly.
BUG=1729031
Automated import of CL 144372
---
apps/SdkSetup/src/com/android/sdksetup/DefaultActivity.java | 2 --
1 file changed, 2 deletions(-)
diff --git a/apps/SdkSetup/src/com/android/sdksetup/DefaultActivity.java b/apps/SdkSetup/src/com/android/sdksetup/DefaultActivity.java
index db6385cae..56f43a461 100644
--- a/apps/SdkSetup/src/com/android/sdksetup/DefaultActivity.java
+++ b/apps/SdkSetup/src/com/android/sdksetup/DefaultActivity.java
@@ -38,9 +38,7 @@ public class DefaultActivity extends Activity {
// Enable the GPS.
// Not needed since this SDK will contain the Settings app.
- LocationManager locationManager = (LocationManager)getSystemService(LOCATION_SERVICE);
Settings.Secure.putString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, LocationManager.GPS_PROVIDER);
- locationManager.updateProviders();
// enable install from non market
Settings.Secure.putInt(getContentResolver(), Settings.Secure.INSTALL_NON_MARKET_APPS, 1);
From bb10b2afb69c14e53f56ef17b7573b76230a4e29 Mon Sep 17 00:00:00 2001
From: Xavier Ducrohet <>
Date: Thu, 2 Apr 2009 22:22:29 -0700
Subject: [PATCH 061/127] AI 144382: am: CL 144366 Activity Launcher filters
out unlauncheable activity (bug #1736754) Activities that do not have an
action, or that are set to not be exported cannot be launched from 'am
start...' so they should not be considered when finding an activity to
launch. Original author: xav Merged from: //branches/cupcake/...
Automated import of CL 144382
---
.../adt/launch/AndroidLaunchController.java | 8 +-
.../adt/launch/LaunchConfigDelegate.java | 24 +++-
.../adt/launch/MainLaunchConfigTab.java | 37 +++--
.../newproject/NewProjectCreationPage.java | 17 ++-
.../common/project/AndroidManifestParser.java | 127 +++++++++++++-----
5 files changed, 144 insertions(+), 69 deletions(-)
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
index 655c038e1..7ee3def57 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java
@@ -1078,7 +1078,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
/**
* Performs the installation of an application whose package has been uploaded on the device.
- * Before doing it, if the application is already running on the device, it is killed.
+ *
* @param launchInfo the {@link DelayedLaunchInfo}.
* @param remotePath the path of the application package in the device tmp folder.
* @param device the device on which to install the application.
@@ -1088,12 +1088,6 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
*/
private String doInstall(DelayedLaunchInfo launchInfo, final String remotePath,
final IDevice device, boolean reinstall) throws IOException {
- // kill running application
- Client application = device.getClient(launchInfo.getPackageName());
- if (application != null) {
- application.kill();
- }
-
InstallReceiver receiver = new InstallReceiver();
try {
String cmd = String.format(
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
index 9f12b16e4..4fa270e4e 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java
@@ -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.ide.eclipse.common.project.AndroidManifestParser.Activity;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
@@ -247,7 +248,7 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
activityName = getActivityName(configuration);
// Get the full activity list and make sure the one we got matches.
- String[] activities = manifestParser.getActivities();
+ Activity[] activities = manifestParser.getActivities();
// first we check that there are, in fact, activities.
if (activities.length == 0) {
@@ -261,8 +262,11 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
// if the activity we got is null, we look for the default one.
AdtPlugin.printErrorToConsole(project,
"No activity specified! Getting the launcher activity.");
- activityName = manifestParser.getLauncherActivity();
-
+ Activity launcherActivity = manifestParser.getLauncherActivity();
+ if (launcherActivity != null) {
+ activityName = launcherActivity.getName();
+ }
+
// if there's no default activity. We revert to a sync-only launch.
if (activityName == null) {
revertToNoActionLaunch(project, config);
@@ -271,8 +275,8 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
// check the one we got from the config matches any from the list
boolean match = false;
- for (String a : activities) {
- if (a != null && a.equals(activityName)) {
+ for (Activity a : activities) {
+ if (a != null && a.getName().equals(activityName)) {
match = true;
break;
}
@@ -282,7 +286,10 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
if (match == false) {
AdtPlugin.printErrorToConsole(project,
"The specified activity does not exist! Getting the launcher activity.");
- activityName = manifestParser.getLauncherActivity();
+ Activity launcherActivity = manifestParser.getLauncherActivity();
+ if (launcherActivity != null) {
+ activityName = launcherActivity.getName();
+ }
// if there's no default activity. We revert to a sync-only launch.
if (activityName == null) {
@@ -291,7 +298,10 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
}
}
} else if (config.mLaunchAction == ACTION_DEFAULT) {
- activityName = manifestParser.getLauncherActivity();
+ Activity launcherActivity = manifestParser.getLauncherActivity();
+ if (launcherActivity != null) {
+ activityName = launcherActivity.getName();
+ }
// if there's no default activity. We revert to a sync-only launch.
if (activityName == null) {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java
index 91bd21cb7..a32c2ee0e 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java
@@ -20,6 +20,7 @@ import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.common.project.ProjectChooserHelper;
+import com.android.ide.eclipse.common.project.AndroidManifestParser.Activity;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
@@ -50,6 +51,8 @@ import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Text;
+import java.util.ArrayList;
+
/**
* Class for the main launch configuration tab.
*/
@@ -66,7 +69,7 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
private Button mProjButton;
private Combo mActivityCombo;
- private String[] mActivities;
+ private final ArrayList mActivities = new ArrayList();
private WidgetListener mListener = new WidgetListener();
@@ -214,8 +217,9 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
// add the activity
int selection = mActivityCombo.getSelectionIndex();
- if (mActivities != null && selection >=0 && selection < mActivities.length) {
- configuration.setAttribute(LaunchConfigDelegate.ATTR_ACTIVITY, mActivities[selection]);
+ if (mActivities != null && selection >=0 && selection < mActivities.size()) {
+ configuration.setAttribute(LaunchConfigDelegate.ATTR_ACTIVITY,
+ mActivities.get(selection).getName());
}
// link the project and the launch config.
@@ -349,11 +353,11 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
mActivityCombo.setEnabled(true);
if (activityName == null || activityName.equals(EMPTY_STRING)) {
mActivityCombo.clearSelection();
- } else if (mActivities != null && mActivities.length > 0) {
+ } else if (mActivities != null && mActivities.size() > 0) {
// look for the name of the activity in the combo.
boolean found = false;
- for (int i = 0 ; i < mActivities.length ; i++) {
- if (activityName.equals(mActivities[i])) {
+ for (int i = 0 ; i < mActivities.size() ; i++) {
+ if (activityName.equals(mActivities.get(i).getName())) {
found = true;
mActivityCombo.select(i);
break;
@@ -404,17 +408,22 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
BaseProjectHelper.getJavaProject(project), null /* errorListener */,
true /* gatherData */, false /* markErrors */);
if (manifestParser != null) {
- mActivities = manifestParser.getActivities();
-
+ Activity[] activities = manifestParser.getActivities();
+
+ mActivities.clear();
mActivityCombo.removeAll();
-
- if (mActivities.length > 0) {
+
+ for (Activity activity : activities) {
+ if (activity.getExported() && activity.hasAction()) {
+ mActivities.add(activity);
+ mActivityCombo.add(activity.getName());
+ }
+ }
+
+ if (mActivities.size() > 0) {
if (mLaunchAction == LaunchConfigDelegate.ACTION_ACTIVITY) {
mActivityCombo.setEnabled(true);
}
- for (String s : mActivities) {
- mActivityCombo.add(s);
- }
} else {
mActivityCombo.setEnabled(false);
}
@@ -435,7 +444,7 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab {
// if we reach this point, either project is null, or we got an exception during
// the parsing. In either case, we empty the activity list.
mActivityCombo.removeAll();
- mActivities = null;
+ mActivities.clear();
}
/**
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java
index e26b31cc3..6e8ff47fe 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java
@@ -27,6 +27,7 @@ import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
+import com.android.ide.eclipse.common.project.AndroidManifestParser.Activity;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.project.ProjectProperties;
@@ -859,6 +860,7 @@ public class NewProjectCreationPage extends WizardPage {
}
String packageName = null;
+ Activity activity = null;
String activityName = null;
int minSdkVersion = AndroidManifestParser.INVALID_MIN_SDK;
try {
@@ -866,11 +868,11 @@ public class NewProjectCreationPage extends WizardPage {
minSdkVersion = manifestData.getApiLevelRequirement();
// try to get the first launcher activity. If none, just take the first activity.
- activityName = manifestData.getLauncherActivity();
- if (activityName == null) {
- String[] activities = manifestData.getActivities();
+ activity = manifestData.getLauncherActivity();
+ if (activity == null) {
+ Activity[] activities = manifestData.getActivities();
if (activities != null && activities.length > 0) {
- activityName = activities[0];
+ activity = activities[0];
}
}
} catch (Exception e) {
@@ -881,7 +883,10 @@ public class NewProjectCreationPage extends WizardPage {
mPackageNameField.setText(packageName);
}
- activityName = AndroidManifestParser.extractActivityName(activityName, packageName);
+ if (activity != null) {
+ activityName = AndroidManifestParser.extractActivityName(activity.getName(),
+ packageName);
+ }
if (activityName != null && activityName.length() > 0) {
mInternalActivityNameUpdate = true;
@@ -1136,7 +1141,7 @@ public class NewProjectCreationPage extends WizardPage {
MSG_ERROR);
}
- String[] activities = manifestData.getActivities();
+ Activity[] activities = manifestData.getActivities();
if (activities == null || activities.length == 0) {
// This is acceptable now as long as no activity needs to be created
if (isCreateActivity()) {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
index f853adacb..69982fc66 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java
@@ -53,6 +53,7 @@ public class AndroidManifestParser {
private final static String ATTRIBUTE_DEBUGGABLE = "debuggable"; //$NON-NLS-$
private final static String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion"; //$NON-NLS-$
private final static String ATTRIBUTE_TARGET_PACKAGE = "targetPackage"; //$NON-NLS-1$
+ private final static String ATTRIBUTE_EXPORTED = "exported"; //$NON-NLS-1$
private final static String NODE_MANIFEST = "manifest"; //$NON-NLS-1$
private final static String NODE_APPLICATION = "application"; //$NON-NLS-1$
private final static String NODE_ACTIVITY = "activity"; //$NON-NLS-1$
@@ -84,7 +85,7 @@ public class AndroidManifestParser {
private final String mName;
private final String mTargetPackage;
- public Instrumentation(String name, String targetPackage) {
+ Instrumentation(String name, String targetPackage) {
mName = name;
mTargetPackage = targetPackage;
}
@@ -104,6 +105,60 @@ public class AndroidManifestParser {
}
}
+ /**
+ * Activity info obtained from the manifest.
+ */
+ public static class Activity {
+ private final String mName;
+ private final boolean mExported;
+ private boolean mHasAction = false;
+ private boolean mHasMainAction = false;
+ private boolean mHasLauncherCategory = false;
+
+ public Activity(String name, boolean exported) {
+ mName = name;
+ mExported = exported;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public boolean getExported() {
+ return mExported;
+ }
+
+ public boolean hasAction() {
+ return mHasAction;
+ }
+
+ public boolean isHomeActivity() {
+ return mHasMainAction && mHasLauncherCategory;
+ }
+
+ void setHasAction(boolean hasAction) {
+ mHasAction = hasAction;
+ }
+
+ /** If the activity doesn't yet have a filter set for the launcher, this resets both
+ * flags. This is to handle multiple intent-filters where one could have the valid
+ * action, and another one of the valid category.
+ */
+ void resetIntentFilter() {
+ if (isHomeActivity() == false) {
+ mHasMainAction = mHasLauncherCategory = false;
+ }
+ }
+
+ void setHasMainAction(boolean hasMainAction) {
+ mHasMainAction = hasMainAction;
+ }
+
+ void setHasLauncherCategory(boolean hasLauncherCategory) {
+ mHasLauncherCategory = hasLauncherCategory;
+ }
+ }
+
/**
* XML error & data handler used when parsing the AndroidManifest.xml file.
*
@@ -117,9 +172,9 @@ public class AndroidManifestParser {
/** Application package */
private String mPackage;
/** List of all activities */
- private final ArrayList mActivities = new ArrayList();
+ private final ArrayList mActivities = new ArrayList();
/** Launcher activity */
- private String mLauncherActivity = null;
+ private Activity mLauncherActivity = null;
/** list of process names declared by the manifest */
private Set mProcesses = null;
/** debuggable attribute value. If null, the attribute is not present. */
@@ -139,9 +194,7 @@ public class AndroidManifestParser {
private boolean mMarkErrors = false;
private int mCurrentLevel = 0;
private int mValidLevel = 0;
- private boolean mFoundMainAction = false;
- private boolean mFoundLauncherCategory = false;
- private String mCurrentActivity = null;
+ private Activity mCurrentActivity = null;
private Locator mLocator;
/**
@@ -173,8 +226,8 @@ public class AndroidManifestParser {
* Returns the list of activities found in the manifest.
* @return An array of fully qualified class names, or empty if no activity were found.
*/
- String[] getActivities() {
- return mActivities.toArray(new String[mActivities.size()]);
+ Activity[] getActivities() {
+ return mActivities.toArray(new Activity[mActivities.size()]);
}
/**
@@ -182,7 +235,7 @@ public class AndroidManifestParser {
* up in the HOME screen.
* @return the fully qualified name of a HOME activity or null if none were found.
*/
- String getLauncherActivity() {
+ Activity getLauncherActivity() {
return mLauncherActivity;
}
@@ -314,27 +367,26 @@ public class AndroidManifestParser {
case LEVEL_INTENT_FILTER:
// only process this level if we are in an activity
if (mCurrentActivity != null && NODE_INTENT.equals(localName)) {
- // if we're at the intent level, lets reset some flag to
- // be used when parsing the children
- mFoundMainAction = false;
- mFoundLauncherCategory = false;
+ mCurrentActivity.resetIntentFilter();
mValidLevel++;
}
break;
case LEVEL_CATEGORY:
- if (mCurrentActivity != null && mLauncherActivity == null) {
+ if (mCurrentActivity != null) {
if (NODE_ACTION.equals(localName)) {
// get the name attribute
- if (ACTION_MAIN.equals(
- getAttributeValue(attributes, ATTRIBUTE_NAME,
- true /* hasNamespace */))) {
- mFoundMainAction = true;
+ String action = getAttributeValue(attributes, ATTRIBUTE_NAME,
+ true /* hasNamespace */);
+ if (action != null) {
+ mCurrentActivity.setHasAction(true);
+ mCurrentActivity.setHasMainAction(
+ ACTION_MAIN.equals(action));
}
} else if (NODE_CATEGORY.equals(localName)) {
- if (CATEGORY_LAUNCHER.equals(
- getAttributeValue(attributes, ATTRIBUTE_NAME,
- true /* hasNamespace */))) {
- mFoundLauncherCategory = true;
+ String category = getAttributeValue(attributes, ATTRIBUTE_NAME,
+ true /* hasNamespace */);
+ if (CATEGORY_LAUNCHER.equals(category)) {
+ mCurrentActivity.setHasLauncherCategory(true);
}
}
@@ -378,8 +430,7 @@ public class AndroidManifestParser {
case LEVEL_INTENT_FILTER:
// if we found both a main action and a launcher category, this is our
// launcher activity!
- if (mCurrentActivity != null &&
- mFoundMainAction && mFoundLauncherCategory) {
+ if (mCurrentActivity != null && mCurrentActivity.isHomeActivity()) {
mLauncherActivity = mCurrentActivity;
}
break;
@@ -432,17 +483,23 @@ public class AndroidManifestParser {
String activityName = getAttributeValue(attributes, ATTRIBUTE_NAME,
true /* hasNamespace */);
if (activityName != null) {
- mCurrentActivity = combinePackageAndClassName(mPackage, activityName);
+ activityName = combinePackageAndClassName(mPackage, activityName);
+
+ // get the exported flag.
+ String exportedStr = getAttributeValue(attributes, ATTRIBUTE_EXPORTED, true);
+ boolean exported = exportedStr == null ||
+ exportedStr.toLowerCase().equals("true"); // $NON-NLS-1$
+ mCurrentActivity = new Activity(activityName, exported);
mActivities.add(mCurrentActivity);
if (mMarkErrors) {
- checkClass(mCurrentActivity, AndroidConstants.CLASS_ACTIVITY,
+ checkClass(activityName, AndroidConstants.CLASS_ACTIVITY,
true /* testVisibility */);
}
} else {
// no activity found! Aapt will output an error,
// so we don't have to do anything
- mCurrentActivity = activityName;
+ mCurrentActivity = null;
}
String processName = getAttributeValue(attributes, ATTRIBUTE_PROCESS,
@@ -570,8 +627,8 @@ public class AndroidManifestParser {
private static SAXParserFactory sParserFactory;
private final String mJavaPackage;
- private final String[] mActivities;
- private final String mLauncherActivity;
+ private final Activity[] mActivities;
+ private final Activity mLauncherActivity;
private final String[] mProcesses;
private final Boolean mDebuggable;
private final int mApiLevelRequirement;
@@ -811,18 +868,18 @@ public class AndroidManifestParser {
/**
* Returns the list of activities found in the manifest.
- * @return An array of fully qualified class names, or empty if no activity were found.
+ * @return An array of {@link Activity}, or empty if no activity were found.
*/
- public String[] getActivities() {
+ public Activity[] getActivities() {
return mActivities;
}
/**
* Returns the name of one activity found in the manifest, that is configured to show
* up in the HOME screen.
- * @return the fully qualified name of a HOME activity or null if none were found.
+ * @return The {@link Activity} representing a HOME activity or null if none were found.
*/
- public String getLauncherActivity() {
+ public Activity getLauncherActivity() {
return mLauncherActivity;
}
@@ -880,8 +937,8 @@ public class AndroidManifestParser {
* @param instrumentations the list of instrumentations parsed from the manifest.
* @param libraries the list of libraries in use parsed from the manifest.
*/
- private AndroidManifestParser(String javaPackage, String[] activities,
- String launcherActivity, String[] processes, Boolean debuggable,
+ private AndroidManifestParser(String javaPackage, Activity[] activities,
+ Activity launcherActivity, String[] processes, Boolean debuggable,
int apiLevelRequirement, Instrumentation[] instrumentations, String[] libraries) {
mJavaPackage = javaPackage;
mActivities = activities;
From f23d59c0688d53ccda33e5580a1ed5d6543aa1fa Mon Sep 17 00:00:00 2001
From: Raphael Moll <>
Date: Fri, 3 Apr 2009 13:31:58 -0700
Subject: [PATCH 062/127] AI 144488: am: CL 144486 ADT: Call the Extract String
Refactoring to create a new XML String ID when selecting a string
reference. Original author: raphael Merged from: //branches/cupcake/...
Automated import of CL 144488
---
.../extractstring/ExtractStringInputPage.java | 390 +++++++++++++++-
.../ExtractStringRefactoring.java | 99 ++--
.../extractstring/XmlStringFileHelper.java} | 6 +-
.../adt/ui/ReferenceChooserDialog.java | 36 +-
.../wizards/newstring/NewStringBaseImpl.java | 439 ------------------
.../wizards/newstring/NewStringWizard.java | 66 ---
.../newstring/NewStringWizardPage.java | 127 -----
7 files changed, 461 insertions(+), 702 deletions(-)
rename tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/{wizards/newstring/NewStringHelper.java => refactorings/extractstring/XmlStringFileHelper.java} (97%)
delete mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringBaseImpl.java
delete mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringWizard.java
delete mode 100644 tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringWizardPage.java
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
index 1f50c07b6..7303b0210 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringInputPage.java
@@ -17,34 +17,72 @@
package com.android.ide.eclipse.adt.refactorings.extractstring;
-import com.android.ide.eclipse.adt.wizards.newstring.NewStringBaseImpl;
-import com.android.ide.eclipse.adt.wizards.newstring.NewStringBaseImpl.INewStringPageCallback;
-import com.android.ide.eclipse.adt.wizards.newstring.NewStringBaseImpl.ValidationStatus;
+import com.android.ide.eclipse.adt.ui.ConfigurationSelector;
+import com.android.ide.eclipse.common.AndroidConstants;
+import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
+import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
+import com.android.sdklib.SdkConstants;
+import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.ltk.ui.refactoring.UserInputWizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
+import java.util.HashMap;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
/**
* @see ExtractStringRefactoring
*/
-class ExtractStringInputPage extends UserInputWizardPage
- implements IWizardPage, INewStringPageCallback {
+class ExtractStringInputPage extends UserInputWizardPage implements IWizardPage {
- private NewStringBaseImpl mImpl;
+ /** Last res file path used, shared across the session instances but specific to the
+ * current project. The default for unknown projects is {@link #DEFAULT_RES_FILE_PATH}. */
+ private static HashMap sLastResFilePath = new HashMap();
+
+ /** The project where the user selection happened. */
+ private final IProject mProject;
+
+ /** Test field where the user enters the new ID to be generated or replaced with. */
+ private Text mStringIdField;
+ /** The configuration selector, to select the resource path of the XML file. */
+ private ConfigurationSelector mConfigSelector;
+ /** The combo to display the existing XML files or enter a new one. */
+ private Combo mResFileCombo;
+
+ /** Regex pattern to read a valid res XML file path. It checks that the are 2 folders and
+ * a leaf file name ending with .xml */
+ private static final Pattern RES_XML_FILE_REGEX = Pattern.compile(
+ "/res/[a-z][a-zA-Z0-9_-]+/[^.]+\\.xml"); //$NON-NLS-1$
+ /** Absolute destination folder root, e.g. "/res/" */
+ private static final String RES_FOLDER_ABS =
+ AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP;
+ /** Relative destination folder root, e.g. "res/" */
+ private static final String RES_FOLDER_REL =
+ SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
+
+ private static final String DEFAULT_RES_FILE_PATH = "/res/values/strings.xml"; //$NON-NLS-1$
+
+ private XmlStringFileHelper mXmlHelper = new XmlStringFileHelper();
public ExtractStringInputPage(IProject project) {
super("ExtractStringInputPage"); //$NON-NLS-1$
- mImpl = new NewStringBaseImpl(project, this);
+ mProject = project;
}
/**
@@ -58,8 +96,11 @@ class ExtractStringInputPage extends UserInputWizardPage
GridLayout layout = new GridLayout();
layout.numColumns = 1;
content.setLayout(layout);
+
+ createStringGroup(content);
+ createResFileGroup(content);
- mImpl.createControl(content);
+ validatePage();
setControl(content);
}
@@ -68,15 +109,18 @@ class ExtractStringInputPage extends UserInputWizardPage
* and by which options.
*
* @param content A composite with a 1-column grid layout
- * @return The {@link Text} field for the new String ID name.
*/
- public Text createStringGroup(Composite content) {
+ public void createStringGroup(Composite content) {
final ExtractStringRefactoring ref = getOurRefactoring();
Group group = new Group(content, SWT.NONE);
group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- group.setText("String Replacement");
+ if (ref.getMode() == ExtractStringRefactoring.Mode.EDIT_SOURCE) {
+ group.setText("String Replacement");
+ } else {
+ group.setText("New String");
+ }
GridLayout layout = new GridLayout();
layout.numColumns = 2;
@@ -85,7 +129,7 @@ class ExtractStringInputPage extends UserInputWizardPage
// line: Textfield for string value (based on selection, if any)
Label label = new Label(group, SWT.NONE);
- label.setText("String:");
+ label.setText("String");
String selectedString = ref.getTokenString();
@@ -97,7 +141,7 @@ class ExtractStringInputPage extends UserInputWizardPage
stringValueField.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
- if (mImpl.validatePage()) {
+ if (validatePage()) {
ref.setNewStringValue(stringValueField.getText());
}
}
@@ -110,23 +154,76 @@ class ExtractStringInputPage extends UserInputWizardPage
// line : Textfield for new ID
label = new Label(group, SWT.NONE);
- label.setText("Replace by R.string.");
+ if (ref.getMode() == ExtractStringRefactoring.Mode.EDIT_SOURCE) {
+ label.setText("Replace by R.string.");
+ } else if (ref.getMode() == ExtractStringRefactoring.Mode.SELECT_NEW_ID) {
+ label.setText("New R.string.");
+ } else {
+ label.setText("ID R.string.");
+ }
- final Text stringIdField = new Text(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
- stringIdField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- stringIdField.setText(guessId(selectedString));
+ mStringIdField = new Text(group, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+ mStringIdField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mStringIdField.setText(guessId(selectedString));
- ref.setNewStringId(stringIdField.getText().trim());
+ ref.setNewStringId(mStringIdField.getText().trim());
- stringIdField.addModifyListener(new ModifyListener() {
+ mStringIdField.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
- if (mImpl.validatePage()) {
- ref.setNewStringId(stringIdField.getText().trim());
+ if (validatePage()) {
+ ref.setNewStringId(mStringIdField.getText().trim());
}
}
});
+ }
+
+ /**
+ * Creates the lower group with the fields to choose the resource confirmation and
+ * the target XML file.
+ *
+ * @param content A composite with a 1-column grid layout
+ */
+ private void createResFileGroup(Composite content) {
+
+ Group group = new Group(content, SWT.NONE);
+ group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ group.setText("XML resource to edit");
+
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ group.setLayout(layout);
- return stringIdField;
+ // line: selection of the res config
+
+ Label label;
+ label = new Label(group, SWT.NONE);
+ label.setText("Configuration:");
+
+ mConfigSelector = new ConfigurationSelector(group);
+ GridData gd = new GridData(2, GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
+ gd.widthHint = ConfigurationSelector.WIDTH_HINT;
+ gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
+ mConfigSelector.setLayoutData(gd);
+ OnConfigSelectorUpdated onConfigSelectorUpdated = new OnConfigSelectorUpdated();
+ mConfigSelector.setOnChangeListener(onConfigSelectorUpdated);
+
+ // line: selection of the output file
+
+ label = new Label(group, SWT.NONE);
+ label.setText("Resource file:");
+
+ mResFileCombo = new Combo(group, SWT.DROP_DOWN);
+ mResFileCombo.select(0);
+ mResFileCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mResFileCombo.addModifyListener(onConfigSelectorUpdated);
+
+ // set output file name to the last one used
+
+ String projPath = mProject.getFullPath().toPortableString();
+ String filePath = sLastResFilePath.get(projPath);
+
+ mResFileCombo.setText(filePath != null ? filePath : DEFAULT_RES_FILE_PATH);
+ onConfigSelectorUpdated.run();
}
/**
@@ -157,8 +254,251 @@ class ExtractStringInputPage extends UserInputWizardPage
return (ExtractStringRefactoring) getRefactoring();
}
- public void postValidatePage(ValidationStatus status) {
- ExtractStringRefactoring ref = getOurRefactoring();
- ref.setTargetFile(mImpl.getResFileProjPath());
+ /**
+ * Validates fields of the wizard input page. Displays errors as appropriate and
+ * enable the "Next" button (or not) by calling {@link #setPageComplete(boolean)}.
+ *
+ * @return True if the page has been positively validated. It may still have warnings.
+ */
+ private boolean validatePage() {
+ boolean success = true;
+
+ // Analyze fatal errors.
+
+ String text = mStringIdField.getText().trim();
+ if (text == null || text.length() < 1) {
+ setErrorMessage("Please provide a resource ID.");
+ success = false;
+ } else {
+ for (int i = 0; i < text.length(); i++) {
+ char c = text.charAt(i);
+ boolean ok = i == 0 ?
+ Character.isJavaIdentifierStart(c) :
+ Character.isJavaIdentifierPart(c);
+ if (!ok) {
+ setErrorMessage(String.format(
+ "The resource ID must be a valid Java identifier. The character %1$c at position %2$d is not acceptable.",
+ c, i+1));
+ success = false;
+ break;
+ }
+ }
+ }
+
+ String resFile = mResFileCombo.getText();
+ if (success) {
+ if (resFile == null || resFile.length() == 0) {
+ setErrorMessage("A resource file name is required.");
+ success = false;
+ } else if (!RES_XML_FILE_REGEX.matcher(resFile).matches()) {
+ setErrorMessage("The XML file name is not valid.");
+ success = false;
+ }
+ }
+
+ // Analyze info & warnings.
+
+ if (success) {
+ setErrorMessage(null);
+
+ ExtractStringRefactoring ref = getOurRefactoring();
+
+ ref.setTargetFile(resFile);
+ sLastResFilePath.put(mProject.getFullPath().toPortableString(), resFile);
+
+ if (mXmlHelper.isResIdDuplicate(mProject, resFile, text)) {
+ String msg = String.format("There's already a string item called '%1$s' in %2$s.",
+ text, resFile);
+ if (ref.getMode() == ExtractStringRefactoring.Mode.SELECT_NEW_ID) {
+ setErrorMessage(msg);
+ success = false;
+ } else {
+ setMessage(msg, WizardPage.WARNING);
+ }
+ } else if (mProject.findMember(resFile) == null) {
+ setMessage(
+ String.format("File %2$s does not exist and will be created.",
+ text, resFile),
+ WizardPage.INFORMATION);
+ } else {
+ setMessage(null);
+ }
+ }
+
+ setPageComplete(success);
+ return success;
}
+
+ public class OnConfigSelectorUpdated implements Runnable, ModifyListener {
+
+ /** Regex pattern to parse a valid res path: it reads (/res/folder-name/)+(filename). */
+ private final Pattern mPathRegex = Pattern.compile(
+ "(/res/[a-z][a-zA-Z0-9_-]+/)(.+)"); //$NON-NLS-1$
+
+ /** Temporary config object used to retrieve the Config Selector value. */
+ private FolderConfiguration mTempConfig = new FolderConfiguration();
+
+ private HashMap> mFolderCache =
+ new HashMap>();
+ private String mLastFolderUsedInCombo = null;
+ private boolean mInternalConfigChange;
+ private boolean mInternalFileComboChange;
+
+ /**
+ * Callback invoked when the {@link ConfigurationSelector} has been changed.
+ *
+ * The callback does the following:
+ *
+ *
Examine the current file name to retrieve the XML filename, if any.
+ *
Recompute the path based on the configuration selector (e.g. /res/values-fr/).
+ *
Examine the path to retrieve all the files in it. Keep those in a local cache.
+ *
If the XML filename from step 1 is not in the file list, it's a custom file name.
+ * Insert it and sort it.
+ *
Re-populate the file combo with all the choices.
+ *
Select the original XML file.
+ */
+ public void run() {
+ if (mInternalConfigChange) {
+ return;
+ }
+
+ // get current leafname, if any
+ String leafName = ""; //$NON-NLS-1$
+ String currPath = mResFileCombo.getText();
+ Matcher m = mPathRegex.matcher(currPath);
+ if (m.matches()) {
+ // Note: groups 1 and 2 cannot be null.
+ leafName = m.group(2);
+ currPath = m.group(1);
+ } else {
+ // There was a path but it was invalid. Ignore it.
+ currPath = ""; //$NON-NLS-1$
+ }
+
+ // recreate the res path from the current configuration
+ mConfigSelector.getConfiguration(mTempConfig);
+ StringBuffer sb = new StringBuffer(RES_FOLDER_ABS);
+ sb.append(mTempConfig.getFolderName(ResourceFolderType.VALUES));
+ sb.append('/');
+
+ String newPath = sb.toString();
+ if (newPath.equals(currPath) && newPath.equals(mLastFolderUsedInCombo)) {
+ // Path has not changed. No need to reload.
+ return;
+ }
+
+ // Get all the files at the new path
+
+ TreeSet filePaths = mFolderCache.get(newPath);
+
+ if (filePaths == null) {
+ filePaths = new TreeSet();
+
+ IFolder folder = mProject.getFolder(newPath);
+ if (folder != null && folder.exists()) {
+ try {
+ for (IResource res : folder.members()) {
+ String name = res.getName();
+ if (res.getType() == IResource.FILE && name.endsWith(".xml")) {
+ filePaths.add(newPath + name);
+ }
+ }
+ } catch (CoreException e) {
+ // Ignore.
+ }
+ }
+
+ mFolderCache.put(newPath, filePaths);
+ }
+
+ currPath = newPath + leafName;
+ if (leafName.length() > 0 && !filePaths.contains(currPath)) {
+ filePaths.add(currPath);
+ }
+
+ // Fill the combo
+ try {
+ mInternalFileComboChange = true;
+
+ mResFileCombo.removeAll();
+
+ for (String filePath : filePaths) {
+ mResFileCombo.add(filePath);
+ }
+
+ int index = -1;
+ if (leafName.length() > 0) {
+ index = mResFileCombo.indexOf(currPath);
+ if (index >= 0) {
+ mResFileCombo.select(index);
+ }
+ }
+
+ if (index == -1) {
+ mResFileCombo.setText(currPath);
+ }
+
+ mLastFolderUsedInCombo = newPath;
+
+ } finally {
+ mInternalFileComboChange = false;
+ }
+
+ // finally validate the whole page
+ validatePage();
+ }
+
+ /**
+ * Callback invoked when {@link ExtractStringInputPage#mResFileCombo} has been
+ * modified.
+ */
+ public void modifyText(ModifyEvent e) {
+ if (mInternalFileComboChange) {
+ return;
+ }
+
+ String wsFolderPath = mResFileCombo.getText();
+
+ // This is a custom path, we need to sanitize it.
+ // First it should start with "/res/". Then we need to make sure there are no
+ // relative paths, things like "../" or "./" or even "//".
+ wsFolderPath = wsFolderPath.replaceAll("/+\\.\\./+|/+\\./+|//+|\\\\+|^/+", "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ wsFolderPath = wsFolderPath.replaceAll("^\\.\\./+|^\\./+", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ wsFolderPath = wsFolderPath.replaceAll("/+\\.\\.$|/+\\.$|/+$", ""); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // We get "res/foo" from selections relative to the project when we want a "/res/foo" path.
+ if (wsFolderPath.startsWith(RES_FOLDER_REL)) {
+ wsFolderPath = RES_FOLDER_ABS + wsFolderPath.substring(RES_FOLDER_REL.length());
+
+ mInternalFileComboChange = true;
+ mResFileCombo.setText(wsFolderPath);
+ mInternalFileComboChange = false;
+ }
+
+ if (wsFolderPath.startsWith(RES_FOLDER_ABS)) {
+ wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length());
+
+ int pos = wsFolderPath.indexOf(AndroidConstants.WS_SEP_CHAR);
+ if (pos >= 0) {
+ wsFolderPath = wsFolderPath.substring(0, pos);
+ }
+
+ String[] folderSegments = wsFolderPath.split(FolderConfiguration.QUALIFIER_SEP);
+
+ if (folderSegments.length > 0) {
+ String folderName = folderSegments[0];
+
+ if (folderName != null && !folderName.equals(wsFolderPath)) {
+ // update config selector
+ mInternalConfigChange = true;
+ mConfigSelector.setConfiguration(folderSegments);
+ mInternalConfigChange = false;
+ }
+ }
+ }
+
+ validatePage();
+ }
+ }
+
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
index a17d81790..8a38e5268 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/ExtractStringRefactoring.java
@@ -16,7 +16,6 @@
package com.android.ide.eclipse.adt.refactorings.extractstring;
-import com.android.ide.eclipse.adt.wizards.newstring.NewStringHelper;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestParser;
@@ -98,8 +97,8 @@ import java.util.Map;
*
On success, the wizard is shown, which let the user input the new ID to use.
*
The wizard sets the user input values into this refactoring instance, e.g. the new string
* ID, the XML file to update, etc. The wizard does use the utility method
- * {@link NewStringHelper#isResIdDuplicate(IProject, String, String)} to check whether the new
- * ID is already defined in the target XML file.
+ * {@link XmlStringFileHelper#isResIdDuplicate(IProject, String, String)} to check whether
+ * the new ID is already defined in the target XML file.
*
Once Preview or Finish is selected in the wizard, the
* {@link #checkFinalConditions(IProgressMonitor)} is called to double-check the user input
* and compute the actual changes.
@@ -122,10 +121,23 @@ import java.util.Map;
*/
public class ExtractStringRefactoring extends Refactoring {
- private enum Mode {
+ public enum Mode {
+ /**
+ * the Extract String refactoring is called on an existing source file.
+ * Its purpose is then to get the selected string of the source and propose to
+ * change it by an XML id. The XML id may be a new one or an existing one.
+ */
EDIT_SOURCE,
- MAKE_ID,
- MAKE_NEW_ID
+ /**
+ * The Extract String refactoring is called without any source file.
+ * Its purpose is then to create a new XML string ID or select/modify an existing one.
+ */
+ SELECT_ID,
+ /**
+ * The Extract String refactoring is called without any source file.
+ * Its purpose is then to create a new XML string ID. The ID must not already exist.
+ */
+ SELECT_NEW_ID
}
/** The {@link Mode} of operation of the refactoring. */
@@ -133,6 +145,8 @@ public class ExtractStringRefactoring extends Refactoring {
/** The file model being manipulated.
* Value is null when not on {@link Mode#EDIT_SOURCE} mode. */
private final IFile mFile;
+ /** The project that contains {@link #mFile} and that contains the target XML file to modify. */
+ private final IProject mProject;
/** The start of the selection in {@link #mFile}.
* Value is -1 when not on {@link Mode#EDIT_SOURCE} mode. */
private final int mSelectionStart;
@@ -157,19 +171,30 @@ public class ExtractStringRefactoring extends Refactoring {
/** The list of changes computed by {@link #checkFinalConditions(IProgressMonitor)} and
* used by {@link #createChange(IProgressMonitor)}. */
private ArrayList mChanges;
-
- private NewStringHelper mHelper = new NewStringHelper();
+
+ private XmlStringFileHelper mXmlHelper = new XmlStringFileHelper();
+
+ private static final String KEY_MODE = "mode"; //$NON-NLS-1$
+ private static final String KEY_FILE = "file"; //$NON-NLS-1$
+ private static final String KEY_PROJECT = "proj"; //$NON-NLS-1$
+ private static final String KEY_SEL_START = "sel-start"; //$NON-NLS-1$
+ private static final String KEY_SEL_END = "sel-end"; //$NON-NLS-1$
+ private static final String KEY_TOK_ESC = "tok-esc"; //$NON-NLS-1$
public ExtractStringRefactoring(Map arguments)
throws NullPointerException {
- mMode = Mode.valueOf(arguments.get("mode")); //$NON-NLS-1$
+ mMode = Mode.valueOf(arguments.get(KEY_MODE));
+
+ IPath path = Path.fromPortableString(arguments.get(KEY_PROJECT));
+ mProject = (IProject) ResourcesPlugin.getWorkspace().getRoot().findMember(path);
if (mMode == Mode.EDIT_SOURCE) {
- IPath path = Path.fromPortableString(arguments.get("file")); //$NON-NLS-1$
+ path = Path.fromPortableString(arguments.get(KEY_FILE));
mFile = (IFile) ResourcesPlugin.getWorkspace().getRoot().findMember(path);
- mSelectionStart = Integer.parseInt(arguments.get("sel-start")); //$NON-NLS-1$
- mSelectionEnd = Integer.parseInt(arguments.get("sel-end")); //$NON-NLS-1$
- mTokenString = arguments.get("tok-esc"); //$NON-NLS-1$
+
+ mSelectionStart = Integer.parseInt(arguments.get(KEY_SEL_START));
+ mSelectionEnd = Integer.parseInt(arguments.get(KEY_SEL_END));
+ mTokenString = arguments.get(KEY_TOK_ESC);
} else {
mFile = null;
mSelectionStart = mSelectionEnd = -1;
@@ -179,12 +204,13 @@ public class ExtractStringRefactoring extends Refactoring {
private Map createArgumentMap() {
HashMap args = new HashMap();
- args.put("mode", mMode.name()); //$NON-NLS-1$
+ args.put(KEY_MODE, mMode.name());
+ args.put(KEY_PROJECT, mProject.getFullPath().toPortableString());
if (mMode == Mode.EDIT_SOURCE) {
- args.put("file", mFile.getFullPath().toPortableString()); //$NON-NLS-1$
- args.put("sel-start", Integer.toString(mSelectionStart)); //$NON-NLS-1$
- args.put("sel-end", Integer.toString(mSelectionEnd)); //$NON-NLS-1$
- args.put("tok-esc", mTokenString); //$NON-NLS-1$
+ args.put(KEY_FILE, mFile.getFullPath().toPortableString());
+ args.put(KEY_SEL_START, Integer.toString(mSelectionStart));
+ args.put(KEY_SEL_END, Integer.toString(mSelectionEnd));
+ args.put(KEY_TOK_ESC, mTokenString);
}
return args;
}
@@ -201,6 +227,7 @@ public class ExtractStringRefactoring extends Refactoring {
public ExtractStringRefactoring(IFile file, ITextSelection selection) {
mMode = Mode.EDIT_SOURCE;
mFile = file;
+ mProject = file.getProject();
mSelectionStart = selection.getOffset();
mSelectionEnd = mSelectionStart + Math.max(0, selection.getLength() - 1);
}
@@ -209,25 +236,35 @@ public class ExtractStringRefactoring extends Refactoring {
* Constructor to use when the Extract String refactoring is called without
* any source file. Its purpose is then to create a new XML string ID.
*
+ * @param project The project where the target XML file to modify is located. Cannot be null.
* @param enforceNew If true the XML ID must be a new one. If false, an existing ID can be
* used.
*/
- public ExtractStringRefactoring(boolean enforceNew) {
- mMode = enforceNew ? Mode.MAKE_NEW_ID : Mode.MAKE_ID;
+ public ExtractStringRefactoring(IProject project, boolean enforceNew) {
+ mMode = enforceNew ? Mode.SELECT_NEW_ID : Mode.SELECT_ID;
mFile = null;
+ mProject = project;
mSelectionStart = mSelectionEnd = -1;
}
-
-
/**
* @see org.eclipse.ltk.core.refactoring.Refactoring#getName()
*/
@Override
public String getName() {
+ if (mMode == Mode.SELECT_ID) {
+ return "Create or USe Android String";
+ } else if (mMode == Mode.SELECT_NEW_ID) {
+ return "Create New Android String";
+ }
+
return "Extract Android String";
}
+ public Mode getMode() {
+ return mMode;
+ }
+
/**
* Gets the actual string selected, after UTF characters have been escaped,
* good for display.
@@ -236,6 +273,10 @@ public class ExtractStringRefactoring extends Refactoring {
return mTokenString;
}
+ public String getXmlStringId() {
+ return mXmlStringId;
+ }
+
/**
* Step 1 of 3 of the refactoring:
* Checks that the current selection meets the initial condition before the ExtractString
@@ -459,7 +500,7 @@ public class ExtractStringRefactoring extends Refactoring {
// Prepare the change for the XML file.
- if (!mHelper.isResIdDuplicate(mFile.getProject(), mTargetXmlFileWsPath, mXmlStringId)) {
+ if (!mXmlHelper.isResIdDuplicate(mProject, mTargetXmlFileWsPath, mXmlStringId)) {
// We actually change it only if the ID doesn't exist yet
Change change = createXmlChange((IFile) targetXml, mXmlStringId, mXmlStringValue,
status, SubMonitor.convert(monitor, 1));
@@ -525,7 +566,7 @@ public class ExtractStringRefactoring extends Refactoring {
content.append("\n"); //$NON-NLS-1$
edit = new InsertEdit(0, content.toString());
- editGroup = new TextEditGroup("Create ID in new XML file", edit);
+ editGroup = new TextEditGroup("Create in new XML file", edit);
} else {
// The file exist. Attempt to parse it as a valid XML document.
try {
@@ -556,7 +597,7 @@ public class ExtractStringRefactoring extends Refactoring {
}
edit = new ReplaceEdit(offset, len, content.toString());
- editGroup = new TextEditGroup("Insert ID in XML file", edit);
+ editGroup = new TextEditGroup("Insert in XML file", edit);
}
} catch (CoreException e) {
// Failed to read file. Ignore. Will return null below.
@@ -702,8 +743,7 @@ public class ExtractStringRefactoring extends Refactoring {
// the FQCN of the R class.
String packageName = null;
String error = null;
- IProject proj = unit.getJavaProject().getProject();
- IResource manifestFile = proj.findMember(AndroidConstants.FN_ANDROID_MANIFEST);
+ IResource manifestFile = mProject.findMember(AndroidConstants.FN_ANDROID_MANIFEST);
if (manifestFile == null || manifestFile.getType() != IResource.FILE) {
error = "File not found";
} else {
@@ -895,7 +935,7 @@ public class ExtractStringRefactoring extends Refactoring {
mXmlStringId);
ExtractStringDescriptor desc = new ExtractStringDescriptor(
- mUnit.getJavaProject().getElementName(), //project
+ mProject.getName(), //project
comment, //description
comment, //comment
createArgumentMap());
@@ -919,8 +959,7 @@ public class ExtractStringRefactoring extends Refactoring {
* compilation unit. The resource may not exist.
*/
private IResource getTargetXmlResource(String xmlFileWsPath) {
- IProject proj = mFile.getProject();
- IResource resource = proj.getFile(xmlFileWsPath);
+ IResource resource = mProject.getFile(xmlFileWsPath);
return resource;
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringHelper.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/XmlStringFileHelper.java
similarity index 97%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringHelper.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/XmlStringFileHelper.java
index 3b8392718..6c8bbdbd3 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringHelper.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/refactorings/extractstring/XmlStringFileHelper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.wizards.newstring;
+package com.android.ide.eclipse.adt.refactorings.extractstring;
import com.android.ide.eclipse.common.project.AndroidXPathFactory;
@@ -35,7 +35,7 @@ import javax.xml.xpath.XPathExpressionException;
/**
*
*/
-public class NewStringHelper {
+class XmlStringFileHelper {
/** A temporary cache of R.string IDs defined by a given xml file. The key is the
* project path of the file, the data is a set of known string Ids for that file. */
@@ -43,7 +43,7 @@ public class NewStringHelper {
/** An instance of XPath, created lazily on demand. */
private XPath mXPath;
- public NewStringHelper() {
+ public XmlStringFileHelper() {
}
/**
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java
index 031b30382..966c5c81f 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/ui/ReferenceChooserDialog.java
@@ -70,6 +70,7 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
private FilteredTree mFilteredTree;
private Button mNewResButton;
private final IProject mProject;
+ private TreeViewer mTreeViewer;
/**
* @param project
@@ -159,8 +160,8 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
mFilteredTree.setLayoutData(data);
mFilteredTree.setFont(parent.getFont());
- TreeViewer treeViewer = mFilteredTree.getViewer();
- Tree tree = treeViewer.getTree();
+ mTreeViewer = mFilteredTree.getViewer();
+ Tree tree = mTreeViewer.getTree();
tree.addSelectionListener(new SelectionListener() {
public void widgetDefaultSelected(SelectionEvent e) {
@@ -172,9 +173,9 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
}
});
- treeViewer.setLabelProvider(new ResourceLabelProvider());
- treeViewer.setContentProvider(new ResourceContentProvider(false /* fullLevels */));
- treeViewer.setInput(mResources);
+ mTreeViewer.setLabelProvider(new ResourceLabelProvider());
+ mTreeViewer.setContentProvider(new ResourceContentProvider(false /* fullLevels */));
+ mTreeViewer.setInput(mResources);
}
protected void handleSelection() {
@@ -242,8 +243,10 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
// We only support adding new strings right now
mNewResButton.setEnabled(type == ResourceType.STRING);
- String title = String.format("New %1$s", type == null ? "Resource" : type.getDisplayName());
+ String title = String.format("New %1$s...",
+ type == null ? "Resource" : type.getDisplayName());
mNewResButton.setText(title);
+ mNewResButton.pack();
}
/**
@@ -259,14 +262,19 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
// We currently only support strings
if (type == ResourceType.STRING) {
- ExtractStringRefactoring ref = new ExtractStringRefactoring(true /*enforceNew*/);
+ ExtractStringRefactoring ref = new ExtractStringRefactoring(
+ mProject, true /*enforceNew*/);
RefactoringWizard wizard = new ExtractStringWizard(ref, mProject);
RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
try {
IWorkbench w = PlatformUI.getWorkbench();
- op.run(w.getDisplay().getActiveShell(), wizard.getDefaultPageTitle());
-
- // TODO Select string
+ if (op.run(w.getDisplay().getActiveShell(), wizard.getDefaultPageTitle()) ==
+ IDialogConstants.OK_ID) {
+ mTreeViewer.refresh();
+
+ // select it if possible
+ setupInitialSelection(type, ref.getXmlStringId());
+ }
} catch (InterruptedException ex) {
// Interrupted. Pass.
}
@@ -335,7 +343,9 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
if (resourceName.equals(resourceItem.getName())) {
// name of the resource match, we select it,
TreePath treePath = new TreePath(new Object[] { resourceType, resourceItem });
- mFilteredTree.getViewer().setSelection(new TreeSelection(treePath));
+ mFilteredTree.getViewer().setSelection(
+ new TreeSelection(treePath),
+ true /*reveal*/);
// and we're done.
return;
@@ -345,7 +355,9 @@ public class ReferenceChooserDialog extends SelectionStatusDialog {
// if we get here, the resource type is valid, but the resource is missing.
// we select and expand the resource type element.
TreePath treePath = new TreePath(new Object[] { resourceType });
- mFilteredTree.getViewer().setSelection(new TreeSelection(treePath));
+ mFilteredTree.getViewer().setSelection(
+ new TreeSelection(treePath),
+ true /*reveal*/);
mFilteredTree.getViewer().setExpandedState(resourceType, true /* expanded */);
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringBaseImpl.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringBaseImpl.java
deleted file mode 100644
index 334b13387..000000000
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newstring/NewStringBaseImpl.java
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.eclipse.org/org/documents/epl-v10.php
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ide.eclipse.adt.wizards.newstring;
-
-
-import com.android.ide.eclipse.adt.ui.ConfigurationSelector;
-import com.android.ide.eclipse.common.AndroidConstants;
-import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
-import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
-import com.android.sdklib.SdkConstants;
-
-import org.eclipse.core.resources.IFolder;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.jface.wizard.WizardPage;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Combo;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Group;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-
-import java.util.HashMap;
-import java.util.TreeSet;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class NewStringBaseImpl {
-
- public interface INewStringPageCallback {
- /**
- * Creates the top group with the field to replace which string and by what
- * and by which options.
- *
- * @param content A composite with a 1-column grid layout
- * @return The {@link Text} field for the new String ID name.
- */
- public Text createStringGroup(Composite content);
-
- /** Implements {@link WizardPage#setErrorMessage(String)} */
- public void setErrorMessage(String newMessage);
- /** Implements {@link WizardPage#setMessage(String, int)} */
- public void setMessage(String msg, int type);
- /** Implements {@link WizardPage#setPageComplete(boolean)} */
- public void setPageComplete(boolean success);
-
- public void postValidatePage(ValidationStatus status);
- }
-
- public class ValidationStatus {
- private String mError = null;
- private String mMessage = null;
- public int mMessageType = WizardPage.NONE;
-
- public boolean success() {
- return getError() != null;
- }
-
- public void setError(String error) {
- mError = error;
- mMessageType = WizardPage.ERROR;
- }
-
- public String getError() {
- return mError;
- }
-
- public void setMessage(String msg, int type) {
- mMessage = msg;
- mMessageType = type;
- }
-
- public String getMessage() {
- return mMessage;
- }
-
- public int getMessageType() {
- return mMessageType;
- }
- }
-
- /** Last res file path used, shared across the session instances but specific to the
- * current project. The default for unknown projects is {@link #DEFAULT_RES_FILE_PATH}. */
- private static HashMap sLastResFilePath = new HashMap();
-
- /** The project where the user selection happened. */
- private final IProject mProject;
- /** Text field where the user enters the new ID. */
- private Text mStringIdField;
- /** The configuration selector, to select the resource path of the XML file. */
- private ConfigurationSelector mConfigSelector;
- /** The combo to display the existing XML files or enter a new one. */
- private Combo mResFileCombo;
-
- private NewStringHelper mHelper = new NewStringHelper();
-
- /** Regex pattern to read a valid res XML file path. It checks that the are 2 folders and
- * a leaf file name ending with .xml */
- private static final Pattern RES_XML_FILE_REGEX = Pattern.compile(
- "/res/[a-z][a-zA-Z0-9_-]+/[^.]+\\.xml"); //$NON-NLS-1$
- /** Absolute destination folder root, e.g. "/res/" */
- private static final String RES_FOLDER_ABS =
- AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP;
- /** Relative destination folder root, e.g. "res/" */
- private static final String RES_FOLDER_REL =
- SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP;
-
- private static final String DEFAULT_RES_FILE_PATH = "/res/values/strings.xml"; //$NON-NLS-1$
-
- private final INewStringPageCallback mWizardPage;
-
- public NewStringBaseImpl(IProject project, INewStringPageCallback wizardPage) {
- mProject = project;
- mWizardPage = wizardPage;
- }
-
- /**
- * Create the UI for the new string wizard.
- */
- public void createControl(Composite parent) {
- mStringIdField = mWizardPage.createStringGroup(parent);
- createResFileGroup(parent);
- }
-
- /**
- * Creates the lower group with the fields to choose the resource confirmation and
- * the target XML file.
- *
- * @param content A composite with a 1-column grid layout
- */
- private void createResFileGroup(Composite content) {
-
- Group group = new Group(content, SWT.NONE);
- group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- group.setText("XML resource to edit");
-
- GridLayout layout = new GridLayout();
- layout.numColumns = 2;
- group.setLayout(layout);
-
- // line: selection of the res config
-
- Label label;
- label = new Label(group, SWT.NONE);
- label.setText("Configuration:");
-
- mConfigSelector = new ConfigurationSelector(group);
- GridData gd = new GridData(2, GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
- gd.widthHint = ConfigurationSelector.WIDTH_HINT;
- gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
- mConfigSelector.setLayoutData(gd);
- OnConfigSelectorUpdated onConfigSelectorUpdated = new OnConfigSelectorUpdated();
- mConfigSelector.setOnChangeListener(onConfigSelectorUpdated);
-
- // line: selection of the output file
-
- label = new Label(group, SWT.NONE);
- label.setText("Resource file:");
-
- mResFileCombo = new Combo(group, SWT.DROP_DOWN);
- mResFileCombo.select(0);
- mResFileCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
- mResFileCombo.addModifyListener(onConfigSelectorUpdated);
-
- // set output file name to the last one used
-
- String projPath = mProject.getFullPath().toPortableString();
- String filePath = sLastResFilePath.get(projPath);
-
- mResFileCombo.setText(filePath != null ? filePath : DEFAULT_RES_FILE_PATH);
- onConfigSelectorUpdated.run();
- }
-
- /**
- * Validates fields of the wizard input page. Displays errors as appropriate and
- * enable the "Next" button (or not) by calling
- * {@link INewStringPageCallback#setPageComplete(boolean)}.
- *
- * @return True if the page has been positively validated. It may still have warnings.
- */
- public boolean validatePage() {
- ValidationStatus status = new ValidationStatus();
-
- validateStringFields(status);
- if (status.success()) {
- validatePathFields(status);
- }
-
- mWizardPage.postValidatePage(status);
-
- mWizardPage.setErrorMessage(status.getError());
- mWizardPage.setMessage(status.getMessage(), status.getMessageType());
- mWizardPage.setPageComplete(status.success());
- return status.success();
- }
-
- public void validateStringFields(ValidationStatus status) {
-
- String text = mStringIdField.getText().trim();
- if (text == null || text.length() < 1) {
- status.setError("Please provide a resource ID to replace with.");
- } else {
- for (int i = 0; i < text.length(); i++) {
- char c = text.charAt(i);
- boolean ok = i == 0 ?
- Character.isJavaIdentifierStart(c) :
- Character.isJavaIdentifierPart(c);
- if (!ok) {
- status.setError(String.format(
- "The resource ID must be a valid Java identifier. The character %1$c at position %2$d is not acceptable.",
- c, i+1));
- break;
- }
- }
- }
- }
-
- public ValidationStatus validatePathFields(ValidationStatus status) {
- String resFile = getResFileProjPath();
- if (resFile == null || resFile.length() == 0) {
- status.setError("A resource file name is required.");
- } else if (!RES_XML_FILE_REGEX.matcher(resFile).matches()) {
- status.setError("The XML file name is not valid.");
- }
-
- if (status.success()) {
- sLastResFilePath.put(mProject.getFullPath().toPortableString(), resFile);
-
- String text = mStringIdField.getText().trim();
-
- if (mHelper.isResIdDuplicate(mProject, resFile, text)) {
- status.setMessage(
- String.format("There's already a string item called '%1$s' in %2$s.",
- text, resFile), WizardPage.WARNING);
- } else if (mProject.findMember(resFile) == null) {
- status.setMessage(
- String.format("File %2$s does not exist and will be created.",
- text, resFile), WizardPage.INFORMATION);
- }
- }
-
- return status;
- }
-
- public String getResFileProjPath() {
- return mResFileCombo.getText().trim();
- }
-
- public class OnConfigSelectorUpdated implements Runnable, ModifyListener {
-
- /** Regex pattern to parse a valid res path: it reads (/res/folder-name/)+(filename). */
- private final Pattern mPathRegex = Pattern.compile(
- "(/res/[a-z][a-zA-Z0-9_-]+/)(.+)"); //$NON-NLS-1$
-
- /** Temporary config object used to retrieve the Config Selector value. */
- private FolderConfiguration mTempConfig = new FolderConfiguration();
-
- private HashMap> mFolderCache =
- new HashMap>();
- private String mLastFolderUsedInCombo = null;
- private boolean mInternalConfigChange;
- private boolean mInternalFileComboChange;
-
- /**
- * Callback invoked when the {@link ConfigurationSelector} has been changed.
- *
- * The callback does the following:
- *
- *
Examine the current file name to retrieve the XML filename, if any.
- *
Recompute the path based on the configuration selector (e.g. /res/values-fr/).
- *
Examine the path to retrieve all the files in it. Keep those in a local cache.
- *
If the XML filename from step 1 is not in the file list, it's a custom file name.
- * Insert it and sort it.
- *
Re-populate the file combo with all the choices.
- *