aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--anttasks/src/com/android/ant/AidlExecTask.java22
-rw-r--r--anttasks/src/com/android/ant/ComputeDependencyTask.java34
-rw-r--r--build/tools.atree1
-rw-r--r--ddms/libs/ddmuilib/src/com/android/ddmuilib/FindDialog.java26
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml49
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java7
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DelegatingAction.java203
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java10
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GlobalCanvasDragInfo.java14
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java1
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java91
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java75
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java32
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java18
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElement.java39
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintEditAction.java49
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ImportPage.java381
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ImportProjectWizard.java83
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ImportedProject.java200
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreator.java184
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizardState.java16
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ProjectNamePage.java50
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewActivityWizard.java174
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java11
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java22
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java42
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizardState.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java28
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java4
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestDragElement.java6
-rw-r--r--files/ant/build.xml4
-rw-r--r--files/proguard-android-optimize.txt64
-rw-r--r--files/proguard-android.txt17
-rwxr-xr-x[-rw-r--r--]ide_common/src/com/android/ide/common/resources/FrameworkResources.java45
-rw-r--r--ide_common/src/com/android/ide/common/resources/ResourceFolder.java161
-rwxr-xr-x[-rw-r--r--]ide_common/src/com/android/ide/common/resources/ResourceRepository.java128
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java30
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java30
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/detector/api/LintUtilsTest.java1
-rw-r--r--rule_api/src/com/android/ide/common/api/IDragElement.java8
-rw-r--r--rule_api/src/com/android/ide/common/api/IViewRule.java16
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java2
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-addon-5.xsd (renamed from sdkmanager/libs/sdklib/src/com/android/sdklib/repository/-sdk-addon-5.xsd)0
-rwxr-xr-xsdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository-7.xsd (renamed from sdkmanager/libs/sdklib/src/com/android/sdklib/repository/-sdk-repository-7.xsd)0
45 files changed, 2011 insertions, 372 deletions
diff --git a/anttasks/src/com/android/ant/AidlExecTask.java b/anttasks/src/com/android/ant/AidlExecTask.java
index 1315dd7..5a39436 100644
--- a/anttasks/src/com/android/ant/AidlExecTask.java
+++ b/anttasks/src/com/android/ant/AidlExecTask.java
@@ -31,11 +31,15 @@ import java.util.List;
/**
* Task to execute aidl.
* <p>
- * It expects 3 attributes:<br>
+ * It expects 5 attributes:<br>
* 'executable' ({@link Path} with a single path) for the location of the aidl executable<br>
* 'framework' ({@link Path} with a single path) for the "preprocessed" file containing all the
* parcelables exported by the framework<br>
* 'genFolder' ({@link Path} with a single path) for the location of the gen folder.
+ * 'aidlOutFolder' ({@link Path} with a single path) for the location of the bin/aidl folder to
+ * copy the aidl files.
+ * 'libraryBinAidlFolderPathRefid' the name of the reference to a path object that contains
+ * libraries aidl output folder.
*
* It also expects one or more inner elements called "source" which are identical to {@link Path}
* elements.
@@ -44,7 +48,7 @@ public class AidlExecTask extends MultiFilesTask {
private String mExecutable;
private String mFramework;
- private Path mLibraryBinFolderPath;
+ private Path mLibraryBinAidlFolderPath;
private String mGenFolder;
private final ArrayList<Path> mPaths = new ArrayList<Path>();
private String mAidlOutFolder;
@@ -75,8 +79,8 @@ public class AidlExecTask extends MultiFilesTask {
}
// add all the library aidl folders to access parcelables that are in libraries
- if (mLibraryBinFolderPath != null) {
- for (String importFolder : mLibraryBinFolderPath.list()) {
+ if (mLibraryBinAidlFolderPath != null) {
+ for (String importFolder : mLibraryBinAidlFolderPath.list()) {
task.createArg().setValue("-I" + importFolder);
}
}
@@ -147,10 +151,10 @@ public class AidlExecTask extends MultiFilesTask {
mFramework = TaskHelper.checkSinglePath("framework", value);
}
- public void setLibraryBinFolderPathRefid(String libraryBinFolderPathRefid) {
- Object libBinRef = getProject().getReference(libraryBinFolderPathRefid);
- if (libBinRef instanceof Path) {
- mLibraryBinFolderPath = (Path) libBinRef;
+ public void setLibraryBinAidlFolderPathRefid(String libraryBinAidlFolderPathRefid) {
+ Object libBinAidlRef = getProject().getReference(libraryBinAidlFolderPathRefid);
+ if (libBinAidlRef instanceof Path) {
+ mLibraryBinAidlFolderPath = (Path) libBinAidlRef;
}
}
@@ -158,7 +162,7 @@ public class AidlExecTask extends MultiFilesTask {
mGenFolder = TaskHelper.checkSinglePath("genFolder", value);
}
- public void setaidlOutFolder(Path value) {
+ public void setAidlOutFolder(Path value) {
mAidlOutFolder = TaskHelper.checkSinglePath("aidlOutFolder", value);
}
diff --git a/anttasks/src/com/android/ant/ComputeDependencyTask.java b/anttasks/src/com/android/ant/ComputeDependencyTask.java
index 62572c8..512f425 100644
--- a/anttasks/src/com/android/ant/ComputeDependencyTask.java
+++ b/anttasks/src/com/android/ant/ComputeDependencyTask.java
@@ -58,7 +58,7 @@ public class ComputeDependencyTask extends GetLibraryListTask {
private String mLibraryPackagesOut;
private String mJarLibraryPathOut;
private String mLibraryNativeFolderPathOut;
- private String mLibraryBinFolderPathOut;
+ private String mLibraryBinAidlFolderPathOut;
private int mTargetApi = -1;
private boolean mVerbose = false;
@@ -78,8 +78,8 @@ public class ComputeDependencyTask extends GetLibraryListTask {
mJarLibraryPathOut = jarLibraryPathOut;
}
- public void setLibraryBinFolderPathOut(String libraryBinFolderPathOut) {
- mLibraryBinFolderPathOut = libraryBinFolderPathOut;
+ public void setLibraryBinAidlFolderPathOut(String libraryBinAidlFolderPathOut) {
+ mLibraryBinAidlFolderPathOut = libraryBinAidlFolderPathOut;
}
public void setLibraryNativeFolderPathOut(String libraryNativeFolderPathOut) {
@@ -115,7 +115,7 @@ public class ComputeDependencyTask extends GetLibraryListTask {
if (mLibraryNativeFolderPathOut == null) {
throw new BuildException("Missing attribute libraryNativeFolderPathOut");
}
- if (mLibraryBinFolderPathOut == null) {
+ if (mLibraryBinAidlFolderPathOut == null) {
throw new BuildException("Missing attribute libraryBinFolderPathOut");
}
if (mTargetApi == -1) {
@@ -131,7 +131,7 @@ public class ComputeDependencyTask extends GetLibraryListTask {
final Path manifestFilePath = new Path(antProject);
final Path resFolderPath = new Path(antProject);
final Path nativeFolderPath = new Path(antProject);
- final Path binFolderPath = new Path(antProject);
+ final Path binAidlFolderPath = new Path(antProject);
final StringBuilder packageStrBuilder = new StringBuilder();
LibraryProcessorFor3rdPartyJars processor = new LibraryProcessorFor3rdPartyJars() {
@@ -143,24 +143,30 @@ public class ComputeDependencyTask extends GetLibraryListTask {
// get the AndroidManifest.xml path.
// FIXME: support renamed location.
PathElement element = manifestFilePath.createPathElement();
- element.setPath(libRootPath + "/" + SdkConstants.FN_ANDROID_MANIFEST_XML);
+ element.setPath(libRootPath + '/' + SdkConstants.FN_ANDROID_MANIFEST_XML);
// get the res path. $PROJECT/res as well as the crunch cache.
// FIXME: support renamed folders.
element = resFolderPath.createPathElement();
- element.setPath(libRootPath + "/" + SdkConstants.FD_OUTPUT +
- "/" + SdkConstants.FD_RES);
+ element.setPath(libRootPath + '/' + SdkConstants.FD_OUTPUT +
+ '/' + SdkConstants.FD_RES);
element = resFolderPath.createPathElement();
- element.setPath(libRootPath + "/" + SdkConstants.FD_RESOURCES);
+ element.setPath(libRootPath + '/' + SdkConstants.FD_RESOURCES);
// get the folder for the native libraries. Always $PROJECT/libs
// FIXME: support renamed folder and/or move libs to bin/libs/
element = nativeFolderPath.createPathElement();
- element.setPath(libRootPath + "/" + SdkConstants.FD_NATIVE_LIBS);
+ element.setPath(libRootPath + '/' + SdkConstants.FD_NATIVE_LIBS);
+
+ // get the bin/aidl folder. $PROJECT/bin/aidl for now
+ // FIXME: support renamed folder.
+ element = binAidlFolderPath.createPathElement();
+ element.setPath(libRootPath + '/' + SdkConstants.FD_OUTPUT +
+ '/' + SdkConstants.FD_AIDL);
// get the bin folder. $PROJECT/bin for now
// FIXME: support renamed folder.
- element = binFolderPath.createPathElement();
+ element = binAidlFolderPath.createPathElement();
element.setPath(libRootPath + "/" + SdkConstants.FD_OUTPUT +
"/" + SdkConstants.FD_AIDL);
@@ -211,8 +217,8 @@ public class ComputeDependencyTask extends GetLibraryListTask {
System.out.println("API<=15: Adding annotations.jar to the classpath.");
jars.add(new File(sdkDir, SdkConstants.FD_TOOLS +
- "/" + SdkConstants.FD_SUPPORT +
- "/" + SdkConstants.FN_ANNOTATIONS_JAR));
+ '/' + SdkConstants.FD_SUPPORT +
+ '/' + SdkConstants.FN_ANNOTATIONS_JAR));
}
@@ -220,7 +226,7 @@ public class ComputeDependencyTask extends GetLibraryListTask {
// (the task themselves can handle a ref to an empty Path)
antProject.addReference(mLibraryNativeFolderPathOut, nativeFolderPath);
antProject.addReference(mLibraryManifestFilePathOut, manifestFilePath);
- antProject.addReference(mLibraryBinFolderPathOut, binFolderPath);
+ antProject.addReference(mLibraryBinAidlFolderPathOut, binAidlFolderPath);
// the rest is done only if there's a library.
if (hasLibraries) {
diff --git a/build/tools.atree b/build/tools.atree
index 414f143..afdeb75 100644
--- a/build/tools.atree
+++ b/build/tools.atree
@@ -66,6 +66,7 @@ bin/lint tools/lint
sdk/templates/build.template tools/lib/build.template
sdk/files/proguard-project.txt tools/lib/proguard-project.txt
sdk/files/proguard-android.txt tools/proguard/proguard-android.txt
+sdk/files/proguard-android-optimize.txt tools/proguard/proguard-android-optimize.txt
# Ant Build Rules
sdk/files/ant tools/ant
diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/FindDialog.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/FindDialog.java
index 6370be4..fe3f438 100644
--- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/FindDialog.java
+++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/FindDialog.java
@@ -44,14 +44,29 @@ public class FindDialog extends Dialog {
private final IFindTarget mTarget;
private Text mSearchText;
private String mPreviousSearchText;
+ private final int mDefaultButtonId;
- private final static int FIND_NEXT_ID = IDialogConstants.CLIENT_ID;
- private final static int FIND_PREVIOUS_ID = IDialogConstants.CLIENT_ID + 1;
+ /** Id of the "Find Next" button */
+ public static final int FIND_NEXT_ID = IDialogConstants.CLIENT_ID;
+
+ /** Id of the "Find Previous button */
+ public static final int FIND_PREVIOUS_ID = IDialogConstants.CLIENT_ID + 1;
public FindDialog(Shell shell, IFindTarget target) {
+ this(shell, target, FIND_PREVIOUS_ID);
+ }
+
+ /**
+ * Construct a find dialog.
+ * @param shell shell to use
+ * @param target delegate to be invoked on user action
+ * @param defaultButtonId one of {@code #FIND_NEXT_ID} or {@code #FIND_PREVIOUS_ID}.
+ */
+ public FindDialog(Shell shell, IFindTarget target, int defaultButtonId) {
super(shell);
mTarget = target;
+ mDefaultButtonId = defaultButtonId;
setShellStyle((getShellStyle() & ~SWT.APPLICATION_MODAL) | SWT.MODELESS);
setBlockOnOpen(true);
@@ -91,8 +106,11 @@ public class FindDialog extends Dialog {
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.CLOSE_ID, IDialogConstants.CLOSE_LABEL, false);
- mFindNext = createButton(parent, FIND_NEXT_ID, "Find Next", false);
- mFindPrevious = createButton(parent, FIND_PREVIOUS_ID, "Find Previous", /* default */ true);
+
+ mFindNext = createButton(parent, FIND_NEXT_ID, "Find Next",
+ mDefaultButtonId == FIND_NEXT_ID);
+ mFindPrevious = createButton(parent, FIND_PREVIOUS_ID, "Find Previous",
+ mDefaultButtonId != FIND_NEXT_ID);
mFindNext.setEnabled(false);
mFindPrevious.setEnabled(false);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index 1f402b9..c2e65ea 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -145,6 +145,24 @@
<run class="com.android.ide.eclipse.adt.internal.project.ExportNature" />
</runtime>
</extension>
+ <extension
+ point="org.eclipse.ui.importWizards">
+ <category
+ id="com.android.ide.eclipse.wizards.category"
+ name="Android" />
+ <wizard
+ canFinishEarly="false"
+ category="com.android.ide.eclipse.wizards.category"
+ class="com.android.ide.eclipse.adt.internal.wizards.newproject.ImportProjectWizard"
+ finalPerspective="org.eclipse.jdt.ui.JavaPerspective"
+ hasPages="true"
+ icon="icons/new_adt_project.png"
+ id="com.android.ide.eclipse.adt.project.ImportProjectWizard"
+ name="Existing Android code into workspace"
+ preferredPerspectives="org.eclipse.jdt.ui.JavaPerspective"
+ project="true">
+ </wizard>
+ </extension>
<extension point="org.eclipse.ui.newWizards">
<category
id="com.android.ide.eclipse.wizards.category"
@@ -172,7 +190,7 @@
hasPages="true"
icon="icons/new_adt_project.png"
id="com.android.ide.eclipse.adt.project.NewProjectWizard.Old"
- name="Empty Android Project"
+ name="Android Blank Project"
preferredPerspectives="org.eclipse.jdt.ui.JavaPerspective"
project="true" />
<wizard
@@ -202,6 +220,18 @@
<wizard
canFinishEarly="false"
category="com.android.ide.eclipse.wizards.category"
+ class="com.android.ide.eclipse.adt.internal.wizards.newproject.ImportProjectWizard"
+ finalPerspective="org.eclipse.jdt.ui.JavaPerspective"
+ hasPages="true"
+ icon="icons/new_adt_project.png"
+ id="com.android.ide.eclipse.adt.project.ImportProjectWizard.NewPrj"
+ name="Android Project from Existing Code"
+ preferredPerspectives="org.eclipse.jdt.ui.JavaPerspective"
+ project="true">
+ </wizard>
+ <wizard
+ canFinishEarly="false"
+ category="com.android.ide.eclipse.wizards.category"
class="com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard"
finalPerspective="org.eclipse.jdt.ui.JavaPerspective"
hasPages="true"
@@ -238,23 +268,12 @@
<wizard
canFinishEarly="false"
category="com.android.ide.eclipse.wizards.category"
- class="com.android.ide.eclipse.adt.internal.wizards.templates.NewTemplateWizard$NewActivityWizard"
- finalPerspective="org.eclipse.jdt.ui.JavaPerspective"
- hasPages="true"
- icon="icons/new_adt_project.png"
- id="com.android.ide.eclipse.editors.wizards.NewTemplateWizard.Activity"
- name="Blank Activity"
- preferredPerspectives="org.eclipse.jdt.ui.JavaPerspective"
- project="false" />
- <wizard
- canFinishEarly="false"
- category="com.android.ide.eclipse.wizards.category"
- class="com.android.ide.eclipse.adt.internal.wizards.templates.NewTemplateWizard$MasterDetailWizard"
+ class="com.android.ide.eclipse.adt.internal.wizards.templates.NewActivityWizard"
finalPerspective="org.eclipse.jdt.ui.JavaPerspective"
hasPages="true"
icon="icons/new_adt_project.png"
- id="com.android.ide.eclipse.editors.wizards.NewTemplateWizard.MasterDetail"
- name="Master Detail Flow"
+ id="com.android.ide.eclipse.editors.wizards.NewActivityWizard"
+ name="Android Activity"
preferredPerspectives="org.eclipse.jdt.ui.JavaPerspective"
project="false" />
<wizard
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java
index 04373e1..a1571e2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java
@@ -304,8 +304,9 @@ public class LinearLayoutRule extends BaseLayoutRule {
for (IDragElement element : elements) {
// This tries to determine if an INode corresponds to an
// IDragElement, by comparing their bounds.
- if (bc.equals(element.getBounds())) {
+ if (element.isSame(it)) {
isDragged = true;
+ break;
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java
index 2f3e0b1..6368783 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java
@@ -27,12 +27,14 @@ import com.android.ide.eclipse.adt.internal.lint.EclipseLintRunner;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
import com.android.ide.eclipse.adt.internal.sdk.Sdk.TargetChangeListener;
import com.android.sdklib.IAndroidTarget;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
@@ -69,9 +71,12 @@ import org.eclipse.ui.forms.editor.IFormPage;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.events.IHyperlinkListener;
+import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.ide.IDEActionFactory;
import org.eclipse.ui.ide.IGotoMarker;
import org.eclipse.ui.internal.browser.WorkbenchBrowserSupport;
+import org.eclipse.ui.part.MultiPageEditorPart;
+import org.eclipse.ui.part.WorkbenchPart;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
@@ -953,7 +958,7 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh
* the hooks should not perform edits on the model without acquiring
* a lock first.
*/
- protected void runEditHooks() {
+ public void runEditHooks() {
if (!mIgnoreXmlUpdate) {
// Check for errors, if enabled
if (AdtPrefs.getPrefs().isLintOnSave()) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DelegatingAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DelegatingAction.java
new file mode 100644
index 0000000..7a41b5b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DelegatingAction.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2012 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.internal.editors.layout.gle2;
+
+import com.android.annotations.NonNull;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.action.IMenuCreator;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.swt.events.HelpListener;
+import org.eclipse.swt.widgets.Event;
+
+/**
+ * Implementation of {@link IAction} which delegates to a different
+ * {@link IAction} which allows a subclass to wrap and customize some of the
+ * behavior of a different action
+ */
+public class DelegatingAction implements IAction {
+ private final IAction mAction;
+
+ /**
+ * Construct a new delegate of the given action
+ *
+ * @param action the action to be delegated
+ */
+ public DelegatingAction(@NonNull IAction action) {
+ mAction = action;
+ }
+
+ @Override
+ public void addPropertyChangeListener(IPropertyChangeListener listener) {
+ mAction.addPropertyChangeListener(listener);
+ }
+
+ @Override
+ public int getAccelerator() {
+ return mAction.getAccelerator();
+ }
+
+ @Override
+ public String getActionDefinitionId() {
+ return mAction.getActionDefinitionId();
+ }
+
+ @Override
+ public String getDescription() {
+ return mAction.getDescription();
+ }
+
+ @Override
+ public ImageDescriptor getDisabledImageDescriptor() {
+ return mAction.getDisabledImageDescriptor();
+ }
+
+ @Override
+ public HelpListener getHelpListener() {
+ return mAction.getHelpListener();
+ }
+
+ @Override
+ public ImageDescriptor getHoverImageDescriptor() {
+ return mAction.getHoverImageDescriptor();
+ }
+
+ @Override
+ public String getId() {
+ return mAction.getId();
+ }
+
+ @Override
+ public ImageDescriptor getImageDescriptor() {
+ return mAction.getImageDescriptor();
+ }
+
+ @Override
+ public IMenuCreator getMenuCreator() {
+ return mAction.getMenuCreator();
+ }
+
+ @Override
+ public int getStyle() {
+ return mAction.getStyle();
+ }
+
+ @Override
+ public String getText() {
+ return mAction.getText();
+ }
+
+ @Override
+ public String getToolTipText() {
+ return mAction.getToolTipText();
+ }
+
+ @Override
+ public boolean isChecked() {
+ return mAction.isChecked();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mAction.isEnabled();
+ }
+
+ @Override
+ public boolean isHandled() {
+ return mAction.isHandled();
+ }
+
+ @Override
+ public void removePropertyChangeListener(IPropertyChangeListener listener) {
+ mAction.removePropertyChangeListener(listener);
+ }
+
+ @Override
+ public void run() {
+ mAction.run();
+ }
+
+ @Override
+ public void runWithEvent(Event event) {
+ mAction.runWithEvent(event);
+ }
+
+ @Override
+ public void setActionDefinitionId(String id) {
+ mAction.setActionDefinitionId(id);
+ }
+
+ @Override
+ public void setChecked(boolean checked) {
+ mAction.setChecked(checked);
+ }
+
+ @Override
+ public void setDescription(String text) {
+ mAction.setDescription(text);
+ }
+
+ @Override
+ public void setDisabledImageDescriptor(ImageDescriptor newImage) {
+ mAction.setDisabledImageDescriptor(newImage);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ mAction.setEnabled(enabled);
+ }
+
+ @Override
+ public void setHelpListener(HelpListener listener) {
+ mAction.setHelpListener(listener);
+ }
+
+ @Override
+ public void setHoverImageDescriptor(ImageDescriptor newImage) {
+ mAction.setHoverImageDescriptor(newImage);
+ }
+
+ @Override
+ public void setId(String id) {
+ mAction.setId(id);
+ }
+
+ @Override
+ public void setImageDescriptor(ImageDescriptor newImage) {
+ mAction.setImageDescriptor(newImage);
+ }
+
+ @Override
+ public void setMenuCreator(IMenuCreator creator) {
+ mAction.setMenuCreator(creator);
+ }
+
+ @Override
+ public void setText(String text) {
+ mAction.setText(text);
+ }
+
+ @Override
+ public void setToolTipText(String text) {
+ mAction.setToolTipText(text);
+ }
+
+ @Override
+ public void setAccelerator(int keycode) {
+ mAction.setAccelerator(keycode);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java
index d8a45b6..e2c1d5e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GestureManager.java
@@ -717,6 +717,7 @@ public class GestureManager {
// operation.
List<SelectionItem> selections = selectionManager.getSelections();
mDragSelection.clear();
+ SelectionItem primary = null;
if (!selections.isEmpty()) {
// Is the cursor on top of a selected element?
@@ -724,6 +725,7 @@ public class GestureManager {
for (SelectionItem cs : selections) {
if (!cs.isRoot() && cs.getRect().contains(p.x, p.y)) {
+ primary = cs;
insideSelection = true;
break;
}
@@ -732,7 +734,7 @@ public class GestureManager {
if (!insideSelection) {
CanvasViewInfo vi = mCanvas.getViewHierarchy().findViewInfoAt(p);
if (vi != null && !vi.isRoot() && !vi.isHidden()) {
- selectionManager.selectSingle(vi);
+ primary = selectionManager.selectSingle(vi);
insideSelection = true;
}
}
@@ -753,6 +755,8 @@ public class GestureManager {
for (SelectionItem cs : selections) {
if (!cs.isRoot() && !cs.isHidden()) {
mDragSelection.add(cs);
+ } else if (cs == primary) {
+ primary = null;
}
}
}
@@ -763,7 +767,7 @@ public class GestureManager {
if (mDragSelection.isEmpty()) {
CanvasViewInfo vi = mCanvas.getViewHierarchy().findViewInfoAt(p);
if (vi != null && !vi.isRoot() && !vi.isHidden()) {
- selectionManager.selectSingle(vi);
+ primary = selectionManager.selectSingle(vi);
mDragSelection.addAll(selections);
}
}
@@ -773,7 +777,7 @@ public class GestureManager {
e.doit = !mDragSelection.isEmpty();
int imageCount = mDragSelection.size();
if (e.doit) {
- mDragElements = SelectionItem.getAsElements(mDragSelection);
+ mDragElements = SelectionItem.getAsElements(mDragSelection, primary);
GlobalCanvasDragInfo.getInstance().startDrag(mDragElements,
mDragSelection.toArray(new SelectionItem[imageCount]),
mCanvas, new Runnable() {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GlobalCanvasDragInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GlobalCanvasDragInfo.java
index 06986cd..b918b00 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GlobalCanvasDragInfo.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GlobalCanvasDragInfo.java
@@ -16,6 +16,8 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
import com.android.ide.common.api.IViewRule;
import com.android.ide.common.api.Rect;
@@ -63,6 +65,10 @@ final class GlobalCanvasDragInfo {
* Registers the XML elements being dragged.
*
* @param elements The elements being dragged
+ * @param primary the "primary" element among the elements; when there is a
+ * single item dragged this will be the same, but in
+ * multi-selection it will be the element under the mouse as the
+ * selection was initiated
* @param selection The selection (which can be null, for example when the
* user drags from the palette)
* @param sourceCanvas An object representing the source we are dragging
@@ -71,8 +77,11 @@ final class GlobalCanvasDragInfo {
* source. It should only be invoked if the drag operation is a
* move, not a copy.
*/
- public void startDrag(SimpleElement[] elements, SelectionItem[] selection,
- Object sourceCanvas, Runnable removeSourceHandler) {
+ public void startDrag(
+ @NonNull SimpleElement[] elements,
+ @Nullable SelectionItem[] selection,
+ @Nullable Object sourceCanvas,
+ @Nullable Runnable removeSourceHandler) {
mCurrentElements = elements;
mCurrentSelection = selection;
mSourceCanvas = sourceCanvas;
@@ -93,6 +102,7 @@ final class GlobalCanvasDragInfo {
}
/** Returns the elements being dragged. */
+ @NonNull
public SimpleElement[] getCurrentElements() {
return mCurrentElements;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java
index d079ff4..3704d8f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageOverlay.java
@@ -190,6 +190,7 @@ public class ImageOverlay extends Overlay implements IImageFactory {
public synchronized void paint(GC gc) {
if (mImage != null) {
boolean valid = mCanvas.getViewHierarchy().isValid();
+ mCanvas.ensureZoomed();
if (!valid) {
gc_setAlpha(gc, 128); // half-transparent
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
index 371852c..f218069 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
@@ -34,6 +34,7 @@ import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepos
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
+import com.android.ide.eclipse.adt.internal.lint.LintEditAction;
import com.android.resources.Density;
import com.android.sdklib.SdkConstants;
import com.android.util.XmlUtils;
@@ -180,6 +181,12 @@ public class LayoutCanvas extends Canvas {
/** Copy action for the Edit or context menu. */
private Action mCopyAction;
+ /** Undo action: delegates to the text editor */
+ private IAction mUndoAction;
+
+ /** Redo action: delegates to the text editor */
+ private IAction mRedoAction;
+
/** Root of the context menu. */
private MenuManager mMenuManager;
@@ -287,11 +294,25 @@ public class LayoutCanvas extends Canvas {
public void controlResized(ControlEvent e) {
super.controlResized(e);
- mHScale.setClientSize(getClientArea().width);
- mVScale.setClientSize(getClientArea().height);
+ // Check editor state:
+ LayoutWindowCoordinator coordinator = LayoutWindowCoordinator.get();
+ if (coordinator != null) {
+ IEditorSite editorSite = getEditorDelegate().getEditor().getEditorSite();
+ coordinator.syncMaximizedState(editorSite.getPage());
+ }
+
+ Rectangle clientArea = getClientArea();
+ mHScale.setClientSize(clientArea.width);
+ mVScale.setClientSize(clientArea.height);
// Update the zoom level in the canvas when you toggle the zoom
- getDisplay().asyncExec(mZoomCheck);
+ if (coordinator != null) {
+ mZoomCheck.run();
+ } else {
+ // During startup, delay updates which can trigger further layout
+ getDisplay().asyncExec(mZoomCheck);
+
+ }
}
});
@@ -329,14 +350,19 @@ public class LayoutCanvas extends Canvas {
return;
}
- IEditorPart editor = getEditorDelegate().getEditor();
- IWorkbenchPage page = editor.getSite().getPage();
- Boolean zoomed = page.isPageZoomed();
- if (mWasZoomed != zoomed) {
- if (mWasZoomed != null) {
- setFitScale(true /*onlyZoomOut*/);
+ LayoutWindowCoordinator coordinator = LayoutWindowCoordinator.get();
+ if (coordinator != null) {
+ Boolean zoomed = coordinator.isEditorMaximized();
+ if (mWasZoomed != zoomed) {
+ if (mWasZoomed != null) {
+ LayoutActionBar actionBar = mEditorDelegate.getGraphicalEditor()
+ .getLayoutActionBar();
+ if (actionBar.isZoomingAllowed()) {
+ setFitScale(true /*onlyZoomOut*/);
+ }
+ }
+ mWasZoomed = zoomed;
}
- mWasZoomed = zoomed;
}
}
};
@@ -605,16 +631,16 @@ public class LayoutCanvas extends Canvas {
mEditorDelegate.getGraphicalEditor().setModel(mViewHierarchy.getRoot());
if (image != null) {
- mHScale.setSize(image.getImageData().width, getClientArea().width);
- mVScale.setSize(image.getImageData().height, getClientArea().height);
+ Rectangle clientArea = getClientArea();
+ mHScale.setSize(image.getImageData().width, clientArea.width);
+ mVScale.setSize(image.getImageData().height, clientArea.height);
if (mZoomFitNextImage) {
- mZoomFitNextImage = false;
// Must be run asynchronously because getClientArea() returns 0 bounds
// when the editor is being initialized
getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
- setFitScale(true);
+ ensureZoomed();
}
});
}
@@ -624,7 +650,18 @@ public class LayoutCanvas extends Canvas {
redraw();
}
- /* package */ void setShowOutline(boolean newState) {
+ void ensureZoomed() {
+ if (mZoomFitNextImage && getClientArea().height > 0) {
+ mZoomFitNextImage = false;
+ LayoutActionBar actionBar = mEditorDelegate.getGraphicalEditor()
+ .getLayoutActionBar();
+ if (actionBar.isZoomingAllowed()) {
+ setFitScale(true);
+ }
+ }
+ }
+
+ void setShowOutline(boolean newState) {
mShowOutline = newState;
redraw();
}
@@ -1225,6 +1262,21 @@ public class LayoutCanvas extends Canvas {
bars.setGlobalActionHandler(ActionFactory.PASTE.getId(), mPasteAction);
bars.setGlobalActionHandler(ActionFactory.DELETE.getId(), mDeleteAction);
bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(), mSelectAllAction);
+
+ // Delegate the Undo and Redo actions to the text editor ones, but wrap them
+ // such that we run lint to update the results on the current page (this is
+ // normally done on each editor operation that goes through
+ // {@link AndroidXmlEditor#wrapUndoEditXmlModel}, but not undo/redo)
+ if (mUndoAction == null) {
+ IAction undoAction = editor.getAction(ActionFactory.UNDO.getId());
+ mUndoAction = new LintEditAction(undoAction, getEditorDelegate().getEditor());
+ }
+ bars.setGlobalActionHandler(ActionFactory.UNDO.getId(), mUndoAction);
+ if (mRedoAction == null) {
+ IAction redoAction = editor.getAction(ActionFactory.REDO.getId());
+ mRedoAction = new LintEditAction(redoAction, getEditorDelegate().getEditor());
+ }
+ bars.setGlobalActionHandler(ActionFactory.REDO.getId(), mRedoAction);
} else {
bars.setGlobalActionHandler(ActionFactory.CUT.getId(),
editor.getAction(ActionFactory.CUT.getId()));
@@ -1236,13 +1288,12 @@ public class LayoutCanvas extends Canvas {
editor.getAction(ActionFactory.DELETE.getId()));
bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
editor.getAction(ActionFactory.SELECT_ALL.getId()));
+ bars.setGlobalActionHandler(ActionFactory.UNDO.getId(),
+ editor.getAction(ActionFactory.UNDO.getId()));
+ bars.setGlobalActionHandler(ActionFactory.REDO.getId(),
+ editor.getAction(ActionFactory.REDO.getId()));
}
- IAction undoAction = editor.getAction(ActionFactory.UNDO.getId());
- bars.setGlobalActionHandler(ActionFactory.UNDO.getId(), undoAction);
- IAction redoAction = editor.getAction(ActionFactory.REDO.getId());
- bars.setGlobalActionHandler(ActionFactory.REDO.getId(), redoAction);
-
bars.updateActionBars();
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java
index 6a6f564..a0672c6 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutWindowCoordinator.java
@@ -16,10 +16,12 @@
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IPartService;
import org.eclipse.ui.IViewReference;
@@ -73,13 +75,19 @@ public class LayoutWindowCoordinator implements IPartListener2 {
*/
private boolean mInitialized;
+ /** Singleton reference */
+ private static LayoutWindowCoordinator sSingleton;
+
/**
* Start the coordinator
*
* @param window the associated window
*/
public static void start(@NonNull IWorkbenchWindow window) {
+ assert sSingleton == null;
+
LayoutWindowCoordinator coordinator = new LayoutWindowCoordinator(window);
+ sSingleton = coordinator;
IPartService service = window.getPartService();
if (service != null) {
@@ -88,7 +96,27 @@ public class LayoutWindowCoordinator implements IPartListener2 {
}
}
- private LayoutWindowCoordinator(IWorkbenchWindow window) {
+ /**
+ * Returns the coordinator. This method will return null if it is called before
+ * {@link #start} has been called, and non null after.
+ *
+ * @return the coordinator
+ */
+ @Nullable
+ public static LayoutWindowCoordinator get() {
+ return sSingleton;
+ }
+
+ /**
+ * Returns true if the main editor window is maximized
+ *
+ * @return true if the main editor window is maximized
+ */
+ public boolean isEditorMaximized() {
+ return mEditorMaximized;
+ }
+
+ private LayoutWindowCoordinator(@NonNull IWorkbenchWindow window) {
mWindow = window;
initialize();
@@ -122,8 +150,9 @@ public class LayoutWindowCoordinator implements IPartListener2 {
mOutlineOpen = true;
}
}
- mEditorMaximized = activePage.isPageZoomed();
- syncActive();
+ if (!syncMaximizedState(activePage)) {
+ syncActive();
+ }
}
static IViewReference findPropertySheetView(IWorkbenchPage activePage) {
@@ -135,6 +164,43 @@ public class LayoutWindowCoordinator implements IPartListener2 {
}
/**
+ * Checks the maximized state of the page and updates internal state if
+ * necessary.
+ * <p>
+ * This is used in Eclipse 4.x, where the {@link IPartListener2} does not
+ * fire {@link IPartListener2#partHidden(IWorkbenchPartReference)} when the
+ * editor is maximized anymore (see issue
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=382120 for details).
+ * Instead, the layout editor listens for resize events, and upon resize it
+ * looks up the part state and calls this method to ensure that the right
+ * maximized state is known to the layout coordinator.
+ *
+ * @param page the active workbench page
+ * @return true if the state changed, false otherwise
+ */
+ public boolean syncMaximizedState(IWorkbenchPage page) {
+ boolean maximized = isPageZoomed(page);
+ if (mEditorMaximized != maximized) {
+ mEditorMaximized = maximized;
+ syncActive();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isPageZoomed(IWorkbenchPage page) {
+ IWorkbenchPartReference reference = page.getActivePartReference();
+ if (reference != null && reference instanceof IEditorReference) {
+ int state = page.getPartState(reference);
+ boolean maximized = (state & IWorkbenchPage.STATE_MAXIMIZED) != 0;
+ return maximized;
+ }
+
+ // If the active reference isn't the editor, then the editor can't be maximized
+ return false;
+ }
+
+ /**
* Syncs the given editor's view state such that the property sheet and or
* outline are shown or hidden according to the visibility of the global
* outline and property sheet views.
@@ -258,8 +324,9 @@ public class LayoutWindowCoordinator implements IPartListener2 {
}
}
+ boolean wasMaximized = mEditorMaximized;
mEditorMaximized = visibleCount <= 1;
- if (mEditorMaximized) {
+ if (mEditorMaximized && !wasMaximized) {
// Only consider -maximizing- the window to be occasion for handling
// a "property sheet closed" event as a "show outline.
// And in fact we may want to remove it once you re-expose things
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java
index 5d49426..d104e37 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionItem.java
@@ -169,13 +169,39 @@ class SelectionItem {
* @return An array of wrapper elements. Never null.
*/
@NonNull
- static SimpleElement[] getAsElements(List<SelectionItem> items) {
- ArrayList<SimpleElement> elements = new ArrayList<SimpleElement>();
+ static SimpleElement[] getAsElements(@NonNull List<SelectionItem> items) {
+ return getAsElements(items, null);
+ }
+
+ /**
+ * Returns elements representing the given selection of canvas items.
+ *
+ * @param items Items to wrap in elements
+ * @param primary The primary selected item which should be listed first
+ * @return An array of wrapper elements. Never null.
+ */
+ @NonNull
+ static SimpleElement[] getAsElements(
+ @NonNull List<SelectionItem> items,
+ @Nullable SelectionItem primary) {
+ List<SimpleElement> elements = new ArrayList<SimpleElement>();
+
+ if (primary != null) {
+ CanvasViewInfo vi = primary.getViewInfo();
+ SimpleElement e = vi.toSimpleElement();
+ e.setSelectionItem(primary);
+ elements.add(e);
+ }
for (SelectionItem cs : items) {
- CanvasViewInfo vi = cs.getViewInfo();
+ if (cs == primary) {
+ // Already handled
+ continue;
+ }
+ CanvasViewInfo vi = cs.getViewInfo();
SimpleElement e = vi.toSimpleElement();
+ e.setSelectionItem(cs);
elements.add(e);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java
index 1450768..0bad5a5 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManager.java
@@ -21,6 +21,7 @@ import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.Selection
import static com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionHandle.PIXEL_RADIUS;
import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
import com.android.ide.common.api.INode;
import com.android.ide.common.layout.GridLayoutRule;
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
@@ -429,11 +430,15 @@ public class SelectionManager implements ISelectionProvider {
/**
* Removes all the currently selected item and only select the given item.
- * Issues a {@link #redraw()} if the selection changes.
+ * Issues a redraw() if the selection changes.
*
* @param vi The new selected item if non-null. Selection becomes empty if null.
+ * @return the item selected, or null if the selection was cleared (e.g. vi was null)
*/
- /* package */ void selectSingle(CanvasViewInfo vi) {
+ @Nullable
+ SelectionItem selectSingle(CanvasViewInfo vi) {
+ SelectionItem item = null;
+
// reset alternate selection if any
mAltSelection = null;
@@ -449,13 +454,14 @@ public class SelectionManager implements ISelectionProvider {
if (!mSelections.isEmpty()) {
if (mSelections.size() == 1 && mSelections.getFirst().getViewInfo() == vi) {
// CanvasSelection remains the same, don't touch it.
- return;
+ return mSelections.getFirst();
}
mSelections.clear();
}
if (vi != null) {
- mSelections.add(createSelection(vi));
+ item = createSelection(vi);
+ mSelections.add(item);
if (vi.isInvisible()) {
redoLayout = true;
}
@@ -467,6 +473,8 @@ public class SelectionManager implements ISelectionProvider {
}
redraw();
+
+ return item;
}
/** Returns true if the view hierarchy is showing exploded items. */
@@ -1039,7 +1047,7 @@ public class SelectionManager implements ISelectionProvider {
new ActionContributionItem(a).fill(menu, -1);
a.setEnabled(true);
- a = selectionManager.new SelectAction("Select None", SELECT_NONE);
+ a = selectionManager.new SelectAction("Deselect All", SELECT_NONE);
new ActionContributionItem(a).fill(menu, -1);
a.setEnabled(haveSelection);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElement.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElement.java
index 4feff25..9acc8c2 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElement.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElement.java
@@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.common.api.IDragElement;
+import com.android.ide.common.api.INode;
import com.android.ide.common.api.Rect;
import java.util.ArrayList;
@@ -47,6 +48,7 @@ public class SimpleElement implements IDragElement {
private IDragAttribute[] mCachedAttributes = null;
private IDragElement[] mCachedElements = null;
+ private SelectionItem mSelectionItem;
/**
* Creates a new {@link SimpleElement} with the specified element name.
@@ -141,6 +143,43 @@ public class SimpleElement implements IDragElement {
mElements.add(e);
}
+ @Override
+ public boolean isSame(@NonNull INode node) {
+ if (mSelectionItem != null) {
+ return node == mSelectionItem.getNode();
+ } else {
+ return node.getBounds().equals(mBounds);
+ }
+ }
+
+ void setSelectionItem(@Nullable SelectionItem selectionItem) {
+ mSelectionItem = selectionItem;
+ }
+
+ @Nullable
+ SelectionItem getSelectionItem() {
+ return mSelectionItem;
+ }
+
+ @Nullable
+ static SimpleElement findPrimary(SimpleElement[] elements, SelectionItem primary) {
+ if (elements == null || elements.length == 0) {
+ return null;
+ }
+
+ if (elements.length == 1 || primary == null) {
+ return elements[0];
+ }
+
+ for (SimpleElement element : elements) {
+ if (element.getSelectionItem() == primary) {
+ return element;
+ }
+ }
+
+ return elements[0];
+ }
+
// reader and writer methods
@Override
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintEditAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintEditAction.java
new file mode 100644
index 0000000..bf05ce0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintEditAction.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 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.internal.lint;
+
+import com.android.annotations.NonNull;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DelegatingAction;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.swt.widgets.Event;
+
+/**
+ * Action intended to wrap an existing XML editor action, and then runs lint after
+ * the edit.
+ */
+public class LintEditAction extends DelegatingAction {
+ private final AndroidXmlEditor mEditor;
+
+ /**
+ * Creates a new {@link LintEditAction} associated with the given editor to
+ * wrap the given action
+ *
+ * @param action the action to be wrapped
+ * @param editor the editor associated with the action
+ */
+ public LintEditAction(@NonNull IAction action, @NonNull AndroidXmlEditor editor) {
+ super(action);
+ mEditor = editor;
+ }
+
+ @Override
+ public void runWithEvent(Event event) {
+ super.runWithEvent(event);
+ mEditor.runEditHooks();
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ImportPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ImportPage.java
new file mode 100644
index 0000000..7ccab06
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ImportPage.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2012 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.internal.wizards.newproject;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.tools.lint.detector.api.LintUtils;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.IColorProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkingSet;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** WizardPage for importing Android projects */
+class ImportPage extends WizardPage implements SelectionListener, IStructuredContentProvider,
+ ICheckStateListener, ILabelProvider, IColorProvider {
+ private final NewProjectWizardState mValues;
+ private List<ImportedProject> mProjectPaths;
+ private final IProject[] mExistingProjects;
+
+ private Text mDir;
+ private Button mBrowseButton;
+ private Button mCopyCheckBox;
+ private Button mRefreshButton;
+ private Button mDeselectAllButton;
+ private Button mSelectAllButton;
+ private Table mTable;
+ private CheckboxTableViewer mCheckboxTableViewer;
+ private WorkingSetGroup mWorkingSetGroup;
+
+ ImportPage(NewProjectWizardState values) {
+ super("importPage"); //$NON-NLS-1$
+ mValues = values;
+ setTitle("Import Projects");
+ setDescription("Select a directory to search for existing Android projects");
+ mWorkingSetGroup = new WorkingSetGroup();
+ setWorkingSets(new IWorkingSet[0]);
+
+ // Record all projects such that we can ensure that the project names are unique
+ IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+ mExistingProjects = workspaceRoot.getProjects();
+ }
+
+ public void init(IStructuredSelection selection, IWorkbenchPart activePart) {
+ setWorkingSets(WorkingSetHelper.getSelectedWorkingSet(selection, activePart));
+ }
+
+ @SuppressWarnings("unused") // SWT constructors have side effects and aren't unused
+ @Override
+ public void createControl(Composite parent) {
+ Composite container = new Composite(parent, SWT.NULL);
+ setControl(container);
+ container.setLayout(new GridLayout(3, false));
+
+ Label directoryLabel = new Label(container, SWT.NONE);
+ directoryLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
+ directoryLabel.setText("Root Directory:");
+
+ mDir = new Text(container, SWT.BORDER);
+ mDir.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ mDir.addSelectionListener(this);
+
+ mBrowseButton = new Button(container, SWT.NONE);
+ mBrowseButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
+ mBrowseButton.setText("Browse...");
+ mBrowseButton.addSelectionListener(this);
+
+ Label projectsLabel = new Label(container, SWT.NONE);
+ projectsLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1));
+ projectsLabel.setText("Projects:");
+
+ mCheckboxTableViewer = CheckboxTableViewer.newCheckList(container,
+ SWT.BORDER | SWT.FULL_SELECTION);
+ mTable = mCheckboxTableViewer.getTable();
+ mTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 4));
+ mTable.addSelectionListener(this);
+ mCheckboxTableViewer.setLabelProvider(this);
+ mCheckboxTableViewer.setContentProvider(this);
+ mCheckboxTableViewer.setInput(this);
+ mCheckboxTableViewer.addCheckStateListener(this);
+
+ mSelectAllButton = new Button(container, SWT.NONE);
+ mSelectAllButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
+ mSelectAllButton.setText("Select All");
+ mSelectAllButton.addSelectionListener(this);
+
+ mDeselectAllButton = new Button(container, SWT.NONE);
+ mDeselectAllButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
+ mDeselectAllButton.setText("Deselect All");
+ mDeselectAllButton.addSelectionListener(this);
+
+ mRefreshButton = new Button(container, SWT.NONE);
+ mRefreshButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
+ mRefreshButton.setText("Refresh");
+ mRefreshButton.addSelectionListener(this);
+ new Label(container, SWT.NONE);
+
+ mCopyCheckBox = new Button(container, SWT.CHECK);
+ mCopyCheckBox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1));
+ mCopyCheckBox.setText("Copy projects into workspace");
+ mCopyCheckBox.addSelectionListener(this);
+
+ Composite group = mWorkingSetGroup.createControl(container);
+ group.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 3, 1));
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+ validatePage();
+ }
+
+ private void refresh() {
+ File root = new File(mDir.getText().trim());
+ mProjectPaths = searchForProjects(root);
+ mCheckboxTableViewer.refresh();
+ mCheckboxTableViewer.setAllChecked(true);
+
+ List<ImportedProject> selected = new ArrayList<ImportedProject>();
+ List<ImportedProject> disabled = new ArrayList<ImportedProject>();
+ for (ImportedProject project : mProjectPaths) {
+ String projectName = project.getProjectName();
+ boolean invalid = false;
+ for (IProject existingProject : mExistingProjects) {
+ if (projectName.equals(existingProject.getName())) {
+ invalid = true;
+ break;
+ }
+ }
+ if (invalid) {
+ disabled.add(project);
+ } else {
+ selected.add(project);
+ }
+ }
+
+ mValues.importProjects = selected;
+
+ mCheckboxTableViewer.setGrayedElements(disabled.toArray());
+ mCheckboxTableViewer.setCheckedElements(selected.toArray());
+ mCheckboxTableViewer.refresh();
+ mCheckboxTableViewer.getTable().setFocus();
+ validatePage();
+ }
+
+ private List<ImportedProject> searchForProjects(File dir) {
+ List<ImportedProject> projects = new ArrayList<ImportedProject>();
+ addProjects(dir, projects);
+ return projects;
+ }
+
+ /** Finds all project directories under the given directory */
+ private void addProjects(File dir, List<ImportedProject> projects) {
+ if (dir.isDirectory()) {
+ if (LintUtils.isProjectDir(dir)) {
+ projects.add(new ImportedProject(dir));
+ }
+
+ File[] children = dir.listFiles();
+ if (children != null) {
+ for (File child : children) {
+ addProjects(child, projects);
+ }
+ }
+ }
+ }
+
+ private void validatePage() {
+ IStatus status = null;
+
+ // Validate project name -- unless we're creating a sample, in which case
+ // the user will get a chance to pick the name on the Sample page
+ if (mProjectPaths == null || mProjectPaths.isEmpty()) {
+ status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
+ "Select a directory to search for existing Android projects");
+ } else if (mValues.importProjects == null || mValues.importProjects.isEmpty()) {
+ status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
+ "Select at least one project");
+ } else {
+ for (ImportedProject project : mValues.importProjects) {
+ if (mCheckboxTableViewer.getGrayed(project)) {
+ status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
+ String.format("Cannot import %1$s because the project name is in use",
+ project.getProjectName()));
+ break;
+ }
+ }
+ }
+
+ // -- update UI & enable finish if there's no error
+ setPageComplete(status == null || status.getSeverity() != IStatus.ERROR);
+ if (status != null) {
+ setMessage(status.getMessage(),
+ status.getSeverity() == IStatus.ERROR
+ ? IMessageProvider.ERROR : IMessageProvider.WARNING);
+ } else {
+ setErrorMessage(null);
+ setMessage(null);
+ }
+ }
+
+ /**
+ * Returns the working sets to which the new project should be added.
+ *
+ * @return the selected working sets to which the new project should be added
+ */
+ private IWorkingSet[] getWorkingSets() {
+ return mWorkingSetGroup.getSelectedWorkingSets();
+ }
+
+ /**
+ * Sets the working sets to which the new project should be added.
+ *
+ * @param workingSets the initial selected working sets
+ */
+ private void setWorkingSets(IWorkingSet[] workingSets) {
+ assert workingSets != null;
+ mWorkingSetGroup.setWorkingSets(workingSets);
+ }
+
+ @Override
+ public IWizardPage getNextPage() {
+ // Sync working set data to the value object, since the WorkingSetGroup
+ // doesn't let us add listeners to do this lazily
+ mValues.workingSets = getWorkingSets();
+
+ return super.getNextPage();
+ }
+
+ // ---- Implements SelectionListener ----
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ Object source = e.getSource();
+ if (source == mBrowseButton) {
+ // Choose directory
+ DirectoryDialog dialog = new DirectoryDialog(getShell(), SWT.OPEN);
+ String path = mDir.getText().trim();
+ if (path.length() > 0) {
+ dialog.setFilterPath(path);
+ }
+ String file = dialog.open();
+ if (file != null) {
+ mDir.setText(file);
+ refresh();
+ }
+ } else if (source == mSelectAllButton) {
+ mCheckboxTableViewer.setAllChecked(true);
+ mValues.importProjects = mProjectPaths;
+ } else if (source == mDeselectAllButton) {
+ mCheckboxTableViewer.setAllChecked(false);
+ mValues.importProjects = Collections.emptyList();
+ } else if (source == mRefreshButton || source == mDir) {
+ refresh();
+ } else if (source == mCopyCheckBox) {
+ mValues.copyIntoWorkspace = mCopyCheckBox.getSelection();
+ }
+
+ validatePage();
+ }
+
+ @Override
+ public void widgetDefaultSelected(SelectionEvent e) {
+ }
+
+ // ---- Implements IStructuredContentProvider ----
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+
+ @Override
+ public Object[] getElements(Object inputElement) {
+ return mProjectPaths != null ? mProjectPaths.toArray() : new Object[0];
+ }
+
+ // ---- Implements ICheckStateListener ----
+
+ @Override
+ public void checkStateChanged(CheckStateChangedEvent event) {
+ // Try to disable other elements that conflict with this
+ Object[] checked = mCheckboxTableViewer.getCheckedElements();
+ List<ImportedProject> selected = new ArrayList<ImportedProject>(checked.length);
+ for (Object o : checked) {
+ if (!mCheckboxTableViewer.getGrayed(o)) {
+ selected.add((ImportedProject) o);
+ }
+ }
+ mValues.importProjects = selected;
+ validatePage();
+ }
+
+ // ---- Implements ILabelProvider ----
+
+ @Override
+ public void addListener(ILabelProviderListener listener) {
+ }
+
+ @Override
+ public void removeListener(ILabelProviderListener listener) {
+ }
+
+ @Override
+ public boolean isLabelProperty(Object element, String property) {
+ return false;
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ return null;
+ }
+
+ @Override
+ public String getText(Object element) {
+ ImportedProject file = (ImportedProject) element;
+ return String.format("%1$s (%2$s)", file.getProjectName(), file.getLocation().getPath());
+ }
+
+ // ---- IColorProvider ----
+
+ @Override
+ public Color getForeground(Object element) {
+ Display display = mTable.getDisplay();
+ if (mCheckboxTableViewer.getGrayed(element)) {
+ return display.getSystemColor(SWT.COLOR_DARK_GRAY);
+ }
+
+ return display.getSystemColor(SWT.COLOR_LIST_FOREGROUND);
+ }
+
+ @Override
+ public Color getBackground(Object element) {
+ return mTable.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ImportProjectWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ImportProjectWizard.java
new file mode 100644
index 0000000..9aaf574
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ImportProjectWizard.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 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.internal.wizards.newproject;
+
+import static com.android.sdklib.SdkConstants.FN_PROJECT_PROGUARD_FILE;
+import static com.android.sdklib.SdkConstants.OS_SDK_TOOLS_LIB_FOLDER;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState.Mode;
+
+import org.eclipse.jdt.ui.actions.OpenJavaPerspectiveAction;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.IWorkbench;
+
+import java.io.File;
+
+
+/**
+ * An "Import Android Project" wizard.
+ */
+public class ImportProjectWizard extends Wizard implements INewWizard {
+ private static final String PROJECT_LOGO_LARGE = "icons/android-64.png"; //$NON-NLS-1$
+
+ private NewProjectWizardState mValues;
+ private ImportPage mImportPage;
+
+ /** Constructs a new wizard default project wizard */
+ public ImportProjectWizard() {
+ }
+
+ @Override
+ public void addPages() {
+ mValues = new NewProjectWizardState(Mode.ANY);
+ mImportPage = new ImportPage(mValues);
+ addPage(mImportPage);
+ }
+
+ @Override
+ public void init(IWorkbench workbench, IStructuredSelection selection) {
+ setHelpAvailable(false); // TODO have help
+ ImageDescriptor desc = AdtPlugin.getImageDescriptor(PROJECT_LOGO_LARGE);
+ setDefaultPageImageDescriptor(desc);
+ }
+
+ @Override
+ public boolean performFinish() {
+ File file = new File(AdtPlugin.getOsSdkFolder(), OS_SDK_TOOLS_LIB_FOLDER + File.separator
+ + FN_PROJECT_PROGUARD_FILE);
+ if (!file.exists()) {
+ AdtPlugin.displayError("Tools Out of Date?",
+ String.format("It looks like you do not have the latest version of the "
+ + "SDK Tools installed. Make sure you update via the SDK Manager "
+ + "first. (Could not find %1$s)", file.getPath()));
+ return false;
+ }
+
+ NewProjectCreator creator = new NewProjectCreator(mValues, getContainer());
+ if (!(creator.createAndroidProjects())) {
+ return false;
+ }
+
+ // Open the default Java Perspective
+ OpenJavaPerspectiveAction action = new OpenJavaPerspectiveAction();
+ action.run();
+ return true;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ImportedProject.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ImportedProject.java
new file mode 100644
index 0000000..dc2f1b5
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ImportedProject.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2012 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.internal.wizards.newproject;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.io.FolderWrapper;
+import com.android.sdklib.AndroidVersion;
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.internal.project.ProjectProperties;
+import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
+import com.android.sdklib.xml.AndroidManifestParser;
+import com.android.sdklib.xml.ManifestData;
+import com.android.sdklib.xml.ManifestData.Activity;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.xml.sax.SAXException;
+
+import java.io.File;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** An Android project to be imported */
+class ImportedProject {
+ private final File mLocation;
+ private String mActivityName;
+ private ManifestData mManifest;
+ private String mProjectName;
+
+ ImportedProject(File location) {
+ super();
+ mLocation = location;
+ }
+
+ File getLocation() {
+ return mLocation;
+ }
+
+ @Nullable
+ ManifestData getManifest() {
+ if (mManifest == null) {
+ try {
+ mManifest = AndroidManifestParser.parse(new FolderWrapper(mLocation));
+ } catch (SAXException e) {
+ // Some sort of error in the manifest file: report to the user in a better way?
+ AdtPlugin.log(e, null);
+ return null;
+ } catch (Exception e) {
+ AdtPlugin.log(e, null);
+ return null;
+ }
+ }
+
+ return mManifest;
+ }
+
+ @Nullable
+ public String getActivityName() {
+ if (mActivityName == null) {
+ // Compute the project name and the package name from the manifest
+ ManifestData manifest = getManifest();
+ if (manifest != null) {
+ if (manifest.getLauncherActivity() != null) {
+ mActivityName = manifest.getLauncherActivity().getName();
+ }
+ if (mActivityName == null || mActivityName.isEmpty()) {
+ Activity[] activities = manifest.getActivities();
+ for (Activity activity : activities) {
+ mActivityName = activity.getName();
+ if (mActivityName != null && !mActivityName.isEmpty()) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return mActivityName;
+ }
+
+ @NonNull
+ public String getProjectName() {
+ if (mProjectName == null) {
+ String activityName = getActivityName();
+ if (activityName == null || activityName.isEmpty()) {
+ // I could also look at the build files, say build.xml from ant, and
+ // try to glean the project name from there
+ mProjectName = mLocation.getName();
+ } else {
+ // Try to derive it from the activity name:
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ IStatus nameStatus = workspace.validateName(activityName, IResource.PROJECT);
+ if (nameStatus.isOK()) {
+ mProjectName = activityName;
+ } else {
+ // Try to derive it by escaping characters
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0, n = activityName.length(); i < n; i++) {
+ char c = activityName.charAt(i);
+ if (c != IPath.DEVICE_SEPARATOR && c != IPath.SEPARATOR && c != '\\') {
+ sb.append(c);
+ }
+ }
+ if (sb.length() == 0) {
+ mProjectName = mLocation.getName();
+ } else {
+ mProjectName = sb.toString();
+ }
+ }
+ }
+ }
+
+ return mProjectName;
+ }
+
+ public IAndroidTarget getTarget() {
+ // Pick a target:
+ // First try to find the one requested by project.properties
+ IAndroidTarget[] targets = Sdk.getCurrent().getTargets();
+ ProjectProperties properties = ProjectProperties.load(mLocation.getPath(),
+ PropertyType.PROJECT);
+ if (properties != null) {
+ String targetProperty = properties.getProperty(ProjectProperties.PROPERTY_TARGET);
+ if (targetProperty != null) {
+ Matcher m = Pattern.compile("android-(.+)").matcher( //$NON-NLS-1$
+ targetProperty.trim());
+ if (m.matches()) {
+ String targetName = m.group(1);
+ int targetLevel;
+ try {
+ targetLevel = Integer.parseInt(targetName);
+ } catch (NumberFormatException nufe) {
+ // pass
+ targetLevel = -1;
+ }
+ for (IAndroidTarget t : targets) {
+ AndroidVersion version = t.getVersion();
+ if (version.isPreview() && targetName.equals(version.getCodename())) {
+ return t;
+ } else if (targetLevel == version.getApiLevel()) {
+ return t;
+ }
+ }
+ if (targetLevel > 0) {
+ // If not found, pick the closest one that is higher than the
+ // api level
+ IAndroidTarget target = targets[targets.length - 1];
+ int targetDelta = target.getVersion().getApiLevel() - targetLevel;
+ for (IAndroidTarget t : targets) {
+ int newDelta = t.getVersion().getApiLevel() - targetLevel;
+ if (newDelta >= 0 && newDelta < targetDelta) {
+ targetDelta = newDelta;
+ target = t;
+ }
+ }
+
+ return target;
+ }
+ }
+ }
+ }
+
+ // If not found, pick the closest one to the one requested by the
+ // project (in project.properties) that is still >= the minSdk version
+ IAndroidTarget target = targets[targets.length - 1];
+ ManifestData manifest = getManifest();
+ if (manifest != null) {
+ int minSdkLevel = manifest.getMinSdkVersion();
+ int targetDelta = target.getVersion().getApiLevel() - minSdkLevel;
+ for (IAndroidTarget t : targets) {
+ int newDelta = t.getVersion().getApiLevel() - minSdkLevel;
+ if (newDelta >= 0 && newDelta < targetDelta) {
+ targetDelta = newDelta;
+ target = t;
+ }
+ }
+ }
+
+ return target;
+ }
+} \ No newline at end of file
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreator.java
index 2dc7c71..41c88d7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreator.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreator.java
@@ -34,6 +34,7 @@ import com.android.io.StreamException;
import com.android.resources.Density;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
+import com.android.sdklib.xml.ManifestData;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
@@ -78,7 +79,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -102,6 +105,7 @@ public class NewProjectCreator {
private static final String PARAM_STRING_CONTENT = "STRING_CONTENT"; //$NON-NLS-1$
private static final String PARAM_IS_NEW_PROJECT = "IS_NEW_PROJECT"; //$NON-NLS-1$
private static final String PARAM_SAMPLE_LOCATION = "SAMPLE_LOCATION"; //$NON-NLS-1$
+ private static final String PARAM_SOURCE = "SOURCE"; //$NON-NLS-1$
private static final String PARAM_SRC_FOLDER = "SRC_FOLDER"; //$NON-NLS-1$
private static final String PARAM_SDK_TARGET = "SDK_TARGET"; //$NON-NLS-1$
private static final String PARAM_MIN_SDK_VERSION = "MIN_SDK_VERSION"; //$NON-NLS-1$
@@ -197,7 +201,6 @@ public class NewProjectCreator {
private final NewProjectWizardState mValues;
private final IRunnableContext mRunnableContext;
- private Object mPackageName;
public NewProjectCreator(NewProjectWizardState values, IRunnableContext runnableContext) {
mValues = values;
@@ -268,6 +271,10 @@ public class NewProjectCreator {
* @return True if the project could be created.
*/
public boolean createAndroidProjects() {
+ if (mValues.importProjects != null && !mValues.importProjects.isEmpty()) {
+ return importProjects();
+ }
+
final ProjectInfo mainData = collectMainPageInfo();
final ProjectInfo testData = collectTestPageInfo();
@@ -275,7 +282,77 @@ public class NewProjectCreator {
WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
@Override
protected void execute(IProgressMonitor monitor) throws InvocationTargetException {
- createProjectAsync(monitor, mainData, testData);
+ createProjectAsync(monitor, mainData, testData, null);
+ }
+ };
+
+ // Run the operation in a different thread
+ runAsyncOperation(op);
+ return true;
+ }
+
+ /**
+ * Imports a list of projects
+ */
+ private boolean importProjects() {
+ assert mValues.importProjects != null && !mValues.importProjects.isEmpty();
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+
+ final List<ProjectInfo> projectData = new ArrayList<ProjectInfo>();
+ for (ImportedProject p : mValues.importProjects) {
+
+ // Compute the project name and the package name from the manifest
+ ManifestData manifest = p.getManifest();
+ if (manifest == null) {
+ continue;
+ }
+ String packageName = manifest.getPackage();
+ String projectName = p.getProjectName();
+ String minSdk = manifest.getMinSdkVersionString();
+
+ final IProject project = workspace.getRoot().getProject(projectName);
+ final IProjectDescription description =
+ workspace.newProjectDescription(project.getName());
+
+ final Map<String, Object> parameters = new HashMap<String, Object>();
+ parameters.put(PARAM_PROJECT, projectName);
+ parameters.put(PARAM_PACKAGE, packageName);
+ parameters.put(PARAM_SDK_TOOLS_DIR, AdtPlugin.getOsSdkToolsFolder());
+ parameters.put(PARAM_IS_NEW_PROJECT, Boolean.FALSE);
+ parameters.put(PARAM_SRC_FOLDER, SdkConstants.FD_SOURCES);
+
+ parameters.put(PARAM_SDK_TARGET, p.getTarget());
+
+ // TODO: Find out if these end up getting used in the import-path through the code!
+ parameters.put(PARAM_MIN_SDK_VERSION, minSdk);
+ parameters.put(PARAM_APPLICATION, STRING_RSRC_PREFIX + STRING_APP_NAME);
+ final HashMap<String, String> dictionary = new HashMap<String, String>();
+ dictionary.put(STRING_APP_NAME, mValues.applicationName);
+
+ if (mValues.copyIntoWorkspace) {
+ parameters.put(PARAM_SOURCE, p.getLocation());
+
+ // TODO: Make sure it isn't *already* in the workspace!
+ //IPath defaultLocation = Platform.getLocation();
+ //if ((!mValues.useDefaultLocation || mValues.useExisting)
+ // && !defaultLocation.isPrefixOf(path)) {
+ //IPath workspaceLocation = Platform.getLocation().append(projectName);
+ //description.setLocation(workspaceLocation);
+ // DON'T SET THE LOCATION: It's IMPLIED and in fact it will generate
+ // an error if you set it!
+ } else {
+ // Create in place
+ description.setLocation(new Path(p.getLocation().getPath()));
+ }
+
+ projectData.add(new ProjectInfo(project, description, parameters, dictionary));
+ }
+
+ // Create a monitored operation to create the actual project
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor) throws InvocationTargetException {
+ createProjectAsync(monitor, null, null, projectData);
}
};
@@ -299,12 +376,9 @@ public class NewProjectCreator {
final IProject project = workspace.getRoot().getProject(mValues.projectName);
final IProjectDescription description = workspace.newProjectDescription(project.getName());
- // keep some variables to make them available once the wizard closes
- mPackageName = mValues.packageName;
-
final Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put(PARAM_PROJECT, mValues.projectName);
- parameters.put(PARAM_PACKAGE, mPackageName);
+ parameters.put(PARAM_PACKAGE, mValues.packageName);
parameters.put(PARAM_APPLICATION, STRING_RSRC_PREFIX + STRING_APP_NAME);
parameters.put(PARAM_SDK_TOOLS_DIR, AdtPlugin.getOsSdkToolsFolder());
parameters.put(PARAM_IS_NEW_PROJECT, mValues.mode == Mode.ANY && !mValues.useExisting);
@@ -468,7 +542,8 @@ public class NewProjectCreator {
*/
private void createProjectAsync(IProgressMonitor monitor,
ProjectInfo mainData,
- ProjectInfo testData)
+ ProjectInfo testData,
+ List<ProjectInfo> importData)
throws InvocationTargetException {
monitor.beginTask("Create Android Project", 100);
try {
@@ -485,23 +560,12 @@ public class NewProjectCreator {
if (mainProject != null) {
final IJavaProject javaProject = JavaCore.create(mainProject);
- Display.getDefault().syncExec(new Runnable() {
-
- @Override
- public void run() {
- IWorkingSet[] workingSets = mValues.workingSets;
- if (workingSets.length > 0 && javaProject != null
- && javaProject.exists()) {
- PlatformUI.getWorkbench().getWorkingSetManager()
- .addToWorkingSets(javaProject, workingSets);
- }
- }
- });
+ Display.getDefault().syncExec(new WorksetAdder(javaProject,
+ mValues.workingSets));
}
}
if (testData != null) {
-
Map<String, Object> parameters = testData.getParameters();
if (parameters.containsKey(PARAM_TARGET_MAIN) && mainProject != null) {
parameters.put(PARAM_REFERENCE_PROJECT, mainProject);
@@ -516,18 +580,45 @@ public class NewProjectCreator {
null);
if (testProject != null) {
final IJavaProject javaProject = JavaCore.create(testProject);
- Display.getDefault().syncExec(new Runnable() {
-
- @Override
- public void run() {
- IWorkingSet[] workingSets = mValues.workingSets;
- if (workingSets.length > 0 && javaProject != null
- && javaProject.exists()) {
- PlatformUI.getWorkbench().getWorkingSetManager()
- .addToWorkingSets(javaProject, workingSets);
+ Display.getDefault().syncExec(new WorksetAdder(javaProject,
+ mValues.workingSets));
+ }
+ }
+
+ if (importData != null) {
+ for (final ProjectInfo data : importData) {
+ ProjectPopulator projectPopulator = null;
+ if (mValues.copyIntoWorkspace) {
+ projectPopulator = new ProjectPopulator() {
+ @Override
+ public void populate(IProject project) {
+ // Copy
+ IFileSystem fileSystem = EFS.getLocalFileSystem();
+ File source = (File) data.getParameters().get(PARAM_SOURCE);
+ IFileStore sourceDir = new ReadWriteFileStore(
+ fileSystem.getStore(source.toURI()));
+ IFileStore destDir = new ReadWriteFileStore(
+ fileSystem.getStore(AdtUtils.getAbsolutePath(project)));
+ try {
+ sourceDir.copy(destDir, EFS.OVERWRITE, null);
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ }
}
- }
- });
+ };
+ }
+ IProject project = createEclipseProject(
+ new SubProgressMonitor(monitor, 50),
+ data.getProject(),
+ data.getDescription(),
+ data.getParameters(),
+ data.getDictionary(),
+ projectPopulator);
+ if (project != null) {
+ final IJavaProject javaProject = JavaCore.create(project);
+ Display.getDefault().syncExec(new WorksetAdder(javaProject,
+ mValues.workingSets));
+ }
}
}
} catch (CoreException e) {
@@ -541,6 +632,10 @@ public class NewProjectCreator {
}
}
+ public interface ProjectPopulator {
+ public void populate(IProject project);
+ }
+
/**
* Creates the actual project, sets its nature and adds the required folders
* and files to it. This is run asynchronously in a different thread.
@@ -558,7 +653,7 @@ public class NewProjectCreator {
IProjectDescription description,
Map<String, Object> parameters,
Map<String, String> dictionary,
- Runnable projectPopulator)
+ ProjectPopulator projectPopulator)
throws CoreException, IOException, StreamException {
// get the project target
@@ -590,7 +685,7 @@ public class NewProjectCreator {
}
if (projectPopulator != null) {
- projectPopulator.run();
+ projectPopulator.populate(project);
}
// Setup class path: mark folders as source folders
@@ -672,7 +767,7 @@ public class NewProjectCreator {
IProgressMonitor monitor,
IProject project,
IAndroidTarget target,
- Runnable projectPopulator)
+ ProjectPopulator projectPopulator)
throws CoreException, IOException, StreamException {
NewProjectCreator creator = new NewProjectCreator(null, null);
@@ -1120,7 +1215,7 @@ public class NewProjectCreator {
* @throws FileNotFoundException
* @throws CoreException
*/
- private void addLocalFile(IProject project, File source, String destName,
+ public static void addLocalFile(IProject project, File source, String destName,
IProgressMonitor monitor) throws FileNotFoundException, CoreException {
IFile dest = project.getFile(destName);
if (dest.exists() == false) {
@@ -1257,4 +1352,23 @@ public class NewProjectCreator {
return str;
}
+
+ private static class WorksetAdder implements Runnable {
+ private final IJavaProject mProject;
+ private final IWorkingSet[] mWorkingSets;
+
+ private WorksetAdder(IJavaProject project, IWorkingSet[] workingSets) {
+ mProject = project;
+ mWorkingSets = workingSets;
+ }
+
+ @Override
+ public void run() {
+ if (mWorkingSets.length > 0 && mProject != null
+ && mProject.exists()) {
+ PlatformUI.getWorkbench().getWorkingSetManager()
+ .addToWorkingSets(mProject, mWorkingSets);
+ }
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizardState.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizardState.java
index 264f09d..1c1fb3a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizardState.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizardState.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.adt.internal.wizards.newproject;
+import com.android.annotations.Nullable;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
@@ -85,7 +86,8 @@ public class NewProjectWizardState {
public boolean packageNameModifiedByUser;
/** True if a new activity should be created */
- public boolean createActivity = true;
+ public boolean createActivity;
+
/** The name of the new activity to be created */
public String activityName;
/** True if the activity name has been manually edited by the user */
@@ -163,6 +165,18 @@ public class NewProjectWizardState {
public String testTargetPackageName;
/**
+ * Copy project into workspace? This flag only applies when importing
+ * projects (creating projects from existing source)
+ */
+ public boolean copyIntoWorkspace;
+
+ /**
+ * List of projects to be imported. Null if not importing projects.
+ */
+ @Nullable
+ public List<ImportedProject> importProjects;
+
+ /**
* Creates a new {@link NewProjectWizardState}
*
* @param mode the mode to run the wizard in
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ProjectNamePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ProjectNamePage.java
index 7592c58..7d3114b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ProjectNamePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/ProjectNamePage.java
@@ -83,7 +83,6 @@ public class ProjectNamePage extends WizardPage implements SelectionListener, Mo
private Text mProjectNameText;
private Text mLocationText;
private Button mCreateSampleRadioButton;
- private Button mCreateExistingRadioButton;
private Button mCreateNewButton;
private Button mUseDefaultCheckBox;
private Button mBrowseButton;
@@ -133,20 +132,14 @@ public class ProjectNamePage extends WizardPage implements SelectionListener, Mo
mProjectNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
mProjectNameText.addModifyListener(this);
- mCreateNewButton = new Button(container, SWT.RADIO);
- mCreateNewButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1));
- mCreateNewButton.setText("Create new project in workspace");
- mCreateNewButton.addSelectionListener(this);
-
- mCreateExistingRadioButton = new Button(container, SWT.RADIO);
- mCreateExistingRadioButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false,
- 3, 1));
- mCreateExistingRadioButton.setText("Create project from existing source");
- mCreateExistingRadioButton.addSelectionListener(this);
-
- // TBD: Should we hide this completely, and make samples something you only invoke
- // from the "New Sample Project" wizard?
if (mValues.mode != Mode.TEST) {
+ mCreateNewButton = new Button(container, SWT.RADIO);
+ mCreateNewButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1));
+ mCreateNewButton.setText("Create new project in workspace");
+ mCreateNewButton.addSelectionListener(this);
+
+ // TBD: Should we hide this completely, and make samples something you only invoke
+ // from the "New Sample Project" wizard?
mCreateSampleRadioButton = new Button(container, SWT.RADIO);
mCreateSampleRadioButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false,
3, 1));
@@ -192,8 +185,8 @@ public class ProjectNamePage extends WizardPage implements SelectionListener, Mo
}
if (mValues.mode == Mode.ANY || mValues.mode == Mode.TEST) {
if (mValues.useExisting) {
- mCreateExistingRadioButton.setSelection(true);
- } else {
+ assert false; // This is now handled by the separate import wizard
+ } else if (mCreateNewButton != null) {
mCreateNewButton.setSelection(true);
}
} else if (mValues.mode == Mode.SAMPLE) {
@@ -271,7 +264,8 @@ public class ProjectNamePage extends WizardPage implements SelectionListener, Mo
Object source = e.getSource();
- if (source == mCreateNewButton && mCreateNewButton.getSelection()) {
+ if (source == mCreateNewButton && mCreateNewButton != null
+ && mCreateNewButton.getSelection()) {
mValues.useExisting = false;
if (mValues.mode == Mode.SAMPLE) {
// Only reset the mode if we're toggling from sample back to create new
@@ -281,24 +275,6 @@ public class ProjectNamePage extends WizardPage implements SelectionListener, Mo
mValues.mode = Mode.ANY;
}
updateLocationState();
- } else if (source == mCreateExistingRadioButton
- && mCreateExistingRadioButton.getSelection()) {
- mValues.useExisting = true;
- if (mValues.mode == Mode.SAMPLE) {
- mValues.mode = Mode.ANY;
- }
- if (!mValues.activityNameModifiedByUser) {
- mValues.createActivity = false;
- }
- try {
- mIgnore = true;
- mValues.useDefaultLocation = false;
- mUseDefaultCheckBox.setSelection(mValues.useDefaultLocation);
- } finally {
- mIgnore = false;
- }
-
- updateLocationState();
} else if (source == mCreateSampleRadioButton && mCreateSampleRadioButton.getSelection()) {
mValues.useExisting = true;
mValues.useDefaultLocation = true;
@@ -378,7 +354,7 @@ public class ProjectNamePage extends WizardPage implements SelectionListener, Mo
*
* @return the selected working sets to which the new project should be added
*/
- public IWorkingSet[] getWorkingSets() {
+ private IWorkingSet[] getWorkingSets() {
return mWorkingSetGroup.getSelectedWorkingSets();
}
@@ -387,7 +363,7 @@ public class ProjectNamePage extends WizardPage implements SelectionListener, Mo
*
* @param workingSets the initial selected working sets
*/
- public void setWorkingSets(IWorkingSet[] workingSets) {
+ private void setWorkingSets(IWorkingSet[] workingSets) {
assert workingSets != null;
mWorkingSetGroup.setWorkingSets(workingSets);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewActivityWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewActivityWizard.java
new file mode 100644
index 0000000..a2e3518
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewActivityWizard.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2012 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.internal.wizards.templates;
+
+import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_MIN_API;
+import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_MIN_API_LEVEL;
+import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_PACKAGE_NAME;
+import static com.android.ide.eclipse.adt.internal.wizards.templates.NewProjectWizard.ATTR_TARGET_API;
+import static org.eclipse.core.resources.IResource.DEPTH_INFINITE;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AdtUtils;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.IWorkbench;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Wizard for creating new activities. This is a hybrid between a New Project
+ * Wizard and a New Template Wizard: it has the "Activity selector" page from
+ * the New Project Wizard, which is used to dynamically select a wizard for the
+ * second page, but beyond that it runs the normal template wizard when it comes
+ * time to create the template.
+ */
+public class NewActivityWizard extends Wizard implements INewWizard {
+ private static final String PROJECT_LOGO_LARGE = "android-64"; //$NON-NLS-1$
+
+ private IWorkbench mWorkbench;
+ private UpdateToolsPage mUpdatePage;
+ private NewProjectPage mMainPage;
+ private NewTemplatePage mTemplatePage;
+ private ActivityPage mActivityPage;
+ private NewProjectWizardState mValues;
+ private NewTemplateWizardState mActivityValues;
+
+ /** Creates a new {@link NewActivityWizard} */
+ public NewActivityWizard() {
+ }
+
+ @Override
+ public void init(IWorkbench workbench, IStructuredSelection selection) {
+ mWorkbench = workbench;
+
+ setWindowTitle("New Activity");
+ setHelpAvailable(false);
+ ImageDescriptor desc = IconFactory.getInstance().getImageDescriptor(PROJECT_LOGO_LARGE);
+ setDefaultPageImageDescriptor(desc);
+
+ if (!UpdateToolsPage.isUpToDate()) {
+ mUpdatePage = new UpdateToolsPage();
+ }
+
+ mValues = new NewProjectWizardState();
+ mActivityPage = new ActivityPage(mValues);
+
+ mActivityValues = mValues.activityValues;
+ List<IProject> projects = AdtUtils.getSelectedProjects(selection);
+ if (projects.size() == 1) {
+ mActivityValues.project = projects.get(0);
+ }
+ }
+
+ @Override
+ public void addPages() {
+ if (mUpdatePage != null) {
+ addPage(mUpdatePage);
+ }
+
+ addPage(mActivityPage);
+ }
+
+ @Override
+ public IWizardPage getStartingPage() {
+ if (mUpdatePage != null && mUpdatePage.isPageComplete()) {
+ return mMainPage;
+ }
+ return super.getStartingPage();
+ }
+
+ @Override
+ public IWizardPage getNextPage(IWizardPage page) {
+ if (page == mActivityPage) {
+ if (mTemplatePage == null) {
+ Set<String> hidden = mActivityValues.hidden;
+ hidden.add(ATTR_PACKAGE_NAME);
+ hidden.add(ATTR_MIN_API);
+ hidden.add(ATTR_MIN_API_LEVEL);
+ hidden.add(ATTR_TARGET_API);
+
+ mTemplatePage = new NewTemplatePage(mActivityValues, true);
+ addPage(mTemplatePage);
+ }
+ return mTemplatePage;
+ }
+
+ return super.getNextPage(page);
+ }
+
+ @Override
+ public boolean canFinish() {
+ // Deal with lazy creation of some pages: these may not be in the page-list yet
+ // since they are constructed lazily, so consider that option here.
+ if (mTemplatePage == null || !mTemplatePage.isPageComplete()) {
+ return false;
+ }
+
+ return super.canFinish();
+ }
+
+ @Override
+ public boolean performFinish() {
+ try {
+ Shell shell = getShell();
+ if (shell != null) {
+ shell.setVisible(false);
+ }
+ IProject project = mActivityValues.project;
+ File outputPath = AdtUtils.getAbsolutePath(project).toFile();
+ assert mValues.createActivity;
+ NewTemplateWizardState activityValues = mValues.activityValues;
+ Map<String, Object> parameters = activityValues.parameters;
+ ManifestInfo manifest = ManifestInfo.get(project);
+ parameters.put(ATTR_PACKAGE_NAME, manifest.getPackage());
+ parameters.put(ATTR_MIN_API, manifest.getMinSdkVersion());
+ parameters.put(ATTR_MIN_API_LEVEL, manifest.getMinSdkName());
+ parameters.put(ATTR_TARGET_API, manifest.getTargetSdkVersion());
+ TemplateHandler activityTemplate = activityValues.getTemplateHandler();
+ activityTemplate.setBackupMergedFiles(false);
+ activityTemplate.render(outputPath, parameters);
+ List<String> filesToOpen = activityTemplate.getFilesToOpen();
+
+ try {
+ project.refreshLocal(DEPTH_INFINITE, new NullProgressMonitor());
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ }
+
+ // Open the primary file/files
+ NewTemplateWizard.openFiles(project, filesToOpen, mWorkbench);
+
+ return true;
+ } catch (Exception ioe) {
+ AdtPlugin.log(ioe, null);
+ return false;
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java
index 869fc2f..b6993c7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectPage.java
@@ -18,7 +18,6 @@ package com.android.ide.eclipse.adt.internal.wizards.templates;
import static com.android.ide.eclipse.adt.AdtUtils.extractClassName;
import static com.android.ide.eclipse.adt.internal.wizards.templates.NewTemplatePage.WIZARD_PAGE_WIDTH;
-import static com.android.tools.lint.detector.api.LintUtils.assertionsEnabled;
import com.android.annotations.Nullable;
import com.android.ide.eclipse.adt.AdtPlugin;
@@ -309,11 +308,11 @@ public class NewProjectPage extends WizardPage
super.setVisible(visible);
// DURING DEVELOPMENT ONLY
- if (assertionsEnabled()) {
- String uniqueProjectName = AdtUtils.getUniqueProjectName("Test", "");
- mProjectText.setText(uniqueProjectName);
- mPackageText.setText("test.pkg");
- }
+ //if (assertionsEnabled()) {
+ // String uniqueProjectName = AdtUtils.getUniqueProjectName("Test", "");
+ // mProjectText.setText(uniqueProjectName);
+ // mPackageText.setText("test.pkg");
+ //}
validatePage();
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java
index 5e359a4..1de4b2c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewProjectWizard.java
@@ -25,7 +25,9 @@ import com.android.ide.eclipse.adt.internal.assetstudio.ConfigureAssetSetPage;
import com.android.ide.eclipse.adt.internal.assetstudio.CreateAssetSetWizardState;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectCreator;
+import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectCreator.ProjectPopulator;
import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard;
+import com.android.sdklib.SdkConstants;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
@@ -228,9 +230,25 @@ public class NewProjectWizard extends Wizard implements INewWizard {
// of the merged files (such as the manifest file) in that case.
template.setBackupMergedFiles(false);
- Runnable projectPopulator = new Runnable() {
+ ProjectPopulator projectPopulator = new ProjectPopulator() {
@Override
- public void run() {
+ public void populate(IProject project) {
+ // Copy in the proguard file; templates don't provide this one.
+ // add the default proguard config
+ File libFolder = new File(AdtPlugin.getOsSdkToolsFolder(),
+ SdkConstants.FD_LIB);
+ try {
+ assert project == newProject;
+ NewProjectCreator.addLocalFile(project,
+ new File(libFolder, SdkConstants.FN_PROJECT_PROGUARD_FILE),
+ // Write ProGuard config files with the extension .pro which
+ // is what is used in the ProGuard documentation and samples
+ SdkConstants.FN_PROJECT_PROGUARD_FILE,
+ new NullProgressMonitor());
+ } catch (Exception e) {
+ AdtPlugin.log(e, null);
+ }
+
// Generate basic output skeleton
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put(ATTR_PACKAGE_NAME, mValues.packageName);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java
index 143db78..28f5ca6 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizard.java
@@ -50,11 +50,9 @@ import java.util.Set;
* Template wizard which creates parameterized templates
*/
public class NewTemplateWizard extends Wizard implements INewWizard {
- /** Template name and location under /templates in the plugin */
+ /** Template name and location under $sdk/templates for the default activity */
static final String BLANK_ACTIVITY = "activities/BlankActivity"; //$NON-NLS-1$
- /** Template name and location under /templates in the plugin */
- static final String MASTER_DETAIL_FLOW = "activities/MasterDetailFlow"; //$NON-NLS-1$
- /** Template name and location under /templates in the plugin */
+ /** Template name and location under $sdk/templates for the custom view template */
static final String CUSTOM_VIEW = "other/CustomView"; //$NON-NLS-1$
private static final String PROJECT_LOGO_LARGE = "android-64"; //$NON-NLS-1$
@@ -209,42 +207,6 @@ public class NewTemplateWizard extends Wizard implements INewWizard {
}
/**
- * Specific New Master Detail Flow wizard
- */
- public static class MasterDetailWizard extends NewTemplateWizard {
- /** Creates a new {@link MasterDetailWizard} */
- public MasterDetailWizard() {
- super(MASTER_DETAIL_FLOW);
- }
-
- @Override
- public void init(IWorkbench workbench, IStructuredSelection selection) {
- super.init(workbench, selection);
- setWindowTitle("New Master Detail Flow");
- super.mMainPage.setTitle("New Master Detail Flow");
- super.mMainPage.setDescription("Creates a new Master Detail Flow");
- }
- }
-
- /**
- * Specific New Blank Activity wizard
- */
- public static class NewActivityWizard extends NewTemplateWizard {
- /** Creates a new {@link NewActivityWizard} */
- public NewActivityWizard() {
- super(BLANK_ACTIVITY);
- }
-
- @Override
- public void init(IWorkbench workbench, IStructuredSelection selection) {
- super.init(workbench, selection);
- setWindowTitle("New Blank Activity");
- super.mMainPage.setTitle("New Blank Activity");
- super.mMainPage.setDescription("Creates a new blank activity");
- }
- }
-
- /**
* Specific New Custom View wizard
*/
public static class NewCustomViewWizard extends NewTemplateWizard {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizardState.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizardState.java
index 1fb73d8..aaf1a6a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizardState.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/NewTemplateWizardState.java
@@ -89,6 +89,4 @@ public class NewTemplateWizardState {
File getTemplateLocation() {
return mTemplateLocation;
}
-
-
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java
index f22a742..ee6115f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/templates/TemplateHandler.java
@@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.internal.wizards.templates;
import static com.android.ide.eclipse.adt.AdtConstants.DOT_FTL;
import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML;
+import static com.android.sdklib.SdkConstants.FD_EXTRAS;
import static com.android.sdklib.SdkConstants.FD_TEMPLATES;
import static com.android.sdklib.SdkConstants.FD_TOOLS;
@@ -277,7 +278,20 @@ class TemplateHandler {
public static File getTemplateRootFolder() {
String location = AdtPrefs.getPrefs().getOsSdkFolder();
if (location != null) {
- File folder = new File(location, FD_TOOLS + File.separator + FD_TEMPLATES);
+ File folder = new File(location, FD_TOOLS + File.separator + FD_TEMPLATES);
+ if (folder.isDirectory()) {
+ return folder;
+ }
+ }
+
+ return null;
+ }
+
+ @Nullable
+ public static File getExtraTemplateRootFolder() {
+ String location = AdtPrefs.getPrefs().getOsSdkFolder();
+ if (location != null) {
+ File folder = new File(location, FD_EXTRAS + File.separator + FD_TEMPLATES);
if (folder.isDirectory()) {
return folder;
}
@@ -300,7 +314,6 @@ class TemplateHandler {
}
return null;
-
}
@Nullable
@@ -941,6 +954,17 @@ class TemplateHandler {
}
}
+ // Add in templates from extras/ as well.
+ root = getExtraTemplateRootFolder();
+ if (root != null) {
+ File[] files = new File(root, folder).listFiles();
+ if (files != null) {
+ for (File file : files) {
+ templates.add(file);
+ }
+ }
+ }
+
return templates;
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
index fc99764..db69ffe 100644
--- a/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
+++ b/eclipse/plugins/com.android.ide.eclipse.gldebugger/src/com/android/ide/eclipse/gltrace/editors/GLFunctionTraceViewer.java
@@ -778,7 +778,9 @@ public class GLFunctionTraceViewer extends EditorPart implements ISelectionProvi
return;
}
- mFindDialog = new FindDialog(Display.getDefault().getActiveShell(), mFindTarget);
+ mFindDialog = new FindDialog(Display.getDefault().getActiveShell(),
+ mFindTarget,
+ FindDialog.FIND_NEXT_ID);
mFindDialog.open(); // blocks until find dialog is closed
mFindDialog = null;
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestDragElement.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestDragElement.java
index eb0a432..5b55532 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestDragElement.java
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestDragElement.java
@@ -21,6 +21,7 @@ import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.common.api.IDragElement;
+import com.android.ide.common.api.INode;
import com.android.ide.common.api.Rect;
import java.util.ArrayList;
@@ -150,5 +151,8 @@ public class TestDragElement implements IDragElement {
+ mRect + "]";
}
-
+ @Override
+ public boolean isSame(INode node) {
+ return node.getBounds().equals(getBounds());
+ }
}
diff --git a/files/ant/build.xml b/files/ant/build.xml
index 542943e..c3073fe 100644
--- a/files/ant/build.xml
+++ b/files/ant/build.xml
@@ -539,7 +539,7 @@
libraryPackagesOut="project.library.packages"
libraryManifestFilePathOut="project.library.manifest.file.path"
libraryResFolderPathOut="project.library.res.folder.path"
- libraryBinFolderPathOut="project.library.bin.folder.path"
+ libraryBinAidlFolderPathOut="project.library.bin.aidl.folder.path"
libraryNativeFolderPathOut="project.library.native.folder.path"
jarLibraryPathOut="project.all.jars.path"
targetApi="${project.target.apilevel}"
@@ -622,7 +622,7 @@
<echo level="info">Handling aidl files...</echo>
<aidl executable="${aidl}"
framework="${project.target.framework.aidl}"
- libraryBinFolderPathRefid="project.library.bin.folder.path"
+ libraryBinAidlFolderPathRefid="project.library.bin.aidl.folder.path"
genFolder="${gen.absolute.dir}"
aidlOutFolder="${out.aidl.absolute.dir}">
<source path="${source.absolute.dir}"/>
diff --git a/files/proguard-android-optimize.txt b/files/proguard-android-optimize.txt
new file mode 100644
index 0000000..f8f5bcb
--- /dev/null
+++ b/files/proguard-android-optimize.txt
@@ -0,0 +1,64 @@
+# This is a configuration file for ProGuard.
+# http://proguard.sourceforge.net/index.html#manual/usage.html
+
+# Optimizations: If you don't want to optimize, use the
+# proguard-android.txt configuration file instead of this one, which
+# turns off the optimization flags. Adding optimization introduces
+# certain risks, since for example not all optimizations performed by
+# ProGuard works on all versions of Dalvik. The following flags turn
+# off various optimizations known to have issues, but the list may not
+# be complete or up to date. (The "arithmetic" optimization can be
+# used if you are only targeting Android 2.0 or later.) Make sure you
+# test thoroughly if you go this route.
+-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
+-optimizationpasses 5
+-allowaccessmodification
+-dontpreverify
+
+# The remainder of this file is identical to the non-optimized version
+# of the Proguard configuration file (except that the other file has
+# flags to turn off optimization).
+
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-verbose
+
+-keepattributes *Annotation*
+-keep public class com.google.vending.licensing.ILicensingService
+-keep public class com.android.vending.licensing.ILicensingService
+
+# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
+-keepclasseswithmembernames class * {
+ native <methods>;
+}
+
+# keep setters in Views so that animations can still work.
+# see http://proguard.sourceforge.net/manual/examples.html#beans
+-keepclassmembers public class * extends android.view.View {
+ void set*(***);
+ *** get*();
+}
+
+# We want to keep methods in Activity that could be used in the XML attribute onClick
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+ public static final android.os.Parcelable$Creator *;
+}
+
+-keepclassmembers class **.R$* {
+ public static <fields>;
+}
+
+# The support library contains references to newer platform versions.
+# Don't warn about those in case this app is linking against an older
+# platform version. We know about them, and they are safe.
+-dontwarn android.support.**
diff --git a/files/proguard-android.txt b/files/proguard-android.txt
index 3cc5c8a..fe73bae 100644
--- a/files/proguard-android.txt
+++ b/files/proguard-android.txt
@@ -10,18 +10,11 @@
# of these optimizations on its own).
-dontoptimize
-dontpreverify
-
-# If you want to enable optimization, you should include the
-# following:
-# -optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
-# -optimizationpasses 5
-# -allowaccessmodification
-#
-# Note that you cannot just include these flags in your own
-# configuration file; if you are including this file, optimization
-# will be turned off. You'll need to either edit this file, or
-# duplicate the contents of this file and remove the include of this
-# file from your project's proguard.config path property.
+# Note that if you want to enable optimization, you cannot just
+# include optimization flags in your own project configuration file;
+# instead you will need to point to the
+# "proguard-android-optimize.txt" file instead of this one from your
+# project.properties file.
-keepattributes *Annotation*
-keep public class com.google.vending.licensing.ILicensingService
diff --git a/ide_common/src/com/android/ide/common/resources/FrameworkResources.java b/ide_common/src/com/android/ide/common/resources/FrameworkResources.java
index 381516c..4d8e681 100644..100755
--- a/ide_common/src/com/android/ide/common/resources/FrameworkResources.java
+++ b/ide_common/src/com/android/ide/common/resources/FrameworkResources.java
@@ -120,34 +120,6 @@ public class FrameworkResources extends ResourceRepository {
ResourceType lastType = null;
String lastTypeName = "";
-
- // Precompute maps from name to ResourceItem such that when we find
- // a public item's name we can quickly locate it. Without this,
- // it's a linear search for each item, n times -- O(n^2).
- // Precomputing a map is O(n) and looking up n times in the map is
- // also O(n).
- Map<ResourceType, Map<String, ResourceItem>> nameMap =
- new HashMap<ResourceType, Map<String, ResourceItem>>();
- for (Entry<ResourceType, List<ResourceItem>> entry: mResourceMap.entrySet()) {
- ResourceType type = entry.getKey();
- if (type == ResourceType.PUBLIC || type == ResourceType.DECLARE_STYLEABLE) {
- // These are large maps (in android-15 for example the "public"
- // ResourceType has 1734 items and declare-styleable has 210) that
- // currently have no public exported names. Therefore, don't bother
- // creating name lookup maps for these. (However, if by chance a future
- // public.xml file does specify these, it will be found by the sequential
- // search if map=null below.)
- continue;
- }
- List<ResourceItem> items = entry.getValue();
- int size = items.size();
- Map<String, ResourceItem> map = new HashMap<String, ResourceItem>(size);
- for (ResourceItem item : items) {
- map.put(item.getName(), item);
- }
- nameMap.put(type, map);
- }
-
while (true) {
int event = parser.next();
if (event == XmlPullParser.START_TAG) {
@@ -183,23 +155,9 @@ public class FrameworkResources extends ResourceRepository {
}
if (type != null) {
ResourceItem match = null;
- Map<String, ResourceItem> map = nameMap.get(type);
+ Map<String, ResourceItem> map = mResourceMap.get(type);
if (map != null) {
match = map.get(name);
- } else {
- // We skipped computing name maps for some large lists
- // that currently don't have any public names, but
- // on the off chance that they will show up, leave the
- // old iteration based lookup here
- List<ResourceItem> typeList = mResourceMap.get(type);
- if (typeList != null) {
- for (ResourceItem item : typeList) {
- if (name.equals(item.getName())) {
- match = item;
- break;
- }
- }
- }
}
if (match != null) {
@@ -276,3 +234,4 @@ public class FrameworkResources extends ResourceRepository {
}
}
}
+
diff --git a/ide_common/src/com/android/ide/common/resources/ResourceFolder.java b/ide_common/src/com/android/ide/common/resources/ResourceFolder.java
index 03b6eb4..d0d91c7 100644
--- a/ide_common/src/com/android/ide/common/resources/ResourceFolder.java
+++ b/ide_common/src/com/android/ide/common/resources/ResourceFolder.java
@@ -28,7 +28,9 @@ import com.android.resources.ResourceType;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* Resource Folder class. Contains list of {@link ResourceFile}s,
@@ -38,10 +40,10 @@ public final class ResourceFolder implements Configurable {
final ResourceFolderType mType;
final FolderConfiguration mConfiguration;
IAbstractFolder mFolder;
- ArrayList<ResourceFile> mFiles = null;
+ List<ResourceFile> mFiles = null;
+ Map<String, ResourceFile> mNames = null;
private final ResourceRepository mRepository;
-
/**
* Creates a new {@link ResourceFolder}
* @param type The type of the folder
@@ -69,32 +71,13 @@ public final class ResourceFolder implements Configurable {
public ResourceFile processFile(IAbstractFile file, ResourceDeltaKind kind,
ScanningContext context) {
// look for this file if it's already been created
- ResourceFile resFile = getFile(file);
+ ResourceFile resFile = getFile(file, context);
if (resFile == null) {
if (kind != ResourceDeltaKind.REMOVED) {
// create a ResourceFile for it.
- // check if that's a single or multi resource type folder. For now we define this by
- // the number of possible resource type output by files in the folder.
- // We have a special case for layout/menu folders which can also generate IDs.
- // This does
- // not make the difference between several resource types from a single file or
- // the ability to have 2 files in the same folder generating 2 different types of
- // resource. The former is handled by MultiResourceFile properly while we don't
- // handle the latter. If we were to add this behavior we'd have to change this call.
- List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(mType);
-
- if (types.size() == 1) {
- resFile = new SingleResourceFile(file, this);
- } else if (types.contains(ResourceType.LAYOUT)) {
- resFile = new IdGeneratingResourceFile(file, this, ResourceType.LAYOUT);
- } else if (types.contains(ResourceType.MENU)) {
- resFile = new IdGeneratingResourceFile(file, this, ResourceType.MENU);
- } else {
- resFile = new MultiResourceFile(file, this);
- }
-
+ resFile = createResourceFile(file);
resFile.load(context);
// add it to the folder
@@ -111,6 +94,29 @@ public final class ResourceFolder implements Configurable {
return resFile;
}
+ private ResourceFile createResourceFile(IAbstractFile file) {
+ // check if that's a single or multi resource type folder. For now we define this by
+ // the number of possible resource type output by files in the folder.
+ // We have a special case for layout/menu folders which can also generate IDs.
+ // This does
+ // not make the difference between several resource types from a single file or
+ // the ability to have 2 files in the same folder generating 2 different types of
+ // resource. The former is handled by MultiResourceFile properly while we don't
+ // handle the latter. If we were to add this behavior we'd have to change this call.
+ List<ResourceType> types = FolderTypeRelationship.getRelatedResourceTypes(mType);
+
+ ResourceFile resFile = null;
+ if (types.size() == 1) {
+ resFile = new SingleResourceFile(file, this);
+ } else if (types.contains(ResourceType.LAYOUT)) {
+ resFile = new IdGeneratingResourceFile(file, this, ResourceType.LAYOUT);
+ } else if (types.contains(ResourceType.MENU)) {
+ resFile = new IdGeneratingResourceFile(file, this, ResourceType.MENU);
+ } else {
+ resFile = new MultiResourceFile(file, this);
+ }
+ return resFile;
+ }
/**
* Adds a {@link ResourceFile} to the folder.
@@ -119,15 +125,68 @@ public final class ResourceFolder implements Configurable {
@VisibleForTesting(visibility=Visibility.PROTECTED)
public void addFile(ResourceFile file) {
if (mFiles == null) {
- mFiles = new ArrayList<ResourceFile>();
+ int initialSize = 16;
+ if (mRepository.isFrameworkRepository()) {
+ String name = mFolder.getName();
+ // Pick some reasonable initial sizes for framework data structures
+ // since they are typically (a) large and (b) their sizes are roughly known
+ // in advance
+ switch (mType) {
+ case DRAWABLE: {
+ // See if it's one of the -mdpi, -hdpi etc folders which
+ // are large (~1250 items)
+ int index = name.indexOf('-');
+ if (index == -1) {
+ initialSize = 230; // "drawable" folder
+ } else {
+ index = name.indexOf('-', index + 1);
+ if (index == -1) {
+ // One of the "drawable-<density>" folders
+ initialSize = 1260;
+ } else {
+ // "drawable-sw600dp-hdpi" etc
+ initialSize = 30;
+ }
+ }
+ break;
+ }
+ case LAYOUT: {
+ // The main layout folder has about ~185 layouts in it;
+ // the others are small
+ if (name.indexOf('-') == -1) {
+ initialSize = 200;
+ }
+ break;
+ }
+ case VALUES: {
+ if (name.indexOf('-') == -1) {
+ initialSize = 32;
+ } else {
+ initialSize = 4;
+ }
+ break;
+ }
+ case ANIM: initialSize = 85; break;
+ case COLOR: initialSize = 32; break;
+ case RAW: initialSize = 4; break;
+ default:
+ // Stick with the 16 default
+ break;
+ }
+ }
+
+ mFiles = new ArrayList<ResourceFile>(initialSize);
+ mNames = new HashMap<String, ResourceFile>(initialSize, 2.0f);
}
mFiles.add(file);
+ mNames.put(file.getFile().getName(), file);
}
protected void removeFile(ResourceFile file, ScanningContext context) {
file.dispose(context);
mFiles.remove(file);
+ mNames.remove(file.getFile().getName());
}
protected void dispose(ScanningContext context) {
@@ -137,6 +196,7 @@ public final class ResourceFolder implements Configurable {
}
mFiles.clear();
+ mNames.clear();
}
}
@@ -191,22 +251,43 @@ public final class ResourceFolder implements Configurable {
* @param name the name of the file.
*/
public boolean hasFile(String name) {
+ if (mNames.containsKey(name)) {
+ return true;
+ }
+
+ // Note: mNames.containsKey(name) is faster, but doesn't give the same result; this
+ // method seems to be called on this ResourceFolder before it has been processed,
+ // so we need to use the file system check instead:
return mFolder.hasFile(name);
}
/**
* Returns the {@link ResourceFile} matching a {@link IAbstractFile} object.
+ *
* @param file The {@link IAbstractFile} object.
+ * @param context a context object with state for the current update, such
+ * as a place to stash errors encountered
* @return the {@link ResourceFile} or null if no match was found.
*/
- private ResourceFile getFile(IAbstractFile file) {
- if (mFiles != null) {
- for (ResourceFile f : mFiles) {
- if (f.getFile().equals(file)) {
- return f;
- }
+ private ResourceFile getFile(IAbstractFile file, ScanningContext context) {
+ assert mFolder.equals(file.getParentFolder());
+
+ if (mNames != null) {
+ ResourceFile resFile = mNames.get(file.getName());
+ if (resFile != null) {
+ return resFile;
}
}
+
+ // If the file actually exists, the resource folder may not have been
+ // scanned yet; add it lazily
+ if (file.exists()) {
+ ResourceFile resFile = createResourceFile(file);
+ resFile.load(context);
+ addFile(resFile);
+ return resFile;
+ }
+
return null;
}
@@ -216,13 +297,23 @@ public final class ResourceFolder implements Configurable {
* @return the {@link ResourceFile} or <code>null</code> if no match was found.
*/
public ResourceFile getFile(String filename) {
- if (mFiles != null) {
- for (ResourceFile f : mFiles) {
- if (f.getFile().getName().equals(filename)) {
- return f;
- }
+ if (mNames != null) {
+ ResourceFile resFile = mNames.get(filename);
+ if (resFile != null) {
+ return resFile;
}
}
+
+ // If the file actually exists, the resource folder may not have been
+ // scanned yet; add it lazily
+ IAbstractFile file = mFolder.getFile(filename);
+ if (file != null && file.exists()) {
+ ResourceFile resFile = createResourceFile(file);
+ resFile.load(new ScanningContext(mRepository));
+ addFile(resFile);
+ return resFile;
+ }
+
return null;
}
diff --git a/ide_common/src/com/android/ide/common/resources/ResourceRepository.java b/ide_common/src/com/android/ide/common/resources/ResourceRepository.java
index 3040dc0..7e0338f 100644..100755
--- a/ide_common/src/com/android/ide/common/resources/ResourceRepository.java
+++ b/ide_common/src/com/android/ide/common/resources/ResourceRepository.java
@@ -36,8 +36,10 @@ import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.IdentityHashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -58,11 +60,12 @@ public abstract class ResourceRepository {
protected final Map<ResourceFolderType, List<ResourceFolder>> mFolderMap =
new EnumMap<ResourceFolderType, List<ResourceFolder>>(ResourceFolderType.class);
- protected final Map<ResourceType, List<ResourceItem>> mResourceMap =
- new EnumMap<ResourceType, List<ResourceItem>>(ResourceType.class);
+ protected final Map<ResourceType, Map<String, ResourceItem>> mResourceMap =
+ new EnumMap<ResourceType, Map<String, ResourceItem>>(
+ ResourceType.class);
- private final Map<List<ResourceItem>, List<ResourceItem>> mReadOnlyListMap =
- new IdentityHashMap<List<ResourceItem>, List<ResourceItem>>();
+ private final Map<Map<String, ResourceItem>, Collection<ResourceItem>> mReadOnlyListMap =
+ new IdentityHashMap<Map<String, ResourceItem>, Collection<ResourceItem>>();
private final boolean mFrameworkRepository;
@@ -193,13 +196,13 @@ public abstract class ResourceRepository {
* @return true if the resource is known
*/
public boolean hasResourceItem(ResourceType type, String name) {
- List<ResourceItem> list = mResourceMap.get(type);
+ Map<String, ResourceItem> map = mResourceMap.get(type);
- if (list != null) {
- for (ResourceItem item : list) {
- if (name.equals(item.getName())) {
- return true;
- }
+ if (map != null) {
+
+ ResourceItem resourceItem = map.get(name);
+ if (resourceItem != null) {
+ return true;
}
}
@@ -225,16 +228,56 @@ public abstract class ResourceRepository {
item = createResourceItem(name);
- List<ResourceItem> list = mResourceMap.get(type);
- if (list == null) {
- list = new ArrayList<ResourceItem>();
- mResourceMap.put(type, list);
+ Map<String, ResourceItem> map = mResourceMap.get(type);
+
+ if (map == null) {
+ if (isFrameworkRepository()) {
+ // Pick initial size for the maps. Also change the load factor to 1.0
+ // to avoid rehashing the whole table when we (as expected) get near
+ // the known rough size of each resource type map.
+ int size;
+ switch (type) {
+ // Based on counts in API 16. Going back to API 10, the counts
+ // are roughly 25-50% smaller (e.g. compared to the top 5 types below
+ // the fractions are 1107 vs 1734, 831 vs 1508, 895 vs 1255,
+ // 733 vs 1064 and 171 vs 783.
+ case PUBLIC: size = 1734; break;
+ case DRAWABLE: size = 1508; break;
+ case STRING: size = 1255; break;
+ case ATTR: size = 1064; break;
+ case STYLE: size = 783; break;
+ case ID: size = 347; break;
+ case DECLARE_STYLEABLE: size = 210; break;
+ case LAYOUT: size = 187; break;
+ case COLOR: size = 120; break;
+ case ANIM: size = 95; break;
+ case DIMEN: size = 81; break;
+ case BOOL: size = 54; break;
+ case INTEGER: size = 52; break;
+ case ARRAY: size = 51; break;
+ case PLURALS: size = 20; break;
+ case XML: size = 14; break;
+ case INTERPOLATOR : size = 13; break;
+ case ANIMATOR: size = 8; break;
+ case RAW: size = 4; break;
+ case MENU: size = 2; break;
+ case MIPMAP: size = 2; break;
+ case FRACTION: size = 1; break;
+ default:
+ size = 2;
+ }
+ map = new HashMap<String, ResourceItem>(size, 1.0f);
+ } else {
+ map = new HashMap<String, ResourceItem>();
+ }
+ mResourceMap.put(type, map);
}
- list.add(item);
+ map.put(item.getName(), item);
if (oldItem != null) {
- list.remove(oldItem);
+ map.remove(oldItem.getName());
+
}
}
@@ -324,16 +367,16 @@ public abstract class ResourceRepository {
* @return a non null collection of resource items
*/
public Collection<ResourceItem> getResourceItemsOfType(ResourceType type) {
- List<ResourceItem> list = mResourceMap.get(type);
+ Map<String, ResourceItem> map = mResourceMap.get(type);
- if (list == null) {
+ if (map == null) {
return Collections.emptyList();
}
- List<ResourceItem> roList = mReadOnlyListMap.get(list);
+ Collection<ResourceItem> roList = mReadOnlyListMap.get(map);
if (roList == null) {
- roList = Collections.unmodifiableList(list);
- mReadOnlyListMap.put(list, roList);
+ roList = Collections.unmodifiableCollection(map.values());
+ mReadOnlyListMap.put(map, roList);
}
return roList;
@@ -345,7 +388,7 @@ public abstract class ResourceRepository {
* @return true if the repository contains resources of the given type, false otherwise.
*/
public boolean hasResourcesOfType(ResourceType type) {
- List<ResourceItem> items = mResourceMap.get(type);
+ Map<String, ResourceItem> items = mResourceMap.get(type);
return (items != null && items.size() > 0);
}
@@ -567,10 +610,10 @@ public abstract class ResourceRepository {
}
protected void removeFile(ResourceType type, ResourceFile file) {
- List<ResourceItem> list = mResourceMap.get(type);
- if (list != null) {
- for (int i = 0 ; i < list.size(); i++) {
- ResourceItem item = list.get(i);
+ Map<String, ResourceItem> map = mResourceMap.get(type);
+ if (map != null) {
+ Collection<ResourceItem> values = map.values();
+ for (ResourceItem item : values) {
item.removeFile(file);
}
}
@@ -587,7 +630,7 @@ public abstract class ResourceRepository {
FolderConfiguration referenceConfig) {
// get the resource item for the given type
- List<ResourceItem> items = mResourceMap.get(type);
+ Map<String, ResourceItem> items = mResourceMap.get(type);
if (items == null) {
return new HashMap<String, ResourceValue>();
}
@@ -595,7 +638,7 @@ public abstract class ResourceRepository {
// create the map
HashMap<String, ResourceValue> map = new HashMap<String, ResourceValue>(items.size());
- for (ResourceItem item : items) {
+ for (ResourceItem item : items.values()) {
ResourceValue value = item.getResourceValue(type, referenceConfig,
isFrameworkRepository());
if (value != null) {
@@ -614,13 +657,15 @@ public abstract class ResourceRepository {
// Since removed files/folders remove source files from existing ResourceItem, loop through
// all resource items and remove the ones that have no source files.
- Collection<List<ResourceItem>> lists = mResourceMap.values();
- for (List<ResourceItem> list : lists) {
- for (int i = 0 ; i < list.size() ;) {
- if (list.get(i).hasNoSourceFile()) {
- list.remove(i);
- } else {
- i++;
+ Collection<Map<String, ResourceItem>> maps = mResourceMap.values();
+ for (Map<String, ResourceItem> map : maps) {
+ Set<String> keySet = map.keySet();
+ Iterator<String> iterator = keySet.iterator();
+ while (iterator.hasNext()) {
+ String name = iterator.next();
+ ResourceItem resourceItem = map.get(name);
+ if (resourceItem.hasNoSourceFile()) {
+ iterator.remove();
}
}
}
@@ -634,17 +679,16 @@ public abstract class ResourceRepository {
* @return the existing ResourceItem or null if no match was found.
*/
private ResourceItem findDeclaredResourceItem(ResourceType type, String name) {
- List<ResourceItem> list = mResourceMap.get(type);
+ Map<String, ResourceItem> map = mResourceMap.get(type);
- if (list != null) {
- for (ResourceItem item : list) {
- // ignore inline
- if (name.equals(item.getName()) && item.isDeclaredInline() == false) {
- return item;
- }
+ if (map != null) {
+ ResourceItem resourceItem = map.get(name);
+ if (resourceItem != null && !resourceItem.isDeclaredInline()) {
+ return resourceItem;
}
}
return null;
}
}
+
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
index abe8a52..288a976 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/LintDriver.java
@@ -18,7 +18,6 @@ package com.android.tools.lint.client.api;
import static com.android.tools.lint.detector.api.LintConstants.ANDROID_MANIFEST_XML;
import static com.android.tools.lint.detector.api.LintConstants.ATTR_IGNORE;
-import static com.android.tools.lint.detector.api.LintConstants.BIN_FOLDER;
import static com.android.tools.lint.detector.api.LintConstants.DOT_CLASS;
import static com.android.tools.lint.detector.api.LintConstants.DOT_JAR;
import static com.android.tools.lint.detector.api.LintConstants.DOT_JAVA;
@@ -529,18 +528,18 @@ public class LintDriver {
// right thing, which is to see if you're pointing right at a project or
// right within it (say at the src/ or res/) folder, and if not, you're
// hopefully pointing at a project tree that you want to scan recursively.
- if (isProjectDir(file)) {
+ if (LintUtils.isProjectDir(file)) {
registerProjectFile(fileToProject, file, file, rootDir);
continue;
} else {
File parent = file.getParentFile();
if (parent != null) {
- if (isProjectDir(parent)) {
+ if (LintUtils.isProjectDir(parent)) {
registerProjectFile(fileToProject, file, parent, parent);
continue;
} else {
parent = parent.getParentFile();
- if (parent != null && isProjectDir(parent)) {
+ if (parent != null && LintUtils.isProjectDir(parent)) {
registerProjectFile(fileToProject, file, parent, parent);
continue;
}
@@ -554,7 +553,7 @@ public class LintDriver {
// Pointed at a file: Search upwards for the containing project
File parent = file.getParentFile();
while (parent != null) {
- if (isProjectDir(parent)) {
+ if (LintUtils.isProjectDir(parent)) {
registerProjectFile(fileToProject, file, parent, parent);
break;
}
@@ -626,7 +625,7 @@ public class LintDriver {
return;
}
- if (isProjectDir(dir)) {
+ if (LintUtils.isProjectDir(dir)) {
registerProjectFile(fileToProject, dir, dir, rootDir);
} else {
File[] files = dir.listFiles();
@@ -640,25 +639,6 @@ public class LintDriver {
}
}
- private boolean isProjectDir(@NonNull File dir) {
- boolean hasManifest = new File(dir, ANDROID_MANIFEST_XML).exists();
- if (hasManifest) {
- // Special case: the bin/ folder can also contain a copy of the
- // manifest file, but this is *not* a project directory
- if (dir.getName().equals(BIN_FOLDER)) {
- // ...unless of course it just *happens* to be a project named bin, in
- // which case we peek at its parent to see if this is the case
- dir = dir.getParentFile();
- if (dir != null && isProjectDir(dir)) {
- // Yes, it's a bin/ directory inside a real project: ignore this dir
- return false;
- }
- }
- }
-
- return hasManifest;
- }
-
private void checkProject(@NonNull Project project) {
File projectDir = project.getDir();
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java
index 448720e..0969228 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java
@@ -16,6 +16,8 @@
package com.android.tools.lint.detector.api;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_MANIFEST_XML;
+import static com.android.tools.lint.detector.api.LintConstants.BIN_FOLDER;
import static com.android.tools.lint.detector.api.LintConstants.DOT_XML;
import static com.android.tools.lint.detector.api.LintConstants.ID_RESOURCE_PREFIX;
import static com.android.tools.lint.detector.api.LintConstants.NEW_ID_RESOURCE_PREFIX;
@@ -518,4 +520,32 @@ public class LintUtils {
return true;
}
+
+ /**
+ * Returns true if the given directory represents an Android project
+ * directory. Note: This doesn't necessarily mean it's an Eclipse directory,
+ * only that it looks like it contains a logical Android project -- one
+ * including a manifest file, a resource folder, etc.
+ *
+ * @param dir the directory to check
+ * @return true if the directory looks like an Android project
+ */
+ public static boolean isProjectDir(@NonNull File dir) {
+ boolean hasManifest = new File(dir, ANDROID_MANIFEST_XML).exists();
+ if (hasManifest) {
+ // Special case: the bin/ folder can also contain a copy of the
+ // manifest file, but this is *not* a project directory
+ if (dir.getName().equals(BIN_FOLDER)) {
+ // ...unless of course it just *happens* to be a project named bin, in
+ // which case we peek at its parent to see if this is the case
+ dir = dir.getParentFile();
+ if (dir != null && isProjectDir(dir)) {
+ // Yes, it's a bin/ directory inside a real project: ignore this dir
+ return false;
+ }
+ }
+ }
+
+ return hasManifest;
+ }
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/detector/api/LintUtilsTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/detector/api/LintUtilsTest.java
index ae125e2..67d2dfa 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/detector/api/LintUtilsTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/detector/api/LintUtilsTest.java
@@ -244,5 +244,4 @@ public class LintUtilsTest extends TestCase {
checkEncoding("UTF_32", true /*bom*/, "\r\n");
checkEncoding("UTF_32LE", true /*bom*/, "\r\n");
}
-
} \ No newline at end of file
diff --git a/rule_api/src/com/android/ide/common/api/IDragElement.java b/rule_api/src/com/android/ide/common/api/IDragElement.java
index 885ba35..50a5014 100644
--- a/rule_api/src/com/android/ide/common/api/IDragElement.java
+++ b/rule_api/src/com/android/ide/common/api/IDragElement.java
@@ -86,6 +86,14 @@ public interface IDragElement {
public abstract IDragElement[] getInnerElements();
/**
+ * Returns true if the given {@link INode} represents this drag element
+ *
+ * @param node the node to be checked
+ * @return true if the given node represents this drag element
+ */
+ public abstract boolean isSame(@NonNull INode node);
+
+ /**
* An XML attribute in the {@link IDragElement}.
* <p/>
* The attribute is always represented by a namespace URI, a name and a value.
diff --git a/rule_api/src/com/android/ide/common/api/IViewRule.java b/rule_api/src/com/android/ide/common/api/IViewRule.java
index bcf4e89..c115795 100644
--- a/rule_api/src/com/android/ide/common/api/IViewRule.java
+++ b/rule_api/src/com/android/ide/common/api/IViewRule.java
@@ -161,7 +161,9 @@ public interface IViewRule {
* @param targetView the corresponding View object for the target layout, or
* null if not known
* @param elements an array of {@link IDragElement} element descriptors for
- * the dragged views
+ * the dragged views. When there are more than one element, the
+ * first element will always be the "primary" element (e.g. the
+ * one that the mouse is actively dragging.)
* @return a {@link DropFeedback} object with drop state (which will be
* supplied to a follow-up {@link #onDropMove} call), or null if the
* drop should be ignored
@@ -178,7 +180,9 @@ public interface IViewRule {
* @param targetNode the {@link INode} for the target layout receiving a
* drop event
* @param elements an array of {@link IDragElement} element descriptors for
- * the dragged views
+ * the dragged views. When there are more than one element, the
+ * first element will always be the "primary" element (e.g. the
+ * one that the mouse is actively dragging.)
* @param feedback the {@link DropFeedback} object created by
* {@link #onDropEnter(INode, Object, IDragElement[])}
* @param where the current mouse drag position
@@ -211,7 +215,9 @@ public interface IViewRule {
* @param targetNode the {@link INode} for the target layout receiving a
* drop event
* @param elements an array of {@link IDragElement} element descriptors for
- * the dragged views
+ * the dragged views. When there are more than one element, the
+ * first element will always be the "primary" element (e.g. the
+ * one that the mouse is actively dragging.)
* @param feedback the {@link DropFeedback} object created by
* {@link #onDropEnter(INode, Object, IDragElement[])}
*/
@@ -230,7 +236,9 @@ public interface IViewRule {
* @param targetNode the {@link INode} for the target layout receiving a
* drop event
* @param elements an array of {@link IDragElement} element descriptors for
- * the dragged views
+ * the dragged views. When there are more than one element, the
+ * first element will always be the "primary" element (e.g. the
+ * one that the mouse is actively dragging.)
* @param feedback the {@link DropFeedback} object created by
* {@link #onDropEnter(INode, Object, IDragElement[])}
* @param where the mouse drop position
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
index a78e1a3..24b9260 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
@@ -168,6 +168,8 @@ public final class SdkConstants {
/** global Android proguard config file */
public final static String FN_ANDROID_PROGUARD_FILE = "proguard-android.txt"; //$NON-NLS-1$
+ /** global Android proguard config file with optimization enabled */
+ public final static String FN_ANDROID_OPT_PROGUARD_FILE = "proguard-android-optimize.txt"; //$NON-NLS-1$
/** default proguard config file with new file extension (for project specific stuff) */
public final static String FN_PROJECT_PROGUARD_FILE = "proguard-project.txt"; //$NON-NLS-1$
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/-sdk-addon-5.xsd b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-addon-5.xsd
index 546b00d..546b00d 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/-sdk-addon-5.xsd
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-addon-5.xsd
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/-sdk-repository-7.xsd b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository-7.xsd
index ea18070..ea18070 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/-sdk-repository-7.xsd
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/repository/sdk-repository-7.xsd