PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/AddSupportJarAction.java

https://bitbucket.org/codefirex/sdk
Java | 533 lines | 338 code | 62 blank | 133 comment | 58 complexity | 7c4dfdd233b6cc568e8c02cbf3f0d346 MD5 | raw file
  1. /*
  2. * Copyright (C) 2011 The Android Open Source Project
  3. *
  4. * Licensed under the Eclipse Public License, Version 1.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.eclipse.org/org/documents/epl-v10.php
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.android.ide.eclipse.adt.internal.actions;
  17. import com.android.SdkConstants;
  18. import com.android.annotations.Nullable;
  19. import com.android.ide.eclipse.adt.AdtConstants;
  20. import com.android.ide.eclipse.adt.AdtPlugin;
  21. import com.android.ide.eclipse.adt.AdtUtils;
  22. import com.android.ide.eclipse.adt.internal.sdk.AdtConsoleSdkLog;
  23. import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
  24. import com.android.ide.eclipse.adt.internal.sdk.Sdk;
  25. import com.android.sdklib.SdkManager;
  26. import com.android.sdklib.internal.project.ProjectProperties;
  27. import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
  28. import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy;
  29. import com.android.sdklib.io.FileOp;
  30. import com.android.sdkuilib.internal.repository.ui.AdtUpdateDialog;
  31. import com.android.utils.NullLogger;
  32. import com.android.utils.Pair;
  33. import org.eclipse.core.filesystem.EFS;
  34. import org.eclipse.core.filesystem.IFileStore;
  35. import org.eclipse.core.filesystem.IFileSystem;
  36. import org.eclipse.core.resources.IFile;
  37. import org.eclipse.core.resources.IFolder;
  38. import org.eclipse.core.resources.IProject;
  39. import org.eclipse.core.resources.IProjectDescription;
  40. import org.eclipse.core.resources.IResource;
  41. import org.eclipse.core.resources.IWorkspace;
  42. import org.eclipse.core.resources.IWorkspaceRoot;
  43. import org.eclipse.core.resources.ResourcesPlugin;
  44. import org.eclipse.core.runtime.CoreException;
  45. import org.eclipse.core.runtime.IAdaptable;
  46. import org.eclipse.core.runtime.IPath;
  47. import org.eclipse.core.runtime.IProgressMonitor;
  48. import org.eclipse.core.runtime.IStatus;
  49. import org.eclipse.core.runtime.NullProgressMonitor;
  50. import org.eclipse.core.runtime.Status;
  51. import org.eclipse.core.runtime.jobs.Job;
  52. import org.eclipse.jdt.core.IJavaProject;
  53. import org.eclipse.jdt.core.JavaCore;
  54. import org.eclipse.jface.action.IAction;
  55. import org.eclipse.jface.viewers.ISelection;
  56. import org.eclipse.jface.viewers.IStructuredSelection;
  57. import org.eclipse.ui.IObjectActionDelegate;
  58. import org.eclipse.ui.IWorkbenchPart;
  59. import org.eclipse.ui.IWorkbenchWindow;
  60. import org.eclipse.ui.IWorkbenchWindowActionDelegate;
  61. import java.io.File;
  62. import java.io.IOException;
  63. import java.util.Iterator;
  64. import java.util.Map;
  65. /**
  66. * An action to add the android-support-v4.jar support library
  67. * to the selected project.
  68. * <p/>
  69. * This should be used by the GLE. The action itself is currently more
  70. * like an example of how to invoke the new {@link AdtUpdateDialog}.
  71. * <p/>
  72. * TODO: make this more configurable.
  73. */
  74. public class AddSupportJarAction implements IObjectActionDelegate {
  75. /** The vendor ID of the support library. */
  76. private static final String VENDOR_ID = "android"; //$NON-NLS-1$
  77. /** The path ID of the support library. */
  78. private static final String SUPPORT_ID = "support"; //$NON-NLS-1$
  79. /** The path ID of the compatibility library (which was its id for releases 1-3). */
  80. private static final String COMPATIBILITY_ID = "compatibility"; //$NON-NLS-1$
  81. private static final String FD_GRIDLAYOUT = "gridlayout"; //$NON-NLS-1$
  82. private static final String FD_V7 = "v7"; //$NON-NLS-1$
  83. private static final String FD_V4 = "v4"; //$NON-NLS-1$
  84. private static final String ANDROID_SUPPORT_V4_JAR = "android-support-v4.jar"; //$NON-NLS-1$
  85. private ISelection mSelection;
  86. /**
  87. * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
  88. */
  89. @Override
  90. public void setActivePart(IAction action, IWorkbenchPart targetPart) {
  91. }
  92. @Override
  93. public void run(IAction action) {
  94. if (mSelection instanceof IStructuredSelection) {
  95. for (Iterator<?> it = ((IStructuredSelection) mSelection).iterator();
  96. it.hasNext();) {
  97. Object element = it.next();
  98. IProject project = null;
  99. if (element instanceof IProject) {
  100. project = (IProject) element;
  101. } else if (element instanceof IAdaptable) {
  102. project = (IProject) ((IAdaptable) element)
  103. .getAdapter(IProject.class);
  104. }
  105. if (project != null) {
  106. install(project);
  107. }
  108. }
  109. }
  110. }
  111. @Override
  112. public void selectionChanged(IAction action, ISelection selection) {
  113. mSelection = selection;
  114. }
  115. /**
  116. * Install the support jar into the given project.
  117. *
  118. * @param project The Android project to install the support jar into
  119. * @return true if the installation was successful
  120. */
  121. public static boolean install(final IProject project) {
  122. File jarPath = installSupport(-1);
  123. if (jarPath != null) {
  124. try {
  125. return copyJarIntoProject(project, jarPath) != null;
  126. } catch (Exception e) {
  127. AdtPlugin.log(e, null);
  128. }
  129. }
  130. return false;
  131. }
  132. /**
  133. * Installs the Android Support library into the SDK extras/ folder. If a minimum
  134. * revision number is specified, this method will check whether the package is already
  135. * installed, and if the installed revision is at least as high as the requested revision,
  136. * this method will exit without performing an update.
  137. *
  138. * @param minimumRevision a minimum revision, or -1 to upgrade
  139. * unconditionally. Note that this does <b>NOT</b> specify which
  140. * revision to install; the latest version will always be
  141. * installed.
  142. * @return the location of the support jar file, or null if something went
  143. * wrong
  144. */
  145. @Nullable
  146. public static File installSupport(int minimumRevision) {
  147. final Sdk sdk = Sdk.getCurrent();
  148. if (sdk == null) {
  149. AdtPlugin.printErrorToConsole(
  150. AddSupportJarAction.class.getSimpleName(), // tag
  151. "Error: Android SDK is not loaded yet."); //$NON-NLS-1$
  152. return null;
  153. }
  154. String sdkLocation = sdk.getSdkLocation();
  155. if (minimumRevision > 0) {
  156. File path = getSupportJarFile();
  157. if (path != null) {
  158. assert path.exists(); // guaranteed by the getSupportJarFile call
  159. int installedRevision = getInstalledRevision();
  160. if (installedRevision != -1 && minimumRevision <= installedRevision) {
  161. return path;
  162. }
  163. }
  164. }
  165. // TODO: For the generic action, check the library isn't in the project already.
  166. // First call the package manager to make sure the package is installed
  167. // and get the installation path of the library.
  168. AdtUpdateDialog window = new AdtUpdateDialog(
  169. AdtPlugin.getShell(),
  170. new AdtConsoleSdkLog(),
  171. sdkLocation);
  172. Pair<Boolean, File> result = window.installExtraPackage(VENDOR_ID, SUPPORT_ID);
  173. // TODO: Make sure the version is at the required level; we know we need at least one
  174. // containing the v7 support
  175. if (!result.getFirst().booleanValue()) {
  176. AdtPlugin.printErrorToConsole("Failed to install Android Support library");
  177. return null;
  178. }
  179. // TODO these "v4" values needs to be dynamic, e.g. we could try to match
  180. // vN/android-support-vN.jar. Eventually we'll want to rely on info from the
  181. // package manifest anyway so this is irrelevant.
  182. File path = new File(result.getSecond(), FD_V4);
  183. final File jarPath = new File(path, ANDROID_SUPPORT_V4_JAR);
  184. if (!jarPath.isFile()) {
  185. AdtPlugin.printErrorToConsole("Android Support Jar not found:",
  186. jarPath.getAbsolutePath());
  187. return null;
  188. }
  189. return jarPath;
  190. }
  191. /**
  192. * Returns the installed revision number of the Android Support
  193. * library, or -1 if the package is not installed.
  194. *
  195. * @return the installed revision number, or -1
  196. */
  197. public static int getInstalledRevision() {
  198. final Sdk sdk = Sdk.getCurrent();
  199. if (sdk != null) {
  200. String sdkLocation = sdk.getSdkLocation();
  201. SdkManager manager = SdkManager.createManager(sdkLocation, NullLogger.getLogger());
  202. Map<String, Integer> versions = manager.getExtrasVersions();
  203. Integer version = versions.get(VENDOR_ID + '/' + SUPPORT_ID);
  204. if (version == null) {
  205. // Check the old compatibility library. When the library is updated in-place
  206. // the manager doesn't change its folder name (since that is a source of
  207. // endless issues on Windows.)
  208. version = versions.get(VENDOR_ID + '/' + COMPATIBILITY_ID);
  209. }
  210. if (version != null) {
  211. return version.intValue();
  212. }
  213. }
  214. return -1;
  215. }
  216. /**
  217. * Similar to {@link #install}, but rather than copy a jar into the given
  218. * project, it creates a new library project in the workspace for the
  219. * support library, and adds a library dependency on the newly
  220. * installed library from the given project.
  221. *
  222. * @param project the project to add a dependency on the library to
  223. * @param waitForFinish If true, block until the task has finished
  224. * @return true if the installation was successful (or if
  225. * <code>waitForFinish</code> is false, if the installation is
  226. * likely to be successful - e.g. the user has at least agreed to
  227. * all installation prompts.)
  228. */
  229. public static boolean installGridLayoutLibrary(final IProject project, boolean waitForFinish) {
  230. final IJavaProject javaProject = JavaCore.create(project);
  231. if (javaProject != null) {
  232. File supportPath = getSupportPackageDir();
  233. if (!supportPath.isDirectory()) {
  234. File path = installSupport(8); // GridLayout arrived in rev 7 and fixed in rev 8
  235. if (path == null) {
  236. return false;
  237. }
  238. assert path.equals(supportPath);
  239. }
  240. File libraryPath = new File(supportPath, FD_V7 + File.separator + FD_GRIDLAYOUT);
  241. if (!libraryPath.isDirectory()) {
  242. // Upgrade support package: it's out of date. The SDK manager will
  243. // perform an upgrade to the latest version if the package is already installed.
  244. File path = installSupport(-1);
  245. if (path == null) {
  246. return false;
  247. }
  248. assert path.equals(libraryPath) : path;
  249. }
  250. // Create workspace copy of the project and add library dependency
  251. IProject libraryProject = createLibraryProject(libraryPath, project, waitForFinish);
  252. if (libraryProject != null) {
  253. return addLibraryDependency(libraryProject, project, waitForFinish);
  254. }
  255. }
  256. return false;
  257. }
  258. /**
  259. * Returns the directory containing the support libraries (v4, v7, v13,
  260. * ...), which may or may not exist
  261. *
  262. * @return a path to the support library or null
  263. */
  264. private static File getSupportPackageDir() {
  265. final Sdk sdk = Sdk.getCurrent();
  266. if (sdk != null) {
  267. String sdkLocation = sdk.getSdkLocation();
  268. SdkManager manager = SdkManager.createManager(sdkLocation, NullLogger.getLogger());
  269. Map<String, Integer> versions = manager.getExtrasVersions();
  270. Integer version = versions.get(VENDOR_ID + '/' + SUPPORT_ID);
  271. if (version != null) {
  272. File supportPath = new File(sdkLocation,
  273. SdkConstants.FD_EXTRAS + File.separator
  274. + VENDOR_ID + File.separator
  275. + SUPPORT_ID);
  276. return supportPath;
  277. }
  278. // Check the old compatibility library. When the library is updated in-place
  279. // the manager doesn't change its folder name (since that is a source of
  280. // endless issues on Windows.)
  281. version = versions.get(VENDOR_ID + '/' + COMPATIBILITY_ID);
  282. if (version != null) {
  283. File supportPath = new File(sdkLocation,
  284. SdkConstants.FD_EXTRAS + File.separator
  285. + VENDOR_ID + File.separator
  286. + COMPATIBILITY_ID);
  287. return supportPath;
  288. }
  289. }
  290. return null;
  291. }
  292. /**
  293. * Returns a path to the installed jar file for the support library,
  294. * or null if it does not exist
  295. *
  296. * @return a path to the v4.jar or null
  297. */
  298. @Nullable
  299. public static File getSupportJarFile() {
  300. File supportDir = getSupportPackageDir();
  301. if (supportDir != null) {
  302. File path = new File(supportDir, FD_V4 + File.separator + ANDROID_SUPPORT_V4_JAR);
  303. if (path.exists()) {
  304. return path;
  305. }
  306. }
  307. return null;
  308. }
  309. /**
  310. * Creates a library project in the Eclipse workspace out of the grid layout project
  311. * in the SDK tree.
  312. *
  313. * @param libraryPath the path to the directory tree containing the project contents
  314. * @param project the project to copy the SDK target out of
  315. * @param waitForFinish whether the operation should finish before this method returns
  316. * @return a library project, or null if it fails for some reason
  317. */
  318. private static IProject createLibraryProject(
  319. final File libraryPath,
  320. final IProject project,
  321. boolean waitForFinish) {
  322. // Install a new library into the workspace. This is a copy rather than
  323. // a reference to the support library version such that modifications
  324. // do not modify the pristine copy in the SDK install area.
  325. final IProject newProject;
  326. try {
  327. IProgressMonitor monitor = new NullProgressMonitor();
  328. IWorkspace workspace = ResourcesPlugin.getWorkspace();
  329. IWorkspaceRoot root = workspace.getRoot();
  330. String name = AdtUtils.getUniqueProjectName(
  331. "gridlayout_v7", "_"); //$NON-NLS-1$ //$NON-NLS-2$
  332. newProject = root.getProject(name);
  333. IProjectDescription description = workspace.newProjectDescription(name);
  334. String[] natures = new String[] { AdtConstants.NATURE_DEFAULT, JavaCore.NATURE_ID };
  335. description.setNatureIds(natures);
  336. newProject.create(description, monitor);
  337. // Copy in the files recursively
  338. IFileSystem fileSystem = EFS.getLocalFileSystem();
  339. IFileStore sourceDir = fileSystem.getStore(libraryPath.toURI());
  340. IFileStore destDir = fileSystem.getStore(newProject.getLocationURI());
  341. sourceDir.copy(destDir, EFS.OVERWRITE, null);
  342. // Make sure the src folder exists
  343. destDir.getChild("src").mkdir(0, null /*monitor*/);
  344. // Set the android platform to the same level as the calling project
  345. ProjectState state = Sdk.getProjectState(project);
  346. String target = state.getProperties().getProperty(ProjectProperties.PROPERTY_TARGET);
  347. if (target != null && target.length() > 0) {
  348. ProjectProperties properties = ProjectProperties.load(libraryPath.getPath(),
  349. PropertyType.PROJECT);
  350. ProjectPropertiesWorkingCopy copy = properties.makeWorkingCopy();
  351. copy.setProperty(ProjectProperties.PROPERTY_TARGET, target);
  352. try {
  353. copy.save();
  354. } catch (Exception e) {
  355. AdtPlugin.log(e, null);
  356. }
  357. }
  358. newProject.open(monitor);
  359. return newProject;
  360. } catch (CoreException e) {
  361. AdtPlugin.log(e, null);
  362. return null;
  363. }
  364. }
  365. /**
  366. * Adds a library dependency on the given library into the given project.
  367. *
  368. * @param libraryProject the library project to depend on
  369. * @param dependentProject the project to write the dependency into
  370. * @param waitForFinish whether this method should wait for the job to
  371. * finish
  372. * @return true if the operation succeeded
  373. */
  374. public static boolean addLibraryDependency(
  375. final IProject libraryProject,
  376. final IProject dependentProject,
  377. boolean waitForFinish) {
  378. // Now add library dependency
  379. // Run an Eclipse asynchronous job to update the project
  380. Job job = new Job("Add Support Library Dependency to Project") {
  381. @Override
  382. protected IStatus run(IProgressMonitor monitor) {
  383. try {
  384. monitor.beginTask("Add library dependency to project build path", 3);
  385. monitor.worked(1);
  386. // TODO: Add library project to the project.properties file!
  387. ProjectState state = Sdk.getProjectState(dependentProject);
  388. ProjectPropertiesWorkingCopy mPropertiesWorkingCopy =
  389. state.getProperties().makeWorkingCopy();
  390. // Get the highest version number of the libraries; there cannot be any
  391. // gaps so we will assign the next library the next number
  392. int nextVersion = 1;
  393. for (String property : mPropertiesWorkingCopy.keySet()) {
  394. if (property.startsWith(ProjectProperties.PROPERTY_LIB_REF)) {
  395. String s = property.substring(
  396. ProjectProperties.PROPERTY_LIB_REF.length());
  397. int version = Integer.parseInt(s);
  398. if (version >= nextVersion) {
  399. nextVersion = version + 1;
  400. }
  401. }
  402. }
  403. IPath relativePath = libraryProject.getLocation().makeRelativeTo(
  404. dependentProject.getLocation());
  405. mPropertiesWorkingCopy.setProperty(
  406. ProjectProperties.PROPERTY_LIB_REF + nextVersion,
  407. relativePath.toString());
  408. try {
  409. mPropertiesWorkingCopy.save();
  410. IResource projectProp = dependentProject.findMember(
  411. SdkConstants.FN_PROJECT_PROPERTIES);
  412. projectProp.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor());
  413. } catch (Exception e) {
  414. String msg = String.format(
  415. "Failed to save %1$s for project %2$s",
  416. SdkConstants.FN_PROJECT_PROPERTIES, dependentProject.getName());
  417. AdtPlugin.log(e, msg);
  418. }
  419. // Project fix-ups
  420. Job fix = FixProjectAction.createFixProjectJob(libraryProject);
  421. fix.schedule();
  422. fix.join();
  423. monitor.worked(1);
  424. return Status.OK_STATUS;
  425. } catch (Exception e) {
  426. return new Status(Status.ERROR, AdtPlugin.PLUGIN_ID, Status.ERROR,
  427. "Failed", e); //$NON-NLS-1$
  428. } finally {
  429. if (monitor != null) {
  430. monitor.done();
  431. }
  432. }
  433. }
  434. };
  435. job.schedule();
  436. if (waitForFinish) {
  437. try {
  438. job.join();
  439. return job.getState() == IStatus.OK;
  440. } catch (InterruptedException e) {
  441. AdtPlugin.log(e, null);
  442. }
  443. }
  444. return true;
  445. }
  446. private static IResource copyJarIntoProject(
  447. IProject project,
  448. File jarPath) throws IOException, CoreException {
  449. IFolder resFolder = project.getFolder(SdkConstants.FD_NATIVE_LIBS);
  450. if (!resFolder.exists()) {
  451. resFolder.create(IResource.FORCE, true /*local*/, null);
  452. }
  453. IFile destFile = resFolder.getFile(jarPath.getName());
  454. IPath loc = destFile.getLocation();
  455. File destPath = loc.toFile();
  456. // Only modify the file if necessary so that we don't trigger unnecessary recompilations
  457. FileOp f = new FileOp();
  458. if (!f.isFile(destPath) || !f.isSameFile(jarPath, destPath)) {
  459. f.copyFile(jarPath, destPath);
  460. // Make sure Eclipse discovers java.io file changes
  461. resFolder.refreshLocal(1, new NullProgressMonitor());
  462. }
  463. return destFile;
  464. }
  465. /**
  466. * @see IWorkbenchWindowActionDelegate#init
  467. */
  468. public void init(IWorkbenchWindow window) {
  469. // pass
  470. }
  471. }