diff options
author | Raphael Moll <ralf@android.com> | 2011-05-18 21:16:38 -0700 |
---|---|---|
committer | Raphael Moll <ralf@android.com> | 2011-05-20 11:59:43 -0700 |
commit | 9c7a0fd82e89b2f637919ff4371213b87624ae6e (patch) | |
tree | 59c6b5d584b47dc4d4ef7f29ccbc50bb14d0ace1 /eclipse | |
parent | 20ab1d2b7251aef8546f3c63e1a8b8eb006c0180 (diff) | |
download | sdk-9c7a0fd82e89b2f637919ff4371213b87624ae6e.zip sdk-9c7a0fd82e89b2f637919ff4371213b87624ae6e.tar.gz sdk-9c7a0fd82e89b2f637919ff4371213b87624ae6e.tar.bz2 |
SDK Manager dialog to perform specific updates from ADT.
As a working case example, this adds an ADT project
context menu to add the Android Compatibility JAR
to an android project.
Change-Id: Icd86930b72558240dc9f5a6f732478253b8cb0fb
Diffstat (limited to 'eclipse')
3 files changed, 354 insertions, 7 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml index 0a5cbb4..9e1a770 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml +++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml @@ -261,6 +261,15 @@ label="Fix Project Properties" menubarPath="com.android.ide.eclipse.adt.AndroidTools/group3"/> <action + class="com.android.ide.eclipse.adt.internal.actions.AddCompatibilityJarAction" + enablesFor="1" + icon="icons/android.png" + id="com.android.ide.eclipse.adt.wizards.actions.AddCompatibilityJarAction" + label="Add Compatibility Library..." + menubarPath="com.android.ide.eclipse.adt.AndroidTools/group3" + tooltip="Add the Compatibility Library to the project"> + </action> + <action class="com.android.ide.eclipse.adt.internal.refactorings.renamepackage.RenamePackageAction" enablesFor="1" id="com.android.ide.eclipse.adt.project.RenamePackageAction" 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 new file mode 100755 index 0000000..9b960cb --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddCompatibilityJarAction.java @@ -0,0 +1,317 @@ +/* + * 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.ide.eclipse.adt.AdtPlugin; +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.Sdk; +import com.android.sdklib.SdkConstants; +import com.android.sdkuilib.internal.repository.AdtUpdateDialog; +import com.android.util.Pair; + +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.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.Status; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.core.runtime.jobs.Job; +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 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.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; + +/** + * An action to add the android-support-v4.jar compatibility 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 AddCompatibilityJarAction implements IObjectActionDelegate { + + private ISelection mSelection; + + /** + * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart) + */ + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + } + + 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) { + updateProject(project); + } + } + } + } + + public void selectionChanged(IAction action, ISelection selection) { + mSelection = selection; + } + + private void updateProject(final IProject project) { + + final IJavaProject javaProject = JavaCore.create(project); + if (javaProject == null) { + // Should not happen. + AdtPlugin.log(IStatus.ERROR, "JavaProject is null for %1$s", project); //$NON-NLS-1$ + } + + final Sdk sdk = Sdk.getCurrent(); + if (sdk == null) { + AdtPlugin.printErrorToConsole( + this.getClass().getSimpleName(), // tag + "Error: Android SDK is not loaded yet."); //$NON-NLS-1$ + return; + } + + // 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.getDisplay().getActiveShell(), + new AdtConsoleSdkLog(), + sdk.getSdkLocation()); + + Pair<Boolean, File> result = window.installExtraPackage( + "android", "compatibility"); //$NON-NLS-1$ //$NON-NLS-2$ + + if (!result.getFirst().booleanValue()) { + AdtPlugin.printErrorToConsole("Failed to install Android Compatibility library"); + return; + } + + // 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(), "v4"); //$NON-NLS-1$ + final File jarPath = new File(path, "android-support-v4.jar"); //$NON-NLS-1$ + + if (!jarPath.isFile()) { + AdtPlugin.printErrorToConsole("Android Compatibility JAR not found:", + jarPath.getAbsolutePath()); + return; + } + + // Then run an Eclipse asynchronous job to update the project + + new Job("Add Compatibility Library to Project") { + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + monitor.beginTask("Add library to project build path", 3); + + IResource jarRes = copyJarIntoProject(project, jarPath, monitor); + + monitor.worked(1); + + IClasspathEntry libEntry = JavaCore.newLibraryEntry( + jarRes.getFullPath(), + null /*sourceAttachmentPath*/, + null /*sourceAttachmentRootPath*/ ); + + if (!ProjectHelper.isEntryInClasspath(javaProject, libEntry)) { + ProjectHelper.addEntryToClasspath(javaProject, libEntry); + } + + monitor.worked(1); + + return Status.OK_STATUS; + } catch (JavaModelException e) { + return e.getJavaModelStatus(); + } catch (CoreException e) { + return e.getStatus(); + } catch (Exception e) { + return new Status(Status.ERROR, AdtPlugin.PLUGIN_ID, Status.ERROR, + "Failed", e); //$NON-NLS-1$ + } finally { + if (monitor != null) { + monitor.done(); + } + } + } + }.schedule(); + } + + private IResource copyJarIntoProject( + IProject project, + File jarPath, + IProgressMonitor monitor) throws IOException, CoreException { + IFolder resFolder = project.getFolder(SdkConstants.FD_NATIVE_LIBS); + if (!resFolder.exists()) { + resFolder.create(IResource.FORCE, true /*local*/, new SubProgressMonitor(monitor, 1)); + } + + 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 + if (!destPath.isFile() || !isSameFile(jarPath, destPath)) { + copyFile(jarPath, destPath); + } + + return destFile; + } + + /** + * Checks whether 2 binary files are the same. + * + * @param source the source file to copy + * @param destination the destination file to write + */ + private boolean isSameFile(File source, File destination) throws IOException { + + if (source.length() != destination.length()) { + return false; + } + + FileInputStream fis1 = null; + FileInputStream fis2 = null; + + try { + fis1 = new FileInputStream(source); + fis2 = new FileInputStream(destination); + + byte[] buffer1 = new byte[8192]; + byte[] buffer2 = new byte[8192]; + + int read1; + while ((read1 = fis1.read(buffer1)) != -1) { + int read2 = 0; + while (read2 < read1) { + int n = fis2.read(buffer2, read2, read1 - read2); + if (n == -1) { + break; + } + } + + if (read2 != read1) { + return false; + } + + if (!Arrays.equals(buffer1, buffer2)) { + return false; + } + } + } finally { + if (fis2 != null) { + try { + fis2.close(); + } catch (IOException e) { + // ignore + } + } + if (fis1 != null) { + try { + fis1.close(); + } catch (IOException e) { + // ignore + } + } + } + + return true; + } + + /** + * Installs a binary file + * + * @param source the source file to copy + * @param destination the destination file to write + */ + private void copyFile(File source, File destination) throws IOException { + FileInputStream fis = null; + FileOutputStream fos = null; + try { + fis = new FileInputStream(source); + fos = new FileOutputStream(destination); + + byte[] buffer = new byte[8192]; + + int read; + while ((read = fis.read(buffer)) != -1) { + fos.write(buffer, 0, read); + } + + } catch (FileNotFoundException e) { + // shouldn't happen since we check before. + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + // ignore + } + } + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + // ignore + } + } + } + } + + /** + * @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/project/ProjectHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java index a8047cb..22af8b0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectHelper.java @@ -57,37 +57,58 @@ public final class ProjectHelper { /** * Adds the corresponding source folder to the class path entries. + * This method does not check whether the entry is already defined in the project. * * @param entries The class path entries to read. A copy will be returned. - * @param new_entry The parent source folder to remove. + * @param newEntry The new class path entry to add. * @return A new class path entries array. */ public static IClasspathEntry[] addEntryToClasspath( - IClasspathEntry[] entries, IClasspathEntry new_entry) { + IClasspathEntry[] entries, IClasspathEntry newEntry) { int n = entries.length; IClasspathEntry[] newEntries = new IClasspathEntry[n + 1]; System.arraycopy(entries, 0, newEntries, 0, n); - newEntries[n] = new_entry; + newEntries[n] = newEntry; return newEntries; } /** * Adds the corresponding source folder to the project's class path entries. + * This method does not check whether the entry is already defined in the project. * * @param javaProject The java project of which path entries to update. - * @param new_entry The parent source folder to remove. + * @param newEntry The new class path entry to add. * @throws JavaModelException */ - public static void addEntryToClasspath( - IJavaProject javaProject, IClasspathEntry new_entry) + public static void addEntryToClasspath(IJavaProject javaProject, IClasspathEntry newEntry) throws JavaModelException { IClasspathEntry[] entries = javaProject.getRawClasspath(); - entries = addEntryToClasspath(entries, new_entry); + entries = addEntryToClasspath(entries, newEntry); javaProject.setRawClasspath(entries, new NullProgressMonitor()); } /** + * Checks whether the given class path entry is already defined in the project. + * + * @param javaProject The java project of which path entries to check. + * @param newEntry The parent source folder to remove. + * @return True if the class path entry is already defined. + * @throws JavaModelException + */ + public static boolean isEntryInClasspath(IJavaProject javaProject, IClasspathEntry newEntry) + throws JavaModelException { + + IClasspathEntry[] entries = javaProject.getRawClasspath(); + for (IClasspathEntry entry : entries) { + if (entry.equals(newEntry)) { + return true; + } + } + return false; + } + + /** * Remove a classpath entry from the array. * @param entries The class path entries to read. A copy will be returned * @param index The index to remove. |