diff options
Diffstat (limited to 'sdkmanager')
6 files changed, 669 insertions, 260 deletions
diff --git a/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java b/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java index f1531e0..9f3fb99 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java +++ b/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java @@ -128,15 +128,49 @@ public class CommandLineProcessor { /** * Raw access to parsed parameter values. - * @param verb The verb name, including {@link #GLOBAL_FLAG_VERB}. - * @param directObject The direct object name, including {@link #NO_VERB_OBJECT}. - * @param longFlagName The long flag name for the given action. + * <p/> + * The default is to scan all parameters. Parameters that have been explicitly set on the + * command line are returned first. Otherwise one with a non-null value is returned. + * <p/> + * Both a verb and a direct object filter can be specified. When they are non-null they limit + * the scope of the search. + * <p/> + * If nothing has been found, return the last default value seen matching the filter. + * + * @param verb The verb name, including {@link #GLOBAL_FLAG_VERB}. If null, all possible + * verbs that match the direct object condition will be examined and the first + * value set will be used. + * @param directObject The direct object name, including {@link #NO_VERB_OBJECT}. If null, + * all possible direct objects that match the verb condition will be examined and + * the first value set will be used. + * @param longFlagName The long flag name for the given action. Mandatory. Cannot be null. * @return The current value object stored in the parameter, which depends on the argument mode. */ public Object getValue(String verb, String directObject, String longFlagName) { - String key = verb + "/" + directObject + "/" + longFlagName; - Arg arg = mArguments.get(key); - return arg.getCurrentValue(); + + if (verb != null && directObject != null) { + String key = verb + "/" + directObject + "/" + longFlagName; + Arg arg = mArguments.get(key); + return arg.getCurrentValue(); + } + + Object lastDefault = null; + for (Arg arg : mArguments.values()) { + if (arg.getLongArg().equals(longFlagName)) { + if (verb == null || arg.getVerb().equals(verb)) { + if (directObject == null || arg.getDirectObject().equals(directObject)) { + if (arg.isInCommandLine()) { + return arg.getCurrentValue(); + } + if (arg.getCurrentValue() != null) { + lastDefault = arg.getCurrentValue(); + } + } + } + } + } + + return lastDefault; } /** @@ -243,6 +277,9 @@ public class CommandLineProcessor { } } } else if (arg != null) { + // This argument was present on the command line + arg.setInCommandLine(true); + // Process keyword String error = null; if (arg.getMode().needsExtra()) { @@ -408,13 +445,15 @@ public class CommandLineProcessor { "Global options:"); listOptions(GLOBAL_FLAG_VERB, NO_VERB_OBJECT); - stdout("\nValid actions are composed of a verb and an optional direct object:"); - for (String[] action : mActions) { - - stdout("- %1$6s %2$-7s: %3$s", - action[ACTION_VERB_INDEX], - action[ACTION_OBJECT_INDEX], - action[ACTION_DESC_INDEX]); + if (verb == null || directObject == null) { + stdout("\nValid actions are composed of a verb and an optional direct object:"); + for (String[] action : mActions) { + + stdout("- %1$6s %2$-7s: %3$s", + action[ACTION_VERB_INDEX], + action[ACTION_OBJECT_INDEX], + action[ACTION_DESC_INDEX]); + } } for (String[] action : mActions) { @@ -575,15 +614,26 @@ public class CommandLineProcessor { * or a String array (in which case the first item is the current by default.) */ static class Arg { + /** Verb for that argument. Never null. */ private final String mVerb; + /** Direct Object for that argument. Never null, but can be empty string. */ private final String mDirectObject; + /** The 1-letter short name of the argument, e.g. -v. */ private final String mShortName; + /** The long name of the argument, e.g. --verbose. */ private final String mLongName; + /** A description. Never null. */ private final String mDescription; + /** A default value. Can be null. */ private final Object mDefaultValue; + /** The argument mode (type + process method). Never null. */ private final MODE mMode; + /** True if this argument is mandatory for this verb/directobject. */ private final boolean mMandatory; + /** Current value. Initially set to the default value. */ private Object mCurrentValue; + /** True if the argument has been used on the command line. */ + private boolean mInCommandLine; /** * Creates a new argument flag description. @@ -612,6 +662,7 @@ public class CommandLineProcessor { mLongName = longName; mDescription = description; mDefaultValue = defaultValue; + mInCommandLine = false; if (defaultValue instanceof String[]) { mCurrentValue = ((String[])defaultValue)[0]; } else { @@ -619,45 +670,65 @@ public class CommandLineProcessor { } } + /** Return true if this argument is mandatory for this verb/directobject. */ public boolean isMandatory() { return mMandatory; } + /** Returns the 1-letter short name of the argument, e.g. -v. */ public String getShortArg() { return mShortName; } + /** Returns the long name of the argument, e.g. --verbose. */ public String getLongArg() { return mLongName; } + /** Returns the description. Never null. */ public String getDescription() { return mDescription; } + /** Returns the verb for that argument. Never null. */ public String getVerb() { return mVerb; } + /** Returns the direct Object for that argument. Never null, but can be empty string. */ public String getDirectObject() { return mDirectObject; } + /** Returns the default value. Can be null. */ public Object getDefaultValue() { return mDefaultValue; } + /** Returns the current value. Initially set to the default value. Can be null. */ public Object getCurrentValue() { return mCurrentValue; } + /** Sets the current value. Can be null. */ public void setCurrentValue(Object currentValue) { mCurrentValue = currentValue; } + /** Returns the argument mode (type + process method). Never null. */ public MODE getMode() { return mMode; } + + /** Returns true if the argument has been used on the command line. */ + public boolean isInCommandLine() { + return mInCommandLine; + } + + /** Sets if the argument has been used on the command line. */ + public void setInCommandLine(boolean inCommandLine) { + mInCommandLine = inCommandLine; + } } /** diff --git a/sdkmanager/app/src/com/android/sdkmanager/Main.java b/sdkmanager/app/src/com/android/sdkmanager/Main.java index 7965e87..3370e76 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/Main.java +++ b/sdkmanager/app/src/com/android/sdkmanager/Main.java @@ -56,8 +56,6 @@ class Main { private ISdkLog mSdkLog; /** The SDK manager parses the SDK folder and gives access to the content. */ private SdkManager mSdkManager; - /** Virtual Machine manager to access the list of AVDs or create new ones. */ - private AvdManager mAvdManager; /** Command-line processor with options specific to SdkManager. */ private SdkCommandLine mSdkCommandLine; /** The working directory, either null or set to an existing absolute canonical directory. */ @@ -201,63 +199,85 @@ class Main { SdkCommandLine.OBJECT_AVD.equals(directObject)) { createAvd(); + } else if (SdkCommandLine.VERB_DELETE.equals(verb) && + SdkCommandLine.OBJECT_AVD.equals(directObject)) { + deleteAvd(); + + } else if (SdkCommandLine.VERB_MOVE.equals(verb) && + SdkCommandLine.OBJECT_AVD.equals(directObject)) { + moveAvd(); + } else if (SdkCommandLine.VERB_CREATE.equals(verb) && SdkCommandLine.OBJECT_PROJECT.equals(directObject)) { - // get the target and try to resolve it. - int targetId = mSdkCommandLine.getCreateProjectTargetId(); - IAndroidTarget[] targets = mSdkManager.getTargets(); - if (targetId < 1 || targetId > targets.length) { - errorAndExit("Target id is not valid. Use '%s list targets' to get the target ids.", - SdkConstants.androidCmdName()); - } - IAndroidTarget target = targets[targetId - 1]; - - ProjectCreator creator = new ProjectCreator(mSdkFolder, - mSdkCommandLine.isVerbose() ? OutputLevel.VERBOSE : - mSdkCommandLine.isSilent() ? OutputLevel.SILENT : - OutputLevel.NORMAL, - mSdkLog); - - String projectDir = getProjectLocation(mSdkCommandLine.getCreateProjectLocation()); - - creator.createProject(projectDir, - mSdkCommandLine.getCreateProjectName(), - mSdkCommandLine.getCreateProjectPackage(), - mSdkCommandLine.getCreateProjectActivity(), - target, - false /* isTestProject*/); + createProject(); } else if (SdkCommandLine.VERB_UPDATE.equals(verb) && SdkCommandLine.OBJECT_PROJECT.equals(directObject)) { - // get the target and try to resolve it. - IAndroidTarget target = null; - int targetId = mSdkCommandLine.getUpdateProjectTargetId(); - if (targetId >= 0) { - IAndroidTarget[] targets = mSdkManager.getTargets(); - if (targetId < 1 || targetId > targets.length) { - errorAndExit("Target id is not valid. Use '%s list targets' to get the target ids.", - SdkConstants.androidCmdName()); - } - target = targets[targetId - 1]; - } - - ProjectCreator creator = new ProjectCreator(mSdkFolder, - mSdkCommandLine.isVerbose() ? OutputLevel.VERBOSE : - mSdkCommandLine.isSilent() ? OutputLevel.SILENT : - OutputLevel.NORMAL, - mSdkLog); - - String projectDir = getProjectLocation(mSdkCommandLine.getUpdateProjectLocation()); - - creator.updateProject(projectDir, - target, - mSdkCommandLine.getUpdateProjectName()); + updateProject(); } else { mSdkCommandLine.printHelpAndExit(null); } } /** + * Creates a new Android project based on command-line parameters + */ + private void createProject() { + // get the target and try to resolve it. + int targetId = mSdkCommandLine.getParamTargetId(); + IAndroidTarget[] targets = mSdkManager.getTargets(); + if (targetId < 1 || targetId > targets.length) { + errorAndExit("Target id is not valid. Use '%s list targets' to get the target ids.", + SdkConstants.androidCmdName()); + } + IAndroidTarget target = targets[targetId - 1]; + + ProjectCreator creator = new ProjectCreator(mSdkFolder, + mSdkCommandLine.isVerbose() ? OutputLevel.VERBOSE : + mSdkCommandLine.isSilent() ? OutputLevel.SILENT : + OutputLevel.NORMAL, + mSdkLog); + + String projectDir = getProjectLocation(mSdkCommandLine.getParamLocationPath()); + + creator.createProject(projectDir, + mSdkCommandLine.getParamName(), + mSdkCommandLine.getParamProjectPackage(), + mSdkCommandLine.getParamProjectActivity(), + target, + false /* isTestProject*/); + } + + /** + * Updates an existing Android project based on command-line parameters + */ + private void updateProject() { + // get the target and try to resolve it. + IAndroidTarget target = null; + int targetId = mSdkCommandLine.getParamTargetId(); + if (targetId >= 0) { + IAndroidTarget[] targets = mSdkManager.getTargets(); + if (targetId < 1 || targetId > targets.length) { + errorAndExit("Target id is not valid. Use '%s list targets' to get the target ids.", + SdkConstants.androidCmdName()); + } + target = targets[targetId - 1]; + } + + ProjectCreator creator = new ProjectCreator(mSdkFolder, + mSdkCommandLine.isVerbose() ? OutputLevel.VERBOSE : + mSdkCommandLine.isSilent() ? OutputLevel.SILENT : + OutputLevel.NORMAL, + mSdkLog); + + String projectDir = getProjectLocation(mSdkCommandLine.getParamLocationPath()); + + creator.updateProject(projectDir, + target, + mSdkCommandLine.getParamName()); + } + + /** * Adjusts the project location to make it absolute & canonical relative to the * working directory, if any. * @@ -325,38 +345,45 @@ class Main { } // get the target skins - String[] skins = target.getSkins(); - mSdkLog.printf(" Skins: "); - if (skins != null) { - boolean first = true; - for (String skin : skins) { - if (first == false) { - mSdkLog.printf(", "); - } else { - first = false; - } - mSdkLog.printf(skin); - } - mSdkLog.printf("\n"); - } else { - mSdkLog.printf("no skins.\n"); - } + displaySkinList(target, " Skins: "); index++; } } + + /** + * Displays the skins valid for the given target. + */ + private void displaySkinList(IAndroidTarget target, String message) { + String[] skins = target.getSkins(); + mSdkLog.printf(message); + if (skins != null) { + boolean first = true; + for (String skin : skins) { + if (first == false) { + mSdkLog.printf(", "); + } else { + first = false; + } + mSdkLog.printf(skin); + } + mSdkLog.printf("\n"); + } else { + mSdkLog.printf("no skins.\n"); + } + } /** * Displays the list of available AVDs. */ private void displayAvdList() { try { - mAvdManager = new AvdManager(mSdkManager, null /* sdklog */); + AvdManager avdManager = new AvdManager(mSdkManager, null /* sdklog */); mSdkLog.printf("Available Android Virtual Devices:\n"); int index = 1; - for (AvdInfo info : mAvdManager.getAvds()) { + for (AvdInfo info : avdManager.getAvds()) { mSdkLog.printf("[%d] %s\n", index, info.getName()); mSdkLog.printf(" Path: %s\n", info.getPath()); @@ -384,7 +411,7 @@ class Main { */ private void createAvd() { // find a matching target - int targetId = mSdkCommandLine.getCreateAvdTargetId(); + int targetId = mSdkCommandLine.getParamTargetId(); IAndroidTarget target = null; if (targetId >= 1 && targetId <= mSdkManager.getTargets().length) { @@ -396,12 +423,12 @@ class Main { try { boolean removePrevious = false; - mAvdManager = new AvdManager(mSdkManager, mSdkLog); + AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog); - String avdName = mSdkCommandLine.getCreateAvdName(); - AvdInfo info = mAvdManager.getAvd(avdName); + String avdName = mSdkCommandLine.getParamName(); + AvdInfo info = avdManager.getAvd(avdName); if (info != null) { - if (mSdkCommandLine.getCreateAvdForce()) { + if (mSdkCommandLine.getFlagForce()) { removePrevious = true; mSdkLog.warning( "Android Virtual Device '%s' already exists and will be replaced.", @@ -412,9 +439,13 @@ class Main { } } - String avdParentFolder = mSdkCommandLine.getCreateAvdLocation(); - if (avdParentFolder == null) { - avdParentFolder = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD; + String paramFolderPath = mSdkCommandLine.getParamLocationPath(); + File avdFolder = null; + if (paramFolderPath != null) { + avdFolder = new File(paramFolderPath); + } else { + avdFolder = new File(AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD, + avdName + AvdManager.AVD_FOLDER_EXTENSION); } Map<String, String> hardwareConfig = null; @@ -425,17 +456,45 @@ class Main { errorAndExit(e.getMessage()); } } - + AvdInfo oldAvdInfo = null; if (removePrevious) { - oldAvdInfo = mAvdManager.getAvd(avdName); + oldAvdInfo = avdManager.getAvd(avdName); + } + + // Validate skin is either default (empty) or NNNxMMM or a valid skin name. + String skin = mSdkCommandLine.getParamSkin(); + if (skin != null && skin.length() == 0) { + skin = null; + } + if (skin != null) { + boolean valid = false; + // Is it a know skin name for this target? + for (String s : target.getSkins()) { + if (skin.equalsIgnoreCase(s)) { + skin = s; // Make skin names case-insensitive. + valid = true; + break; + } + } + + // Is it NNNxMMM? + if (!valid) { + valid = skin.matches("[0-9]{2,}x[0-9]{2,}"); + } + + if (!valid) { + displaySkinList(target, "Valid skins: "); + errorAndExit("'%s' is not a valid skin name or size (NNNxMMM)", skin); + return; + } } - AvdInfo newAvdInfo = mAvdManager.createAvd(avdParentFolder, + AvdInfo newAvdInfo = avdManager.createAvd(avdFolder, avdName, target, - mSdkCommandLine.getCreateAvdSkin(), - mSdkCommandLine.getCreateAvdSdCard(), + skin, + mSdkCommandLine.getParamSdCard(), hardwareConfig, removePrevious, mSdkLog); @@ -446,10 +505,10 @@ class Main { mSdkLog.warning("Removing previous AVD directory at %s", oldAvdInfo.getPath()); // Remove the old data directory File dir = new File(oldAvdInfo.getPath()); - mAvdManager.recursiveDelete(dir); + avdManager.recursiveDelete(dir); dir.delete(); // Remove old avd info from manager - mAvdManager.removeAvd(oldAvdInfo); + avdManager.removeAvd(oldAvdInfo); } } catch (AndroidLocationException e) { @@ -458,6 +517,126 @@ class Main { } /** + * Delete an AVD. + */ + private void deleteAvd() { + try { + String avdName = mSdkCommandLine.getParamName(); + AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog); + AvdInfo info = avdManager.getAvd(avdName); + + if (info == null) { + errorAndExit("There is no Android Virtual Device named '%s'.", avdName); + return; + } + + avdManager.deleteAvd(info, mSdkLog); + } catch (AndroidLocationException e) { + errorAndExit(e.getMessage()); + } + } + + /** + * Move an AVD. + */ + private void moveAvd() { + try { + String avdName = mSdkCommandLine.getParamName(); + AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog); + AvdInfo info = avdManager.getAvd(avdName); + + if (info == null) { + errorAndExit("There is no Android Virtual Device named '%s'.", avdName); + return; + } + + // This is a rename if there's a new name for the AVD + String newName = mSdkCommandLine.getParamMoveNewName(); + if (newName != null && newName.equals(info.getName())) { + // same name, not actually a rename operation + newName = null; + } + + // This is a move (of the data files) if there's a new location path + String paramFolderPath = mSdkCommandLine.getParamLocationPath(); + if (paramFolderPath != null) { + // check if paths are the same. Use File methods to account for OS idiosyncrasies. + try { + File f1 = new File(paramFolderPath).getCanonicalFile(); + File f2 = new File(info.getPath()).getCanonicalFile(); + if (f1.equals(f2)) { + // same canonical path, so not actually a move + paramFolderPath = null; + } + } catch (IOException e) { + // Fail to resolve canonical path. Fail now since a move operation might fail + // later and be harder to recover from. + errorAndExit(e.getMessage()); + return; + } + } + + if (newName == null && paramFolderPath == null) { + mSdkLog.warning("Move operation aborted: same AVD name, same canonical data path"); + return; + } + + // If a rename was requested and no data move was requested, check if the original + // data path is our default constructed from the AVD name. In this case we still want + // to rename that folder too. + if (newName != null && paramFolderPath == null) { + // Compute the original data path + File originalFolder = new File( + AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD, + info.getName() + AvdManager.AVD_FOLDER_EXTENSION); + if (originalFolder.equals(info.getPath())) { + try { + // The AVD is using the default data folder path based on the AVD name. + // That folder needs to be adjusted to use the new name. + File f = new File(AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD, + newName + AvdManager.AVD_FOLDER_EXTENSION); + paramFolderPath = f.getCanonicalPath(); + } catch (IOException e) { + // Fail to resolve canonical path. Fail now rather than later. + errorAndExit(e.getMessage()); + } + } + } + + // Check for conflicts + + if (newName != null && avdManager.getAvd(newName) != null) { + errorAndExit("There is already an AVD named '%s'.", newName); + return; + } + if (newName != null) { + if (avdManager.getAvd(newName) != null) { + errorAndExit("There is already an AVD named '%s'.", newName); + return; + } + + File ini = info.getIniFile(); + if (ini.equals(AvdInfo.getIniFile(newName))) { + errorAndExit("The AVD file '%s' is in the way.", ini.getCanonicalPath()); + return; + } + } + + if (paramFolderPath != null && new File(paramFolderPath).exists()) { + errorAndExit( + "There is already a file or directory at '%s'.\nUse --path to specify a different data folder.", + paramFolderPath); + } + + avdManager.moveAvd(info, newName, paramFolderPath, mSdkLog); + } catch (AndroidLocationException e) { + errorAndExit(e.getMessage()); + } catch (IOException e) { + errorAndExit(e.getMessage()); + } + } + + /** * Prompts the user to setup a hardware config for a Platform-based AVD. * @throws IOException */ diff --git a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java index fe93396..34a69bd 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java +++ b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java @@ -27,7 +27,6 @@ public class SdkCommandLine extends CommandLineProcessor { public final static String VERB_LIST = "list"; public final static String VERB_CREATE = "create"; - public final static String VERB_RENAME = "rename"; public final static String VERB_MOVE = "move"; public final static String VERB_DELETE = "delete"; public final static String VERB_UPDATE = "update"; @@ -51,6 +50,7 @@ public class SdkCommandLine extends CommandLineProcessor { public static final String KEY_SKIN = "skin"; public static final String KEY_SDCARD = "sdcard"; public static final String KEY_FORCE = "force"; + public static final String KEY_RENAME = "rename"; /** * Action definitions for SdkManager command line. @@ -64,92 +64,94 @@ public class SdkCommandLine extends CommandLineProcessor { * </ul> */ private final static String[][] ACTIONS = { - { VERB_LIST, - NO_VERB_OBJECT, + { VERB_LIST, NO_VERB_OBJECT, "Lists existing targets or virtual devices." }, - { VERB_LIST, - OBJECT_AVD, + { VERB_LIST, OBJECT_AVD, "Lists existing Android Virtual Devices.", OBJECT_AVDS }, - { VERB_LIST, - OBJECT_TARGET, + { VERB_LIST, OBJECT_TARGET, "Lists existing targets.", OBJECT_TARGETS }, - { VERB_CREATE, - OBJECT_AVD, + { VERB_CREATE, OBJECT_AVD, "Creates a new Android Virtual Device." }, - { VERB_RENAME, - OBJECT_AVD, - "Renames a new Android Virtual Device." }, - { VERB_MOVE, - OBJECT_AVD, - "Moves a new Android Virtual Device." }, - { VERB_DELETE, - OBJECT_AVD, - "Deletes a new Android Virtual Device." }, + { VERB_MOVE, OBJECT_AVD, + "Moves or renames an Android Virtual Device." }, + { VERB_DELETE, OBJECT_AVD, + "Deletes an Android Virtual Device." }, - { VERB_CREATE, - OBJECT_PROJECT, + { VERB_CREATE, OBJECT_PROJECT, "Creates a new Android Project." }, - { VERB_UPDATE, - OBJECT_PROJECT, + { VERB_UPDATE, OBJECT_PROJECT, "Updates an Android Project (must have an AndroidManifest.xml)." }, }; public SdkCommandLine(ISdkLog logger) { super(logger, ACTIONS); + // --- create avd --- + define(MODE.STRING, false, - VERB_CREATE, OBJECT_AVD, - "p", KEY_PATH, - "Location path of the parent directory where the new AVD will be created", null); + VERB_CREATE, OBJECT_AVD, "p", KEY_PATH, + "Location path of the directory where the new AVD will be created", null); define(MODE.STRING, true, - VERB_CREATE, OBJECT_AVD, - "n", KEY_NAME, + VERB_CREATE, OBJECT_AVD, "n", KEY_NAME, "Name of the new AVD", null); define(MODE.INTEGER, true, - VERB_CREATE, OBJECT_AVD, - "t", KEY_TARGET_ID, + VERB_CREATE, OBJECT_AVD, "t", KEY_TARGET_ID, "Target id of the new AVD", null); - define(MODE.STRING, true, - VERB_CREATE, OBJECT_AVD, - "s", KEY_SKIN, + define(MODE.STRING, false, + VERB_CREATE, OBJECT_AVD, "s", KEY_SKIN, "Skin of the new AVD", null); define(MODE.STRING, false, - VERB_CREATE, OBJECT_AVD, - "c", KEY_SDCARD, + VERB_CREATE, OBJECT_AVD, "c", KEY_SDCARD, "Path to a shared SD card image, or size of a new sdcard for the new AVD", null); define(MODE.BOOLEAN, false, - VERB_CREATE, OBJECT_AVD, - "f", KEY_FORCE, + VERB_CREATE, OBJECT_AVD, "f", KEY_FORCE, "Force creation (override an existing AVD)", false); + // --- delete avd --- + + define(MODE.STRING, true, + VERB_DELETE, OBJECT_AVD, "n", KEY_NAME, + "Name of the AVD to delete", null); + + // --- move avd --- + + define(MODE.STRING, true, + VERB_MOVE, OBJECT_AVD, "n", KEY_NAME, + "Name of the AVD to move or rename", null); + define(MODE.STRING, false, + VERB_MOVE, OBJECT_AVD, "r", KEY_RENAME, + "New name of the AVD to rename", null); + define(MODE.STRING, false, + VERB_MOVE, OBJECT_AVD, "p", KEY_PATH, + "New location path of the directory where to move the AVD", null); + + // --- create project --- + define(MODE.ENUM, true, - VERB_CREATE, OBJECT_PROJECT, - "m", KEY_MODE, + VERB_CREATE, OBJECT_PROJECT, "m", KEY_MODE, "Project mode", new String[] { ARG_ACTIVITY, ARG_ALIAS }); define(MODE.STRING, true, VERB_CREATE, OBJECT_PROJECT, "p", KEY_PATH, "Location path of new project", null); define(MODE.INTEGER, true, - VERB_CREATE, OBJECT_PROJECT, - "t", KEY_TARGET_ID, + VERB_CREATE, OBJECT_PROJECT, "t", KEY_TARGET_ID, "Target id of the new project", null); define(MODE.STRING, true, - VERB_CREATE, OBJECT_PROJECT, - "k", KEY_PACKAGE, + VERB_CREATE, OBJECT_PROJECT, "k", KEY_PACKAGE, "Package name", null); define(MODE.STRING, true, - VERB_CREATE, OBJECT_PROJECT, - "a", KEY_ACTIVITY, + VERB_CREATE, OBJECT_PROJECT, "a", KEY_ACTIVITY, "Activity name", null); define(MODE.STRING, false, - VERB_CREATE, OBJECT_PROJECT, - "n", KEY_NAME, + VERB_CREATE, OBJECT_PROJECT, "n", KEY_NAME, "Project name", null); + // --- update project --- + define(MODE.STRING, true, VERB_UPDATE, OBJECT_PROJECT, "p", KEY_PATH, @@ -164,79 +166,55 @@ public class SdkCommandLine extends CommandLineProcessor { "Project name", null); } - // -- some helpers for AVD action flags + // -- some helpers for generic action flags - /** Helper to retrieve the --out location for the new AVD action. */ - public String getCreateAvdLocation() { - return ((String) getValue(VERB_CREATE, OBJECT_AVD, KEY_PATH)); + /** Helper to retrieve the --path value. */ + public String getParamLocationPath() { + return ((String) getValue(null, null, KEY_PATH)); } - /** Helper to retrieve the --target id for the new AVD action. */ - public int getCreateAvdTargetId() { - return ((Integer) getValue(VERB_CREATE, OBJECT_AVD, KEY_TARGET_ID)).intValue(); + /** Helper to retrieve the --target id value. */ + public int getParamTargetId() { + return ((Integer) getValue(null, null, KEY_TARGET_ID)).intValue(); } - /** Helper to retrieve the --name for the new AVD action. */ - public String getCreateAvdName() { - return ((String) getValue(VERB_CREATE, OBJECT_AVD, KEY_NAME)); + /** Helper to retrieve the --name value. */ + public String getParamName() { + return ((String) getValue(null, null, KEY_NAME)); } - /** Helper to retrieve the --skin name for the new AVD action. */ - public String getCreateAvdSkin() { - return ((String) getValue(VERB_CREATE, OBJECT_AVD, KEY_SKIN)); + /** Helper to retrieve the --skin value. */ + public String getParamSkin() { + return ((String) getValue(null, null, KEY_SKIN)); } - /** Helper to retrieve the --sdcard data for the new AVD action. */ - public String getCreateAvdSdCard() { - return ((String) getValue(VERB_CREATE, OBJECT_AVD, KEY_SDCARD)); + /** Helper to retrieve the --sdcard value. */ + public String getParamSdCard() { + return ((String) getValue(null, null, KEY_SDCARD)); } - public boolean getCreateAvdForce() { - return ((Boolean) getValue(VERB_CREATE, OBJECT_AVD, KEY_FORCE)).booleanValue(); + /** Helper to retrieve the --force flag. */ + public boolean getFlagForce() { + return ((Boolean) getValue(null, null, KEY_FORCE)).booleanValue(); } + // -- some helpers for avd action flags - // -- some helpers for project action flags - - /** Helper to retrieve the --out location for the new project action. */ - public String getCreateProjectLocation() { - return ((String) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_PATH)); - } - - /** Helper to retrieve the --target id for the new project action. */ - public int getCreateProjectTargetId() { - return ((Integer) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_TARGET_ID)).intValue(); + /** Helper to retrieve the --rename value for a move verb. */ + public String getParamMoveNewName() { + return ((String) getValue(VERB_MOVE, null, KEY_RENAME)); } - /** Helper to retrieve the --name for the new project action. */ - public String getCreateProjectName() { - return ((String) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_NAME)); - } - /** Helper to retrieve the --package for the new project action. */ - public String getCreateProjectPackage() { - return ((String) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_PACKAGE)); - } - - /** Helper to retrieve the --activity for the new project action. */ - public String getCreateProjectActivity() { - return ((String) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_ACTIVITY)); - } - - // -- some helpers for update action flags - - /** Helper to retrieve the --out location for the update project action. */ - public String getUpdateProjectLocation() { - return ((String) getValue(VERB_UPDATE, OBJECT_PROJECT, KEY_PATH)); - } + // -- some helpers for project action flags - /** Helper to retrieve the --target id for the update project action. */ - public int getUpdateProjectTargetId() { - return ((Integer) getValue(VERB_UPDATE, OBJECT_PROJECT, KEY_TARGET_ID)).intValue(); + /** Helper to retrieve the --package value. */ + public String getParamProjectPackage() { + return ((String) getValue(null, OBJECT_PROJECT, KEY_PACKAGE)); } - /** Helper to retrieve the --name for the update project action. */ - public String getUpdateProjectName() { - return ((String) getValue(VERB_UPDATE, OBJECT_PROJECT, KEY_NAME)); + /** Helper to retrieve the --activity for the new project action. */ + public String getParamProjectActivity() { + return ((String) getValue(null, OBJECT_PROJECT, KEY_ACTIVITY)); } } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java index 83a90e6..f6fb622 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java @@ -377,9 +377,11 @@ public final class SdkManager { * @return the map of (key,value) pairs, or null if the parsing failed. */ public static Map<String, String> parsePropertyFile(File buildProp, ISdkLog log) { + FileInputStream fis = null; + BufferedReader reader = null; try { - FileInputStream fis = new FileInputStream(buildProp); - BufferedReader reader = new BufferedReader(new InputStreamReader(fis)); + fis = new FileInputStream(buildProp); + reader = new BufferedReader(new InputStreamReader(fis)); String line = null; Map<String, String> map = new HashMap<String, String>(); @@ -407,6 +409,21 @@ public final class SdkManager { log.warning("Error parsing '%1$s': %2$s.", buildProp.getAbsolutePath(), e.getMessage()); } + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + // pass + } + } + if (fis != null) { + try { + fis.close(); + } catch (IOException e) { + // pass + } + } } return null; diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java index a43b8e6..d466182 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java @@ -39,11 +39,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * Virtual Device Manager to access the list of AVDs or create new ones. + * Android Virtual Device Manager to manage AVDs. */ public final class AvdManager { - private static final String AVD_FOLDER_EXTENSION = ".avd"; + public static final String AVD_FOLDER_EXTENSION = ".avd"; private final static String AVD_INFO_PATH = "path"; private final static String AVD_INFO_TARGET = "target"; @@ -55,21 +55,50 @@ public final class AvdManager { private final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("\\d+[MK]?"); + /** An immutable structure describing an Android Virtual Device. */ public static final class AvdInfo { - String name; - String path; - IAndroidTarget target; + private String mName; + private String mPath; + private IAndroidTarget mTarget; + + /** Creates a new AVD info. Valures are immutable. */ + public AvdInfo(String name, String path, IAndroidTarget target) { + mName = name; + mPath = path; + mTarget = target; + } + /** Returns the name of the AVD. */ public String getName() { - return name; + return mName; } + /** Returns the path of the AVD data directory. */ public String getPath() { - return path; + return mPath; } + /** Returns the target of the AVD. */ public IAndroidTarget getTarget() { - return target; + return mTarget; + } + + /** + * Helper method that returns the .ini {@link File} for a given AVD name. + * @throws AndroidLocationException if there's a problem getting android root directory. + */ + public static File getIniFile(String name) throws AndroidLocationException { + String avdRoot; + avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD; + return new File(avdRoot, name + ".ini"); + } + + /** + * Returns the .ini {@link File} for this AVD. + * @throws AndroidLocationException if there's a problem getting android root directory. + */ + public File getIniFile() throws AndroidLocationException { + return getIniFile(mName); } } @@ -97,7 +126,7 @@ public final class AvdManager { */ public AvdInfo getAvd(String name) { for (AvdInfo info : mAvdList) { - if (info.name.equals(name)) { + if (info.getName().equals(name)) { return info; } } @@ -107,30 +136,20 @@ public final class AvdManager { /** * Creates a new AVD. It is expected that there is no existing AVD with this name already. - * @param parentFolder the folder to contain the AVD. A new folder will be created in this - * folder with the name of the AVD + * @param avdFolder the data folder for the AVD. It will be created as needed. * @param name the name of the AVD * @param target the target of the AVD - * @param skinName the name of the skin. Can be null. + * @param skinName the name of the skin. Can be null. Must have been verified by caller. * @param sdcard the parameter value for the sdCard. Can be null. This is either a path to * an existing sdcard image or a sdcard size (\d+, \d+K, \dM). * @param hardwareConfig the hardware setup for the AVD * @param removePrevious If true remove any previous files. */ - public AvdInfo createAvd(String parentFolder, String name, IAndroidTarget target, + public AvdInfo createAvd(File avdFolder, String name, IAndroidTarget target, String skinName, String sdcard, Map<String,String> hardwareConfig, boolean removePrevious, ISdkLog log) { try { - File rootDirectory = new File(parentFolder); - if (rootDirectory.isDirectory() == false) { - if (log != null) { - log.error(null, "Folder %s does not exist.", parentFolder); - } - return null; - } - - File avdFolder = new File(parentFolder, name + AVD_FOLDER_EXTENSION); if (avdFolder.exists()) { if (removePrevious) { // AVD already exists and removePrevious is set, try to remove the @@ -139,23 +158,19 @@ public final class AvdManager { } else { // AVD shouldn't already exist if removePrevious is false. if (log != null) { - log.error(null, "Folder %s is in the way.", avdFolder.getAbsolutePath()); + log.error(null, + "Folder %s is in the way. Use --force if you want to overwrite.", + avdFolder.getAbsolutePath()); } return null; } + } else { + // create the AVD folder. + avdFolder.mkdir(); } - // create the AVD folder. - avdFolder.mkdir(); - - HashMap<String, String> values = new HashMap<String, String>(); - - // prepare the ini file. - String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD; - File iniFile = new File(avdRoot, name + ".ini"); - values.put(AVD_INFO_PATH, avdFolder.getAbsolutePath()); - values.put(AVD_INFO_TARGET, target.hashString()); - createConfigIni(iniFile, values); + // actually write the ini file + createAvdIniFile(name, avdFolder, target); // writes the userdata.img in it. String imagePath = target.getPath(IAndroidTarget.IMAGES); @@ -175,22 +190,10 @@ public final class AvdManager { fis.close(); // Config file - values.clear(); + HashMap<String, String> values = new HashMap<String, String>(); if (skinName != null) { - // check that the skin name is valid - String[] skinNames = target.getSkins(); - boolean found = false; - for (String n : skinNames) { - if (n.equals(skinName)) { - values.put("skin", skinName); - found = true; - break; - } - } - - if (found == false && log != null) { - log.warning("Skin '%1$s' does not exists, using default skin.", skinName); - } + // assume skin name is valid + values.put("skin", skinName); } if (sdcard != null) { @@ -246,10 +249,7 @@ public final class AvdManager { } // create the AvdInfo object, and add it to the list - AvdInfo avdInfo = new AvdInfo(); - avdInfo.name = name; - avdInfo.path = avdFolder.getAbsolutePath(); - avdInfo.target = target; + AvdInfo avdInfo = new AvdInfo(name, avdFolder.getAbsolutePath(), target); mAvdList.add(avdInfo); @@ -268,6 +268,133 @@ public final class AvdManager { } /** + * Creates the ini file for an AVD. + * + * @param name of the AVD. + * @param avdFolder path for the data folder of the AVD. + * @param target of the AVD. + * @throws AndroidLocationException if there's a problem getting android root directory. + * @throws IOException if {@link File#getAbsolutePath()} fails. + */ + private void createAvdIniFile(String name, File avdFolder, IAndroidTarget target) + throws AndroidLocationException, IOException { + HashMap<String, String> values = new HashMap<String, String>(); + File iniFile = AvdInfo.getIniFile(name); + values.put(AVD_INFO_PATH, avdFolder.getAbsolutePath()); + values.put(AVD_INFO_TARGET, target.hashString()); + createConfigIni(iniFile, values); + } + + /** + * Creates the ini file for an AVD. + * + * @param info of the AVD. + * @throws AndroidLocationException if there's a problem getting android root directory. + * @throws IOException if {@link File#getAbsolutePath()} fails. + */ + private void createAvdIniFile(AvdInfo info) throws AndroidLocationException, IOException { + createAvdIniFile(info.getName(), new File(info.getPath()), info.getTarget()); + } + + /** + * Actually deletes the files of an existing AVD. + * <p/> + * This also remove it from the manager's list, The caller does not need to + * call {@link #removeAvd(AvdInfo)} afterwards. + * + * @param avdInfo the information on the AVD to delete + */ + public void deleteAvd(AvdInfo avdInfo, ISdkLog log) { + try { + String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD; + + File f = avdInfo.getIniFile(); + if (f.exists()) { + log.warning("Deleting file %s", f.getCanonicalPath()); + if (!f.delete()) { + log.error(null, "Failed to delete %s", f.getCanonicalPath()); + } + } + + f = new File(avdInfo.getPath()); + if (f.exists()) { + log.warning("Deleting folder %s", f.getCanonicalPath()); + recursiveDelete(f); + if (!f.delete()) { + log.error(null, "Failed to delete %s", f.getCanonicalPath()); + } + } + + removeAvd(avdInfo); + } catch (AndroidLocationException e) { + log.error(e, null); + } catch (IOException e) { + log.error(e, null); + } + } + + /** + * Moves and/or rename an existing AVD and its files. + * This also change it in the manager's list. + * <p/> + * The caller should make sure the name or path given are valid, do not exist and are + * actually different than current values. + * + * @param avdInfo the information on the AVD to move. + * @param newName the new name of the AVD if non null. + * @param paramFolderPath the new data folder if non null. + * @return True if the move succeeded or there was nothing to do. + * If false, this method will have had already output error in the log. + */ + public boolean moveAvd(AvdInfo avdInfo, String newName, String paramFolderPath, ISdkLog log) { + + try { + if (paramFolderPath != null) { + File f = new File(avdInfo.getPath()); + log.warning("Moving '%s' to '%s'.", avdInfo.getPath(), paramFolderPath); + if (!f.renameTo(new File(paramFolderPath))) { + log.error(null, "Failed to move '%s' to '%s'.", + avdInfo.getPath(), paramFolderPath); + return false; + } + + // update avd info + AvdInfo info = new AvdInfo(avdInfo.getName(), paramFolderPath, avdInfo.getTarget()); + mAvdList.remove(avdInfo); + mAvdList.add(info); + avdInfo = info; + + // update the ini file + createAvdIniFile(avdInfo); + } + + if (newName != null) { + File oldIniFile = avdInfo.getIniFile(); + File newIniFile = AvdInfo.getIniFile(newName); + + log.warning("Moving '%s' to '%s'.", oldIniFile.getPath(), newIniFile.getPath()); + if (!oldIniFile.renameTo(newIniFile)) { + log.error(null, "Failed to move '%s' to '%s'.", + oldIniFile.getPath(), newIniFile.getPath()); + return false; + } + + // update avd info + AvdInfo info = new AvdInfo(newName, avdInfo.getPath(), avdInfo.getTarget()); + mAvdList.remove(avdInfo); + mAvdList.add(info); + } + } catch (AndroidLocationException e) { + log.error(e, null); + } catch (IOException e) { + log.error(e, null); + } + + // nothing to do or succeeded + return true; + } + + /** * Helper method to recursively delete a folder's content (but not the folder itself). * * @throws SecurityException like {@link File#delete()} does if file/folder is not writable. @@ -332,15 +459,13 @@ public final class AvdManager { return null; } - AvdInfo info = new AvdInfo(); Matcher matcher = INI_NAME_PATTERN.matcher(path.getName()); - if (matcher.matches()) { - info.name = matcher.group(1); - } else { - info.name = path.getName(); // really this should not happen. - } - info.path = avdPath; - info.target = target; + + AvdInfo info = new AvdInfo( + matcher.matches() ? matcher.group(1) : path.getName(), // should not happen + avdPath, + target + ); return info; } diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java index 4a0ee06..9d0b928 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java @@ -56,14 +56,18 @@ public final class AvdSelector { private Label mDescription; /** - * Creates a new SDK Target Selector. + * Creates a new SDK Target Selector, and fills it with a list of {@link AvdInfo}, filtered + * by a {@link IAndroidTarget}. + * <p/>Only the {@link AvdInfo} able to run application developed for the given + * {@link IAndroidTarget} will be displayed. * * @param parent The parent composite where the selector will be added. * @param avds The list of AVDs. This is <em>not</em> copied, the caller must not modify. * @param allowMultipleSelection True if more than one SDK target can be selected at the same * time. */ - public AvdSelector(Composite parent, AvdInfo[] avds, boolean allowMultipleSelection) { + public AvdSelector(Composite parent, AvdInfo[] avds, IAndroidTarget filter, + boolean allowMultipleSelection) { mAvds = avds; // Layout has 1 column @@ -99,11 +103,34 @@ public final class AvdSelector { adjustColumnsWidth(mTable, column0, column1, column2, column3); setupSelectionListener(mTable); - fillTable(mTable, null /* target filter */); + fillTable(mTable, filter); setupTooltip(mTable); } /** + * Creates a new SDK Target Selector, and fills it with a list of {@link AvdInfo}. + * + * @param parent The parent composite where the selector will be added. + * @param avds The list of AVDs. This is <em>not</em> copied, the caller must not modify. + * @param allowMultipleSelection True if more than one SDK target can be selected at the same + * time. + */ + public AvdSelector(Composite parent, AvdInfo[] avds, boolean allowMultipleSelection) { + this(parent, avds, null /* filter */, allowMultipleSelection); + } + + + public void setTableHeightHint(int heightHint) { + GridData data = new GridData(); + data.heightHint = heightHint; + data.grabExcessVerticalSpace = true; + data.grabExcessHorizontalSpace = true; + data.horizontalAlignment = GridData.FILL; + data.verticalAlignment = GridData.FILL; + mTable.setLayoutData(data); + } + + /** * Sets a new set of AVD, with an optional filter. * <p/>This must be called from the UI thread. * @@ -208,6 +235,18 @@ public final class AvdSelector { } /** + * Enables the receiver if the argument is true, and disables it otherwise. + * A disabled control is typically not selectable from the user interface + * and draws with an inactive or "grayed" look. + * + * @param enabled the new enabled state. + */ + public void setEnabled(boolean enabled) { + mTable.setEnabled(enabled); + mDescription.setEnabled(enabled); + } + + /** * Adds a listener to adjust the columns width when the parent is resized. * <p/> * If we need something more fancy, we might want to use this: |