aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddSupportJarAction.java626
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AvdManagerAction.java78
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java276
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/FixProjectAction.java154
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java354
5 files changed, 1488 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddSupportJarAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddSupportJarAction.java
new file mode 100644
index 000000000..f0af8bbd2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddSupportJarAction.java
@@ -0,0 +1,626 @@
+/*
+ * Copyright (C) 2011 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.actions;
+
+import com.android.SdkConstants;
+import com.android.annotations.Nullable;
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AdtUtils;
+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.SdkManager;
+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.ui.AdtUpdateDialog;
+import com.android.utils.NullLogger;
+import com.android.utils.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.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+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;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * An action to add the android-support-v4.jar support library
+ * to the selected project.
+ * <p/>
+ * This should be used by the GLE. The action itself is currently more
+ * like an example of how to invoke the new {@link AdtUpdateDialog}.
+ * <p/>
+ * TODO: make this more configurable.
+ */
+public class AddSupportJarAction implements IObjectActionDelegate {
+
+ /** The vendor ID of the support library. */
+ private static final String VENDOR_ID = "android"; //$NON-NLS-1$
+ /** The path ID of the support library. */
+ private static final String SUPPORT_ID = "support"; //$NON-NLS-1$
+ /** The path ID of the compatibility library (which was its id for releases 1-3). */
+ private static final String COMPATIBILITY_ID = "compatibility"; //$NON-NLS-1$
+ private static final String FD_GRIDLAYOUT = "gridlayout"; //$NON-NLS-1$
+ private static final String FD_V7 = "v7"; //$NON-NLS-1$
+ private static final String FD_V4 = "v4"; //$NON-NLS-1$
+ private static final String FD_V13 = "v13"; //$NON-NLS-1$
+ private static final String FD_APPCOMPAT = "appcompat"; //$NON-NLS-1$
+ private static final String FD_LIBS = "libs"; //$NON-NLS-1$
+ private static final String ANDROID_SUPPORT_V4_JAR = "android-support-v4.jar"; //$NON-NLS-1$
+ private static final String ANDROID_SUPPORT_V13_JAR = "android-support-v13.jar";//$NON-NLS-1$
+ private static final String APPCOMPAT_V7_JAR = "android-support-v7-appcompat.jar";//$NON-NLS-1$
+ private static final String APP_COMPAT_LIB_NAME = "appcompat_v7"; //$NON-NLS-1$
+ private ISelection mSelection;
+
+ /**
+ * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
+ */
+ @Override
+ public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+ }
+
+ @Override
+ public void run(IAction action) {
+ if (mSelection instanceof IStructuredSelection) {
+
+ for (Iterator<?> it = ((IStructuredSelection) mSelection).iterator();
+ it.hasNext();) {
+ Object element = it.next();
+ IProject project = null;
+ if (element instanceof IProject) {
+ project = (IProject) element;
+ } else if (element instanceof IAdaptable) {
+ project = (IProject) ((IAdaptable) element)
+ .getAdapter(IProject.class);
+ }
+ if (project != null) {
+ install(project);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void selectionChanged(IAction action, ISelection selection) {
+ mSelection = selection;
+ }
+
+ /**
+ * Install the support jar into the given project.
+ *
+ * @param project The Android project to install the support jar into
+ * @return true if the installation was successful
+ */
+ public static boolean install(final IProject project) {
+ File jarPath = installSupport(-1);
+ if (jarPath != null) {
+ try {
+ return copyJarIntoProject(project, jarPath) != null;
+ } catch (Exception e) {
+ AdtPlugin.log(e, null);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Installs the Android Support library into the SDK extras/ folder. If a minimum
+ * revision number is specified, this method will check whether the package is already
+ * installed, and if the installed revision is at least as high as the requested revision,
+ * this method will exit without performing an update.
+ *
+ * @param minimumRevision a minimum revision, or -1 to upgrade
+ * unconditionally. Note that this does <b>NOT</b> specify which
+ * revision to install; the latest version will always be
+ * installed.
+ * @return the location of the support jar file, or null if something went
+ * wrong
+ */
+ @Nullable
+ public static File installSupport(int minimumRevision) {
+
+ final Sdk sdk = Sdk.getCurrent();
+ if (sdk == null) {
+ AdtPlugin.printErrorToConsole(
+ AddSupportJarAction.class.getSimpleName(), // tag
+ "Error: Android SDK is not loaded yet."); //$NON-NLS-1$
+ return null;
+ }
+
+ String sdkLocation = sdk.getSdkOsLocation();
+ if (minimumRevision > 0) {
+ File path = getSupportJarFile();
+ if (path != null) {
+ assert path.exists(); // guaranteed by the getSupportJarFile call
+ int installedRevision = getInstalledRevision();
+ if (installedRevision != -1 && minimumRevision <= installedRevision) {
+ return path;
+ }
+ }
+ }
+
+ // TODO: For the generic action, check the library isn't in the project already.
+
+ // First call the package manager to make sure the package is installed
+ // and get the installation path of the library.
+
+ AdtUpdateDialog window = new AdtUpdateDialog(
+ AdtPlugin.getShell(),
+ new AdtConsoleSdkLog(),
+ sdkLocation);
+
+ Pair<Boolean, File> result = window.installExtraPackage(VENDOR_ID, SUPPORT_ID);
+
+ // 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 Support library");
+ return null;
+ }
+
+ // TODO these "v4" values needs to be dynamic, e.g. we could try to match
+ // vN/android-support-vN.jar. Eventually we'll want to rely on info from the
+ // package manifest anyway so this is irrelevant.
+
+ File path = new File(result.getSecond(), FD_V4);
+ final File jarPath = new File(path, ANDROID_SUPPORT_V4_JAR);
+
+ if (!jarPath.isFile()) {
+ AdtPlugin.printErrorToConsole("Android Support Jar not found:",
+ jarPath.getAbsolutePath());
+ return null;
+ }
+
+ return jarPath;
+ }
+
+ /**
+ * Returns the installed revision number of the Android Support
+ * library, or -1 if the package is not installed.
+ *
+ * @return the installed revision number, or -1
+ */
+ public static int getInstalledRevision() {
+ final Sdk sdk = Sdk.getCurrent();
+ if (sdk != null) {
+ String sdkLocation = sdk.getSdkOsLocation();
+ SdkManager manager = SdkManager.createManager(sdkLocation, NullLogger.getLogger());
+ Map<String, Integer> versions = manager.getExtrasVersions();
+ Integer version = versions.get(VENDOR_ID + '/' + SUPPORT_ID);
+ if (version == null) {
+ // Check the old compatibility library. When the library is updated in-place
+ // the manager doesn't change its folder name (since that is a source of
+ // endless issues on Windows.)
+ version = versions.get(VENDOR_ID + '/' + COMPATIBILITY_ID);
+ }
+ if (version != null) {
+ return version.intValue();
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * 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
+ * support 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 installGridLayoutLibrary(final IProject project, boolean waitForFinish) {
+ final IJavaProject javaProject = JavaCore.create(project);
+ if (javaProject != null) {
+
+ File supportPath = getSupportPackageDir();
+ if (!supportPath.isDirectory()) {
+ File path = installSupport(8); // GridLayout arrived in rev 7 and fixed in rev 8
+ if (path == null) {
+ return false;
+ }
+ assert path.equals(supportPath);
+ }
+ File libraryPath = new File(supportPath, FD_V7 + File.separator + FD_GRIDLAYOUT);
+ 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(-1);
+ 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,
+ "gridlayout_v7", waitForFinish); //$NON-NLS-1$
+ if (libraryProject != null) {
+ return addLibraryDependency(libraryProject, project, waitForFinish);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * 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
+ * support 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 installAppCompatLibrary(final IProject project, boolean waitForFinish) {
+ final IJavaProject javaProject = JavaCore.create(project);
+ if (javaProject != null) {
+
+ // Don't add in the library if it already exists
+ ProjectState state = Sdk.getProjectState(project);
+ ProjectPropertiesWorkingCopy copy = state.getProperties().makeWorkingCopy();
+ for (String property : copy.keySet()) {
+ if (property.startsWith(ProjectProperties.PROPERTY_LIB_REF)) {
+ String libraryReference = copy.getProperty(property);
+ if (libraryReference != null && libraryReference.contains(APP_COMPAT_LIB_NAME)) {
+ return true;
+ }
+ }
+ }
+
+ File supportPath = getSupportPackageDir();
+ if (!supportPath.isDirectory()) {
+ File path = installSupport(7);
+ if (path == null) {
+ return false;
+ }
+ assert path.equals(supportPath);
+ }
+ File libraryPath = new File(supportPath, FD_V7 + File.separator + FD_APPCOMPAT);
+ 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(-1);
+ if (path == null) {
+ return false;
+ }
+ assert path.equals(libraryPath) : path;
+ }
+
+ // Check to see if there's already a version of the library available
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ IWorkspaceRoot root = workspace.getRoot();
+ IProject libraryProject = root.getProject(APP_COMPAT_LIB_NAME);
+ if (!libraryProject.exists()) {
+ // Create workspace copy of the project and add library dependency
+ libraryProject = createLibraryProject(libraryPath, project,
+ APP_COMPAT_LIB_NAME, waitForFinish);
+ }
+ if (libraryProject != null) {
+ return addLibraryDependency(libraryProject, project, waitForFinish);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the directory containing the support libraries (v4, v7, v13,
+ * ...), which may or may not exist
+ *
+ * @return a path to the support library or null
+ */
+ private static File getSupportPackageDir() {
+ final Sdk sdk = Sdk.getCurrent();
+ if (sdk != null) {
+ String sdkLocation = sdk.getSdkOsLocation();
+ SdkManager manager = SdkManager.createManager(sdkLocation, NullLogger.getLogger());
+ Map<String, Integer> versions = manager.getExtrasVersions();
+ Integer version = versions.get(VENDOR_ID + '/' + SUPPORT_ID);
+ if (version != null) {
+ File supportPath = new File(sdkLocation,
+ SdkConstants.FD_EXTRAS + File.separator
+ + VENDOR_ID + File.separator
+ + SUPPORT_ID);
+ return supportPath;
+ }
+
+ // Check the old compatibility library. When the library is updated in-place
+ // the manager doesn't change its folder name (since that is a source of
+ // endless issues on Windows.)
+ version = versions.get(VENDOR_ID + '/' + COMPATIBILITY_ID);
+ if (version != null) {
+ File supportPath = new File(sdkLocation,
+ SdkConstants.FD_EXTRAS + File.separator
+ + VENDOR_ID + File.separator
+ + COMPATIBILITY_ID);
+ return supportPath;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a path to the installed jar file for the support library,
+ * or null if it does not exist
+ *
+ * @return a path to the v4.jar or null
+ */
+ @Nullable
+ public static File getSupportJarFile() {
+ File supportDir = getSupportPackageDir();
+ if (supportDir != null) {
+ File path = new File(supportDir, FD_V4 + File.separator + ANDROID_SUPPORT_V4_JAR);
+ if (path.exists()) {
+ return path;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a path to the installed jar file for the support library,
+ * or null if it does not exist
+ *
+ * @return a path to the v13.jar or null
+ */
+ @Nullable
+ public static File getSupport13JarFile() {
+ File supportDir = getSupportPackageDir();
+ if (supportDir != null) {
+ File path = new File(supportDir, FD_V13 + File.separator + ANDROID_SUPPORT_V13_JAR);
+ if (path.exists()) {
+ return path;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * 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,
+ final String libraryName,
+ boolean waitForFinish) {
+
+ // Install a new library into the workspace. This is a copy rather than
+ // a reference to the support library version such that modifications
+ // do not modify the pristine copy in the SDK install area.
+
+ final IProject newProject;
+ try {
+ IProgressMonitor monitor = new NullProgressMonitor();
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ IWorkspaceRoot root = workspace.getRoot();
+
+ String name = AdtUtils.getUniqueProjectName(
+ libraryName, "_"); //$NON-NLS-1$
+ newProject = root.getProject(name);
+ IProjectDescription description = workspace.newProjectDescription(name);
+ String[] natures = new String[] { AdtConstants.NATURE_DEFAULT, JavaCore.NATURE_ID };
+ description.setNatureIds(natures);
+ newProject.create(description, 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(SdkConstants.SRC_FOLDER).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(
+ destDir.toLocalFile(EFS.NONE, new NullProgressMonitor()).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 Support 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) throws IOException, CoreException {
+ IFolder resFolder = project.getFolder(SdkConstants.FD_NATIVE_LIBS);
+ if (!resFolder.exists()) {
+ resFolder.create(IResource.FORCE, true /*local*/, null);
+ }
+
+ IFile destFile = resFolder.getFile(jarPath.getName());
+ IPath loc = destFile.getLocation();
+ File destPath = loc.toFile();
+
+ // Only modify the file if necessary so that we don't trigger unnecessary recompilations
+ FileOp f = new FileOp();
+ if (!f.isFile(destPath) || !f.isSameFile(jarPath, destPath)) {
+ f.copyFile(jarPath, destPath);
+ // Make sure Eclipse discovers java.io file changes
+ resFolder.refreshLocal(1, new NullProgressMonitor());
+ }
+
+ return destFile;
+ }
+
+ /**
+ * @see IWorkbenchWindowActionDelegate#init
+ */
+ public void init(IWorkbenchWindow window) {
+ // pass
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AvdManagerAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AvdManagerAction.java
new file mode 100644
index 000000000..86f2d3c0a
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AvdManagerAction.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.actions;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.sdk.AdtConsoleSdkLog;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdkuilib.repository.AvdManagerWindow;
+import com.android.sdkuilib.repository.AvdManagerWindow.AvdInvocationContext;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+/**
+ * Delegate for the toolbar/menu action "AVD Manager".
+ * It displays the AVD Manager window.
+ */
+public class AvdManagerAction implements IWorkbenchWindowActionDelegate, IObjectActionDelegate {
+
+ @Override
+ public void dispose() {
+ // nothing to dispose.
+ }
+
+ @Override
+ public void init(IWorkbenchWindow window) {
+ // no init
+ }
+
+ @Override
+ public void run(IAction action) {
+ final Sdk sdk = Sdk.getCurrent();
+ if (sdk != null) {
+ // Although orthogonal to the avd manager action, this is a good time
+ // to check whether the SDK has changed on disk.
+ AdtPlugin.getDefault().refreshSdk();
+
+ // Runs the updater window, directing all logs to the ADT console.
+ AvdManagerWindow window = new AvdManagerWindow(
+ AdtPlugin.getShell(),
+ new AdtConsoleSdkLog(),
+ sdk.getSdkOsLocation(),
+ AvdInvocationContext.IDE);
+ window.open();
+ } else {
+ AdtPlugin.displayError("Android SDK",
+ "Location of the Android SDK has not been setup in the preferences.");
+ }
+ }
+
+ @Override
+ public void selectionChanged(IAction action, ISelection selection) {
+ // nothing related to the current selection.
+ }
+
+ @Override
+ public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+ // nothing to do.
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java
new file mode 100755
index 000000000..78fcfd448
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.actions;
+
+import com.android.SdkConstants;
+import com.android.annotations.Nullable;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.BuildToolInfo;
+import com.android.utils.GrabProcessOutput;
+import com.android.utils.GrabProcessOutput.IProcessOutput;
+import com.android.utils.GrabProcessOutput.Wait;
+import com.android.utils.SdkUtils;
+
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Iterator;
+
+/**
+ * Runs dexdump on the classes.dex of a selected project.
+ */
+public class DexDumpAction implements IObjectActionDelegate {
+
+ private ISelection mSelection;
+
+ @Override
+ public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+ // pass
+ }
+
+ @Override
+ public void run(IAction action) {
+ if (mSelection instanceof IStructuredSelection) {
+ for (Iterator<?> it = ((IStructuredSelection)mSelection).iterator(); it.hasNext();) {
+ Object element = it.next();
+ IProject project = null;
+ if (element instanceof IProject) {
+ project = (IProject)element;
+ } else if (element instanceof IAdaptable) {
+ project = (IProject)((IAdaptable)element).getAdapter(IProject.class);
+ }
+ if (project != null) {
+ dexDumpProject(project);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void selectionChanged(IAction action, ISelection selection) {
+ mSelection = selection;
+ }
+
+ /**
+ * Calls {@link #runDexDump(IProject, IProgressMonitor)} inside a job.
+ *
+ * @param project on which to run dexdump.
+ */
+ private void dexDumpProject(final IProject project) {
+ new Job("Dexdump") {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ return runDexDump(project, monitor);
+ }
+ }.schedule();
+ }
+
+ /**
+ * Runs <code>dexdump</code> on the classex.dex of the project.
+ * Saves the output in a temporary file.
+ * On success, opens the file in the default text editor.
+ *
+ * @param project on which to run dexdump.
+ * @param monitor The job's monitor.
+ */
+ private IStatus runDexDump(final IProject project, IProgressMonitor monitor) {
+ File dstFile = null;
+ boolean removeDstFile = true;
+ try {
+ if (monitor != null) {
+ monitor.beginTask(String.format("Dump dex of %1$s", project.getName()), 2);
+ }
+
+ Sdk current = Sdk.getCurrent();
+ if (current == null) {
+ AdtPlugin.printErrorToConsole(project,
+ "DexDump: missing current SDK"); //$NON-NLS-1$
+ return Status.OK_STATUS;
+ }
+
+ BuildToolInfo buildToolInfo = current.getLatestBuildTool();
+ if (buildToolInfo == null) {
+ AdtPlugin.printErrorToConsole(project,
+ "SDK missing build tools. Please install build tools using SDK Manager.");
+ return Status.OK_STATUS;
+ }
+
+ File buildToolsFolder = buildToolInfo.getLocation();
+ File dexDumpFile = new File(buildToolsFolder, SdkConstants.FN_DEXDUMP);
+
+ IPath binPath = project.getFolder(SdkConstants.FD_OUTPUT).getLocation();
+ if (binPath == null) {
+ AdtPlugin.printErrorToConsole(project,
+ "DexDump: missing project /bin folder. Please compile first."); //$NON-NLS-1$
+ return Status.OK_STATUS;
+ }
+
+ File classesDexFile =
+ new File(binPath.toOSString(), SdkConstants.FN_APK_CLASSES_DEX);
+ if (!classesDexFile.exists()) {
+ AdtPlugin.printErrorToConsole(project,
+ "DexDump: missing classex.dex for project. Please compile first.");//$NON-NLS-1$
+ return Status.OK_STATUS;
+ }
+
+ try {
+ dstFile = File.createTempFile(
+ "dexdump_" + project.getName() + "_", //$NON-NLS-1$ //$NON-NLS-2$
+ ".txt"); //$NON-NLS-1$
+ } catch (Exception e) {
+ AdtPlugin.logAndPrintError(e, project.getName(),
+ "DexDump: createTempFile failed."); //$NON-NLS-1$
+ return Status.OK_STATUS;
+ }
+
+ // --- Exec command line and save result to dst file
+
+ String[] command = new String[2];
+ command[0] = dexDumpFile.getAbsolutePath();
+ command[1] = classesDexFile.getAbsolutePath();
+
+ try {
+ final Process process = Runtime.getRuntime().exec(command);
+
+ final BufferedWriter writer = new BufferedWriter(new FileWriter(dstFile));
+ try {
+ final String lineSep = SdkUtils.getLineSeparator();
+
+ int err = GrabProcessOutput.grabProcessOutput(
+ process,
+ Wait.WAIT_FOR_READERS,
+ new IProcessOutput() {
+ @Override
+ public void out(@Nullable String line) {
+ if (line != null) {
+ try {
+ writer.write(line);
+ writer.write(lineSep);
+ } catch (IOException ignore) {}
+ }
+ }
+
+ @Override
+ public void err(@Nullable String line) {
+ if (line != null) {
+ AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE,
+ project, line);
+ }
+ }
+ });
+
+ if (err == 0) {
+ // The command worked. In this case we don't remove the
+ // temp file in the finally block.
+ removeDstFile = false;
+ } else {
+ AdtPlugin.printErrorToConsole(project,
+ "DexDump failed with code " + Integer.toString(err)); //$NON-NLS-1$
+ return Status.OK_STATUS;
+ }
+ } finally {
+ writer.close();
+ }
+ } catch (InterruptedException e) {
+ // ?
+ }
+
+ if (monitor != null) {
+ monitor.worked(1);
+ }
+
+ // --- Open the temp file in an editor
+
+ final String dstPath = dstFile.getAbsolutePath();
+ AdtPlugin.getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ IFileStore fileStore =
+ EFS.getLocalFileSystem().getStore(new Path(dstPath));
+ if (!fileStore.fetchInfo().isDirectory() &&
+ fileStore.fetchInfo().exists()) {
+
+ IWorkbench wb = PlatformUI.getWorkbench();
+ IWorkbenchWindow win = wb == null ? null : wb.getActiveWorkbenchWindow();
+ final IWorkbenchPage page = win == null ? null : win.getActivePage();
+
+ if (page != null) {
+ try {
+ IDE.openEditorOnFileStore(page, fileStore);
+ } catch (PartInitException e) {
+ AdtPlugin.logAndPrintError(e, project.getName(),
+ "Opening DexDump result failed. Result is available at %1$s", //$NON-NLS-1$
+ dstPath);
+ }
+ }
+ }
+ }
+ });
+
+ if (monitor != null) {
+ monitor.worked(1);
+ }
+
+ return Status.OK_STATUS;
+
+ } catch (IOException e) {
+ AdtPlugin.logAndPrintError(e, project.getName(),
+ "DexDump failed."); //$NON-NLS-1$
+ return Status.OK_STATUS;
+
+ } finally {
+ // By default we remove the temp file on failure.
+ if (removeDstFile && dstFile != null) {
+ try {
+ dstFile.delete();
+ } catch (Exception e) {
+ AdtPlugin.logAndPrintError(e, project.getName(),
+ "DexDump: can't delete temp file %1$s.", //$NON-NLS-1$
+ dstFile.getAbsoluteFile());
+ }
+ }
+ if (monitor != null) {
+ monitor.done();
+ }
+ }
+ }
+}
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
new file mode 100644
index 000000000..254219fc1
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/FixProjectAction.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2007 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.actions;
+
+import com.android.annotations.NonNull;
+import com.android.ide.eclipse.adt.internal.project.AndroidNature;
+import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+import java.util.Iterator;
+
+/**
+ * Action to fix the project properties:
+ * <ul>
+ * <li>Make sure the framework archive is present with the link to the java
+ * doc</li>
+ * </ul>
+ */
+public class FixProjectAction implements IObjectActionDelegate {
+
+ private ISelection mSelection;
+
+ /**
+ * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
+ */
+ @Override
+ public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+ }
+
+ @Override
+ public void run(IAction action) {
+ if (mSelection instanceof IStructuredSelection) {
+
+ for (Iterator<?> it = ((IStructuredSelection) mSelection).iterator();
+ it.hasNext();) {
+ Object element = it.next();
+ IProject project = null;
+ if (element instanceof IProject) {
+ project = (IProject) element;
+ } else if (element instanceof IAdaptable) {
+ project = (IProject) ((IAdaptable) element)
+ .getAdapter(IProject.class);
+ }
+ if (project != null) {
+ fixProject(project);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void selectionChanged(IAction action, ISelection selection) {
+ mSelection = selection;
+ }
+
+ private void fixProject(final IProject project) {
+ 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) {
+ try {
+ if (monitor != null) {
+ monitor.beginTask("Fix Project Properties", 6);
+ }
+
+ ProjectHelper.fixProject(project);
+ if (monitor != null) {
+ monitor.worked(1);
+ }
+
+ // fix the nature order to have the proper project icon
+ ProjectHelper.fixProjectNatureOrder(project);
+ if (monitor != null) {
+ monitor.worked(1);
+ }
+
+ // now we fix the builders
+ AndroidNature.configureResourceManagerBuilder(project);
+ if (monitor != null) {
+ monitor.worked(1);
+ }
+
+ AndroidNature.configurePreBuilder(project);
+ if (monitor != null) {
+ monitor.worked(1);
+ }
+
+ AndroidNature.configureApkBuilder(project);
+ if (monitor != null) {
+ monitor.worked(1);
+ }
+
+ return Status.OK_STATUS;
+ } catch (JavaModelException e) {
+ return e.getJavaModelStatus();
+ } catch (CoreException e) {
+ return e.getStatus();
+ } finally {
+ if (monitor != null) {
+ monitor.done();
+ }
+ }
+ }
+ };
+ }
+
+ /**
+ * @see IWorkbenchWindowActionDelegate#init
+ */
+ public void init(IWorkbenchWindow window) {
+ // pass
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java
new file mode 100644
index 000000000..48667bea0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.actions;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.android.ide.eclipse.adt.internal.sdk.AdtConsoleSdkLog;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.io.FileOp;
+import com.android.sdklib.repository.ISdkChangeListener;
+import com.android.utils.GrabProcessOutput;
+import com.android.utils.GrabProcessOutput.IProcessOutput;
+import com.android.utils.GrabProcessOutput.Wait;
+import com.android.sdkuilib.repository.SdkUpdaterWindow;
+import com.android.sdkuilib.repository.SdkUpdaterWindow.SdkInvocationContext;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Delegate for the toolbar/menu action "Android SDK Manager".
+ * It displays the Android SDK Manager.
+ */
+public class SdkManagerAction implements IWorkbenchWindowActionDelegate, IObjectActionDelegate {
+
+ @Override
+ public void dispose() {
+ // nothing to dispose.
+ }
+
+ @Override
+ public void init(IWorkbenchWindow window) {
+ // no init
+ }
+
+ @Override
+ public void run(IAction action) {
+ // Although orthogonal to the sdk manager action, this is a good time
+ // to check whether the SDK has changed on disk.
+ AdtPlugin.getDefault().refreshSdk();
+
+ if (!openExternalSdkManager()) {
+ // If we failed to execute the sdk manager, check the SDK location.
+ // If it's not properly set, the check will display a dialog to state
+ // so to the user and a link to the prefs.
+ // Here's it's ok to call checkSdkLocationAndId() since it will not try
+ // to run the SdkManagerAction (it might run openExternalSdkManager though.)
+ // If checkSdkLocationAndId tries to open the SDK Manager, it end up using
+ // the internal one.
+ if (AdtPlugin.getDefault().checkSdkLocationAndId()) {
+ // The SDK check was successful, yet the sdk manager fail to launch anyway.
+ AdtPlugin.displayError(
+ "Android SDK",
+ "Failed to run the Android SDK Manager. Check the Android Console View for details.");
+ }
+ }
+ }
+
+ /**
+ * A custom implementation of {@link ProgressMonitorDialog} that allows us
+ * to rename the "Cancel" button to "Close" from the internal task.
+ */
+ private static class CloseableProgressMonitorDialog extends ProgressMonitorDialog {
+
+ public CloseableProgressMonitorDialog(Shell parent) {
+ super(parent);
+ }
+
+ public void changeCancelToClose() {
+ if (cancel != null && !cancel.isDisposed()) {
+ Display display = getShell() == null ? null : getShell().getDisplay();
+ if (display != null) {
+ display.syncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (cancel != null && !cancel.isDisposed()) {
+ cancel.setText(IDialogConstants.CLOSE_LABEL);
+ }
+ }
+ });
+ }
+ }
+ }
+ }
+
+ /**
+ * Opens the SDK Manager as an external application.
+ * This call is asynchronous, it doesn't wait for the manager to be closed.
+ * <p/>
+ * Important: this method must NOT invoke {@link AdtPlugin#checkSdkLocationAndId}
+ * (in any of its variations) since the dialog uses this method to invoke the sdk
+ * manager if needed.
+ *
+ * @return True if the application was found and executed. False if it could not
+ * be located or could not be launched.
+ */
+ public static boolean openExternalSdkManager() {
+
+ // On windows this takes a couple seconds and it's not clear the launch action
+ // has been invoked. To prevent the user from randomly clicking the "open sdk manager"
+ // button multiple times, show a progress window that will automatically close
+ // after a couple seconds.
+
+ // By default openExternalSdkManager will return false.
+ final AtomicBoolean returnValue = new AtomicBoolean(false);
+
+ final CloseableProgressMonitorDialog p =
+ new CloseableProgressMonitorDialog(AdtPlugin.getShell());
+ p.setOpenOnRun(true);
+ try {
+ p.run(true /*fork*/, true /*cancelable*/, new IRunnableWithProgress() {
+ @Override
+ public void run(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException {
+
+ // Get the SDK locatiom from the current SDK or as fallback
+ // directly from the ADT preferences.
+ Sdk sdk = Sdk.getCurrent();
+ String osSdkLocation = sdk == null ? null : sdk.getSdkOsLocation();
+ if (osSdkLocation == null || !new File(osSdkLocation).isDirectory()) {
+ osSdkLocation = AdtPrefs.getPrefs().getOsSdkFolder();
+ }
+
+ // If there's no SDK location or it's not a valid directory,
+ // there's nothing we can do. When this is invoked from run()
+ // the checkSdkLocationAndId method call should display a dialog
+ // telling the user to configure the preferences.
+ if (osSdkLocation == null || !new File(osSdkLocation).isDirectory()) {
+ return;
+ }
+
+ final int numIter = 30; //30*100=3s to wait for window
+ final int sleepMs = 100;
+ monitor.beginTask("Starting Android SDK Manager", numIter);
+
+ File androidBat = FileOp.append(
+ osSdkLocation,
+ SdkConstants.FD_TOOLS,
+ SdkConstants.androidCmdName());
+
+ if (!androidBat.exists()) {
+ AdtPlugin.printErrorToConsole("SDK Manager",
+ "Missing %s file in Android SDK.", SdkConstants.androidCmdName());
+ return;
+ }
+
+ if (monitor.isCanceled()) {
+ // Canceled by user; return true as if it succeeded.
+ returnValue.set(true);
+ return;
+ }
+
+ p.changeCancelToClose();
+
+ try {
+ final AdtConsoleSdkLog logger = new AdtConsoleSdkLog();
+
+ String command[] = new String[] {
+ androidBat.getAbsolutePath(),
+ "sdk" //$NON-NLS-1$
+ };
+ Process process = Runtime.getRuntime().exec(command);
+ GrabProcessOutput.grabProcessOutput(
+ process,
+ Wait.ASYNC,
+ new IProcessOutput() {
+ @Override
+ public void out(@Nullable String line) {
+ // Ignore stdout
+ }
+
+ @Override
+ public void err(@Nullable String line) {
+ if (line != null) {
+ logger.info("[SDK Manager] %s", line);
+ }
+ }
+ });
+
+ // Set openExternalSdkManager to return true.
+ returnValue.set(true);
+ } catch (Exception ignore) {
+ }
+
+ // This small wait prevents the progress dialog from closing too fast.
+ for (int i = 0; i < numIter; i++) {
+ if (monitor.isCanceled()) {
+ // Canceled by user; return true as if it succeeded.
+ returnValue.set(true);
+ return;
+ }
+ if (i == 10) {
+ monitor.subTask("Initializing... SDK Manager will show up shortly.");
+ }
+ try {
+ Thread.sleep(sleepMs);
+ monitor.worked(1);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+
+ monitor.done();
+ }
+ });
+ } catch (Exception e) {
+ AdtPlugin.log(e, "SDK Manager exec failed"); //$NON-NLS-1#
+ return false;
+ }
+
+ return returnValue.get();
+ }
+
+ /**
+ * Opens the SDK Manager bundled within ADT.
+ * The call is blocking and does not return till the SD Manager window is closed.
+ *
+ * @return True if the SDK location is known and the SDK Manager was started.
+ * False if the SDK location is not set and we can't open a SDK Manager to
+ * manage files in an unknown location.
+ */
+ public static boolean openAdtSdkManager() {
+ final Sdk sdk = Sdk.getCurrent();
+ if (sdk == null) {
+ return false;
+ }
+
+ // Runs the updater window, directing only warning/errors logs to the ADT console
+ // (normal log is just dropped, which is fine since the SDK Manager has its own
+ // log window now.)
+
+ SdkUpdaterWindow window = new SdkUpdaterWindow(
+ AdtPlugin.getShell(),
+ new AdtConsoleSdkLog() {
+ @Override
+ public void info(@NonNull String msgFormat, Object... args) {
+ // Do not show non-error/warning log in Eclipse.
+ };
+ @Override
+ public void verbose(@NonNull String msgFormat, Object... args) {
+ // Do not show non-error/warning log in Eclipse.
+ };
+ },
+ sdk.getSdkOsLocation(),
+ SdkInvocationContext.IDE);
+
+ ISdkChangeListener listener = new ISdkChangeListener() {
+ @Override
+ public void onSdkLoaded() {
+ // Ignore initial load of the SDK.
+ }
+
+ /**
+ * Unload all we can from the SDK before new packages are installed.
+ * Typically we need to get rid of references to dx from platform-tools
+ * and to any platform resource data.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public void preInstallHook() {
+
+ // TODO we need to unload as much of as SDK as possible. Otherwise
+ // on Windows we end up with Eclipse locking some files and we can't
+ // replace them.
+ //
+ // At this point, we know what the user wants to install so it would be
+ // possible to pass in flags to know what needs to be unloaded. Typically
+ // we need to:
+ // - unload dex if platform-tools is going to be updated. There's a vague
+ // attempt below at removing any references to dex and GCing. Seems
+ // to do the trick.
+ // - unload any target that is going to be updated since it may have
+ // resource data used by a current layout editor (e.g. data/*.ttf
+ // and various data/res/*.xml).
+ //
+ // Most important we need to make sure there isn't a build going on
+ // and if there is one, either abort it or wait for it to complete and
+ // then we want to make sure we don't get any attempt to use the SDK
+ // before the postInstallHook is called.
+
+ if (sdk != null) {
+ sdk.unloadTargetData(true /*preventReload*/);
+ sdk.unloadDexWrappers();
+ }
+ }
+
+ /**
+ * Nothing to do. We'll reparse the SDK later in onSdkReload.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public void postInstallHook() {
+ }
+
+ /**
+ * Reparse the SDK in case anything was add/removed.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public void onSdkReload() {
+ AdtPlugin.getDefault().reparseSdk();
+ }
+ };
+
+ window.addListener(listener);
+ window.open();
+
+ return true;
+ }
+
+ @Override
+ public void selectionChanged(IAction action, ISelection selection) {
+ // nothing related to the current selection.
+ }
+
+ @Override
+ public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+ // nothing to do.
+ }
+}