* has this add-ons installed, we'll use that one.\r
*\r
* @param osSdkRoot The OS path of the SDK root folder.\r
+ * @param suggestedDir A suggestion for the installation folder name, based on the root\r
+ * folder used in the zip archive.\r
+ * @param sdkManager An existing SDK manager to list current platforms and addons.\r
* @return A new {@link File} corresponding to the directory to use to install this package.\r
*/\r
@Override\r
- public File getInstallFolder(String osSdkRoot) {\r
+ public File getInstallFolder(String osSdkRoot, String suggestedDir, SdkManager sdkManager) {\r
File addons = new File(osSdkRoot, SdkConstants.FD_ADDONS);\r
\r
- String name = String.format("%s-%d", getName(), getApiLevel()); // $NON-NLS-1$\r
+ // First find if this add-on is already installed. If so, reuse the same directory.\r
+ for (IAndroidTarget target : sdkManager.getTargets()) {\r
+ if (!target.isPlatform() &&\r
+ target.getApiVersionNumber() == getApiLevel() &&\r
+ target.getName().equals(getName()) &&\r
+ target.getVendor().equals(getVendor())) {\r
+ return new File(target.getLocation());\r
+ }\r
+ }\r
+\r
+ // Otherwise, see about reusing the suggestedDir. It must not be already used or\r
+ // add some index to it, or we try to make up one.\r
+ String name = suggestedDir;\r
\r
- // FIXME this will fail if the name is not ASCII compatible. This could easily\r
- // happen: a Chinese or Japanese name etc for example,\r
- // to name a few.\r
- name = name.toLowerCase();\r
- name = name.replaceAll("[^a-zA-Z0-9_-]+", "_"); // $NON-NLS-1$\r
- name = name.replaceAll("_+", "_"); // $NON-NLS-1$\r
+ if (suggestedDir == null || suggestedDir.length() == 0) {\r
+ name = String.format("addon-%s-%s-%d", getName(), getVendor(), getApiLevel()); //$NON-NLS-1$\r
+ name = name.toLowerCase();\r
+ name = name.replaceAll("[^a-z0-9_-]+", "_"); //$NON-NLS-1$ //$NON-NLS-2$\r
+ name = name.replaceAll("_+", "_"); //$NON-NLS-1$ //$NON-NLS-2$\r
+ }\r
\r
- File folder = new File(addons, name);\r
+ for (int i = 0; i < 100; i++) {\r
+ String name2 = i == 0 ? name : String.format("%s-%d", name, i); //$NON-NLS-1$\r
+ File folder = new File(addons, name2);\r
+ if (!folder.exists()) {\r
+ return folder;\r
+ }\r
+ }\r
\r
- // TODO find similar existing addon in addons folder\r
- return folder;\r
+ // We shouldn't really get here. I mean, seriously, we tried hard enough.\r
+ return null;\r
}\r
\r
/**\r
\r
package com.android.sdklib.internal.repository;\r
\r
+import com.android.sdklib.SdkManager;\r
+\r
import java.io.File;\r
import java.io.FileInputStream;\r
import java.io.FileOutputStream;\r
*\r
* @return True if the archive was installed, false otherwise.\r
*/\r
- public boolean install(String osSdkRoot, boolean forceHttp, ITaskMonitor monitor) {\r
+ public boolean install(String osSdkRoot,\r
+ boolean forceHttp,\r
+ SdkManager sdkManager,\r
+ ITaskMonitor monitor) {\r
\r
File archiveFile = null;\r
try {\r
\r
archiveFile = downloadFile(monitor, forceHttp);\r
if (archiveFile != null) {\r
- if (unarchive(osSdkRoot, archiveFile, monitor)) {\r
+ if (unarchive(osSdkRoot, archiveFile, sdkManager, monitor)) {\r
monitor.setResult("Installed: %1$s", name);\r
return true;\r
}\r
/**\r
* Install the given archive in the given folder.\r
*/\r
- private boolean unarchive(String osSdkRoot, File archiveFile, ITaskMonitor monitor) {\r
- String name = getParentPackage().getShortDescription();\r
- String desc = String.format("Installing %1$s", name);\r
- monitor.setDescription(desc);\r
+ private boolean unarchive(String osSdkRoot,\r
+ File archiveFile,\r
+ SdkManager sdkManager,\r
+ ITaskMonitor monitor) {\r
+ String pkgName = getParentPackage().getShortDescription();\r
+ String pkgDesc = String.format("Installing %1$s", pkgName);\r
+ monitor.setDescription(pkgDesc);\r
+\r
+ // We always unzip in a temp folder which name depends on the package type\r
+ // (e.g. addon, tools, etc.) and then move the folder to the destination folder.\r
+ // If the destination folder exists, it will be renamed and deleted at the very\r
+ // end if everything succeeded.\r
\r
- File destFolder = getParentPackage().getInstallFolder(osSdkRoot);\r
+ String pkgKind = getParentPackage().getClass().getSimpleName();\r
\r
- File unzipDestFolder = destFolder;\r
+ File destFolder = null;\r
+ File unzipDestFolder = null;\r
File renamedDestFolder = null;\r
\r
try {\r
- // If this folder already exists, unzip in a temporary folder and then move/unlink.\r
- if (destFolder.exists()) {\r
- // Find a new temp folder that doesn't exist yet\r
- unzipDestFolder = findTempFolder(destFolder, "new"); //$NON-NLS-1$\r
+ // Find a new temp folder that doesn't exist yet\r
+ unzipDestFolder = findTempFolder(osSdkRoot, pkgKind, "new"); //$NON-NLS-1$\r
\r
- if (unzipDestFolder == null) {\r
- // this should not seriously happen.\r
- monitor.setResult("Failed to find a suitable temp directory similar to %1$s.",\r
- destFolder.getPath());\r
- return false;\r
- }\r
+ if (unzipDestFolder == null) {\r
+ // this should not seriously happen.\r
+ monitor.setResult("Failed to find a temp directory in %1$s.", osSdkRoot);\r
+ return false;\r
}\r
\r
if (!unzipDestFolder.mkdirs()) {\r
- monitor.setResult("Failed to create directory %1$s",\r
- unzipDestFolder.getPath());\r
+ monitor.setResult("Failed to create directory %1$s", unzipDestFolder.getPath());\r
+ return false;\r
+ }\r
+\r
+ String[] zipRootFolder = new String[] { null };\r
+ if (!unzipFolder(archiveFile, getSize(),\r
+ unzipDestFolder, pkgDesc,\r
+ zipRootFolder, monitor)) {\r
return false;\r
}\r
\r
- if (!unzipFolder(archiveFile, getSize(), unzipDestFolder, desc, monitor)) {\r
+ // Compute destination directory\r
+ destFolder = getParentPackage().getInstallFolder(\r
+ osSdkRoot, zipRootFolder[0], sdkManager);\r
+\r
+ if (destFolder == null) {\r
+ // this should not seriously happen.\r
+ monitor.setResult("Failed to compute installation directory for %1$s.", pkgName);\r
return false;\r
}\r
\r
- if (unzipDestFolder != destFolder) {\r
- // Swap the old folder by the new one.\r
- // Both original folders will be deleted in the finally clause below.\r
- renamedDestFolder = findTempFolder(destFolder, "old"); //$NON-NLS-1$\r
+ // Swap the old folder by the new one.\r
+ if (destFolder.isDirectory()) {\r
+ renamedDestFolder = findTempFolder(osSdkRoot, pkgKind, "old"); //$NON-NLS-1$\r
if (renamedDestFolder == null) {\r
// this should not seriously happen.\r
- monitor.setResult("Failed to find a suitable temp directory similar to %1$s.",\r
- destFolder.getPath());\r
+ monitor.setResult("Failed to find a temp directory in %1$s.", osSdkRoot);\r
return false;\r
}\r
\r
return false;\r
\r
}\r
- if (!unzipDestFolder.renameTo(destFolder)) {\r
- monitor.setResult("Failed to rename directory %1$s to %2$s",\r
- unzipDestFolder.getPath(), destFolder.getPath());\r
- return false;\r
- }\r
}\r
\r
+ if (!unzipDestFolder.renameTo(destFolder)) {\r
+ monitor.setResult("Failed to rename directory %1$s to %2$s",\r
+ unzipDestFolder.getPath(), destFolder.getPath());\r
+ return false;\r
+ }\r
+\r
+ unzipDestFolder = null;\r
return true;\r
\r
} finally {\r
// Cleanup if the unzip folder is still set.\r
deleteFileOrFolder(renamedDestFolder);\r
- if (unzipDestFolder != destFolder) {\r
- deleteFileOrFolder(unzipDestFolder);\r
- }\r
+ deleteFileOrFolder(unzipDestFolder);\r
}\r
}\r
\r
+ /**\r
+ * Unzips a zip file into the given destination directory.\r
+ *\r
+ * The archive file MUST have a unique "root" folder. This root folder is skipped when\r
+ * unarchiving. However we return that root folder name to the caller, as it can be used\r
+ * as a template to know what destination directory to use in the Add-on case.\r
+ */\r
private boolean unzipFolder(File archiveFile,\r
long compressedSize,\r
File unzipDestFolder,\r
String description,\r
+ String[] outZipRootFolder,\r
ITaskMonitor monitor) {\r
\r
description += " (%1$d%%)";\r
if (pos < 0 || pos == name.length() - 1) {\r
continue;\r
} else {\r
+ if (outZipRootFolder[0] == null && pos > 0) {\r
+ outZipRootFolder[0] = name.substring(0, pos);\r
+ }\r
name = name.substring(pos + 1);\r
}\r
\r
}\r
\r
/**\r
- * Finds a temp folder which name is similar to the one of the ideal folder\r
- * and with a ".tmpN" appended.\r
+ * Finds a temp folder in the form of osBasePath/temp/prefix.suffixNNN.\r
* <p/>\r
* This operation is not atomic so there's no guarantee the folder can't get\r
* created in between. This is however unlikely and the caller can assume the\r
* returned folder does not exist yet.\r
* <p/>\r
- * Returns null if no such folder can be found (e.g. if all candidates exist),\r
- * which is rather unlikely.\r
+ * Returns null if no such folder can be found (e.g. if all candidates exist,\r
+ * which is rather unlikely) or if the base temp folder cannot be created.\r
*/\r
- private File findTempFolder(File idealFolder, String suffix) {\r
- String basePath = idealFolder.getPath();\r
+ private File findTempFolder(String osBasePath, String prefix, String suffix) {\r
+ File baseTempFolder = new File(osBasePath, "temp");\r
+\r
+ if (!baseTempFolder.isDirectory()) {\r
+ if (!baseTempFolder.mkdirs()) {\r
+ return null;\r
+ }\r
+ }\r
\r
for (int i = 1; i < 100; i++) {\r
- File folder = new File(String.format("%1$s.%2$s%3$02d", basePath, suffix, i)); //$NON-NLS-1$\r
+ File folder = new File(baseTempFolder,\r
+ String.format("%1$s.%2$s%3$02d", prefix, suffix, i)); //$NON-NLS-1$\r
if (!folder.exists()) {\r
return folder;\r
}\r
package com.android.sdklib.internal.repository;\r
\r
import com.android.sdklib.IAndroidTarget;\r
-import com.android.sdklib.ISdkLog;\r
import com.android.sdklib.SdkConstants;\r
import com.android.sdklib.SdkManager;\r
import com.android.sdklib.internal.repository.Archive.Arch;\r
}\r
\r
/**\r
- * Returns the packages found by the last call to {@link #parseSdk(String)}.\r
+ * Returns the packages found by the last call to {@link #parseSdk(String, SdkManager)}.\r
*/\r
public Package[] getPackages() {\r
return mPackages;\r
\r
/**\r
* Clear the internal packages list. After this call, {@link #getPackages()} will return\r
- * null till {@link #parseSdk(String)} is called.\r
+ * null till {@link #parseSdk(String, SdkManager)} is called.\r
*/\r
public void clearPackages() {\r
mPackages = null;\r
* at any time later.\r
*\r
* @param osSdkRoot The path to the SDK folder.\r
+ * @param sdkManager An existing SDK manager to list current platforms and addons.\r
* @return The packages found. Can be retrieved later using {@link #getPackages()}.\r
*/\r
- public Package[] parseSdk(String osSdkRoot) {\r
+ public Package[] parseSdk(String osSdkRoot, SdkManager sdkManager) {\r
ArrayList<Package> packages = new ArrayList<Package>();\r
\r
Package pkg = scanDoc(new File(osSdkRoot, SdkConstants.FD_DOCS));\r
}\r
\r
// for platforms and add-ons, rely on the SdkManager parser\r
- SdkManager sdkman = SdkManager.createManager(osSdkRoot, new ISdkLog() {\r
- // A dummy sdk logger that doesn't log anything.\r
- public void error(Throwable t, String errorFormat, Object... args) {\r
- // pass\r
- }\r
- public void printf(String msgFormat, Object... args) {\r
- // pass\r
- }\r
- public void warning(String warningFormat, Object... args) {\r
- // pass\r
- }\r
- });\r
-\r
- for(IAndroidTarget target : sdkman.getTargets()) {\r
+ for(IAndroidTarget target : sdkManager.getTargets()) {\r
pkg = null;\r
\r
if (target.isPlatform()) {\r