diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/avd_manager.png b/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/avd_manager.png
new file mode 100755
index 000000000..7dbbbb6a4
Binary files /dev/null and b/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/avd_manager.png differ
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index 6022a2027..dc6062d95 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -538,6 +538,22 @@
label="Refactor">
+
+
+
+
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
index 8ef0f2caf..936f47cb8 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
@@ -1386,8 +1386,6 @@ public class AdtPlugin extends AbstractUIPlugin {
/**
* Pings the usage start server.
- * @param pluginName the name of the plugin to appear in the stats
- * @param pluginVersion the {@link Version} of the plugin.
*/
private void pingUsageServer() {
// get the version of the plugin
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/AvdManagerAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/AvdManagerAction.java
new file mode 100755
index 000000000..d3f328ee3
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/AvdManagerAction.java
@@ -0,0 +1,34 @@
+/*
+ * 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.wizards.actions;
+
+import com.android.ide.eclipse.adt.wizards.avdmanager.AvdManagerWizard;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ui.IWorkbenchWizard;
+
+/**
+ * Delegate for the toolbar/menu action "Android AVD Manager".
+ * It displays the Android AVD Manager.
+ */
+public class AvdManagerAction extends OpenWizardAction {
+
+ @Override
+ protected IWorkbenchWizard instanciateWizard(IAction action) {
+ return new AvdManagerWizard();
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/avdmanager/AvdManagerListPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/avdmanager/AvdManagerListPage.java
new file mode 100755
index 000000000..a7691ca3c
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/avdmanager/AvdManagerListPage.java
@@ -0,0 +1,632 @@
+/*
+ * 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.wizards.avdmanager;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.sdk.Sdk;
+import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
+import com.android.prefs.AndroidLocation;
+import com.android.prefs.AndroidLocation.AndroidLocationException;
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.ISdkLog;
+import com.android.sdklib.avd.AvdManager;
+import com.android.sdklib.avd.AvdManager.AvdInfo;
+import com.android.sdkuilib.AvdSelector;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.TreeMap;
+
+/**
+ * This is the single page of the {@link AvdManagerWizard} which provides the ability to display
+ * the AVDs and edit them quickly.
+ */
+class AvdManagerListPage extends WizardPage {
+
+ private AvdSelector mAvdSelector;
+ private Button mDeleteButton;
+ private Button mRefreshButton;
+ private Text mCreateName;
+ private Combo mCreateTargetCombo;
+ private Text mCreateSdCard;
+ private Combo mCreateSkinCombo;
+ private Button mCreateForce;
+ private Button mCreateButton;
+ private HashSet mKnownAvdNames = new HashSet();
+ private TreeMap mCurrentTargets = new TreeMap();
+ private ITargetChangeListener mSdkTargetChangeListener;
+
+ /**
+ * Constructs a new {@link AvdManagerListPage}.
+ *
+ * Called by {@link AvdManagerWizard#createMainPage()}.
+ */
+ protected AvdManagerListPage(String pageName) {
+ super(pageName);
+ setPageComplete(false);
+ }
+
+ /**
+ * Called by the parent Wizard to create the UI for this Wizard Page.
+ *
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NULL);
+ composite.setFont(parent.getFont());
+
+ initializeDialogUnits(parent);
+
+ composite.setLayout(new GridLayout(1, false /*makeColumnsEqualWidth*/));
+ composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ createAvdGroup(composite);
+ createCreateGroup(composite);
+ registerSdkChangeListener();
+
+ // Show description the first time
+ setErrorMessage(null);
+ setMessage(null);
+ setControl(composite);
+
+ // Update state the first time
+ reloadAvdList();
+ reloadTargetCombo();
+ validatePage();
+ }
+
+ private void registerSdkChangeListener() {
+
+ mSdkTargetChangeListener = new ITargetChangeListener() {
+ public void onProjectTargetChange(IProject changedProject) {
+ // Ignore
+ }
+
+ public void onTargetsLoaded() {
+ // Update the AVD list, since the SDK change will influence the "good" avd list
+ reloadAvdList();
+ // Update the sdk target combo with the new targets
+ reloadTargetCombo();
+ validatePage();
+ }
+ };
+ AdtPlugin.getDefault().addTargetListener(mSdkTargetChangeListener);
+
+ }
+
+ @Override
+ public void dispose() {
+ if (mSdkTargetChangeListener != null) {
+ AdtPlugin.getDefault().removeTargetListener(mSdkTargetChangeListener);
+ mSdkTargetChangeListener = null;
+ }
+
+ super.dispose();
+ }
+
+ // --- UI creation ---
+
+ /**
+ * Creates the AVD selector and refresh & delete buttons.
+ */
+ private void createAvdGroup(Composite parent) {
+ int col = 0;
+
+ final Composite grid2 = new Composite(parent, SWT.NONE);
+ grid2.setLayout(new GridLayout(2, false /*makeColumnsEqualWidth*/));
+ grid2.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ Label label = new Label(grid2, SWT.NONE);
+ label.setText("List of existing Android Virtual Devices:");
+ label.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false));
+
+ mRefreshButton = new Button(grid2, SWT.PUSH);
+ mRefreshButton.setText("Refresh");
+ mRefreshButton.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false));
+ mRefreshButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ reloadAvdList();
+ }
+ });
+
+ mAvdSelector = new AvdSelector(parent, null);
+ mAvdSelector.setSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+ updateDeleteButton();
+ }
+ });
+
+ mDeleteButton = new Button(parent, SWT.PUSH);
+ mDeleteButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, false));
+ mDeleteButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ onDelete();
+ }
+ });
+ updateDeleteButton();
+ }
+
+ private void updateDeleteButton() {
+ AvdInfo selected = mAvdSelector.getFirstSelected();
+ mDeleteButton.setText(String.format("Delete %s...",
+ selected == null ? "" : selected.getName())); //$NON-NLS-1$
+ mDeleteButton.pack();
+ mDeleteButton.setEnabled(selected != null);
+ }
+
+ /**
+ * Creates the "Create" group
+ */
+ private void createCreateGroup(Composite parent) {
+
+ Group grid = new Group(parent, SWT.SHADOW_ETCHED_IN);
+ grid.setLayout(new GridLayout(4, false /*makeColumnsEqualWidth*/));
+ grid.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ grid.setFont(parent.getFont());
+ grid.setText("Create AVD");
+
+ // first line
+
+ Label label = new Label(grid, SWT.NONE);
+ label.setText("Name");
+
+ mCreateName = new Text(grid, SWT.BORDER);
+ mCreateName.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mCreateName.addModifyListener(new CreateNameModifyListener());
+
+ label = new Label(grid, SWT.NONE);
+ label.setText("Target");
+
+ mCreateTargetCombo = new Combo(grid, SWT.READ_ONLY | SWT.DROP_DOWN);
+ mCreateTargetCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mCreateTargetCombo.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+ reloadSkinCombo();
+ validatePage();
+ }
+ });
+
+ // second line
+
+ label = new Label(grid, SWT.NONE);
+ label.setText("SDCard");
+
+ ValidateListener validateListener = new ValidateListener();
+
+ mCreateSdCard = new Text(grid, SWT.BORDER);
+ mCreateSdCard.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mCreateSdCard.addModifyListener(validateListener);
+
+ label = new Label(grid, SWT.NONE);
+ label.setText("Skin");
+
+ mCreateSkinCombo = new Combo(grid, SWT.READ_ONLY | SWT.DROP_DOWN);
+ mCreateSkinCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ // dummies for alignment
+ label = new Label(grid, SWT.NONE);
+ label = new Label(grid, SWT.NONE);
+
+ mCreateForce = new Button(grid, SWT.CHECK);
+ mCreateForce.setText("Force");
+ mCreateForce.setEnabled(false);
+ mCreateForce.addSelectionListener(validateListener);
+
+ mCreateButton = new Button(grid, SWT.PUSH);
+ mCreateButton.setText("Create AVD");
+ mCreateButton.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false));
+ mCreateButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+ onCreate();
+ }
+ });
+ }
+
+ /**
+ * Callback when the AVD name is changed.
+ * Enables the force checkbox if the name is a duplicate.
+ */
+ private class CreateNameModifyListener implements ModifyListener {
+
+ public void modifyText(ModifyEvent e) {
+ String name = mCreateName.getText().trim();
+ if (mKnownAvdNames.contains(name)) {
+ mCreateForce.setEnabled(true);
+ } else {
+ mCreateForce.setEnabled(false);
+ mCreateForce.setSelection(false);
+ }
+ validatePage();
+ }
+ }
+
+ private class ValidateListener extends SelectionAdapter implements ModifyListener {
+
+ public void modifyText(ModifyEvent e) {
+ validatePage();
+ }
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ super.widgetSelected(e);
+ validatePage();
+ }
+ }
+
+ private void reloadTargetCombo() {
+
+ String selected = null;
+ int index = mCreateTargetCombo.getSelectionIndex();
+ if (index >= 0) {
+ selected = mCreateTargetCombo.getItem(index);
+ }
+
+ mCurrentTargets.clear();
+ mCreateTargetCombo.removeAll();
+
+ boolean found = false;
+ index = -1;
+
+ Sdk sdk = Sdk.getCurrent();
+ if (sdk != null) {
+ for (IAndroidTarget target : sdk.getTargets()) {
+ String name = String.format("%s - %s",
+ target.getName(),
+ target.getApiVersionName());
+ mCurrentTargets.put(name, target);
+ mCreateTargetCombo.add(name);
+ if (!found) {
+ index++;
+ found = name.equals(selected);
+ }
+ }
+ }
+
+ mCreateTargetCombo.setEnabled(mCurrentTargets.size() > 0);
+
+ if (found) {
+ mCreateTargetCombo.select(index);
+ }
+
+ reloadSkinCombo();
+ }
+
+ private void reloadSkinCombo() {
+ String selected = null;
+ int index = mCreateSkinCombo.getSelectionIndex();
+ if (index >= 0) {
+ selected = mCreateSkinCombo.getItem(index);
+ }
+
+ mCreateSkinCombo.removeAll();
+ mCreateSkinCombo.setEnabled(false);
+
+ index = mCreateTargetCombo.getSelectionIndex();
+ if (index >= 0) {
+
+ String targetName = mCreateTargetCombo.getItem(index);
+
+ boolean found = false;
+ IAndroidTarget target = mCurrentTargets.get(targetName);
+ if (target != null) {
+ mCreateSkinCombo.add(String.format("Default (%s)", target.getDefaultSkin()));
+
+ index = -1;
+ for (String skin : target.getSkins()) {
+ mCreateSkinCombo.add(skin);
+ if (!found) {
+ index++;
+ found = skin.equals(selected);
+ }
+ }
+
+ mCreateSkinCombo.setEnabled(true);
+
+ if (found) {
+ mCreateSkinCombo.select(index);
+ } else {
+ mCreateSkinCombo.select(0); // default
+ }
+ }
+ }
+ }
+
+ /**
+ * Validates the fields, displays errors and warnings.
+ * Enables the finish button if there are no errors.
+ *
+ * Not really used here yet. Keep as a placeholder.
+ */
+ private void validatePage() {
+ String error = null;
+ String warning = null;
+
+
+ // Validate AVD name
+ String avdName = mCreateName.getText().trim();
+ boolean hasAvdName = avdName.length() > 0;
+ if (hasAvdName && !AvdManager.RE_AVD_NAME.matcher(avdName).matches()) {
+ error = String.format(
+ "AVD name '%1$s' contains invalid characters. Allowed characters are: %2$s",
+ avdName, AvdManager.CHARS_AVD_NAME);
+ }
+
+ // Validate target
+ if (hasAvdName && error == null && mCreateTargetCombo.getSelectionIndex() < 0) {
+ error = "A target must be selected in order to create an AVD.";
+ }
+
+ // Validate SDCard path or value
+ if (error == null) {
+ String sdName = mCreateSdCard.getText().trim();
+
+ if (sdName.length() > 0 &&
+ !new File(sdName).isFile() &&
+ !AvdManager.SDCARD_SIZE_PATTERN.matcher(sdName).matches()) {
+ error = "SD Card must be either a file path or a size such as 128K or 64M.";
+ }
+ }
+
+ // Check for duplicate AVD name
+ if (hasAvdName && error == null) {
+ if (mKnownAvdNames.contains(avdName) && !mCreateForce.getSelection()) {
+ error = String.format(
+ "The AVD name '%s' is already used. " +
+ "Check \"Force\" if you really want to create a new AVD with that name and delete the previous one.",
+ avdName);
+ }
+ }
+
+ // Validate the create button
+ boolean can_create = hasAvdName && error == null;
+ if (can_create) {
+ can_create &= mCreateTargetCombo.getSelectionIndex() >= 0;
+ }
+ mCreateButton.setEnabled(can_create);
+
+ // -- update UI
+ setPageComplete(true);
+ if (error != null) {
+ setMessage(error, WizardPage.ERROR);
+ } else if (warning != null) {
+ setMessage(warning, WizardPage.WARNING);
+ } else {
+ setErrorMessage(null);
+ setMessage(null);
+ }
+ }
+
+ /**
+ * Reloads the AVD list in the AVD selector.
+ * Tries to preserve the selection.
+ */
+ private void reloadAvdList() {
+ AvdInfo selected = mAvdSelector.getFirstSelected();
+
+ AvdManager avdm = getAvdManager();
+
+ AvdInfo[] avds = avdm == null ? null : avdm.getValidAvds();
+ mAvdSelector.setAvds(avds, null /*filter*/);
+
+ // Keep the list of known AVD names to check if they exist quickly. however
+ // use the list of all AVDs, including broken ones (unless we don't know their
+ // name).
+ mKnownAvdNames.clear();
+ if (avdm != null) {
+ for (AvdInfo avd : avdm.getAllAvds()) {
+ String name = avd.getName();
+ if (name != null) {
+ mKnownAvdNames.add(name);
+ }
+ }
+ }
+
+ mAvdSelector.setSelection(selected);
+ }
+
+ /**
+ * Triggered when the user selects the "delete" button.
+ * Deletes the currently selected AVD, if any.
+ */
+ private void onDelete() {
+ AvdInfo avdInfo = mAvdSelector.getFirstSelected();
+ AvdManager avdm = getAvdManager();
+ if (avdInfo == null || avdm == null) {
+ return;
+ }
+
+ // Confirm you want to delete this AVD
+ if (!AdtPlugin.displayPrompt("Delete Android Virtual Device",
+ String.format("Please confirm that you want to delete the Android Virtual Device named '%s'. This operation cannot be reverted.",
+ avdInfo.getName()))) {
+ return;
+ }
+
+ SdkLog log = new SdkLog(String.format("Result of deleting AVD '%s':", avdInfo.getName()));
+
+ boolean success = avdm.deleteAvd(avdInfo, log);
+
+ log.display(success);
+ reloadAvdList();
+ updateDeleteButton();
+ }
+
+ /**
+ * Triggered when the user selects the "create" button.
+ */
+ private void onCreate() {
+ String avdName = mCreateName.getText().trim();
+ String sdName = mCreateSdCard.getText().trim();
+ int targetIndex = mCreateTargetCombo.getSelectionIndex();
+ int skinIndex = mCreateSkinCombo.getSelectionIndex();
+ boolean force = mCreateForce.getSelection();
+ AvdManager avdm = getAvdManager();
+
+ if (avdm == null ||
+ avdName.length() == 0 ||
+ targetIndex < 0) {
+ return;
+ }
+
+ String targetName = mCreateTargetCombo.getItem(targetIndex);
+ IAndroidTarget target = mCurrentTargets.get(targetName);
+ if (target == null) {
+ return;
+ }
+
+ String skinName = null;
+ if (skinIndex > 0) {
+ // index 0 is the default, we don't use it
+ skinName = mCreateSkinCombo.getItem(skinIndex);
+ }
+
+ SdkLog log = new SdkLog(String.format("Result of creating AVD '%s':", avdName));
+
+ File avdFolder;
+ try {
+ avdFolder = new File(
+ AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD,
+ avdName + AvdManager.AVD_FOLDER_EXTENSION);
+ } catch (AndroidLocationException e) {
+ AdtPlugin.logAndPrintError(e, null /*tag*/,
+ "AndroidLocation.getFolder failed"); //$NON-NLS-1$
+ return;
+ }
+
+ ISdkLog oldLog = null;
+ boolean success = false;
+ try {
+ // Temporarily change the AvdManager's logger for ours, since the API no longer
+ // takes a logger argument.
+ // TODO revisit this later. See comments in AvdManager#mSdkLog.
+ oldLog = avdm.setSdkLog(log);
+
+ AvdInfo avdInfo = avdm.createAvd(
+ avdFolder,
+ avdName,
+ target,
+ skinName,
+ sdName,
+ null, // hardwareConfig,
+ force);
+
+ success = avdInfo != null;
+
+ } finally {
+ avdm.setSdkLog(oldLog);
+ }
+
+ log.display(success);
+
+ if (success) {
+ // clear the name field on success
+ mCreateName.setText(""); //$NON-NLS-1$
+ }
+
+ reloadAvdList();
+ }
+
+ /**
+ * Collects all log from the AVD action and displays it in a dialog.
+ */
+ private class SdkLog implements ISdkLog {
+
+ final ArrayList logMessages = new ArrayList();
+ private final String mMessage;
+
+ public SdkLog(String message) {
+ mMessage = message;
+ }
+
+ public void error(Throwable throwable, String errorFormat, Object... arg) {
+ if (errorFormat != null) {
+ logMessages.add(String.format("Error: " + errorFormat, arg));
+ }
+
+ if (throwable != null) {
+ logMessages.add(throwable.getMessage());
+ }
+ }
+
+ public void warning(String warningFormat, Object... arg) {
+ logMessages.add(String.format("Warning: " + warningFormat, arg));
+ }
+
+ public void printf(String msgFormat, Object... arg) {
+ logMessages.add(String.format(msgFormat, arg));
+ }
+
+ /**
+ * Displays the log if anything was captured.
+ */
+ public void display(boolean success) {
+ if (logMessages.size() > 0) {
+ StringBuilder sb = new StringBuilder(mMessage + "\n");
+ for (String msg : logMessages) {
+ sb.append('\n');
+ sb.append(msg);
+ }
+ if (success) {
+ AdtPlugin.displayWarning("Android Virtual Devices Manager", sb.toString());
+ } else {
+ AdtPlugin.displayError("Android Virtual Devices Manager", sb.toString());
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the current AVD Manager or null if none has been created yet.
+ * This can happen when the SDK hasn't finished loading or the manager failed to
+ * parse the AVD directory.
+ */
+ private AvdManager getAvdManager() {
+ Sdk sdk = Sdk.getCurrent();
+ if (sdk != null) {
+ return sdk.getAvdManager();
+ }
+ return null;
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/avdmanager/AvdManagerWizard.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/avdmanager/AvdManagerWizard.java
new file mode 100755
index 000000000..20cad5a5b
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/avdmanager/AvdManagerWizard.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008 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.wizards.avdmanager;
+
+import com.android.ide.eclipse.editors.IconFactory;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.IWorkbench;
+
+/**
+ * The "AVD Manager Wizard" provides a quick way to edit AVDs.
+ *
+ * The wizard has one page, {@link AvdManagerListPage}, used to display and edit the AVDs.
+ * In fact the whole UI is not really a wizard. It has just been implemented that way
+ * to get something quick out of the door. We'll need to revisit this when we implement
+ * the final standalone AVD Manager UI and this Wizard will go away.
+ */
+public class AvdManagerWizard extends Wizard implements INewWizard {
+
+ private static final String PROJECT_LOGO_LARGE = "android_large"; //$NON-NLS-1$
+
+ protected static final String MAIN_PAGE_NAME = "avdManagerListPage"; //$NON-NLS-1$
+
+ private AvdManagerListPage mMainPage;
+
+ public void init(IWorkbench workbench, IStructuredSelection selection) {
+ setHelpAvailable(false); // TODO have help
+ setWindowTitle("Android Virtual Devices Manager");
+ setImageDescriptor();
+
+ mMainPage = createMainPage();
+ mMainPage.setTitle("Android Virtual Devices Manager");
+ mMainPage.setDescription("Displays existing Android Virtual Devices. Lets you create new ones or delete existing ones.");
+ }
+
+ /**
+ * Creates the wizard page.
+ *
+ * Please do NOT override this method.
+ *
+ * This is protected so that it can be overridden by unit tests.
+ * However the contract of this class is private and NO ATTEMPT will be made
+ * to maintain compatibility between different versions of the plugin.
+ */
+ protected AvdManagerListPage createMainPage() {
+ return new AvdManagerListPage(MAIN_PAGE_NAME);
+ }
+
+ // -- Methods inherited from org.eclipse.jface.wizard.Wizard --
+ //
+ // The Wizard class implements most defaults and boilerplate code needed by
+ // IWizard
+
+ /**
+ * Adds pages to this wizard.
+ */
+ @Override
+ public void addPages() {
+ addPage(mMainPage);
+ }
+
+ /**
+ * Performs any actions appropriate in response to the user having pressed
+ * the Finish button, or refuse if finishing now is not permitted: here, it does nothing.
+ *
+ * @return True
+ */
+ @Override
+ public boolean performFinish() {
+ return true;
+ }
+
+ /**
+ * Returns an image descriptor for the wizard logo.
+ */
+ private void setImageDescriptor() {
+ ImageDescriptor desc = IconFactory.getInstance().getImageDescriptor(PROJECT_LOGO_LARGE);
+ setDefaultPageImageDescriptor(desc);
+ }
+
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiClassAttributeNode.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiClassAttributeNode.java
index c872b6f31..f5c0cfa71 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiClassAttributeNode.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiClassAttributeNode.java
@@ -463,7 +463,7 @@ public class UiClassAttributeNode extends UiTextAttributeNode {
* Computes and return the {@link IPackageFragmentRoot}s corresponding to the source folders of
* the specified project.
* @param project the project
- * @param b
+ * @param include_containers True to include containers
* @return an array of IPackageFragmentRoot.
*/
private IPackageFragmentRoot[] getPackageFragmentRoots(IProject project,
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
index 35d9bb165..1e6b58537 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
@@ -35,7 +35,6 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.regex.Pattern;
/**
* Main class for the 'android' application.
@@ -50,11 +49,6 @@ class Main {
private final static String[] BOOLEAN_YES_REPLIES = new String[] { "yes", "y" };
private final static String[] BOOLEAN_NO_REPLIES = new String[] { "no", "n" };
-
- /** Regex used to validate characters that compose an AVD name. */
- private final static Pattern RE_AVD_NAME = Pattern.compile("[a-zA-Z0-9._-]+");
- /** List of valid characters for an AVD name. Used for display purposes. */
- private final static String CHARS_AVD_NAME = "a-z A-Z 0-9 . _ -";
/** Path to the SDK folder. This is the parent of {@link #TOOLSDIR}. */
@@ -515,10 +509,10 @@ class Main {
String avdName = mSdkCommandLine.getParamName();
- if (!RE_AVD_NAME.matcher(avdName).matches()) {
+ if (!AvdManager.RE_AVD_NAME.matcher(avdName).matches()) {
errorAndExit(
"AVD name '%1$s' contains invalid characters.\nAllowed characters are: %2$s",
- avdName, CHARS_AVD_NAME);
+ avdName, AvdManager.CHARS_AVD_NAME);
return;
}
@@ -593,18 +587,6 @@ class Main {
hardwareConfig,
removePrevious);
- if (newAvdInfo != null &&
- oldAvdInfo != null &&
- !oldAvdInfo.getPath().equals(newAvdInfo.getPath())) {
- mSdkLog.warning("Removing previous AVD directory at %s", oldAvdInfo.getPath());
- // Remove the old data directory
- File dir = new File(oldAvdInfo.getPath());
- avdManager.recursiveDelete(dir);
- dir.delete();
- // Remove old AVD info from manager
- avdManager.removeAvd(oldAvdInfo);
- }
-
} catch (AndroidLocationException e) {
errorAndExit(e.getMessage());
}
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
index 23b2f26b6..041e8b563 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
@@ -125,7 +125,13 @@ public final class AvdManager {
/**
* Pattern for matching SD Card sizes, e.g. "4K" or "16M".
*/
- private final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("\\d+[MK]?");
+ public final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("\\d+[MK]");
+
+ /** Regex used to validate characters that compose an AVD name. */
+ public final static Pattern RE_AVD_NAME = Pattern.compile("[a-zA-Z0-9._-]+");
+
+ /** List of valid characters for an AVD name. Used for display purposes. */
+ public final static String CHARS_AVD_NAME = "a-z A-Z 0-9 . _ -";
/** An immutable structure describing an Android Virtual Device. */
public static final class AvdInfo {
@@ -301,15 +307,38 @@ public final class AvdManager {
private final ArrayList mAllAvdList = new ArrayList();
private AvdInfo[] mValidAvdList;
private AvdInfo[] mBrokenAvdList;
- private ISdkLog mSdkLog;
private final SdkManager mSdk;
+ /**
+ * TODO remove this field. Each caller should give its own logger to the methods
+ * here instead of relying on a global logger. Otherwise that means that each
+ * caller needs to re-instantiate this just to change the logger, which is plain
+ * ugly.
+ *
+ * We'll revisit this in the final AVD Manager UI for donut.
+ */
+ private ISdkLog mSdkLog;
+
public AvdManager(SdkManager sdk, ISdkLog sdkLog) throws AndroidLocationException {
mSdk = sdk;
mSdkLog = sdkLog;
buildAvdList(mAllAvdList);
}
+ /**
+ * Changes the current {@link ISdkLog}.
+ *
+ * This method should not exist. See comments for {@link #mSdkLog}.
+ *
+ * @return The {@link ISdkLog} that was currently being used.
+ * @see #mSdkLog
+ */
+ public ISdkLog setSdkLog(ISdkLog sdkLog) {
+ ISdkLog oldLogger = mSdkLog;
+ mSdkLog = sdkLog;
+ return oldLogger;
+ }
+
/**
* Returns all the existing AVDs.
* @return a newly allocated array containing all the AVDs.
@@ -405,14 +434,17 @@ public final class AvdManager {
/**
* Creates a new AVD. It is expected that there is no existing AVD with this name already.
+ *
* @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. 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
+ * an existing sdcard image or a sdcard size (\d+, \d+K, \dM).
+ * @param hardwareConfig the hardware setup for the AVD. Can be null to use defaults.
* @param removePrevious If true remove any previous files.
+ * @return The new {@link AvdInfo} in case of success (which has just been added to the
+ * internal list) or null in case of failure.
*/
public AvdInfo createAvd(File avdFolder, String name, IAndroidTarget target,
String skinName, String sdcard, Map hardwareConfig,
@@ -482,7 +514,7 @@ public final class AvdManager {
}
// Now the skin.
- if (skinName == null) {
+ if (skinName == null || skinName.length() == 0) {
skinName = target.getDefaultSkin();
}
@@ -505,7 +537,7 @@ public final class AvdManager {
values.put(AVD_INI_SKIN_NAME, skinName);
}
- if (sdcard != null) {
+ if (sdcard != null && sdcard.length() > 0) {
File sdcardFile = new File(sdcard);
if (sdcardFile.isFile()) {
// sdcard value is an external sdcard, so we put its path into the config.ini
@@ -571,15 +603,33 @@ public final class AvdManager {
}
// create the AvdInfo object, and add it to the list
- AvdInfo avdInfo = new AvdInfo(name, avdFolder.getAbsolutePath(), target.hashString(),
+ AvdInfo newAvdInfo = new AvdInfo(name,
+ avdFolder.getAbsolutePath(),
+ target.hashString(),
target, values);
+
+ AvdInfo oldAvdInfo = getAvd(name, false /*validAvdOnly*/);
synchronized (mAllAvdList) {
- mAllAvdList.add(avdInfo);
+ if (oldAvdInfo != null && removePrevious) {
+ mAllAvdList.remove(oldAvdInfo);
+ }
+ mAllAvdList.add(newAvdInfo);
mValidAvdList = mBrokenAvdList = null;
}
-
- return avdInfo;
+
+ if (removePrevious &&
+ newAvdInfo != null &&
+ oldAvdInfo != null &&
+ !oldAvdInfo.getPath().equals(newAvdInfo.getPath())) {
+ mSdkLog.warning("Removing previous AVD directory at %s", oldAvdInfo.getPath());
+ // Remove the old data directory
+ File dir = new File(oldAvdInfo.getPath());
+ recursiveDelete(dir);
+ dir.delete();
+ }
+
+ return newAvdInfo;
} catch (AndroidLocationException e) {
if (mSdkLog != null) {
mSdkLog.error(e, null);
@@ -724,8 +774,9 @@ public final class AvdManager {
* these operations fail.
*
* @param avdInfo the information on the AVD to delete
+ * @return True if the AVD was deleted with no error.
*/
- public void deleteAvd(AvdInfo avdInfo, ISdkLog log) {
+ public boolean deleteAvd(AvdInfo avdInfo, ISdkLog log) {
try {
boolean error = false;
@@ -758,6 +809,7 @@ public final class AvdManager {
avdInfo.getName());
} else {
log.printf("AVD '%1$s' deleted.\n", avdInfo.getName());
+ return true;
}
} catch (AndroidLocationException e) {
@@ -765,6 +817,7 @@ public final class AvdManager {
} catch (IOException e) {
log.error(e, null);
}
+ return false;
}
/**
@@ -905,10 +958,8 @@ public final class AvdManager {
* Parses an AVD .ini file to create an {@link AvdInfo}.
*
* @param path The path to the AVD .ini file
- * @param acceptError When false, an AVD that fails to load will be discarded and null will be
- * returned. When true, such an AVD will be returned with an error description.
- * @return A new {@link AvdInfo} or null if the file is not valid or null if the AVD is not
- * valid and acceptError is false.
+ * @return A new {@link AvdInfo} with an {@link AvdStatus} indicating whether this AVD is
+ * valid or not.
*/
private AvdInfo parseAvdInfo(File path) {
Map map = SdkManager.parsePropertyFile(path, mSdkLog);
@@ -1012,7 +1063,6 @@ public final class AvdManager {
* @param toolLocation The path to the mksdcard tool.
* @param size The size of the new SD Card, compatible with {@link #SDCARD_SIZE_PATTERN}.
* @param location The path of the new sdcard image file to generate.
- * @param log The logger object, to report errors.
* @return True if the sdcard could be created.
*/
private boolean createSdCard(String toolLocation, String size, String location) {
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java
index 20c211ba7..729741b67 100644
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java
@@ -164,7 +164,7 @@ public final class AvdSelector {
* If the selection is actually changed, this will invoke the selection listener
* (if any) with a null event.
*
- * @param target the target to be selection
+ * @param target the target to be selected. Use null to deselect everything.
* @return true if the target could be selected, false otherwise.
*/
public boolean setSelection(AvdInfo target) {