aboutsummaryrefslogtreecommitdiffstats
path: root/eclipse/plugins
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2012-03-06 14:20:00 -0800
committerTor Norbye <tnorbye@google.com>2012-03-08 07:10:05 -0800
commit9bfb05e9d9ea4b2b969e50c3096e2fdb95653648 (patch)
treeb74b3aa318789abfa19441131a53134fee8ed960 /eclipse/plugins
parentce07f83d079dae516b8f3aca9220d6efd782d381 (diff)
downloadsdk-9bfb05e9d9ea4b2b969e50c3096e2fdb95653648.zip
sdk-9bfb05e9d9ea4b2b969e50c3096e2fdb95653648.tar.gz
sdk-9bfb05e9d9ea4b2b969e50c3096e2fdb95653648.tar.bz2
Handle the android support gridlayout library automatically
This changeset adds support for the android support library's GridLayout library project. When you create a new layout with the GridLayout, or when you drop a GridLayout, the IDE checks whether you need the compatibility version of GridLayout (e.g. min sdk < 14), and if so, offers to install it. This will then first run the SDK manager to install the android support package into extras, and then it creates a local library project in the Eclipse workspace, and updates the library dependency to reference it. Finally, it rewrites tags such that the layout will use the compatibility package for the <GridLayout> and <Space> tags. This is done in the node handler, so client rule code will automatically get the right compatibility tag; they don't need to handle it there. Change-Id: I6da926eee7ffa956832ddd311d4180e8ff38ae07
Diffstat (limited to 'eclipse/plugins')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java46
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddCompatibilityJarAction.java245
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/FixProjectAction.java16
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java5
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestInfo.java99
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java17
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/CompatibilityLibraryHelper.java176
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java91
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java16
13 files changed, 642 insertions, 87 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java
index 1b9f815..3129f4d 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java
@@ -196,6 +196,7 @@ public class LayoutConstants {
/** The fully qualified class name of a RelativeLayout view */
public static final String FQCN_GRID_LAYOUT = "android.widget.GridLayout"; //$NON-NLS-1$
+ public static final String FQCN_GRID_LAYOUT_V7 = "android.support.v7.widget.GridLayout"; //$NON-NLS-1$
/** The fully qualified class name of a FrameLayout view */
public static final String FQCN_FRAME_LAYOUT = "android.widget.FrameLayout"; //$NON-NLS-1$
@@ -247,6 +248,7 @@ public class LayoutConstants {
/** The fully qualified class name of a Space */
public static final String FQCN_SPACE = "android.widget.Space"; //$NON-NLS-1$
+ public static final String FQCN_SPACE_V7 = "android.support.v7.widget.Space"; //$NON-NLS-1$
/** The fully qualified class name of a TextView view */
public static final String FQCN_TEXT_VIEW = "android.widget.TextView"; //$NON-NLS-1$
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java
index 62b6804..7e0392a 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java
@@ -373,8 +373,12 @@ public class AdtUtils {
* @param offset the offset to be checked
* @return a list (possibly empty but never null) of matching markers
*/
- public static List<IMarker> findMarkersOnLine(String markerType,
- IResource file, IDocument document, int offset) {
+ @NonNull
+ public static List<IMarker> findMarkersOnLine(
+ @NonNull String markerType,
+ @NonNull IResource file,
+ @NonNull IDocument document,
+ int offset) {
List<IMarker> matchingMarkers = new ArrayList<IMarker>(2);
try {
IMarker[] markers = file.findMarkers(markerType, true, IResource.DEPTH_ZERO);
@@ -413,6 +417,7 @@ public class AdtUtils {
*
* @return the available and open Android projects, never null
*/
+ @NonNull
public static IJavaProject[] getOpenAndroidProjects() {
return BaseProjectHelper.getAndroidProjects(new IProjectFilter() {
@Override
@@ -423,6 +428,43 @@ public class AdtUtils {
}
/**
+ * Returns a unique project name, based on the given {@code base} file name
+ * possibly with a {@code conjunction} and a new number behind it to ensure
+ * that the project name is unique. For example,
+ * {@code getUniqueProjectName("project", "_")} will return
+ * {@code "project"} if that name does not already exist, and if it does, it
+ * will return {@code "project_2"}.
+ *
+ * @param base the base name to use, such as "foo"
+ * @param conjunction a string to insert between the base name and the
+ * number.
+ * @return a unique project name based on the given base and conjunction
+ */
+ public static String getUniqueProjectName(String base, String conjunction) {
+ // We're using all workspace projects here rather than just open Android project
+ // via getOpenAndroidProjects because the name cannot conflict with non-Android
+ // or closed projects either
+ IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
+ IProject[] projects = workspaceRoot.getProjects();
+
+ for (int i = 1; i < 1000; i++) {
+ String name = i == 1 ? base : base + conjunction + Integer.toString(i);
+ boolean found = false;
+ for (IProject project : projects) {
+ if (project.getName().equals(name)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return name;
+ }
+ }
+
+ return base;
+ }
+
+ /**
* Returns the name of the parent folder for the given editor input
*
* @param editorInput the editor input to check
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddCompatibilityJarAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddCompatibilityJarAction.java
index b758b67..2428e60 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddCompatibilityJarAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddCompatibilityJarAction.java
@@ -17,18 +17,28 @@
package com.android.ide.eclipse.adt.internal.actions;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AdtUtils;
import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
import com.android.ide.eclipse.adt.internal.sdk.AdtConsoleSdkLog;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.SdkConstants;
+import com.android.sdklib.internal.project.ProjectProperties;
+import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
+import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy;
import com.android.sdklib.io.FileOp;
import com.android.sdkuilib.internal.repository.sdkman2.AdtUpdateDialog;
import com.android.util.Pair;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.filesystem.IFileSystem;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
@@ -117,12 +127,22 @@ public class AddCompatibilityJarAction implements IObjectActionDelegate {
AdtPlugin.log(IStatus.ERROR, "JavaProject is null for %1$s", project); //$NON-NLS-1$
}
+ File jarPath = installSupport();
+ if (jarPath != null) {
+ return addJar(javaProject, jarPath, waitForFinish);
+ } else {
+ return false;
+ }
+ }
+
+ private static File installSupport() {
+
final Sdk sdk = Sdk.getCurrent();
if (sdk == null) {
AdtPlugin.printErrorToConsole(
AddCompatibilityJarAction.class.getSimpleName(), // tag
"Error: Android SDK is not loaded yet."); //$NON-NLS-1$
- return false;
+ return null;
}
// TODO: For the generic action, check the library isn't in the project already.
@@ -138,9 +158,12 @@ public class AddCompatibilityJarAction implements IObjectActionDelegate {
Pair<Boolean, File> result = window.installExtraPackage(
"android", "support"); //$NON-NLS-1$ //$NON-NLS-2$
+ // TODO: Make sure the version is at the required level; we know we need at least one
+ // containing the v7 support
+
if (!result.getFirst().booleanValue()) {
AdtPlugin.printErrorToConsole("Failed to install Android Compatibility library");
- return false;
+ return null;
}
// TODO these "v4" values needs to be dynamic, e.g. we could try to match
@@ -153,11 +176,19 @@ public class AddCompatibilityJarAction implements IObjectActionDelegate {
if (!jarPath.isFile()) {
AdtPlugin.printErrorToConsole("Android Compatibility JAR not found:",
jarPath.getAbsolutePath());
- return false;
+ return null;
}
- // Then run an Eclipse asynchronous job to update the project
+ return jarPath;
+ }
+
+ private static boolean addJar(
+ final IJavaProject javaProject,
+ final File jarPath,
+ boolean waitForFinish) {
+ // Run an Eclipse asynchronous job to update the project
+ final IProject project = javaProject.getProject();
Job job = new Job("Add Compatibility Library to Project") {
@Override
protected IStatus run(IProgressMonitor monitor) {
@@ -208,6 +239,212 @@ public class AddCompatibilityJarAction implements IObjectActionDelegate {
return true;
}
+ /**
+ * Similar to {@link #install}, but rather than copy a jar into the given
+ * project, it creates a new library project in the workspace for the
+ * compatibility library, and adds a library dependency on the newly
+ * installed library from the given project.
+ *
+ * @param project the project to add a dependency on the library to
+ * @param waitForFinish If true, block until the task has finished
+ * @return true if the installation was successful (or if
+ * <code>waitForFinish</code> is false, if the installation is
+ * likely to be successful - e.g. the user has at least agreed to
+ * all installation prompts.)
+ */
+ public static boolean installLibrary(final IProject project, boolean waitForFinish) {
+ final IJavaProject javaProject = JavaCore.create(project);
+ if (javaProject != null) {
+
+ File sdk = new File(Sdk.getCurrent().getSdkLocation());
+ File supportPath = new File(sdk,
+ SdkConstants.FD_EXTRAS + File.separator
+ + "android" + File.separator //$NON-NLS-1$
+ + "support"); //$NON-NLS-1$
+ if (!supportPath.isDirectory()) {
+ File path = installSupport();
+ if (path == null) {
+ return false;
+ }
+ assert path.equals(supportPath);
+ }
+ File libraryPath = new File(supportPath,
+ "v7" + File.separator //$NON-NLS-1$
+ + "gridlayout"); //$NON-NLS-1$
+ if (!libraryPath.isDirectory()) {
+ // Upgrade support package: it's out of date. The SDK manager will
+ // perform an upgrade to the latest version if the package is already installed.
+ File path = installSupport();
+ if (path == null) {
+ return false;
+ }
+ assert path.equals(libraryPath) : path;
+ }
+
+ // Create workspace copy of the project and add library dependency
+ IProject libraryProject = createLibraryProject(libraryPath, project, waitForFinish);
+ if (libraryProject != null) {
+ return addLibraryDependency(libraryProject, project, waitForFinish);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Creates a library project in the Eclipse workspace out of the grid layout project
+ * in the SDK tree.
+ *
+ * @param libraryPath the path to the directory tree containing the project contents
+ * @param project the project to copy the SDK target out of
+ * @param waitForFinish whether the operation should finish before this method returns
+ * @return a library project, or null if it fails for some reason
+ */
+ private static IProject createLibraryProject(
+ final File libraryPath,
+ final IProject project,
+ boolean waitForFinish) {
+
+ // Install a new library into the workspace. This is a copy rather than
+ // a reference to the compatibility library version such that modifications
+ // do not modify the pristine copy in the SDK install area.
+
+ final IProject newProject;
+ try {
+ IProgressMonitor monitor = new NullProgressMonitor();
+ IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+
+ String name = AdtUtils.getUniqueProjectName(
+ "gridlayout_v7", "_"); //$NON-NLS-1$ //$NON-NLS-2$
+ newProject = root.getProject(name);
+ newProject.create(monitor);
+
+ // Copy in the files recursively
+ IFileSystem fileSystem = EFS.getLocalFileSystem();
+ IFileStore sourceDir = fileSystem.getStore(libraryPath.toURI());
+ IFileStore destDir = fileSystem.getStore(newProject.getLocationURI());
+ sourceDir.copy(destDir, EFS.OVERWRITE, null);
+
+ // Make sure the src folder exists
+ destDir.getChild("src").mkdir(0, null /*monitor*/);
+
+ // Set the android platform to the same level as the calling project
+ ProjectState state = Sdk.getProjectState(project);
+ String target = state.getProperties().getProperty(ProjectProperties.PROPERTY_TARGET);
+ if (target != null && target.length() > 0) {
+ ProjectProperties properties = ProjectProperties.load(libraryPath.getPath(),
+ PropertyType.PROJECT);
+ ProjectPropertiesWorkingCopy copy = properties.makeWorkingCopy();
+ copy.setProperty(ProjectProperties.PROPERTY_TARGET, target);
+ try {
+ copy.save();
+ } catch (Exception e) {
+ AdtPlugin.log(e, null);
+ }
+ }
+
+ newProject.open(monitor);
+
+ return newProject;
+ } catch (CoreException e) {
+ AdtPlugin.log(e, null);
+ return null;
+ }
+ }
+
+ /**
+ * Adds a library dependency on the given library into the given project.
+ *
+ * @param libraryProject the library project to depend on
+ * @param dependentProject the project to write the dependency into
+ * @param waitForFinish whether this method should wait for the job to
+ * finish
+ * @return true if the operation succeeded
+ */
+ public static boolean addLibraryDependency(
+ final IProject libraryProject,
+ final IProject dependentProject,
+ boolean waitForFinish) {
+
+ // Now add library dependency
+
+ // Run an Eclipse asynchronous job to update the project
+ Job job = new Job("Add Compatibility Library Dependency to Project") {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ monitor.beginTask("Add library dependency to project build path", 3);
+ monitor.worked(1);
+
+ // TODO: Add library project to the project.properties file!
+ ProjectState state = Sdk.getProjectState(dependentProject);
+ ProjectPropertiesWorkingCopy mPropertiesWorkingCopy =
+ state.getProperties().makeWorkingCopy();
+
+ // Get the highest version number of the libraries; there cannot be any
+ // gaps so we will assign the next library the next number
+ int nextVersion = 1;
+ for (String property : mPropertiesWorkingCopy.keySet()) {
+ if (property.startsWith(ProjectProperties.PROPERTY_LIB_REF)) {
+ String s = property.substring(
+ ProjectProperties.PROPERTY_LIB_REF.length());
+ int version = Integer.parseInt(s);
+ if (version >= nextVersion) {
+ nextVersion = version + 1;
+ }
+ }
+ }
+
+ IPath relativePath = libraryProject.getLocation().makeRelativeTo(
+ dependentProject.getLocation());
+
+ mPropertiesWorkingCopy.setProperty(
+ ProjectProperties.PROPERTY_LIB_REF + nextVersion,
+ relativePath.toString());
+ try {
+ mPropertiesWorkingCopy.save();
+ IResource projectProp = dependentProject.findMember(
+ SdkConstants.FN_PROJECT_PROPERTIES);
+ projectProp.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor());
+ } catch (Exception e) {
+ String msg = String.format(
+ "Failed to save %1$s for project %2$s",
+ SdkConstants.FN_PROJECT_PROPERTIES, dependentProject.getName());
+ AdtPlugin.log(e, msg);
+ }
+
+ // Project fix-ups
+ Job fix = FixProjectAction.createFixProjectJob(libraryProject);
+ fix.schedule();
+ fix.join();
+
+ monitor.worked(1);
+
+ return Status.OK_STATUS;
+ } catch (Exception e) {
+ return new Status(Status.ERROR, AdtPlugin.PLUGIN_ID, Status.ERROR,
+ "Failed", e); //$NON-NLS-1$
+ } finally {
+ if (monitor != null) {
+ monitor.done();
+ }
+ }
+ }
+ };
+ job.schedule();
+
+ if (waitForFinish) {
+ try {
+ job.join();
+ return job.getState() == IStatus.OK;
+ } catch (InterruptedException e) {
+ AdtPlugin.log(e, null);
+ }
+ }
+
+ return true;
+ }
+
private static IResource copyJarIntoProject(
IProject project,
File jarPath,
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/FixProjectAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/FixProjectAction.java
index c073022..254219f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/FixProjectAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/FixProjectAction.java
@@ -16,6 +16,7 @@
package com.android.ide.eclipse.adt.internal.actions;
+import com.android.annotations.NonNull;
import com.android.ide.eclipse.adt.internal.project.AndroidNature;
import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
@@ -82,7 +83,18 @@ public class FixProjectAction implements IObjectActionDelegate {
}
private void fixProject(final IProject project) {
- new Job("Fix Project Properties") {
+ createFixProjectJob(project).schedule();
+ }
+
+ /**
+ * Creates a job to fix the project
+ *
+ * @param project the project to fix
+ * @return a job to perform the fix (not yet scheduled)
+ */
+ @NonNull
+ public static Job createFixProjectJob(@NonNull final IProject project) {
+ return new Job("Fix Project Properties") {
@Override
protected IStatus run(IProgressMonitor monitor) {
@@ -129,7 +141,7 @@ public class FixProjectAction implements IObjectActionDelegate {
}
}
}
- }.schedule();
+ };
}
/**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
index 8234f25..63bc255 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java
@@ -588,7 +588,7 @@ public class PreCompilerBuilder extends BaseBuilder {
Messages.Removing_Generated_Classes);
// remove all the derived resources from the 'gen' source folder.
- if (mGenFolder != null) {
+ if (mGenFolder != null && mGenFolder.exists()) {
// gen folder should not be derived, but previous version could set it to derived
// so we make sure this isn't the case (or it'll get deleted by the clean)
mGenFolder.setDerived(false, monitor);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java
index 9b186a1..7b860fe 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ClientRulesEngine.java
@@ -193,7 +193,9 @@ class ClientRulesEngine implements IClientRulesEngine {
Sdk currentSdk = Sdk.getCurrent();
if (currentSdk != null) {
IAndroidTarget target = currentSdk.getTarget(mRulesEngine.getEditor().getProject());
- return target.getVersion().getApiLevel();
+ if (target != null) {
+ return target.getVersion().getApiLevel();
+ }
}
return -1;
@@ -353,6 +355,7 @@ class ClientRulesEngine implements IClientRulesEngine {
// First check to make sure fragments are available, and if not,
// warn the user.
IAndroidTarget target = Sdk.getCurrent().getTarget(project);
+ // No, this should be using the min SDK instead!
if (target.getVersion().getApiLevel() < 11 && oldFragmentType == null) {
// Compatibility library must be present
MessageDialog dialog =
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
index a4306fa..ea464c1 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java
@@ -37,6 +37,7 @@ import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElement
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode;
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.project.CompatibilityLibraryHelper;
import org.eclipse.swt.graphics.Rectangle;
import org.w3c.dom.NamedNodeMap;
@@ -256,6 +257,13 @@ public class NodeProxy implements INode {
private INode insertOrAppend(String viewFqcn, int index) {
checkEditOK();
+ AndroidXmlEditor editor = mNode.getEditor();
+ if (editor != null) {
+ // Possibly replace the tag with a compatibility version if the
+ // minimum SDK requires it
+ viewFqcn = CompatibilityLibraryHelper.getTagFor(editor.getProject(), viewFqcn);
+ }
+
// Find the descriptor for this FQCN
ViewElementDescriptor vd = getFqcnViewDescriptor(viewFqcn);
if (vd == null) {
@@ -277,14 +285,12 @@ public class NodeProxy implements INode {
}
}
+ // Set default attributes -- but only for new widgets (not when moving or copying)
RulesEngine engine = null;
- AndroidXmlEditor editor = mNode.getEditor();
LayoutEditorDelegate delegate = LayoutEditorDelegate.fromEditor(editor);
if (delegate != null) {
engine = delegate.getRulesEngine();
}
-
- // Set default attributes -- but only for new widgets (not when moving or copying)
if (engine == null || engine.getInsertType().isCreate()) {
// TODO: This should probably use IViewRule#getDefaultAttributes() at some point
DescriptorsUtils.setDefaultLayoutAttributes(uiNew, false /*updateLayout*/);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestInfo.java
index 4ec3801..f749e2b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestInfo.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/manifest/ManifestInfo.java
@@ -29,6 +29,8 @@ import static com.android.sdklib.xml.AndroidManifest.NODE_ACTIVITY;
import static com.android.sdklib.xml.AndroidManifest.NODE_USES_SDK;
import static org.eclipse.jdt.core.search.IJavaSearchConstants.REFERENCES;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
@@ -103,6 +105,7 @@ public class ManifestInfo {
private Map<String, String> mActivityThemes;
private IAbstractFile mManifestFile;
private long mLastModified;
+ private int mMinSdk;
private int mTargetSdk;
private String mApplicationIcon;
private String mApplicationLabel;
@@ -130,6 +133,7 @@ public class ManifestInfo {
* @param project the project the finder is associated with
* @return a {@ManifestInfo} for the given project, never null
*/
+ @NonNull
public static ManifestInfo get(IProject project) {
ManifestInfo finder = null;
try {
@@ -174,6 +178,7 @@ public class ManifestInfo {
mActivityThemes = new HashMap<String, String>();
mManifestTheme = null;
mTargetSdk = 1; // Default when not specified
+ mMinSdk = 1; // Default when not specified
mPackage = ""; //$NON-NLS-1$
mApplicationIcon = null;
mApplicationLabel = null;
@@ -227,32 +232,8 @@ public class ManifestInfo {
NodeList usesSdks = root.getElementsByTagName(NODE_USES_SDK);
if (usesSdks.getLength() > 0) {
Element usesSdk = (Element) usesSdks.item(0);
- String targetSdk = null;
- if (usesSdk.hasAttributeNS(NS_RESOURCES, ATTRIBUTE_TARGET_SDK_VERSION)) {
- targetSdk = usesSdk.getAttributeNS(NS_RESOURCES,
- ATTRIBUTE_TARGET_SDK_VERSION);
- } else if (usesSdk.hasAttributeNS(NS_RESOURCES, ATTRIBUTE_MIN_SDK_VERSION)) {
- targetSdk = usesSdk.getAttributeNS(NS_RESOURCES,
- ATTRIBUTE_MIN_SDK_VERSION);
- }
- if (targetSdk != null) {
- int apiLevel = -1;
- try {
- apiLevel = Integer.valueOf(targetSdk);
- } catch (NumberFormatException e) {
- // Handle codename
- if (Sdk.getCurrent() != null) {
- IAndroidTarget target = Sdk.getCurrent().getTargetFromHashString(
- "android-" + targetSdk); //$NON-NLS-1$
- if (target != null) {
- // codename future API level is current api + 1
- apiLevel = target.getVersion().getApiLevel() + 1;
- }
- }
- }
-
- mTargetSdk = apiLevel;
- }
+ mMinSdk = getApiVersion(usesSdk, ATTRIBUTE_MIN_SDK_VERSION, 1);
+ mTargetSdk = getApiVersion(usesSdk, ATTRIBUTE_TARGET_SDK_VERSION, mMinSdk);
}
} else {
mManifestTheme = defaultTheme;
@@ -264,11 +245,40 @@ public class ManifestInfo {
}
}
+ private static int getApiVersion(Element usesSdk, String attribute, int defaultApiLevel) {
+ String valueString = null;
+ if (usesSdk.hasAttributeNS(NS_RESOURCES, attribute)) {
+ valueString = usesSdk.getAttributeNS(NS_RESOURCES, attribute);
+ }
+
+ if (valueString != null) {
+ int apiLevel = -1;
+ try {
+ apiLevel = Integer.valueOf(valueString);
+ } catch (NumberFormatException e) {
+ // Handle codename
+ if (Sdk.getCurrent() != null) {
+ IAndroidTarget target = Sdk.getCurrent().getTargetFromHashString(
+ "android-" + valueString); //$NON-NLS-1$
+ if (target != null) {
+ // codename future API level is current api + 1
+ apiLevel = target.getVersion().getApiLevel() + 1;
+ }
+ }
+ }
+
+ return apiLevel;
+ }
+
+ return defaultApiLevel;
+ }
+
/**
* Returns the default package registered in the Android manifest
*
* @return the default package registered in the manifest
*/
+ @NonNull
public String getPackage() {
sync();
return mPackage;
@@ -280,6 +290,7 @@ public class ManifestInfo {
*
* @return a map from activity fqcn to theme style
*/
+ @NonNull
public Map<String, String> getActivityThemes() {
sync();
return mActivityThemes;
@@ -293,6 +304,7 @@ public class ManifestInfo {
* @param screenSize the screen size to obtain a default theme for, or null if unknown
* @return the theme to use for this project, never null
*/
+ @NonNull
public String getDefaultTheme(IAndroidTarget renderingTarget, ScreenSize screenSize) {
sync();
@@ -320,6 +332,7 @@ public class ManifestInfo {
*
* @return the application icon, or null
*/
+ @Nullable
public String getApplicationIcon() {
sync();
return mApplicationIcon;
@@ -330,16 +343,38 @@ public class ManifestInfo {
*
* @return the application label, or null
*/
+ @Nullable
public String getApplicationLabel() {
sync();
return mApplicationLabel;
}
/**
+ * Returns the target SDK version
+ *
+ * @return the target SDK version
+ */
+ public int getTargetSdkVersion() {
+ sync();
+ return mTargetSdk;
+ }
+
+ /**
+ * Returns the minimum SDK version
+ *
+ * @return the minimum SDK version
+ */
+ public int getMinSdkVersion() {
+ sync();
+ return mMinSdk;
+ }
+
+ /**
* Returns the {@link IPackageFragment} for the package registered in the manifest
*
* @return the {@link IPackageFragment} for the package registered in the manifest
*/
+ @Nullable
public IPackageFragment getPackageFragment() {
sync();
try {
@@ -367,6 +402,7 @@ public class ManifestInfo {
* @param pkg the package containing activities
* @return the activity name
*/
+ @Nullable
public static String guessActivity(IProject project, String layoutName, String pkg) {
final AtomicReference<String> activity = new AtomicReference<String>();
SearchRequestor requestor = new SearchRequestor() {
@@ -446,6 +482,7 @@ public class ManifestInfo {
* @return the activity name
*/
@SuppressWarnings("all")
+ @Nullable
public String guessActivityBySetContentView(String layoutName) {
if (false) {
// These should be fields
@@ -568,7 +605,13 @@ public class ManifestInfo {
return scope;
}
- /** Returns the first package root for the given java project */
+ /**
+ * Returns the first package root for the given java project
+ *
+ * @param javaProject the project to search in
+ * @return the first package root, or null
+ */
+ @Nullable
public static IPackageFragmentRoot getSourcePackageRoot(IJavaProject javaProject) {
IPackageFragmentRoot packageRoot = null;
List<IPath> sources = BaseProjectHelper.getSourceClasspaths(javaProject);
@@ -589,8 +632,10 @@ public class ManifestInfo {
/**
* Computes the minimum SDK and target SDK versions for the project
*
+ * @param project the project to look up the versions for
* @return a pair of (minimum SDK, target SDK) versions, never null
*/
+ @NonNull
public static Pair<Integer, Integer> computeSdkVersions(IProject project) {
int mMinSdkVersion = 1;
int mTargetSdkVersion = 1;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java
index fde228b..1137901 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/AddSuppressAttribute.java
@@ -28,7 +28,6 @@ import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-import com.android.tools.lint.checks.DuplicateIdDetector;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
@@ -195,21 +194,9 @@ class AddSuppressAttribute implements ICompletionProposal {
return null;
}
- // Some issues cannot find a specific node scope associated with the error
- // (for example because it involves cross-file analysis and at the end of
- // the project scan when the warnings are computed the DOM model is no longer
- // available). Until that's resolved, we need to filter these out such that
- // we don't add misleading annotations on individual elements; the fallback
- // path is the DOM document itself instead.
- if (id.equals(DuplicateIdDetector.CROSS_LAYOUT.getId())) {
- node = document.getDocumentElement();
- }
-
+ node = document.getDocumentElement();
if (node == null) {
- node = document.getDocumentElement();
- if (node == null) {
- return null;
- }
+ return null;
}
String desc = String.format("Add ignore '%1$s\' to element", id);
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/CompatibilityLibraryHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/CompatibilityLibraryHelper.java
new file mode 100644
index 0000000..8f6de3a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/CompatibilityLibraryHelper.java
@@ -0,0 +1,176 @@
+/*
+ * 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.project;
+
+import static com.android.ide.common.layout.LayoutConstants.FQCN_GRID_LAYOUT;
+import static com.android.ide.common.layout.LayoutConstants.FQCN_GRID_LAYOUT_V7;
+import static com.android.ide.common.layout.LayoutConstants.FQCN_SPACE;
+import static com.android.ide.common.layout.LayoutConstants.FQCN_SPACE_V7;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.eclipse.adt.AdtUtils;
+import com.android.ide.eclipse.adt.internal.actions.AddCompatibilityJarAction;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState.LibraryState;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Helper class for the Android Support Library. The support library provides
+ * (for example) a backport of GridLayout, which must be used as a library
+ * project rather than a jar library since it has resources. This class provides
+ * support for finding the library project, or downloading and installing it on
+ * demand if it does not, as well as translating tags such as
+ * {@code <GridLayout>} into {@code <com.android.support.v7.GridLayout>} if it
+ * does not.
+ */
+public class CompatibilityLibraryHelper {
+ /**
+ * Returns the correct tag to use for the given view tag. This is normally
+ * the same as the tag itself. However, for some views which are not available
+ * on all platforms, this will:
+ * <ul>
+ * <li> Check if the view is available in the compatibility library,
+ * and if so, if the support library is not installed, will offer to
+ * install it via the SDK manager.
+ * <li> (The tool may also offer to adjust the minimum SDK of the project
+ * up to a level such that the given tag is supported directly, and then
+ * this method will return the original tag.)
+ * <li> Check whether the compatibility library is included in the project, and
+ * if not, offer to copy it into the workspace and add a library dependency.
+ * <li> Return the alternative tag. For example, for "GridLayout", it will
+ * (if the minimum SDK is less than 14) return "com.android.support.v7.GridLayout"
+ * instead.
+ * </ul>
+ *
+ * @param project the project to add the dependency into
+ * @param tag the tag to look up, such as "GridLayout"
+ * @return the tag to use in the layout, normally the same as the input tag but possibly
+ * an equivalent compatibility library tag instead.
+ */
+ @NonNull
+ public static String getTagFor(@NonNull IProject project, @NonNull String tag) {
+ boolean isGridLayout = tag.equals(FQCN_GRID_LAYOUT);
+ boolean isSpace = tag.equals(FQCN_SPACE);
+ if (isGridLayout || isSpace) {
+ int minSdk = ManifestInfo.get(project).getMinSdkVersion();
+ if (minSdk < 14) {
+ // See if the support library is installed in the SDK area
+ // See if there is a local project in the workspace providing the
+ // project
+ IProject supportProject = getSupportProjectV7();
+ if (supportProject != null) {
+ // Make sure I have a dependency on it
+ ProjectState state = Sdk.getProjectState(project);
+ if (state != null) {
+ for (LibraryState library : state.getLibraries()) {
+ if (supportProject.equals(library.getProjectState().getProject())) {
+ // Found it: you have the compatibility library and have linked
+ // to it: use the alternative tag
+ return isGridLayout ? FQCN_GRID_LAYOUT_V7 : FQCN_SPACE_V7;
+ }
+ }
+ }
+ }
+
+ // Ask user to install it
+ String message = String.format(
+ "%1$s requires API level 14 or higher, or a compatibility "
+ + "library for older versions.\n\n"
+ + " Do you want to install the compatibility library?", tag);
+ MessageDialog dialog =
+ new MessageDialog(
+ Display.getCurrent().getActiveShell(),
+ "Warning",
+ null,
+ message,
+ MessageDialog.QUESTION,
+ new String[] {
+ "Install", "Cancel"
+ },
+ 1 /* default button: Cancel */);
+ int answer = dialog.open();
+ if (answer == 0) {
+ if (supportProject != null) {
+ // Just add library dependency
+ if (!AddCompatibilityJarAction.addLibraryDependency(
+ supportProject,
+ project,
+ true /* waitForFinish */)) {
+ return tag;
+ }
+ } else {
+ // Install library AND add dependency
+ if (!AddCompatibilityJarAction.installLibrary(
+ project,
+ true /* waitForFinish */)) {
+ return tag;
+ }
+ }
+
+ return isGridLayout ? FQCN_GRID_LAYOUT_V7 : FQCN_SPACE_V7;
+ }
+ }
+ }
+
+ return tag;
+ }
+
+ /** Cache for {@link #getSupportProjectV7()} */
+ private static IProject sCachedProject;
+
+ /**
+ * Finds and returns the support project in the workspace, if any.
+ *
+ * @return the android support library project, or null if not found
+ */
+ @Nullable
+ public static IProject getSupportProjectV7() {
+ if (sCachedProject != null) {
+ if (sCachedProject.isAccessible()) {
+ return sCachedProject;
+ } else {
+ sCachedProject = null;
+ }
+ }
+
+ sCachedProject = findSupportProjectV7();
+ return sCachedProject;
+ }
+
+ @Nullable
+ private static IProject findSupportProjectV7() {
+ for (IJavaProject javaProject : AdtUtils.getOpenAndroidProjects()) {
+ IProject project = javaProject.getProject();
+ ProjectState state = Sdk.getProjectState(project);
+ if (state.isLibrary()) {
+ ManifestInfo manifestInfo = ManifestInfo.get(project);
+ if (manifestInfo.getPackage().equals("android.support.v7.gridlayout")) { //$NON-NLS-1$
+ return project;
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java
index eb0ddf1..e118ff7 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java
@@ -17,6 +17,7 @@
package com.android.ide.eclipse.adt.internal.resources.manager;
import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.build.BuildHelper;
import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
@@ -27,9 +28,11 @@ import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
+import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
import java.io.File;
import java.io.FileInputStream;
@@ -253,8 +256,6 @@ public final class ProjectClassLoader extends ClassLoader {
// get a java project from it
IJavaProject javaProject = JavaCore.create(mJavaProject.getProject());
- IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot();
-
ArrayList<URL> oslibraryList = new ArrayList<URL>();
IClasspathEntry[] classpaths = javaProject.readRawClasspath();
if (classpaths != null) {
@@ -266,38 +267,30 @@ public final class ProjectClassLoader extends ClassLoader {
e = JavaCore.getResolvedClasspathEntry(e);
}
- // get the IPath
- IPath path = e.getPath();
-
- // check the name ends with .jar
- if (AdtConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) {
- boolean local = false;
- IResource resource = wsRoot.findMember(path);
- if (resource != null && resource.exists() &&
- resource.getType() == IResource.FILE) {
- local = true;
- try {
- oslibraryList.add(new File(resource.getLocation().toOSString())
- .toURI().toURL());
- } catch (MalformedURLException mue) {
- // pass
- }
- }
-
- if (local == false) {
- // if the jar path doesn't match a workspace resource,
- // then we get an OSString and check if this links to a valid file.
- String osFullPath = path.toOSString();
-
- File f = new File(osFullPath);
- if (f.exists()) {
- try {
- oslibraryList.add(f.toURI().toURL());
- } catch (MalformedURLException mue) {
- // pass
+ handleClassPathEntry(e, oslibraryList);
+ } else if (e.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
+ // get the container.
+ try {
+ IClasspathContainer container = JavaCore.getClasspathContainer(
+ e.getPath(), javaProject);
+ // ignore the system and default_system types as they represent
+ // libraries that are part of the runtime.
+ if (container != null &&
+ container.getKind() == IClasspathContainer.K_APPLICATION) {
+ IClasspathEntry[] entries = container.getClasspathEntries();
+ for (IClasspathEntry entry : entries) {
+ // TODO: Xav -- is this necessary?
+ if (entry.getEntryKind() == IClasspathEntry.CPE_VARIABLE) {
+ entry = JavaCore.getResolvedClasspathEntry(entry);
}
+
+ handleClassPathEntry(entry, oslibraryList);
}
}
+ } catch (JavaModelException jme) {
+ // can't resolve the container? ignore it.
+ AdtPlugin.log(jme, "Failed to resolve ClasspathContainer: %s",
+ e.getPath());
}
}
}
@@ -305,4 +298,40 @@ public final class ProjectClassLoader extends ClassLoader {
return oslibraryList.toArray(new URL[oslibraryList.size()]);
}
+
+ private void handleClassPathEntry(IClasspathEntry e, ArrayList<URL> oslibraryList) {
+ // get the IPath
+ IPath path = e.getPath();
+
+ // check the name ends with .jar
+ if (AdtConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) {
+ boolean local = false;
+ IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
+ if (resource != null && resource.exists() &&
+ resource.getType() == IResource.FILE) {
+ local = true;
+ try {
+ oslibraryList.add(new File(resource.getLocation().toOSString())
+ .toURI().toURL());
+ } catch (MalformedURLException mue) {
+ // pass
+ }
+ }
+
+ if (local == false) {
+ // if the jar path doesn't match a workspace resource,
+ // then we get an OSString and check if this links to a valid file.
+ String osFullPath = path.toOSString();
+
+ File f = new File(osFullPath);
+ if (f.exists()) {
+ try {
+ oslibraryList.add(f.toURI().toURL());
+ } catch (MalformedURLException mue) {
+ // pass
+ }
+ }
+ }
+ }
+ }
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
index 63f381f..6388644 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
@@ -332,7 +332,7 @@ public final class Sdk {
/**
* Initializes a new project with a target. This creates the <code>project.properties</code>
* file.
- * @param project the project to intialize
+ * @param project the project to initialize
* @param target the project's target.
* @throws IOException if creating the file failed in any way.
* @throws StreamException
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java
index 4e17125..815848e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java
@@ -18,6 +18,9 @@
package com.android.ide.eclipse.adt.internal.wizards.newxmlfile;
+import static com.android.ide.common.layout.LayoutConstants.FQCN_GRID_LAYOUT;
+import static com.android.ide.common.layout.LayoutConstants.GRID_LAYOUT;
+
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
@@ -26,7 +29,9 @@ import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.formatting.XmlFormatPreferences;
import com.android.ide.eclipse.adt.internal.editors.formatting.XmlFormatStyle;
import com.android.ide.eclipse.adt.internal.editors.formatting.XmlPrettyPrinter;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.android.ide.eclipse.adt.internal.project.CompatibilityLibraryHelper;
import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileCreationPage.TypeInfo;
import com.android.resources.ResourceFolderType;
import com.android.util.Pair;
@@ -201,6 +206,17 @@ public class NewXmlFileWizard extends Wizard implements INewWizard {
StringBuilder sb = new StringBuilder(XML_HEADER_LINE);
+ if (folderType == ResourceFolderType.LAYOUT && root.equals(GRID_LAYOUT)) {
+ IProject project = file.getParent().getProject();
+ int minSdk = ManifestInfo.get(project).getMinSdkVersion();
+ if (minSdk < 14) {
+ root = CompatibilityLibraryHelper.getTagFor(project, FQCN_GRID_LAYOUT);
+ if (root.equals(FQCN_GRID_LAYOUT)) {
+ root = GRID_LAYOUT;
+ }
+ }
+ }
+
sb.append('<').append(root);
if (xmlns != null) {
sb.append('\n').append(" xmlns:android=\"").append(xmlns).append('"'); //$NON-NLS-1$